Fri Feb 10 06:36:03 2012

Asterisk developer's documentation


features.c File Reference

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

#include "asterisk.h"
#include "asterisk/_private.h"
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.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/audiohook.h"
#include "asterisk/global_datastores.h"
#include "asterisk/astobj2.h"
#include "asterisk/cel.h"
#include "asterisk/test.h"

Include dependency graph for features.c:

Go to the source code of this file.

Data Structures

struct  ast_bridge_thread_obj
struct  ast_dial_features
struct  ast_park_call_args
struct  ast_parkinglot
 Structure for parking lots which are put in a container. More...
struct  ast_parkinglot::parkinglot_parklist
struct  feature_group
struct  feature_group_exten
struct  feature_groups
struct  feature_list
struct  park_app_args
struct  parkeduser
 Description of one parked call, added to a list while active, then removed. The list belongs to a parkinglot. More...
struct  parking_dp_context
struct  parking_dp_map
struct  parking_dp_ramp
struct  parking_dp_ramp_map
struct  parking_dp_space_map
struct  parking_dp_spaces
struct  parkinglot_cfg

Defines

#define AST_MAX_WATCHERS   256
#define DEFAULT_ATXFER_CALLBACK_RETRIES   2
#define DEFAULT_ATXFER_DROP_CALL   0
#define DEFAULT_ATXFER_LOOP_DELAY   10000
#define DEFAULT_COMEBACK_CONTEXT   "parkedcallstimeout"
#define DEFAULT_COMEBACK_DIAL_TIME   30
#define DEFAULT_COMEBACK_TO_ORIGIN   1
#define DEFAULT_FEATURE_DIGIT_TIMEOUT   1000
#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER   15000
#define DEFAULT_PARK_EXTENSION   "700"
#define DEFAULT_PARK_TIME   45000
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT   3000
#define FEATURES_COUNT   ARRAY_LEN(builtin_features)
#define HFS_FORMAT   "%-25s %-7s %-7s\n"
#define MAX_DIAL_FEATURE_OPTIONS   30

Enumerations

enum  {
  BRIDGE_OPT_PLAYTONE = (1 << 0), OPT_CALLEE_HANGUP = (1 << 1), OPT_CALLER_HANGUP = (1 << 2), OPT_DURATION_LIMIT = (1 << 3),
  OPT_DURATION_STOP = (1 << 4), OPT_CALLEE_TRANSFER = (1 << 5), OPT_CALLER_TRANSFER = (1 << 6), OPT_CALLEE_MONITOR = (1 << 7),
  OPT_CALLER_MONITOR = (1 << 8), OPT_CALLEE_PARK = (1 << 9), OPT_CALLER_PARK = (1 << 10), OPT_CALLEE_KILL = (1 << 11)
}
enum  { OPT_ARG_DURATION_LIMIT = 0, OPT_ARG_DURATION_STOP, OPT_ARG_ARRAY_SIZE }
enum  ast_park_call_options { AST_PARK_OPT_RINGING = (1 << 0), AST_PARK_OPT_RANDOMIZE = (1 << 1), AST_PARK_OPT_SILENCE = (1 << 2) }
enum  feature_interpret_op { FEATURE_INTERPRET_DETECT, FEATURE_INTERPRET_DO, FEATURE_INTERPRET_CHECK }

Functions

static void __fini_feature_groups (void)
static void __fini_feature_list (void)
static void __init_feature_groups (void)
static void __init_feature_list (void)
static int action_bridge (struct mansession *s, const struct message *m)
 Bridge channels together.
static void add_features_datastores (struct ast_channel *caller, struct ast_channel *callee, struct ast_bridge_config *config)
static int adsi_announce_park (struct ast_channel *chan, char *parkingexten)
 Announce call parking by ADSI.
int ast_bridge_call (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
 bridge the call and set CDR
int ast_bridge_timelimit (struct ast_channel *chan, struct ast_bridge_config *config, char *parse, struct timeval *calldurationlimit)
 parse L option and read associated channel variables to set warning, warning frequency, and timelimit
int ast_can_pickup (struct ast_channel *chan)
 Test if a channel can be picked up.
void ast_channel_log (char *title, struct ast_channel *chan)
int ast_do_pickup (struct ast_channel *chan, struct ast_channel *target)
 Pickup a call target.
int ast_feature_detect (struct ast_channel *chan, struct ast_flags *features, const char *code, struct ast_call_feature *feature)
 detect a feature before bridging
int ast_features_init (void)
int ast_features_reload (void)
 Reload call features from features.conf.
struct ast_call_featureast_find_call_feature (const char *name)
 look for a call feature entry by its sname
int ast_masq_park_call (struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
 Park a call via a masqueraded channel.
int ast_masq_park_call_exten (struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout)
 Park a call via a masqueraded channel.
int ast_park_call (struct ast_channel *park_me, struct ast_channel *parker, int timeout, const char *park_exten, int *extout)
 Park a call and read back parked location.
int ast_park_call_exten (struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout)
 Park a call and read back parked location.
int ast_parking_ext_valid (const char *exten_str, struct ast_channel *chan, const char *context)
 Determine if parking extension exists in a given context.
int ast_pickup_call (struct ast_channel *chan)
 Pickup a call.
const char * ast_pickup_ext (void)
 Determine system call pickup extension.
void ast_rdlock_call_features (void)
void ast_register_feature (struct ast_call_feature *feature)
 register new feature into feature_list
void ast_unlock_call_features (void)
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 void ast_unregister_groups (void)
 Remove all feature groups in the list.
static void atxfer_fail_cleanup (struct ast_channel *transferee, struct ast_channel *transferer, struct ast_party_connected_line *connected_line)
static void * bridge_call_thread (void *data)
 bridge the call
static void bridge_call_thread_launch (void *data)
 create thread for the parked call
static int bridge_exec (struct ast_channel *chan, const char *data)
 Bridge channels.
static struct parking_dp_contextbuild_dialplan_useage_context (struct ast_parkinglot *lot)
static int build_dialplan_useage_map (struct parking_dp_map *usage_map, int complain)
static struct parking_dp_rampbuild_dialplan_useage_ramp (const char *exten, int exclusive)
static struct parking_dp_spacesbuild_dialplan_useage_spaces (int start, int stop)
static struct ast_parkinglotbuild_parkinglot (const char *pl_name, struct ast_variable *var)
 Build parkinglot from configuration and chain it in if it doesn't already exist.
static int builtin_atxfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
 Attended transfer.
static int builtin_automixmonitor (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
static int builtin_automonitor (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
 Monitor a channel by DTMF.
static int builtin_blindtransfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
 Blind transfer user to another extension.
static int builtin_disconnect (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
static int builtin_parkcall (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
 support routing for one touch call parking
static char * callback_dialoptions (struct ast_flags *features_callee, struct ast_flags *features_caller, char *options, size_t len)
static int check_compat (struct ast_channel *c, struct ast_channel *newchan)
 make channels compatible
static void check_goto_on_transfer (struct ast_channel *chan)
 Check goto on transfer.
static void clear_dialed_interfaces (struct ast_channel *chan)
static struct ast_parkinglotcopy_parkinglot (const char *name, const struct ast_parkinglot *parkinglot)
 Copy parkinglot and store it with new name.
static struct ast_parkinglotcreate_dynamic_parkinglot (const char *name, struct ast_channel *chan)
static struct ast_parkinglotcreate_parkinglot (const char *name)
 Allocate parking lot structure.
static void destroy_dialplan_usage_context (struct parking_dp_context *doomed)
static void destroy_dialplan_usage_map (struct parking_dp_map *doomed)
static void destroy_space (const char *context, int space)
static void dial_features_destroy (void *data)
static void * dial_features_duplicate (void *data)
static int dialplan_usage_add_parkinglot (struct parking_dp_map *usage_map, struct ast_parkinglot *lot, int complain)
static int dialplan_usage_add_parkinglot_data (struct parking_dp_context *ctx_node, struct ast_parkinglot *lot, int complain)
static void do_bridge_masquerade (struct ast_channel *chan, struct ast_channel *tmpchan)
 Actual bridge.
static void * do_parking_thread (void *ignore)
 Take care of parked calls and unpark them if needed.
static int feature_check (struct ast_channel *chan, struct ast_flags *features, char *code)
 Check if a feature exists.
static int feature_exec_app (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)
 exec an app by feature
static int feature_interpret (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense)
 Check the dynamic features.
static int feature_interpret_helper (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, char *dynamic_features_buf, struct ast_flags *features, feature_interpret_op operation, struct ast_call_feature *feature)
 Helper function for feature_interpret and ast_feature_detect.
static struct ast_channelfeature_request_and_dial (struct ast_channel *caller, const char *caller_name, struct ast_channel *requestor, struct ast_channel *transferee, const char *type, struct ast_format_cap *cap, const char *addr, int timeout, int *outstate, const char *language)
static int find_channel_by_group (void *obj, void *arg, void *data, int flags)
static struct ast_call_featurefind_dynamic_feature (const char *name)
 find a call feature by name
static struct feature_groupfind_group (const char *name)
 Find a group by name.
static struct ast_parkinglotfind_parkinglot (const char *name)
 Find parkinglot by name.
static const char * findparkinglotname (struct ast_channel *chan)
 Find parking lot name from channel.
static int finishup (struct ast_channel *chan)
static struct ast_extenget_parking_exten (const char *exten_str, struct ast_channel *chan, const char *context)
static char * handle_feature_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command to list configured features.
static char * handle_features_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_parkedcalls (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command to list parked calls.
static int load_config (int reload)
static int manage_parked_call (struct parkeduser *pu, const struct pollfd *pfds, int nfds, struct pollfd **new_pfds, int *new_nfds, int *ms)
static void manage_parkinglot (struct ast_parkinglot *curlot, const struct pollfd *pfds, int nfds, struct pollfd **new_pfds, int *new_nfds, int *ms)
 Run management on parkinglots, called once per parkinglot.
static int manager_park (struct mansession *s, const struct message *m)
 Create manager event for parked calls.
static int manager_parking_status (struct mansession *s, const struct message *m)
 Dump parking lot status.
static int masq_park_call (struct ast_channel *rchan, struct ast_channel *peer, struct ast_park_call_args *args)
 Park call via masqueraded channel and announce parking spot on peer channel.
static enum ast_device_state metermaidstate (const char *data)
 metermaids callback from devicestate.c
static void notify_metermaids (const char *exten, char *context, enum ast_device_state state)
 Notify metermaids that we've changed an extension.
static void park_add_hints (const char *context, int start, int stop)
 Add parking hints for all defined parking spaces.
static int park_call_exec (struct ast_channel *chan, const char *data)
 Park a call.
static int park_call_full (struct ast_channel *chan, struct ast_channel *peer, struct ast_park_call_args *args)
static void park_space_abort (struct parkeduser *pu)
static struct parkeduserpark_space_reserve (struct ast_channel *park_me, struct ast_channel *parker, struct ast_park_call_args *args)
static int parked_call_exec (struct ast_channel *chan, const char *data)
 Pickup parked call.
static int parkinglot_activate (struct ast_parkinglot *parkinglot)
static int parkinglot_activate_cb (void *obj, void *arg, int flags)
static struct ast_parkinglotparkinglot_addref (struct ast_parkinglot *parkinglot)
static int parkinglot_cmp_cb (void *obj, void *arg, int flags)
static int parkinglot_config_read (const char *pl_name, struct parkinglot_cfg *cfg, struct ast_variable *var)
static void parkinglot_destroy (void *obj)
 Destroy a parking lot.
static void parkinglot_feature_flag_cfg (const char *pl_name, int *param, struct ast_variable *var)
static int parkinglot_hash_cb (const void *obj, const int flags)
static int parkinglot_is_marked_cb (void *obj, void *arg, int flags)
static int parkinglot_markall_cb (void *obj, void *arg, int flags)
static void parkinglot_unref (struct ast_parkinglot *parkinglot)
 Unreference parkinglot object.
static struct ast_cdrpick_unlocked_cdr (struct ast_cdr *cdr)
 return the first unlocked cdr in a possible chain
static int play_message_in_bridged_call (struct ast_channel *caller_chan, struct ast_channel *callee_chan, const char *audiofile)
 Play message to both caller and callee in bridged call, plays synchronously, autoservicing the other channel during the message, so please don't use this for very long messages.
static int play_message_on_chan (struct ast_channel *play_to, struct ast_channel *other, const char *msg, const char *audiofile)
static int play_message_to_chans (struct ast_channel *left, struct ast_channel *right, int which, const char *msg, const char *audiofile)
static void post_manager_event (const char *s, struct parkeduser *pu)
 Output parking event to manager.
static void process_applicationmap_line (struct ast_variable *var)
static int process_config (struct ast_config *cfg)
static const char * real_ctx (struct ast_channel *transferer, struct ast_channel *transferee)
 Find the context for the transfer.
static struct feature_groupregister_group (const char *fgname)
 Add new feature group.
static void register_group_feature (struct feature_group *fg, const char *exten, struct ast_call_feature *feature)
 Add feature to group.
static int remap_feature (const char *name, const char *value)
static void remove_dead_context_usage (const char *context, struct parking_dp_context *old_ctx, struct parking_dp_context *new_ctx)
static void remove_dead_dialplan_useage (struct parking_dp_map *old_map, struct parking_dp_map *new_map)
static void remove_dead_ramp_usage (const char *context, struct parking_dp_ramp_map *old_ramps, struct parking_dp_ramp_map *new_ramps)
static void remove_dead_spaces_usage (const char *context, struct parking_dp_space_map *old_spaces, struct parking_dp_space_map *new_spaces, void(*destroy_space)(const char *context, int space))
static void remove_exten_if_exist (const char *context, const char *exten, int priority)
static void set_bridge_features_on_config (struct ast_bridge_config *config, const char *features)
static void set_c_e_p (struct ast_channel *chan, const char *context, const char *ext, int pri)
 store context, extension and priority
static void set_config_flags (struct ast_channel *chan, 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 void unmap_features (void)
static int usage_context_add_ramp (struct parking_dp_ramp_map *ramp_map, const char *exten, int exclusive, struct ast_parkinglot *lot, int complain)
static int usage_context_add_spaces (struct parking_dp_space_map *space_map, int start, int stop, struct ast_parkinglot *lot, int complain)
static int xfer_park_call_helper (struct ast_channel *park_me, struct ast_channel *parker, struct ast_exten *park_exten)

Variables

static int adsipark
static char * app_bridge = "Bridge"
static unsigned int atxfercallbackretries
static unsigned int atxferdropcall
static unsigned int atxferloopdelay
static int atxfernoanswertimeout
static struct ast_app_option bridge_exec_options [128] = { [ 'p' ] = { .flag = BRIDGE_OPT_PLAYTONE }, [ 'h' ] = { .flag = OPT_CALLEE_HANGUP }, [ 'H' ] = { .flag = OPT_CALLER_HANGUP }, [ 'k' ] = { .flag = OPT_CALLEE_PARK }, [ 'K' ] = { .flag = OPT_CALLER_PARK }, [ 'L' ] = { .flag = OPT_DURATION_LIMIT , .arg_index = OPT_ARG_DURATION_LIMIT + 1 }, [ 'S' ] = { .flag = OPT_DURATION_STOP , .arg_index = OPT_ARG_DURATION_STOP + 1 }, [ 't' ] = { .flag = OPT_CALLEE_TRANSFER }, [ 'T' ] = { .flag = OPT_CALLER_TRANSFER }, [ 'w' ] = { .flag = OPT_CALLEE_MONITOR }, [ 'W' ] = { .flag = OPT_CALLER_MONITOR }, [ 'x' ] = { .flag = OPT_CALLEE_KILL }, }
static struct ast_call_feature builtin_features []
static struct ast_cli_entry cli_features []
static char courtesytone [256]
static struct ast_parkinglotdefault_parkinglot
 Default parking lot.
static struct ast_datastore_info dial_features_info
static int featuredigittimeout
static ast_rwlock_t features_lock = { {0} , NULL, 1 }
static ast_mutex_t features_reload_lock = { PTHREAD_MUTEX_INITIALIZER , NULL, 1 }
static int force_reload_load
static struct ast_appmixmonitor_app = NULL
static int mixmonitor_ok = 1
static struct ast_appmonitor_app = NULL
static int monitor_ok = 1
static struct ast_app_option park_call_options [128] = { [ 'r' ] = { .flag = AST_PARK_OPT_RINGING }, [ 'R' ] = { .flag = AST_PARK_OPT_RANDOMIZE }, [ 's' ] = { .flag = AST_PARK_OPT_SILENCE }, }
static const char * parkcall = "Park"
static const char * parkedcall = "ParkedCall"
static int parkeddynamic = 0
static int parkedplay = 0
static char parking_con_dial [] = "park-dial"
 Context for parking dialback to parker.
static pthread_t parking_thread
static struct parkinglot_cfg parkinglot_cfg_default
static struct parkinglot_cfg parkinglot_cfg_default_default
static struct ao2_containerparkinglots
 The configured parking lots container. Always at least one - the default parking lot.
static struct ast_datastore_info pickup_active
static char pickup_ext [AST_MAX_EXTENSION]
static char pickupfailsound [256]
static char pickupsound [256]
static char * registrar = "features"
static struct ast_appstopmixmonitor_app = NULL
static int stopmixmonitor_ok = 1
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 features.c.


Define Documentation

#define AST_MAX_WATCHERS   256

Definition at line 395 of file features.c.

#define DEFAULT_ATXFER_CALLBACK_RETRIES   2

Definition at line 390 of file features.c.

Referenced by process_config().

#define DEFAULT_ATXFER_DROP_CALL   0

Do not drop call.

Definition at line 388 of file features.c.

Referenced by process_config().

#define DEFAULT_ATXFER_LOOP_DELAY   10000

ms

Definition at line 389 of file features.c.

Referenced by process_config().

#define DEFAULT_COMEBACK_CONTEXT   "parkedcallstimeout"

Definition at line 391 of file features.c.

#define DEFAULT_COMEBACK_DIAL_TIME   30

Definition at line 393 of file features.c.

Referenced by parkinglot_config_read().

#define DEFAULT_COMEBACK_TO_ORIGIN   1

Definition at line 392 of file features.c.

#define DEFAULT_FEATURE_DIGIT_TIMEOUT   1000

ms

Definition at line 386 of file features.c.

Referenced by process_config().

#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER   15000

ms

Definition at line 387 of file features.c.

Referenced by process_config().

#define DEFAULT_PARK_EXTENSION   "700"

Definition at line 384 of file features.c.

#define DEFAULT_PARK_TIME   45000

ms

Definition at line 383 of file features.c.

#define DEFAULT_TRANSFER_DIGIT_TIMEOUT   3000

ms

Definition at line 385 of file features.c.

Referenced by process_config().

#define FEATURES_COUNT   ARRAY_LEN(builtin_features)

#define HFS_FORMAT   "%-25s %-7s %-7s\n"

Referenced by handle_feature_show().

#define MAX_DIAL_FEATURE_OPTIONS   30

Definition at line 396 of file features.c.

Referenced by manage_parked_call().


Enumeration Type Documentation

anonymous enum

Enumerator:
BRIDGE_OPT_PLAYTONE 
OPT_CALLEE_HANGUP 
OPT_CALLER_HANGUP 
OPT_DURATION_LIMIT 
OPT_DURATION_STOP 
OPT_CALLEE_TRANSFER 
OPT_CALLER_TRANSFER 
OPT_CALLEE_MONITOR 
OPT_CALLER_MONITOR 
OPT_CALLEE_PARK 
OPT_CALLER_PARK 
OPT_CALLEE_KILL 

Definition at line 7353 of file features.c.

07353      {
07354    BRIDGE_OPT_PLAYTONE = (1 << 0),
07355    OPT_CALLEE_HANGUP =  (1 << 1),
07356    OPT_CALLER_HANGUP =  (1 << 2),
07357    OPT_DURATION_LIMIT = (1 << 3),
07358    OPT_DURATION_STOP =  (1 << 4),
07359    OPT_CALLEE_TRANSFER = (1 << 5),
07360    OPT_CALLER_TRANSFER = (1 << 6),
07361    OPT_CALLEE_MONITOR = (1 << 7),
07362    OPT_CALLER_MONITOR = (1 << 8),
07363    OPT_CALLEE_PARK = (1 << 9),
07364    OPT_CALLER_PARK = (1 << 10),
07365    OPT_CALLEE_KILL = (1 << 11),
07366 };

anonymous enum

Enumerator:
OPT_ARG_DURATION_LIMIT 
OPT_ARG_DURATION_STOP 
OPT_ARG_ARRAY_SIZE 

Definition at line 7368 of file features.c.

07368      {
07369    OPT_ARG_DURATION_LIMIT = 0,
07370    OPT_ARG_DURATION_STOP,
07371    /* note: this entry _MUST_ be the last one in the enum */
07372    OPT_ARG_ARRAY_SIZE,
07373 };

Options to pass to park_call_full

Enumerator:
AST_PARK_OPT_RINGING  Provide ringing to the parked caller instead of music on hold
AST_PARK_OPT_RANDOMIZE  Randomly choose a parking spot for the caller instead of choosing the first one that is available.
AST_PARK_OPT_SILENCE  Do not announce the parking number

Definition at line 1043 of file features.c.

01043                            {
01044    /*! Provide ringing to the parked caller instead of music on hold */
01045    AST_PARK_OPT_RINGING =   (1 << 0),
01046    /*! Randomly choose a parking spot for the caller instead of choosing
01047     *  the first one that is available. */
01048    AST_PARK_OPT_RANDOMIZE = (1 << 1),
01049    /*! Do not announce the parking number */
01050    AST_PARK_OPT_SILENCE = (1 << 2),
01051 };

Enumerator:
FEATURE_INTERPRET_DETECT 
FEATURE_INTERPRET_DO 
FEATURE_INTERPRET_CHECK 

Definition at line 416 of file features.c.

00416              {
00417    FEATURE_INTERPRET_DETECT, /* Used by ast_feature_detect */
00418    FEATURE_INTERPRET_DO,     /* Used by feature_interpret */
00419    FEATURE_INTERPRET_CHECK,  /* Used by feature_check */
00420 } feature_interpret_op;


Function Documentation

static void __fini_feature_groups ( void   )  [static]

Definition at line 414 of file features.c.

00416 {

static void __fini_feature_list ( void   )  [static]

Definition at line 2850 of file features.c.

02854 {

static void __init_feature_groups ( void   )  [static]

Definition at line 414 of file features.c.

00416 {

static void __init_feature_list ( void   )  [static]

Definition at line 2850 of file features.c.

02854 {

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

Bridge channels together.

Parameters:
s 
m Make sure valid channels were specified, send errors if any of the channels could not be found/locked, answer channels if needed, create the placeholder channels and grab the other channels make the channels compatible, send error if we fail doing so setup the bridge thread object and start the bridge.
Return values:
0 on success or on incorrect use.
1 on failure to bridge channels.

Definition at line 6843 of file features.c.

References ast_channel::_state, ast_answer(), ast_calloc, ast_channel_alloc, ast_channel_get_by_name_prefix(), ast_channel_language(), ast_channel_linkedid(), ast_channel_make_compatible(), ast_channel_name(), ast_channel_unref, ast_hangup(), ast_log(), ast_manager_event_multichan, AST_STATE_DOWN, AST_STATE_UP, ast_streamfile(), ast_strlen_zero(), ast_true(), ast_waitstream(), astman_get_header(), astman_send_ack(), astman_send_error(), bridge_call_thread_launch(), ast_bridge_thread_obj::chan, do_bridge_masquerade(), errno, EVENT_FLAG_CALL, LOG_WARNING, ast_bridge_thread_obj::peer, playtone(), ast_bridge_thread_obj::return_to_pbx, and xfersound.

Referenced by ast_features_init().

06844 {
06845    const char *channela = astman_get_header(m, "Channel1");
06846    const char *channelb = astman_get_header(m, "Channel2");
06847    const char *playtone = astman_get_header(m, "Tone");
06848    struct ast_channel *chana = NULL, *chanb = NULL, *chans[2];
06849    struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
06850    struct ast_bridge_thread_obj *tobj = NULL;
06851 
06852    /* make sure valid channels were specified */
06853    if (ast_strlen_zero(channela) || ast_strlen_zero(channelb)) {
06854       astman_send_error(s, m, "Missing channel parameter in request");
06855       return 0;
06856    }
06857 
06858    /* Start with chana */
06859    chana = ast_channel_get_by_name_prefix(channela, strlen(channela));
06860 
06861    /* send errors if any of the channels could not be found/locked */
06862    if (!chana) {
06863       char buf[256];
06864       snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
06865       astman_send_error(s, m, buf);
06866       return 0;
06867    }
06868 
06869    /* Answer the channels if needed */
06870    if (chana->_state != AST_STATE_UP)
06871       ast_answer(chana);
06872 
06873    /* create the placeholder channels and grab the other channels */
06874    if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
06875       NULL, NULL, ast_channel_linkedid(chana), 0, "Bridge/%s", ast_channel_name(chana)))) {
06876       astman_send_error(s, m, "Unable to create temporary channel!");
06877       chana = ast_channel_unref(chana);
06878       return 1;
06879    }
06880 
06881    do_bridge_masquerade(chana, tmpchana);
06882 
06883    chana = ast_channel_unref(chana);
06884 
06885    /* now do chanb */
06886    chanb = ast_channel_get_by_name_prefix(channelb, strlen(channelb));
06887    /* send errors if any of the channels could not be found/locked */
06888    if (!chanb) {
06889       char buf[256];
06890       snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
06891       ast_hangup(tmpchana);
06892       astman_send_error(s, m, buf);
06893       return 0;
06894    }
06895 
06896    /* Answer the channels if needed */
06897    if (chanb->_state != AST_STATE_UP)
06898       ast_answer(chanb);
06899 
06900    /* create the placeholder channels and grab the other channels */
06901    if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
06902       NULL, NULL, ast_channel_linkedid(chanb), 0, "Bridge/%s", ast_channel_name(chanb)))) {
06903       astman_send_error(s, m, "Unable to create temporary channels!");
06904       ast_hangup(tmpchana);
06905       chanb = ast_channel_unref(chanb);
06906       return 1;
06907    }
06908 
06909    do_bridge_masquerade(chanb, tmpchanb);
06910 
06911    chanb = ast_channel_unref(chanb);
06912 
06913    /* make the channels compatible, send error if we fail doing so */
06914    if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
06915       ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb));
06916       astman_send_error(s, m, "Could not make channels compatible for manager bridge");
06917       ast_hangup(tmpchana);
06918       ast_hangup(tmpchanb);
06919       return 1;
06920    }
06921 
06922    /* setup the bridge thread object and start the bridge */
06923    if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
06924       ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb), strerror(errno));
06925       astman_send_error(s, m, "Unable to spawn a new bridge thread");
06926       ast_hangup(tmpchana);
06927       ast_hangup(tmpchanb);
06928       return 1;
06929    }
06930 
06931    tobj->chan = tmpchana;
06932    tobj->peer = tmpchanb;
06933    tobj->return_to_pbx = 1;
06934 
06935    if (ast_true(playtone)) {
06936       if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, ast_channel_language(tmpchanb))) {
06937          if (ast_waitstream(tmpchanb, "") < 0)
06938             ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", ast_channel_name(tmpchanb));
06939       }
06940    }
06941 
06942    chans[0] = tmpchana;
06943    chans[1] = tmpchanb;
06944 
06945    ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeAction", 2, chans,
06946             "Response: Success\r\n"
06947             "Channel1: %s\r\n"
06948             "Channel2: %s\r\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb));
06949 
06950    bridge_call_thread_launch(tobj);
06951 
06952    astman_send_ack(s, m, "Launched bridge thread with success");
06953 
06954    return 0;
06955 }

static void add_features_datastores ( struct ast_channel caller,
struct ast_channel callee,
struct ast_bridge_config config 
) [static]

Definition at line 3765 of file features.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_copy_flags, ast_datastore_alloc, ast_datastore_free(), AST_FLAGS_ALL, ast_log(), ast_datastore::data, DATASTORE_INHERIT_FOREVER, dial_features_info, ast_dial_features::features_callee, ast_bridge_config::features_callee, ast_dial_features::features_caller, ast_bridge_config::features_caller, ast_datastore::inheritance, ast_dial_features::is_caller, and LOG_WARNING.

Referenced by ast_bridge_call().

03766 {
03767    struct ast_datastore *ds_callee_features = NULL, *ds_caller_features = NULL;
03768    struct ast_dial_features *callee_features = NULL, *caller_features = NULL;
03769 
03770    ast_channel_lock(caller);
03771    ds_caller_features = ast_channel_datastore_find(caller, &dial_features_info, NULL);
03772    ast_channel_unlock(caller);
03773    if (!ds_caller_features) {
03774       if (!(ds_caller_features = ast_datastore_alloc(&dial_features_info, NULL))) {
03775          ast_log(LOG_WARNING, "Unable to create channel datastore for caller features. Aborting!\n");
03776          return;
03777       }
03778       if (!(caller_features = ast_calloc(1, sizeof(*caller_features)))) {
03779          ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n");
03780          ast_datastore_free(ds_caller_features);
03781          return;
03782       }
03783       ds_caller_features->inheritance = DATASTORE_INHERIT_FOREVER;
03784       caller_features->is_caller = 1;
03785       ast_copy_flags(&(caller_features->features_callee), &(config->features_callee), AST_FLAGS_ALL);
03786       ast_copy_flags(&(caller_features->features_caller), &(config->features_caller), AST_FLAGS_ALL);
03787       ds_caller_features->data = caller_features;
03788       ast_channel_lock(caller);
03789       ast_channel_datastore_add(caller, ds_caller_features);
03790       ast_channel_unlock(caller);
03791    } else {
03792       /* If we don't return here, then when we do a builtin_atxfer we will copy the disconnect
03793        * flags over from the atxfer to the caller */
03794       return;
03795    }
03796 
03797    ast_channel_lock(callee);
03798    ds_callee_features = ast_channel_datastore_find(callee, &dial_features_info, NULL);
03799    ast_channel_unlock(callee);
03800    if (!ds_callee_features) {
03801       if (!(ds_callee_features = ast_datastore_alloc(&dial_features_info, NULL))) {
03802          ast_log(LOG_WARNING, "Unable to create channel datastore for callee features. Aborting!\n");
03803          return;
03804       }
03805       if (!(callee_features = ast_calloc(1, sizeof(*callee_features)))) {
03806          ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n");
03807          ast_datastore_free(ds_callee_features);
03808          return;
03809       }
03810       ds_callee_features->inheritance = DATASTORE_INHERIT_FOREVER;
03811       callee_features->is_caller = 0;
03812       ast_copy_flags(&(callee_features->features_callee), &(config->features_caller), AST_FLAGS_ALL);
03813       ast_copy_flags(&(callee_features->features_caller), &(config->features_callee), AST_FLAGS_ALL);
03814       ds_callee_features->data = callee_features;
03815       ast_channel_lock(callee);
03816       ast_channel_datastore_add(callee, ds_callee_features);
03817       ast_channel_unlock(callee);
03818    }
03819 
03820    return;
03821 }

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

Announce call parking by ADSI.

Parameters:
chan .
parkingexten . Create message to show for ADSI, display message.
Return values:
0 on success.
-1 on failure.

Definition at line 981 of file features.c.

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

Referenced by park_call_full().

00982 {
00983    int res;
00984    int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
00985    char tmp[256];
00986    char *message[5] = {NULL, NULL, NULL, NULL, NULL};
00987 
00988    snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
00989    message[0] = tmp;
00990    res = ast_adsi_load_session(chan, NULL, 0, 1);
00991    if (res == -1)
00992       return res;
00993    return ast_adsi_print(chan, message, justify, 1);
00994 }

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

bridge the call and set CDR

Bridge a call, optionally allowing redirection.

Parameters:
chan The bridge considers this channel the caller.
peer The bridge considers this channel the callee.
config Configuration for this bridge.
Set start time, check for two channels,check if monitor on check for feature activation, create new CDR
Return values:
res on success.
-1 on failure to bridge.

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 3851 of file features.c.

References ast_channel::_softhangup, ast_channel::_state, ast_cdr::accountcode, add_features_datastores(), ast_channel::amaflags, ast_cdr::amaflags, ast_cdr::answer, ast_channel::appl, AST_BRIDGE_RETRY, ast_bridged_channel(), ast_cdr_alloc(), ast_cdr_answer(), AST_CDR_ANSWERED, ast_cdr_appenduserfield(), ast_cdr_detach(), ast_cdr_discard(), ast_cdr_dup_unique_swap(), ast_cdr_end(), AST_CDR_FLAG_BRIDGED, AST_CDR_FLAG_DIALED, AST_CDR_FLAG_MAIN, AST_CDR_FLAG_POST_DISABLED, AST_CDR_NULL, ast_cdr_setaccount(), ast_cdr_setanswer(), ast_cdr_setcid(), ast_cdr_setdisposition(), ast_cdr_setuserfield(), ast_cdr_specialized_reset(), ast_cdr_start(), ast_cdr_update(), AST_CEL_BRIDGE_END, AST_CEL_BRIDGE_START, ast_cel_report_event(), ast_channel_accountcode(), ast_channel_bridge(), ast_channel_connected_line_macro(), ast_channel_get_by_name(), ast_channel_lock, ast_channel_lock_both, ast_channel_log(), ast_channel_name(), AST_CHANNEL_NAME, ast_channel_redirecting_macro(), ast_channel_set_linkgroup(), ast_channel_setoption(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_unref, ast_check_hangup(), ast_clear_flag, AST_CONTROL_AOC, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_MCID, AST_CONTROL_OPTION, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_copy_string(), ast_debug, ast_default_amaflags, ast_dtmf_stream(), ast_exists_extension(), AST_FEATURE_NO_H_EXTEN, AST_FEATURE_RETURN_PASSDIGITS, AST_FEATURE_RETURN_SUCCESS, AST_FEATURE_WARNING_ACTIVE, AST_FLAG_BRIDGE_HANGUP_DONT, AST_FLAG_BRIDGE_HANGUP_RUN, AST_FLAG_IN_AUTOLOOP, AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF_BEGIN, AST_FRAME_DTMF_END, ast_frfree, ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_opt_end_cdr_before_h_exten, ast_opt_transmit_silence, AST_OPTION_AUDIO_MODE, AST_OPTION_DIGIT_DETECT, AST_OPTION_FAX_DETECT, AST_OPTION_FLAG_REQUEST, AST_OPTION_RELAXDTMF, AST_OPTION_TDD, AST_OPTION_TONE_VERIFY, ast_raw_answer(), ast_set2_flag, ast_set_flag, ast_softhangup(), AST_SOFTHANGUP_APPUNLOAD, AST_SOFTHANGUP_ASYNCGOTO, AST_SOFTHANGUP_UNBRIDGE, ast_spawn_extension(), AST_STATE_RINGING, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_tvcmp(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), ast_verb, ast_write(), ast_channel::caller, ast_channel::cdr, ast_cdr::channel, clear_dialed_interfaces(), ast_channel::context, ast_option_header::data, ast_frame::data, ast_channel::data, ast_frame::datalen, ast_cdr::dcontext, ast_cdr::disposition, ast_cdr::dst, ast_cdr::dstchannel, ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, ast_channel::exten, f, feature_check(), feature_interpret(), FEATURE_MAX_LEN, FEATURE_SENSE_CHAN, FEATURE_SENSE_PEER, ast_bridge_config::feature_start_time, ast_bridge_config::feature_timer, featuredigittimeout, ast_bridge_config::features_callee, ast_bridge_config::features_caller, ast_frame::frametype, ast_party_caller::id, ast_frame_subclass::integer, ast_cdr::lastapp, ast_cdr::lastdata, ast_frame::len, LOG_DEBUG, LOG_WARNING, ast_channel::macrocontext, monitor_app, monitor_ok, ast_cdr::next, ast_party_id::number, option_debug, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pick_unlocked_cdr(), ast_channel::priority, ast_frame::ptr, S_COR, S_OR, set_bridge_features_on_config(), set_config_flags(), ast_cdr::start, ast_party_number::str, ast_frame::subclass, ast_bridge_config::timelimit, ast_cdr::uniqueid, ast_cdr::userfield, ast_party_number::valid, and ast_channel::visible_indication.

Referenced by app_exec(), bridge_call_thread(), bridge_exec(), builtin_atxfer(), dial_exec_full(), parked_call_exec(), and try_calling().

03852 {
03853    /* Copy voice back and forth between the two channels.  Give the peer
03854       the ability to transfer calls with '#<extension' syntax. */
03855    struct ast_frame *f;
03856    struct ast_channel *who;
03857    char chan_featurecode[FEATURE_MAX_LEN + 1]="";
03858    char peer_featurecode[FEATURE_MAX_LEN + 1]="";
03859    char orig_channame[AST_CHANNEL_NAME];
03860    char orig_peername[AST_CHANNEL_NAME];
03861    int res;
03862    int diff;
03863    int hasfeatures=0;
03864    int hadfeatures=0;
03865    int autoloopflag;
03866    int sendingdtmfdigit = 0;
03867    int we_disabled_peer_cdr = 0;
03868    struct ast_option_header *aoh;
03869    struct ast_cdr *bridge_cdr = NULL;
03870    struct ast_cdr *chan_cdr = chan->cdr; /* the proper chan cdr, if there are forked cdrs */
03871    struct ast_cdr *peer_cdr = peer->cdr; /* the proper chan cdr, if there are forked cdrs */
03872    struct ast_cdr *new_chan_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
03873    struct ast_cdr *new_peer_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
03874    struct ast_silence_generator *silgen = NULL;
03875    const char *h_context;
03876 
03877    pbx_builtin_setvar_helper(chan, "BRIDGEPEER", ast_channel_name(peer));
03878    pbx_builtin_setvar_helper(peer, "BRIDGEPEER", ast_channel_name(chan));
03879 
03880    set_bridge_features_on_config(config, pbx_builtin_getvar_helper(chan, "BRIDGE_FEATURES"));
03881    add_features_datastores(chan, peer, config);
03882 
03883    /* This is an interesting case.  One example is if a ringing channel gets redirected to
03884     * an extension that picks up a parked call.  This will make sure that the call taken
03885     * out of parking gets told that the channel it just got bridged to is still ringing. */
03886    if (chan->_state == AST_STATE_RINGING && peer->visible_indication != AST_CONTROL_RINGING) {
03887       ast_indicate(peer, AST_CONTROL_RINGING);
03888    }
03889 
03890    if (monitor_ok) {
03891       const char *monitor_exec;
03892       struct ast_channel *src = NULL;
03893       if (!monitor_app) {
03894          if (!(monitor_app = pbx_findapp("Monitor")))
03895             monitor_ok=0;
03896       }
03897       if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 
03898          src = chan;
03899       else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
03900          src = peer;
03901       if (monitor_app && src) {
03902          char *tmp = ast_strdupa(monitor_exec);
03903          pbx_exec(src, monitor_app, tmp);
03904       }
03905    }
03906 
03907    set_config_flags(chan, config);
03908 
03909    /* Answer if need be */
03910    if (chan->_state != AST_STATE_UP) {
03911       if (ast_raw_answer(chan, 1)) {
03912          return -1;
03913       }
03914    }
03915 
03916 #ifdef FOR_DEBUG
03917    /* show the two channels and cdrs involved in the bridge for debug & devel purposes */
03918    ast_channel_log("Pre-bridge CHAN Channel info", chan);
03919    ast_channel_log("Pre-bridge PEER Channel info", peer);
03920 #endif
03921    /* two channels are being marked as linked here */
03922    ast_channel_set_linkgroup(chan,peer);
03923 
03924    /* copy the userfield from the B-leg to A-leg if applicable */
03925    if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
03926       char tmp[256];
03927 
03928       ast_channel_lock(chan);
03929       if (!ast_strlen_zero(chan->cdr->userfield)) {
03930          snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield);
03931          ast_cdr_appenduserfield(chan, tmp);
03932       } else {
03933          ast_cdr_setuserfield(chan, peer->cdr->userfield);
03934       }
03935       ast_channel_unlock(chan);
03936       /* Don't delete the CDR; just disable it. */
03937       ast_set_flag(peer->cdr, AST_CDR_FLAG_POST_DISABLED);
03938       we_disabled_peer_cdr = 1;
03939    }
03940    ast_copy_string(orig_channame,ast_channel_name(chan),sizeof(orig_channame));
03941    ast_copy_string(orig_peername,ast_channel_name(peer),sizeof(orig_peername));
03942 
03943    if (!chan_cdr || (chan_cdr && !ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED))) {
03944       ast_channel_lock_both(chan, peer);
03945       if (chan_cdr) {
03946          ast_set_flag(chan_cdr, AST_CDR_FLAG_MAIN);
03947          ast_cdr_update(chan);
03948          bridge_cdr = ast_cdr_dup_unique_swap(chan_cdr);
03949          /* rip any forked CDR's off of the chan_cdr and attach
03950           * them to the bridge_cdr instead */
03951          bridge_cdr->next = chan_cdr->next;
03952          chan_cdr->next = NULL;
03953          ast_copy_string(bridge_cdr->lastapp, S_OR(chan->appl, ""), sizeof(bridge_cdr->lastapp));
03954          ast_copy_string(bridge_cdr->lastdata, S_OR(chan->data, ""), sizeof(bridge_cdr->lastdata));
03955          if (peer_cdr && !ast_strlen_zero(peer_cdr->userfield)) {
03956             ast_copy_string(bridge_cdr->userfield, peer_cdr->userfield, sizeof(bridge_cdr->userfield));
03957          }
03958          ast_cdr_setaccount(peer, ast_channel_accountcode(chan));
03959       } else {
03960          /* better yet, in a xfer situation, find out why the chan cdr got zapped (pun unintentional) */
03961          bridge_cdr = ast_cdr_alloc(); /* this should be really, really rare/impossible? */
03962          ast_copy_string(bridge_cdr->channel, ast_channel_name(chan), sizeof(bridge_cdr->channel));
03963          ast_copy_string(bridge_cdr->dstchannel, ast_channel_name(peer), sizeof(bridge_cdr->dstchannel));
03964          ast_copy_string(bridge_cdr->uniqueid, ast_channel_uniqueid(chan), sizeof(bridge_cdr->uniqueid));
03965          ast_copy_string(bridge_cdr->lastapp, S_OR(chan->appl, ""), sizeof(bridge_cdr->lastapp));
03966          ast_copy_string(bridge_cdr->lastdata, S_OR(chan->data, ""), sizeof(bridge_cdr->lastdata));
03967          ast_cdr_setcid(bridge_cdr, chan);
03968          bridge_cdr->disposition = (chan->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NULL;
03969          bridge_cdr->amaflags = chan->amaflags ? chan->amaflags :  ast_default_amaflags;
03970          ast_copy_string(bridge_cdr->accountcode, ast_channel_accountcode(chan), sizeof(bridge_cdr->accountcode));
03971          /* Destination information */
03972          ast_copy_string(bridge_cdr->dst, chan->exten, sizeof(bridge_cdr->dst));
03973          ast_copy_string(bridge_cdr->dcontext, chan->context, sizeof(bridge_cdr->dcontext));
03974          if (peer_cdr) {
03975             bridge_cdr->start = peer_cdr->start;
03976             ast_copy_string(bridge_cdr->userfield, peer_cdr->userfield, sizeof(bridge_cdr->userfield));
03977          } else {
03978             ast_cdr_start(bridge_cdr);
03979          }
03980       }
03981       ast_channel_unlock(chan);
03982       ast_channel_unlock(peer);
03983 
03984       ast_debug(4, "bridge answer set, chan answer set\n");
03985       /* peer_cdr->answer will be set when a macro runs on the peer;
03986          in that case, the bridge answer will be delayed while the
03987          macro plays on the peer channel. The peer answered the call
03988          before the macro started playing. To the phone system,
03989          this is billable time for the call, even tho the caller
03990          hears nothing but ringing while the macro does its thing. */
03991 
03992       /* Another case where the peer cdr's time will be set, is when
03993          A self-parks by pickup up phone and dialing 700, then B
03994          picks up A by dialing its parking slot; there may be more 
03995          practical paths that get the same result, tho... in which
03996          case you get the previous answer time from the Park... which
03997          is before the bridge's start time, so I added in the 
03998          tvcmp check to the if below */
03999 
04000       if (peer_cdr && !ast_tvzero(peer_cdr->answer) && ast_tvcmp(peer_cdr->answer, bridge_cdr->start) >= 0) {
04001          ast_cdr_setanswer(bridge_cdr, peer_cdr->answer);
04002          ast_cdr_setdisposition(bridge_cdr, peer_cdr->disposition);
04003          if (chan_cdr) {
04004             ast_cdr_setanswer(chan_cdr, peer_cdr->answer);
04005             ast_cdr_setdisposition(chan_cdr, peer_cdr->disposition);
04006          }
04007       } else {
04008          ast_cdr_answer(bridge_cdr);
04009          if (chan_cdr) {
04010             ast_cdr_answer(chan_cdr); /* for the sake of cli status checks */
04011          }
04012       }
04013       if (ast_test_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT) && (chan_cdr || peer_cdr)) {
04014          if (chan_cdr) {
04015             ast_set_flag(chan_cdr, AST_CDR_FLAG_BRIDGED);
04016          }
04017          if (peer_cdr) {
04018             ast_set_flag(peer_cdr, AST_CDR_FLAG_BRIDGED);
04019          }
04020       }
04021       /* the DIALED flag may be set if a dialed channel is transfered
04022        * and then bridged to another channel.  In order for the
04023        * bridge CDR to be written, the DIALED flag must not be
04024        * present. */
04025       ast_clear_flag(bridge_cdr, AST_CDR_FLAG_DIALED);
04026    }
04027    ast_cel_report_event(chan, AST_CEL_BRIDGE_START, NULL, NULL, peer);
04028 
04029    /* If we are bridging a call, stop worrying about forwarding loops. We presume that if
04030     * a call is being bridged, that the humans in charge know what they're doing. If they
04031     * don't, well, what can we do about that? */
04032    clear_dialed_interfaces(chan);
04033    clear_dialed_interfaces(peer);
04034 
04035    for (;;) {
04036       struct ast_channel *other; /* used later */
04037    
04038       res = ast_channel_bridge(chan, peer, config, &f, &who);
04039 
04040       if (ast_test_flag(chan, AST_FLAG_ZOMBIE)
04041          || ast_test_flag(peer, AST_FLAG_ZOMBIE)) {
04042          /* Zombies are present time to leave! */
04043          res = -1;
04044          if (f) {
04045             ast_frfree(f);
04046          }
04047          goto before_you_go;
04048       }
04049 
04050       /* When frame is not set, we are probably involved in a situation
04051          where we've timed out.
04052          When frame is set, we'll come this code twice; once for DTMF_BEGIN
04053          and also for DTMF_END. If we flow into the following 'if' for both, then 
04054          our wait times are cut in half, as both will subtract from the
04055          feature_timer. Not good!
04056       */
04057       if (config->feature_timer && (!f || f->frametype == AST_FRAME_DTMF_END)) {
04058          /* Update feature timer for next pass */
04059          diff = ast_tvdiff_ms(ast_tvnow(), config->feature_start_time);
04060          if (res == AST_BRIDGE_RETRY) {
04061             /* The feature fully timed out but has not been updated. Skip
04062              * the potential round error from the diff calculation and
04063              * explicitly set to expired. */
04064             config->feature_timer = -1;
04065          } else {
04066             config->feature_timer -= diff;
04067          }
04068 
04069          if (hasfeatures) {
04070             if (config->feature_timer <= 0) {
04071                /* Not *really* out of time, just out of time for
04072                   digits to come in for features. */
04073                ast_debug(1, "Timed out for feature!\n");
04074                if (!ast_strlen_zero(peer_featurecode)) {
04075                   ast_dtmf_stream(chan, peer, peer_featurecode, 0, f ? f->len : 0);
04076                   memset(peer_featurecode, 0, sizeof(peer_featurecode));
04077                }
04078                if (!ast_strlen_zero(chan_featurecode)) {
04079                   ast_dtmf_stream(peer, chan, chan_featurecode, 0, f ? f->len : 0);
04080                   memset(chan_featurecode, 0, sizeof(chan_featurecode));
04081                }
04082                if (f)
04083                   ast_frfree(f);
04084                hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
04085                if (!hasfeatures) {
04086                   /* No more digits expected - reset the timer */
04087                   config->feature_timer = 0;
04088                }
04089                hadfeatures = hasfeatures;
04090                /* Continue as we were */
04091                continue;
04092             } else if (!f) {
04093                /* The bridge returned without a frame and there is a feature in progress.
04094                 * However, we don't think the feature has quite yet timed out, so just
04095                 * go back into the bridge. */
04096                continue;
04097             }
04098          } else {
04099             if (config->feature_timer <=0) {
04100                /* We ran out of time */
04101                config->feature_timer = 0;
04102                who = chan;
04103                if (f)
04104                   ast_frfree(f);
04105                f = NULL;
04106                res = 0;
04107             }
04108          }
04109       }
04110       if (res < 0) {
04111          if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer)) {
04112             ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", ast_channel_name(chan), ast_channel_name(peer));
04113          }
04114          goto before_you_go;
04115       }
04116       
04117       if (!f || (f->frametype == AST_FRAME_CONTROL &&
04118             (f->subclass.integer == AST_CONTROL_HANGUP || f->subclass.integer == AST_CONTROL_BUSY ||
04119                f->subclass.integer == AST_CONTROL_CONGESTION))) {
04120          /*
04121           * If the bridge was broken for a hangup that isn't real,
04122           * then don't run the h extension, because the channel isn't
04123           * really hung up. This should really only happen with AST_SOFTHANGUP_ASYNCGOTO,
04124           * but it doesn't hurt to check AST_SOFTHANGUP_UNBRIDGE either.
04125           */
04126          ast_channel_lock(chan);
04127          if (chan->_softhangup & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE)) {
04128             ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT);
04129          }
04130          ast_channel_unlock(chan);
04131          res = -1;
04132          break;
04133       }
04134       /* many things should be sent to the 'other' channel */
04135       other = (who == chan) ? peer : chan;
04136       if (f->frametype == AST_FRAME_CONTROL) {
04137          switch (f->subclass.integer) {
04138          case AST_CONTROL_RINGING:
04139          case AST_CONTROL_FLASH:
04140          case AST_CONTROL_MCID:
04141          case -1:
04142             ast_indicate(other, f->subclass.integer);
04143             break;
04144          case AST_CONTROL_CONNECTED_LINE:
04145             if (!ast_channel_connected_line_macro(who, other, f, who != chan, 1)) {
04146                break;
04147             }
04148             ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
04149             break;
04150          case AST_CONTROL_REDIRECTING:
04151             if (!ast_channel_redirecting_macro(who, other, f, who != chan, 1)) {
04152                break;
04153             }
04154             ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
04155             break;
04156          case AST_CONTROL_AOC:
04157          case AST_CONTROL_HOLD:
04158          case AST_CONTROL_UNHOLD:
04159             ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
04160             break;
04161          case AST_CONTROL_OPTION:
04162             aoh = f->data.ptr;
04163             /* Forward option Requests, but only ones we know are safe
04164              * These are ONLY sent by chan_iax2 and I'm not convinced that
04165              * they are useful. I haven't deleted them entirely because I
04166              * just am not sure of the ramifications of removing them. */
04167             if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
04168                   switch (ntohs(aoh->option)) {
04169                case AST_OPTION_TONE_VERIFY:
04170                case AST_OPTION_TDD:
04171                case AST_OPTION_RELAXDTMF:
04172                case AST_OPTION_AUDIO_MODE:
04173                case AST_OPTION_DIGIT_DETECT:
04174                case AST_OPTION_FAX_DETECT:
04175                   ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 
04176                      f->datalen - sizeof(struct ast_option_header), 0);
04177                }
04178             }
04179             break;
04180          }
04181       } else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
04182          struct ast_flags *cfg;
04183          char dtmfcode[2] = { f->subclass.integer, };
04184          size_t featurelen;
04185 
04186          if (who == chan) {
04187             featurelen = strlen(chan_featurecode);
04188             cfg = &(config->features_caller);
04189          } else {
04190             featurelen = strlen(peer_featurecode);
04191             cfg = &(config->features_callee);
04192          }
04193          /* Take a peek if this (possibly) matches a feature. If not, just pass this
04194           * DTMF along untouched. If this is not the first digit of a multi-digit code
04195           * then we need to fall through and stream the characters if it matches */
04196          if (featurelen == 0
04197             && feature_check(chan, cfg, &dtmfcode[0]) == AST_FEATURE_RETURN_PASSDIGITS) {
04198             if (option_debug > 3) {
04199                ast_log(LOG_DEBUG, "Passing DTMF through, since it is not a feature code\n");
04200             }
04201             ast_write(other, f);
04202             sendingdtmfdigit = 1;
04203          } else {
04204             /* If ast_opt_transmit_silence is set, then we need to make sure we are
04205              * transmitting something while we hold on to the DTMF waiting for a
04206              * feature. */
04207             if (!silgen && ast_opt_transmit_silence) {
04208                silgen = ast_channel_start_silence_generator(other);
04209             }
04210             if (option_debug > 3) {
04211                ast_log(LOG_DEBUG, "Not passing DTMF through, since it may be a feature code\n");
04212             }
04213          }
04214       } else if (f->frametype == AST_FRAME_DTMF_END) {
04215          char *featurecode;
04216          int sense;
04217          unsigned int dtmfduration = f->len;
04218 
04219          hadfeatures = hasfeatures;
04220          /* This cannot overrun because the longest feature is one shorter than our buffer */
04221          if (who == chan) {
04222             sense = FEATURE_SENSE_CHAN;
04223             featurecode = chan_featurecode;
04224          } else  {
04225             sense = FEATURE_SENSE_PEER;
04226             featurecode = peer_featurecode;
04227          }
04228 
04229          if (sendingdtmfdigit == 1) {
04230             /* We let the BEGIN go through happily, so let's not bother with the END,
04231              * since we already know it's not something we bother with */
04232             ast_write(other, f);
04233             sendingdtmfdigit = 0;
04234          } else {
04235             /*! append the event to featurecode. we rely on the string being zero-filled, and
04236              * not overflowing it. 
04237              * \todo XXX how do we guarantee the latter ?
04238              */
04239             featurecode[strlen(featurecode)] = f->subclass.integer;
04240             /* Get rid of the frame before we start doing "stuff" with the channels */
04241             ast_frfree(f);
04242             f = NULL;
04243             if (silgen) {
04244                ast_channel_stop_silence_generator(other, silgen);
04245                silgen = NULL;
04246             }
04247             config->feature_timer = 0;
04248             res = feature_interpret(chan, peer, config, featurecode, sense);
04249             switch(res) {
04250             case AST_FEATURE_RETURN_PASSDIGITS:
04251                ast_dtmf_stream(other, who, featurecode, 0, dtmfduration);
04252                /* Fall through */
04253             case AST_FEATURE_RETURN_SUCCESS:
04254                memset(featurecode, 0, sizeof(chan_featurecode));
04255                break;
04256             }
04257             if (res >= AST_FEATURE_RETURN_PASSDIGITS) {
04258                res = 0;
04259             } else {
04260                break;
04261             }
04262             hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
04263             if (hadfeatures && !hasfeatures) {
04264                /* Feature completed or timed out */
04265                config->feature_timer = 0;
04266             } else if (hasfeatures) {
04267                if (config->timelimit) {
04268                   /* No warning next time - we are waiting for feature code */
04269                   ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
04270                }
04271                config->feature_start_time = ast_tvnow();
04272                config->feature_timer = featuredigittimeout;
04273                ast_debug(1, "Set feature timer to %ld ms\n", config->feature_timer);
04274             }
04275          }
04276       }
04277       if (f)
04278          ast_frfree(f);
04279    }
04280    ast_cel_report_event(chan, AST_CEL_BRIDGE_END, NULL, NULL, peer);
04281 
04282 before_you_go:
04283    /* Just in case something weird happened and we didn't clean up the silence generator... */
04284    if (silgen) {
04285       ast_channel_stop_silence_generator(who == chan ? peer : chan, silgen);
04286       silgen = NULL;
04287    }
04288 
04289    if (ast_test_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT)) {
04290       ast_clear_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT); /* its job is done */
04291       if (bridge_cdr) {
04292          ast_cdr_discard(bridge_cdr);
04293          /* QUESTION: should we copy bridge_cdr fields to the peer before we throw it away? */
04294       }
04295       return res; /* if we shouldn't do the h-exten, we shouldn't do the bridge cdr, either! */
04296    }
04297 
04298    if (config->end_bridge_callback) {
04299       config->end_bridge_callback(config->end_bridge_callback_data);
04300    }
04301 
04302    /* run the hangup exten on the chan object IFF it was NOT involved in a parking situation 
04303     * if it were, then chan belongs to a different thread now, and might have been hung up long
04304      * ago.
04305     */
04306    if (ast_test_flag(&config->features_caller, AST_FEATURE_NO_H_EXTEN)) {
04307       h_context = NULL;
04308    } else if (ast_exists_extension(chan, chan->context, "h", 1,
04309       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
04310       h_context = chan->context;
04311    } else if (!ast_strlen_zero(chan->macrocontext)
04312       && ast_exists_extension(chan, chan->macrocontext, "h", 1,
04313          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
04314       h_context = chan->macrocontext;
04315    } else {
04316       h_context = NULL;
04317    }
04318    if (h_context) {
04319       struct ast_cdr *swapper = NULL;
04320       char savelastapp[AST_MAX_EXTENSION];
04321       char savelastdata[AST_MAX_EXTENSION];
04322       char save_context[AST_MAX_CONTEXT];
04323       char save_exten[AST_MAX_EXTENSION];
04324       int  save_prio;
04325       int  found = 0;   /* set if we find at least one match */
04326       int  spawn_error = 0;
04327 
04328       /*
04329        * Make sure that the channel is marked as hungup since we are
04330        * going to run the "h" exten on it.
04331        */
04332       ast_softhangup(chan, AST_SOFTHANGUP_APPUNLOAD);
04333 
04334       autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
04335       ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
04336       if (bridge_cdr && ast_opt_end_cdr_before_h_exten) {
04337          ast_cdr_end(bridge_cdr);
04338       }
04339 
04340       /* swap the bridge cdr and the chan cdr for a moment, and let the endbridge
04341          dialplan code operate on it */
04342       ast_channel_lock(chan);
04343       if (bridge_cdr) {
04344          swapper = chan->cdr;
04345          ast_copy_string(savelastapp, bridge_cdr->lastapp, sizeof(bridge_cdr->lastapp));
04346          ast_copy_string(savelastdata, bridge_cdr->lastdata, sizeof(bridge_cdr->lastdata));
04347          chan->cdr = bridge_cdr;
04348       }
04349       ast_copy_string(save_context, chan->context, sizeof(save_context));
04350       ast_copy_string(save_exten, chan->exten, sizeof(save_exten));
04351       save_prio = chan->priority;
04352       if (h_context != chan->context) {
04353          ast_copy_string(chan->context, h_context, sizeof(chan->context));
04354       }
04355       ast_copy_string(chan->exten, "h", sizeof(chan->exten));
04356       chan->priority = 1;
04357       ast_channel_unlock(chan);
04358 
04359       while ((spawn_error = ast_spawn_extension(chan, chan->context, chan->exten,
04360          chan->priority,
04361          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
04362          &found, 1)) == 0) {
04363          chan->priority++;
04364       }
04365       if (found && spawn_error) {
04366          /* Something bad happened, or a hangup has been requested. */
04367          ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", chan->context, chan->exten, chan->priority, ast_channel_name(chan));
04368          ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", chan->context, chan->exten, chan->priority, ast_channel_name(chan));
04369       }
04370 
04371       /* swap it back */
04372       ast_channel_lock(chan);
04373       ast_copy_string(chan->context, save_context, sizeof(chan->context));
04374       ast_copy_string(chan->exten, save_exten, sizeof(chan->exten));
04375       chan->priority = save_prio;
04376       if (bridge_cdr) {
04377          if (chan->cdr == bridge_cdr) {
04378             chan->cdr = swapper;
04379          } else {
04380             bridge_cdr = NULL;
04381          }
04382       }
04383       /* An "h" exten has been run, so indicate that one has been run. */
04384       ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_RUN);
04385       ast_channel_unlock(chan);
04386 
04387       /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */
04388       if (bridge_cdr) {
04389          ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp));
04390          ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
04391       }
04392       ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
04393    }
04394    
04395    /* obey the NoCDR() wishes. -- move the DISABLED flag to the bridge CDR if it was set on the channel during the bridge... */
04396    new_chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
04397    if (bridge_cdr && new_chan_cdr && ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED))
04398       ast_set_flag(bridge_cdr, AST_CDR_FLAG_POST_DISABLED);
04399 
04400    /* we can post the bridge CDR at this point */
04401    if (bridge_cdr) {
04402       ast_cdr_end(bridge_cdr);
04403       ast_cdr_detach(bridge_cdr);
04404    }
04405    
04406    /* do a specialized reset on the beginning channel
04407       CDR's, if they still exist, so as not to mess up
04408       issues in future bridges;
04409       
04410       Here are the rules of the game:
04411       1. The chan and peer channel pointers will not change
04412          during the life of the bridge.
04413       2. But, in transfers, the channel names will change.
04414          between the time the bridge is started, and the
04415          time the channel ends. 
04416          Usually, when a channel changes names, it will
04417          also change CDR pointers.
04418       3. Usually, only one of the two channels (chan or peer)
04419          will change names.
04420       4. Usually, if a channel changes names during a bridge,
04421          it is because of a transfer. Usually, in these situations,
04422          it is normal to see 2 bridges running simultaneously, and
04423          it is not unusual to see the two channels that change
04424          swapped between bridges.
04425       5. After a bridge occurs, we have 2 or 3 channels' CDRs
04426          to attend to; if the chan or peer changed names,
04427          we have the before and after attached CDR's.
04428    */
04429 
04430    if (new_chan_cdr) {
04431       struct ast_channel *chan_ptr = NULL;
04432 
04433       if (strcasecmp(orig_channame, ast_channel_name(chan)) != 0) { 
04434          /* old channel */
04435          if ((chan_ptr = ast_channel_get_by_name(orig_channame))) {
04436             ast_channel_lock(chan_ptr);
04437             if (!ast_bridged_channel(chan_ptr)) {
04438                struct ast_cdr *cur;
04439                for (cur = chan_ptr->cdr; cur; cur = cur->next) {
04440                   if (cur == chan_cdr) {
04441                      break;
04442                   }
04443                }
04444                if (cur) {
04445                   ast_cdr_specialized_reset(chan_cdr, 0);
04446                }
04447             }
04448             ast_channel_unlock(chan_ptr);
04449             chan_ptr = ast_channel_unref(chan_ptr);
04450          }
04451          /* new channel */
04452          ast_cdr_specialized_reset(new_chan_cdr, 0);
04453       } else {
04454          ast_cdr_specialized_reset(chan->cdr, 0); /* nothing changed, reset the chan cdr  */
04455       }
04456    }
04457 
04458    {
04459       struct ast_channel *chan_ptr = NULL;
04460       new_peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
04461       if (new_chan_cdr && ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED) && new_peer_cdr && !ast_test_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED))
04462          ast_set_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED); /* DISABLED is viral-- it will propagate across a bridge */
04463       if (strcasecmp(orig_peername, ast_channel_name(peer)) != 0) { 
04464          /* old channel */
04465          if ((chan_ptr = ast_channel_get_by_name(orig_peername))) {
04466             ast_channel_lock(chan_ptr);
04467             if (!ast_bridged_channel(chan_ptr)) {
04468                struct ast_cdr *cur;
04469                for (cur = chan_ptr->cdr; cur; cur = cur->next) {
04470                   if (cur == peer_cdr) {
04471                      break;
04472                   }
04473                }
04474                if (cur) {
04475                   ast_cdr_specialized_reset(peer_cdr, 0);
04476                }
04477             }
04478             ast_channel_unlock(chan_ptr);
04479             chan_ptr = ast_channel_unref(chan_ptr);
04480          }
04481          /* new channel */
04482          if (new_peer_cdr) {
04483             ast_cdr_specialized_reset(new_peer_cdr, 0);
04484          }
04485       } else {
04486          if (we_disabled_peer_cdr) {
04487             ast_clear_flag(peer->cdr, AST_CDR_FLAG_POST_DISABLED);
04488          }
04489          ast_cdr_specialized_reset(peer->cdr, 0); /* nothing changed, reset the peer cdr  */
04490       }
04491    }
04492    
04493    return res;
04494 }

int ast_bridge_timelimit ( struct ast_channel chan,
struct ast_bridge_config config,
char *  parse,
struct timeval *  calldurationlimit 
)

parse L option and read associated channel variables to set warning, warning frequency, and timelimit

Note:
caller must be aware of freeing memory for warning_sound, end_sound, and start_sound

Definition at line 7390 of file features.c.

References ast_channel_lock, ast_channel_unlock, AST_FEATURE_PLAY_WARNING, ast_log(), ast_set_flag, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_true(), ast_verb, ast_bridge_config::end_sound, ast_bridge_config::features_callee, ast_bridge_config::features_caller, LOG_WARNING, pbx_builtin_getvar_helper(), ast_bridge_config::play_warning, S_OR, ast_bridge_config::start_sound, strsep(), ast_bridge_config::timelimit, var, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound.

Referenced by bridge_exec(), and dial_exec_full().

07392 {
07393    char *stringp = ast_strdupa(parse);
07394    char *limit_str, *warning_str, *warnfreq_str;
07395    const char *var;
07396    int play_to_caller = 0, play_to_callee = 0;
07397    int delta;
07398 
07399    limit_str = strsep(&stringp, ":");
07400    warning_str = strsep(&stringp, ":");
07401    warnfreq_str = strsep(&stringp, ":");
07402 
07403    config->timelimit = atol(limit_str);
07404    if (warning_str)
07405       config->play_warning = atol(warning_str);
07406    if (warnfreq_str)
07407       config->warning_freq = atol(warnfreq_str);
07408 
07409    if (!config->timelimit) {
07410       ast_log(LOG_WARNING, "Bridge does not accept L(%s), hanging up.\n", limit_str);
07411       config->timelimit = config->play_warning = config->warning_freq = 0;
07412       config->warning_sound = NULL;
07413       return -1; /* error */
07414    } else if ( (delta = config->play_warning - config->timelimit) > 0) {
07415       int w = config->warning_freq;
07416 
07417       /*
07418        * If the first warning is requested _after_ the entire call
07419        * would end, and no warning frequency is requested, then turn
07420        * off the warning. If a warning frequency is requested, reduce
07421        * the 'first warning' time by that frequency until it falls
07422        * within the call's total time limit.
07423        *
07424        * Graphically:
07425        *                timelim->|    delta        |<-playwarning
07426        *      0__________________|_________________|
07427        *                       | w  |    |    |    |
07428        *
07429        * so the number of intervals to cut is 1+(delta-1)/w
07430        */
07431       if (w == 0) {
07432          config->play_warning = 0;
07433       } else {
07434          config->play_warning -= w * ( 1 + (delta-1)/w );
07435          if (config->play_warning < 1)
07436             config->play_warning = config->warning_freq = 0;
07437       }
07438    }
07439    
07440    ast_channel_lock(chan);
07441 
07442    var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLER");
07443    play_to_caller = var ? ast_true(var) : 1;
07444 
07445    var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLEE");
07446    play_to_callee = var ? ast_true(var) : 0;
07447 
07448    if (!play_to_caller && !play_to_callee)
07449       play_to_caller = 1;
07450 
07451    var = pbx_builtin_getvar_helper(chan, "LIMIT_WARNING_FILE");
07452    config->warning_sound = !ast_strlen_zero(var) ? ast_strdup(var) : ast_strdup("timeleft");
07453 
07454    /* The code looking at config wants a NULL, not just "", to decide
07455     * that the message should not be played, so we replace "" with NULL.
07456     * Note, pbx_builtin_getvar_helper _can_ return NULL if the variable is
07457     * not found.
07458     */
07459 
07460    var = pbx_builtin_getvar_helper(chan, "LIMIT_TIMEOUT_FILE");
07461    config->end_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
07462 
07463    var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
07464    config->start_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
07465 
07466    ast_channel_unlock(chan);
07467 
07468    /* undo effect of S(x) in case they are both used */
07469    calldurationlimit->tv_sec = 0;
07470    calldurationlimit->tv_usec = 0;
07471 
07472    /* more efficient to do it like S(x) does since no advanced opts */
07473    if (!config->play_warning && !config->start_sound && !config->end_sound && config->timelimit) {
07474       calldurationlimit->tv_sec = config->timelimit / 1000;
07475       calldurationlimit->tv_usec = (config->timelimit % 1000) * 1000;
07476       ast_verb(3, "Setting call duration limit to %.3lf seconds.\n",
07477          calldurationlimit->tv_sec + calldurationlimit->tv_usec / 1000000.0);
07478       config->timelimit = play_to_caller = play_to_callee =
07479       config->play_warning = config->warning_freq = 0;
07480    } else {
07481       ast_verb(4, "Limit Data for this call:\n");
07482       ast_verb(4, "timelimit      = %ld ms (%.3lf s)\n", config->timelimit, config->timelimit / 1000.0);
07483       ast_verb(4, "play_warning   = %ld ms (%.3lf s)\n", config->play_warning, config->play_warning / 1000.0);
07484       ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
07485       ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
07486       ast_verb(4, "warning_freq   = %ld ms (%.3lf s)\n", config->warning_freq, config->warning_freq / 1000.0);
07487       ast_verb(4, "start_sound    = %s\n", S_OR(config->start_sound, ""));
07488       ast_verb(4, "warning_sound  = %s\n", config->warning_sound);
07489       ast_verb(4, "end_sound      = %s\n", S_OR(config->end_sound, ""));
07490    }
07491    if (play_to_caller)
07492       ast_set_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
07493    if (play_to_callee)
07494       ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
07495    return 0;
07496 }

int ast_can_pickup ( struct ast_channel chan  ) 

Test if a channel can be picked up.

Parameters:
chan Channel to test if can be picked up.
Note:
This function assumes that chan is locked.
Returns:
TRUE if channel can be picked up.

Definition at line 7197 of file features.c.

References ast_channel::_state, ast_channel_datastore_find(), AST_FLAG_ZOMBIE, AST_STATE_DOWN, AST_STATE_RING, AST_STATE_RINGING, ast_test_flag, ast_channel::masq, and ast_channel::pbx.

Referenced by find_by_mark(), find_by_part(), find_channel_by_group(), pickup_by_exten(), and pickup_by_name_cb().

07198 {
07199    if (!chan->pbx && !chan->masq && !ast_test_flag(chan, AST_FLAG_ZOMBIE)
07200       && (chan->_state == AST_STATE_RINGING
07201          || chan->_state == AST_STATE_RING
07202          /*
07203           * Check the down state as well because some SIP devices do not
07204           * give 180 ringing when they can just give 183 session progress
07205           * instead.  Issue 14005.  (Some ISDN switches as well for that
07206           * matter.)
07207           */
07208          || chan->_state == AST_STATE_DOWN)
07209       && !ast_channel_datastore_find(chan, &pickup_active, NULL)) {
07210       return 1;
07211    }
07212    return 0;
07213 }

void ast_channel_log ( char *  title,
struct ast_channel chan 
)

Definition at line 3698 of file features.c.

References ast_channel_accountcode(), ast_channel_dialcontext(), ast_channel_linkedid(), ast_channel_name(), ast_channel_uniqueid(), ast_log(), and LOG_NOTICE.

Referenced by ast_bridge_call().

03699 {
03700    ast_log(LOG_NOTICE, "______ %s (%lx)______\n", title, (unsigned long) chan);
03701    ast_log(LOG_NOTICE, "CHAN: name: %s;  appl: %s; data: %s; contxt: %s;  exten: %s; pri: %d;\n",
03702       ast_channel_name(chan), chan->appl, chan->data, chan->context, chan->exten, chan->priority);
03703    ast_log(LOG_NOTICE, "CHAN: acctcode: %s;  dialcontext: %s; amaflags: %x; maccontxt: %s;  macexten: %s; macpri: %d;\n",
03704       ast_channel_accountcode(chan), ast_channel_dialcontext(chan), chan->amaflags, chan->macrocontext, chan->macroexten, chan->macropriority);
03705    ast_log(LOG_NOTICE, "CHAN: masq: %p;  masqr: %p; _bridge: %p; uniqueID: %s; linkedID:%s\n",
03706       chan->masq, chan->masqr,
03707       chan->_bridge, ast_channel_uniqueid(chan), ast_channel_linkedid(chan));
03708    if (chan->masqr) {
03709       ast_log(LOG_NOTICE, "CHAN: masquerading as: %s;  cdr: %p;\n",
03710          ast_channel_name(chan->masqr), chan->masqr->cdr);
03711    }
03712    if (chan->_bridge) {
03713       ast_log(LOG_NOTICE, "CHAN: Bridged to %s\n", ast_channel_name(chan->_bridge));
03714    }
03715 
03716    ast_log(LOG_NOTICE, "===== done ====\n");
03717 }

int ast_do_pickup ( struct ast_channel chan,
struct ast_channel target 
)

Pickup a call target.

Parameters:
chan channel that initiated pickup.
target channel to be picked up.
Note:
This function assumes that target is locked.
Return values:
0 on success.
-1 on failure.

< A masquerade changes channel names.

< A masquerade changes channel names.

Definition at line 7273 of file features.c.

References ast_answer(), AST_CEL_PICKUP, ast_cel_report_event(), ast_channel_connected_line_macro(), ast_channel_datastore_add(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_masquerade(), ast_channel_name(), ast_channel_queue_connected_line_update(), ast_channel_unlock, ast_channel_update_connected_line(), ast_connected_line_copy_from_caller(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, AST_CONTROL_ANSWER, ast_datastore_alloc, ast_datastore_free(), ast_debug, ast_do_masquerade(), AST_FLAG_ANSWERED_ELSEWHERE, ast_log(), ast_manager_event_multichan, ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_queue_control(), ast_set_flag, ast_strdupa, ast_channel::caller, ast_channel::connected, EVENT_FLAG_CALL, LOG_WARNING, and ast_party_connected_line::source.

Referenced by ast_pickup_call(), pickup_by_channel(), pickup_by_exten(), pickup_by_group(), pickup_by_mark(), and pickup_by_part().

07274 {
07275    struct ast_party_connected_line connected_caller;
07276    struct ast_channel *chans[2] = { chan, target };
07277    struct ast_datastore *ds_pickup;
07278    const char *chan_name;/*!< A masquerade changes channel names. */
07279    const char *target_name;/*!< A masquerade changes channel names. */
07280    int res = -1;
07281 
07282    target_name = ast_strdupa(ast_channel_name(target));
07283    ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, ast_channel_name(chan));
07284 
07285    /* Mark the target to block any call pickup race. */
07286    ds_pickup = ast_datastore_alloc(&pickup_active, NULL);
07287    if (!ds_pickup) {
07288       ast_log(LOG_WARNING,
07289          "Unable to create channel datastore on '%s' for call pickup\n", target_name);
07290       return -1;
07291    }
07292    ast_channel_datastore_add(target, ds_pickup);
07293 
07294    ast_party_connected_line_init(&connected_caller);
07295    ast_party_connected_line_copy(&connected_caller, &target->connected);
07296    ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */
07297    connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
07298    if (ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
07299       ast_channel_update_connected_line(chan, &connected_caller, NULL);
07300    }
07301    ast_party_connected_line_free(&connected_caller);
07302 
07303    ast_channel_lock(chan);
07304    chan_name = ast_strdupa(ast_channel_name(chan));
07305    ast_connected_line_copy_from_caller(&connected_caller, &chan->caller);
07306    ast_channel_unlock(chan);
07307    connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
07308    ast_channel_queue_connected_line_update(chan, &connected_caller, NULL);
07309    ast_party_connected_line_free(&connected_caller);
07310 
07311    ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan);
07312 
07313    if (ast_answer(chan)) {
07314       ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
07315       goto pickup_failed;
07316    }
07317 
07318    if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
07319       ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name);
07320       goto pickup_failed;
07321    }
07322    
07323    /* setting this flag to generate a reason header in the cancel message to the ringing channel */
07324    ast_set_flag(chan, AST_FLAG_ANSWERED_ELSEWHERE);
07325 
07326    if (ast_channel_masquerade(target, chan)) {
07327       ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name,
07328          target_name);
07329       goto pickup_failed;
07330    }
07331 
07332    /* If you want UniqueIDs, set channelvars in manager.conf to CHANNEL(uniqueid) */
07333    ast_manager_event_multichan(EVENT_FLAG_CALL, "Pickup", 2, chans,
07334       "Channel: %s\r\n"
07335       "TargetChannel: %s\r\n",
07336       chan_name, target_name);
07337 
07338    /* Do the masquerade manually to make sure that it is completed. */
07339    ast_do_masquerade(target);
07340    res = 0;
07341 
07342 pickup_failed:
07343    ast_channel_lock(target);
07344    if (!ast_channel_datastore_remove(target, ds_pickup)) {
07345       ast_datastore_free(ds_pickup);
07346    }
07347 
07348    return res;
07349 }

int ast_feature_detect ( struct ast_channel chan,
struct ast_flags features,
const char *  code,
struct ast_call_feature feature 
)

detect a feature before bridging

Parameters:
chan 
features an ast_flags ptr
code ptr of input code
feature 
Return values:
ast_call_feature ptr to be set if found

Definition at line 3286 of file features.c.

References FEATURE_INTERPRET_DETECT, and feature_interpret_helper().

Referenced by detect_disconnect().

03286                                                                                                                                  {
03287 
03288    return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_DETECT, feature);
03289 }

int ast_features_init ( void   ) 

Provided by features.c

Definition at line 8142 of file features.c.

References action_bridge(), ao2_container_alloc, ARRAY_LEN, ast_cli_register_multiple(), ast_devstate_prov_add(), ast_manager_register_xml, ast_pthread_create, ast_register_application2(), AST_TEST_REGISTER, bridge_exec(), do_parking_thread(), EVENT_FLAG_CALL, load_config(), manager_park(), manager_parking_status(), metermaidstate(), park_call_exec(), parkcall, parked_call_exec(), parking_thread, parkinglot_cmp_cb(), parkinglot_hash_cb(), and parkinglots.

Referenced by main().

08143 {
08144    int res;
08145 
08146    parkinglots = ao2_container_alloc(7, parkinglot_hash_cb, parkinglot_cmp_cb);
08147    if (!parkinglots) {
08148       return -1;
08149    }
08150 
08151    res = load_config(0);
08152    if (res) {
08153       return res;
08154    }
08155    ast_cli_register_multiple(cli_features, ARRAY_LEN(cli_features));
08156    ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
08157    ast_register_application2(app_bridge, bridge_exec, NULL, NULL, NULL);
08158    res = ast_register_application2(parkedcall, parked_call_exec, NULL, NULL, NULL);
08159    if (!res)
08160       res = ast_register_application2(parkcall, park_call_exec, NULL, NULL, NULL);
08161    if (!res) {
08162       ast_manager_register_xml("ParkedCalls", 0, manager_parking_status);
08163       ast_manager_register_xml("Park", EVENT_FLAG_CALL, manager_park);
08164       ast_manager_register_xml("Bridge", EVENT_FLAG_CALL, action_bridge);
08165    }
08166 
08167    res |= ast_devstate_prov_add("Park", metermaidstate);
08168 #if defined(TEST_FRAMEWORK)
08169    res |= AST_TEST_REGISTER(features_test);
08170 #endif   /* defined(TEST_FRAMEWORK) */
08171 
08172    return res;
08173 }

int ast_features_reload ( void   ) 

Reload call features from features.conf.

Definition at line 6759 of file features.c.

References ast_context_destroy(), ast_context_find(), ast_mutex_lock, ast_mutex_unlock, features_reload_lock, load_config(), parking_con_dial, and registrar.

Referenced by handle_features_reload().

06760 {
06761    struct ast_context *con;
06762    int res;
06763 
06764    ast_mutex_lock(&features_reload_lock);/* Searialize reloading features.conf */
06765 
06766    /*
06767     * Always destroy the parking_con_dial context to remove buildup
06768     * of recalled extensions in the context.  At worst, the parked
06769     * call gets hungup attempting to run an invalid extension when
06770     * we are trying to callback the parker or the preset return
06771     * extension.  This is a small window of opportunity on an
06772     * execution chain that is not expected to happen very often.
06773     */
06774    con = ast_context_find(parking_con_dial);
06775    if (con) {
06776       ast_context_destroy(con, registrar);
06777    }
06778 
06779    res = load_config(1);
06780    ast_mutex_unlock(&features_reload_lock);
06781 
06782    return res;
06783 }

struct ast_call_feature* ast_find_call_feature ( const char *  name  )  [read]

look for a call feature entry by its sname

Parameters:
name a string ptr, should match "automon", "blindxfer", "atxfer", etc.

Definition at line 3019 of file features.c.

References FEATURES_COUNT, find_dynamic_feature(), and ast_call_feature::sname.

Referenced by action_atxfer(), handle_request_info(), and process_config().

03020 {
03021    int x;
03022    for (x = 0; x < FEATURES_COUNT; x++) {
03023       if (!strcasecmp(name, builtin_features[x].sname))
03024          return &builtin_features[x];
03025    }
03026 
03027    return find_dynamic_feature(name);
03028 }

int ast_masq_park_call ( struct ast_channel park_me,
struct ast_channel parker,
int  timeout,
int *  extout 
)

Park a call via a masqueraded channel.

Parameters:
park_me Channel to be parked.
parker Channel parking the 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.
Masquerade the park_me channel into a new, empty channel which is then parked.

Note:
Use ast_masq_park_call_exten() instead.
Return values:
0 on success.
-1 on failure.

Definition at line 1764 of file features.c.

References ast_channel_name(), ast_strdupa, masq_park_call(), ast_park_call_args::orig_chan_name, and ast_park_call_args::timeout.

Referenced by handle_soft_key_event_message(), handle_stimulus_message(), parkandannounce_exec(), and rpt_exec().

01765 {
01766    struct ast_park_call_args args = {
01767       .timeout = timeout,
01768       .extout = extout,
01769    };
01770 
01771    if (peer) {
01772       args.orig_chan_name = ast_strdupa(ast_channel_name(peer));
01773    }
01774    return masq_park_call(rchan, peer, &args);
01775 }

int ast_masq_park_call_exten ( struct ast_channel park_me,
struct ast_channel parker,
const char *  park_exten,
const char *  park_context,
int  timeout,
int *  extout 
)

Park a call via a masqueraded channel.

Since:
1.8.9
Parameters:
park_me Channel to be parked.
parker Channel parking the call.
park_exten Parking lot access extension
park_context Parking lot context
timeout is a timeout in milliseconds
extout is a parameter to an int that will hold the parked location, or NULL if you want.
Masquerade the park_me channel into a new, empty channel which is then parked.

Return values:
0 on success.
-1 on failure.

Definition at line 1710 of file features.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_channel_name(), ast_get_extension_app_data(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), create_dynamic_parkinglot(), find_parkinglot(), get_parking_exten(), masq_park_call(), ast_park_call_args::orig_chan_name, parkeddynamic, ast_park_call_args::parkinglot, parkinglot_unref(), parse(), park_app_args::pl_name, and ast_park_call_args::timeout.

Referenced by __analog_ss_thread(), analog_ss_thread(), and mgcp_ss().

01711 {
01712    int res;
01713    char *parse;
01714    const char *app_data;
01715    struct ast_exten *exten;
01716    struct park_app_args app_args;
01717    struct ast_park_call_args args = {
01718       .timeout = timeout,
01719       .extout = extout,
01720    };
01721 
01722    if (parker) {
01723       args.orig_chan_name = ast_strdupa(ast_channel_name(parker));
01724    }
01725    if (!park_exten || !park_context) {
01726       return masq_park_call(park_me, parker, &args);
01727    }
01728 
01729    /*
01730     * Determiine if the specified park extension has an exclusive
01731     * parking lot to use.
01732     */
01733    if (parker && parker != park_me) {
01734       ast_autoservice_start(park_me);
01735    }
01736    exten = get_parking_exten(park_exten, parker, park_context);
01737    if (exten) {
01738       app_data = ast_get_extension_app_data(exten);
01739       if (!app_data) {
01740          app_data = "";
01741       }
01742       parse = ast_strdupa(app_data);
01743       AST_STANDARD_APP_ARGS(app_args, parse);
01744    
01745       if (!ast_strlen_zero(app_args.pl_name)) {
01746          /* Find the specified exclusive parking lot */
01747          args.parkinglot = find_parkinglot(app_args.pl_name);
01748          if (!args.parkinglot && parkeddynamic) {
01749             args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
01750          }
01751       }
01752    }
01753    if (parker && parker != park_me) {
01754       ast_autoservice_stop(park_me);
01755    }
01756 
01757    res = masq_park_call(park_me, parker, &args);
01758    if (args.parkinglot) {
01759       parkinglot_unref(args.parkinglot);
01760    }
01761    return res;
01762 }

int ast_park_call ( struct ast_channel park_me,
struct ast_channel parker,
int  timeout,
const char *  park_exten,
int *  extout 
)

Park a call and read back parked location.

Parameters:
park_me Channel to be parked.
parker Channel parking the call.
timeout is a timeout in milliseconds
park_exten Parking lot access extension (Not used)
extout is a parameter to an int that will hold the parked location, or NULL if you want.
Park the park_me channel, and read back the parked location to the parker channel. If the call is not picked up within a specified period of time, then the call will return to the last step that it was in (in terms of exten, priority and context).

Note:
Use ast_park_call_exten() instead.
Return values:
0 on success.
-1 on failure.

Definition at line 1613 of file features.c.

References park_call_full(), and ast_park_call_args::timeout.

01614 {
01615    struct ast_park_call_args args = {
01616       .timeout = timeout,
01617       .extout = extout,
01618    };
01619 
01620    return park_call_full(park_me, parker, &args);
01621 }

int ast_park_call_exten ( struct ast_channel park_me,
struct ast_channel parker,
const char *  park_exten,
const char *  park_context,
int  timeout,
int *  extout 
)

Park a call and read back parked location.

Since:
1.8.9
Parameters:
park_me Channel to be parked.
parker Channel parking the call.
park_exten Parking lot access extension
park_context Parking lot context
timeout is a timeout in milliseconds
extout is a parameter to an int that will hold the parked location, or NULL if you want.
Park the park_me channel, and read back the parked location to the parker channel. If the call is not picked up within a specified period of time, then the call will return to the last step that it was in (in terms of exten, priority and context).

Return values:
0 on success.
-1 on failure.

Definition at line 1562 of file features.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_get_extension_app_data(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), create_dynamic_parkinglot(), find_parkinglot(), get_parking_exten(), park_call_full(), parkeddynamic, ast_park_call_args::parkinglot, parkinglot_unref(), parse(), park_app_args::pl_name, and ast_park_call_args::timeout.

Referenced by iax_park_thread(), and sip_park_thread().

01563 {
01564    int res;
01565    char *parse;
01566    const char *app_data;
01567    struct ast_exten *exten;
01568    struct park_app_args app_args;
01569    struct ast_park_call_args args = {
01570       .timeout = timeout,
01571       .extout = extout,
01572    };
01573 
01574    if (!park_exten || !park_context) {
01575       return park_call_full(park_me, parker, &args);
01576    }
01577 
01578    /*
01579     * Determiine if the specified park extension has an exclusive
01580     * parking lot to use.
01581     */
01582    if (parker && parker != park_me) {
01583       ast_autoservice_start(park_me);
01584    }
01585    exten = get_parking_exten(park_exten, parker, park_context);
01586    if (exten) {
01587       app_data = ast_get_extension_app_data(exten);
01588       if (!app_data) {
01589          app_data = "";
01590       }
01591       parse = ast_strdupa(app_data);
01592       AST_STANDARD_APP_ARGS(app_args, parse);
01593    
01594       if (!ast_strlen_zero(app_args.pl_name)) {
01595          /* Find the specified exclusive parking lot */
01596          args.parkinglot = find_parkinglot(app_args.pl_name);
01597          if (!args.parkinglot && parkeddynamic) {
01598             args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
01599          }
01600       }
01601    }
01602    if (parker && parker != park_me) {
01603       ast_autoservice_stop(park_me);
01604    }
01605 
01606    res = park_call_full(park_me, parker, &args);
01607    if (args.parkinglot) {
01608       parkinglot_unref(args.parkinglot);
01609    }
01610    return res;
01611 }

int ast_parking_ext_valid ( const char *  exten_str,
struct ast_channel chan,
const char *  context 
)

Determine if parking extension exists in a given context.

Return values:
0 if extension does not exist
1 if extension does exist

Definition at line 801 of file features.c.

References get_parking_exten().

Referenced by __analog_ss_thread(), analog_ss_thread(), dp_lookup(), handle_request_refer(), mgcp_ss(), and socket_process().

00802 {
00803    return get_parking_exten(exten_str, chan, context) ? 1 : 0;
00804 }

int ast_pickup_call ( struct ast_channel chan  ) 

Pickup a call.

Parameters:
chan channel that initiated pickup.
Walk list of channels, checking it is not itself, channel is pbx one, check that the callgroup for both channels are the same and the channel is ringing. Answer calling channel, flag channel as answered on queue, masq channels together.

< Potential pickup target

Definition at line 7239 of file features.c.

References ast_answer(), ast_channel_callback(), ast_channel_name(), ast_channel_unlock, ast_channel_unref, ast_debug, ast_do_pickup(), ast_log(), ast_stream_and_wait(), ast_strlen_zero(), find_channel_by_group(), LOG_NOTICE, LOG_WARNING, pbx_builtin_setvar_helper(), pickupfailsound, and pickupsound.

Referenced by __analog_ss_thread(), analog_ss_thread(), cb_events(), mgcp_ss(), and sip_pickup_thread().

07240 {
07241    struct ast_channel *target;/*!< Potential pickup target */
07242    int res = -1;
07243    ast_debug(1, "pickup attempt by %s\n", ast_channel_name(chan));
07244 
07245    /* The found channel is already locked. */
07246    target = ast_channel_callback(find_channel_by_group, NULL, chan, 0);
07247    if (target) {
07248       ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
07249 
07250       res = ast_do_pickup(chan, target);
07251       ast_channel_unlock(target);
07252       if (!res) {
07253          if (!ast_strlen_zero(pickupsound)) {
07254             pbx_builtin_setvar_helper(target, "BRIDGE_PLAY_SOUND", pickupsound);
07255          }
07256       } else {
07257          ast_log(LOG_WARNING, "pickup %s failed by %s\n", ast_channel_name(target), ast_channel_name(chan));
07258       }
07259       target = ast_channel_unref(target);
07260    }
07261 
07262    if (res < 0) {
07263       ast_debug(1, "No call pickup possible... for %s\n", ast_channel_name(chan));
07264       if (!ast_strlen_zero(pickupfailsound)) {
07265          ast_answer(chan);
07266          ast_stream_and_wait(chan, pickupfailsound, "");
07267       }
07268    }
07269 
07270    return res;
07271 }

const char* ast_pickup_ext ( void   ) 

void ast_rdlock_call_features ( void   ) 

Definition at line 3009 of file features.c.

References ast_rwlock_rdlock, and features_lock.

Referenced by handle_request_info().

03010 {
03011    ast_rwlock_rdlock(&features_lock);
03012 }

void ast_register_feature ( struct ast_call_feature feature  ) 

register new feature into feature_list

register new feature into feature_set

Definition at line 2853 of file features.c.

References ast_log(), AST_RWLIST_INSERT_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_verb, ast_call_feature::feature_entry, LOG_NOTICE, and ast_call_feature::sname.

Referenced by process_applicationmap_line().

02854 {
02855    if (!feature) {
02856       ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
02857       return;
02858    }
02859   
02860    AST_RWLIST_WRLOCK(&feature_list);
02861    AST_RWLIST_INSERT_HEAD(&feature_list,feature,feature_entry);
02862    AST_RWLIST_UNLOCK(&feature_list);
02863 
02864    ast_verb(2, "Registered Feature '%s'\n",feature->sname);
02865 }

void ast_unlock_call_features ( void   ) 

Definition at line 3014 of file features.c.

References ast_rwlock_unlock, and features_lock.

Referenced by handle_request_info().

03015 {
03016    ast_rwlock_unlock(&features_lock);
03017 }

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 2933 of file features.c.

References ast_free, AST_RWLIST_REMOVE, AST_RWLIST_UNLOCK, and AST_RWLIST_WRLOCK.

02934 {
02935    if (!feature) {
02936       return;
02937    }
02938 
02939    AST_RWLIST_WRLOCK(&feature_list);
02940    AST_RWLIST_REMOVE(&feature_list, feature, feature_entry);
02941    AST_RWLIST_UNLOCK(&feature_list);
02942 
02943    ast_free(feature);
02944 }

static void ast_unregister_features ( void   )  [static]

Remove all features in the list.

Definition at line 2947 of file features.c.

References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, and ast_call_feature::feature_entry.

Referenced by process_config().

02948 {
02949    struct ast_call_feature *feature;
02950 
02951    AST_RWLIST_WRLOCK(&feature_list);
02952    while ((feature = AST_RWLIST_REMOVE_HEAD(&feature_list, feature_entry))) {
02953       ast_free(feature);
02954    }
02955    AST_RWLIST_UNLOCK(&feature_list);
02956 }

static void ast_unregister_groups ( void   )  [static]

Remove all feature groups in the list.

Definition at line 2973 of file features.c.

References ast_free, AST_LIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_string_field_free_memory, and feature_group::features.

Referenced by process_config().

02974 {
02975    struct feature_group *fg;
02976    struct feature_group_exten *fge;
02977 
02978    AST_RWLIST_WRLOCK(&feature_groups);
02979    while ((fg = AST_LIST_REMOVE_HEAD(&feature_groups, entry))) {
02980       while ((fge = AST_LIST_REMOVE_HEAD(&fg->features, entry))) {
02981          ast_string_field_free_memory(fge);
02982          ast_free(fge);
02983       }
02984 
02985       ast_string_field_free_memory(fg);
02986       ast_free(fg);
02987    }
02988    AST_RWLIST_UNLOCK(&feature_groups);
02989 }

static void atxfer_fail_cleanup ( struct ast_channel transferee,
struct ast_channel transferer,
struct ast_party_connected_line connected_line 
) [static]

Definition at line 2396 of file features.c.

References ast_channel_connected_line_macro(), ast_channel_update_connected_line(), ast_party_connected_line_free(), and finishup().

Referenced by builtin_atxfer().

02397 {
02398    finishup(transferee);
02399 
02400    /*
02401     * Restore party B connected line info about party A.
02402     *
02403     * Party B was the caller to party C and is the last known mode
02404     * for party B.
02405     */
02406    if (ast_channel_connected_line_macro(transferee, transferer, connected_line, 1, 0)) {
02407       ast_channel_update_connected_line(transferer, connected_line, NULL);
02408    }
02409    ast_party_connected_line_free(connected_line);
02410 }

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

bridge the call

Parameters:
data thread bridge.
Set Last Data for respective channels, reset cdr for channels bridge call, check if we're going back to dialplan if not hangup both legs of the call

Definition at line 916 of file features.c.

References ast_channel::appl, ast_bridge_call(), ast_channel_name(), ast_check_hangup(), ast_free, ast_hangup(), ast_log(), ast_pbx_start(), AST_PBX_SUCCESS, ast_bridge_thread_obj::bconfig, ast_bridge_thread_obj::chan, ast_channel::data, LOG_VERBOSE, LOG_WARNING, ast_bridge_thread_obj::peer, and ast_bridge_thread_obj::return_to_pbx.

Referenced by bridge_call_thread_launch().

00917 {
00918    struct ast_bridge_thread_obj *tobj = data;
00919    int res;
00920 
00921    tobj->chan->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge";
00922    tobj->chan->data = ast_channel_name(tobj->peer);
00923    tobj->peer->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge";
00924    tobj->peer->data = ast_channel_name(tobj->chan);
00925 
00926    ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
00927 
00928    if (tobj->return_to_pbx) {
00929       if (!ast_check_hangup(tobj->peer)) {
00930          ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", ast_channel_name(tobj->peer));
00931          res = ast_pbx_start(tobj->peer);
00932          if (res != AST_PBX_SUCCESS)
00933             ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", ast_channel_name(tobj->peer));
00934       } else
00935          ast_hangup(tobj->peer);
00936       if (!ast_check_hangup(tobj->chan)) {
00937          ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", ast_channel_name(tobj->chan));
00938          res = ast_pbx_start(tobj->chan);
00939          if (res != AST_PBX_SUCCESS)
00940             ast_log(LOG_WARNING, "FAILED continuing PBX on chan %s\n", ast_channel_name(tobj->chan));
00941       } else
00942          ast_hangup(tobj->chan);
00943    } else {
00944       ast_hangup(tobj->chan);
00945       ast_hangup(tobj->peer);
00946    }
00947 
00948    ast_free(tobj);
00949 
00950    return NULL;
00951 }

static void bridge_call_thread_launch ( void *  data  )  [static]

create thread for the parked call

Parameters:
data Create thread and attributes, call bridge_call_thread

Definition at line 959 of file features.c.

References ast_pthread_create, bridge_call_thread(), and thread.

Referenced by action_bridge(), and builtin_atxfer().

00960 {
00961    pthread_t thread;
00962    pthread_attr_t attr;
00963    struct sched_param sched;
00964 
00965    pthread_attr_init(&attr);
00966    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00967    ast_pthread_create(&thread, &attr, bridge_call_thread, data);
00968    pthread_attr_destroy(&attr);
00969    memset(&sched, 0, sizeof(sched));
00970    pthread_setschedparam(thread, SCHED_RR, &sched);
00971 }

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

Bridge channels.

Parameters:
chan 
data channel to bridge with.
Split data, check we aren't bridging with ourself, check valid channel, answer call if not already, check compatible channels, setup bridge config now bridge call, if transfered party hangs up return to PBX extension.

Definition at line 7508 of file features.c.

References ast_channel::_state, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_bridge_call(), ast_bridge_timelimit(), ast_channel_alloc, ast_channel_get_by_name_prefix(), ast_channel_language(), ast_channel_linkedid(), ast_channel_make_compatible(), ast_channel_name(), ast_channel_unref, ast_check_hangup(), ast_debug, AST_DECLARE_APP_ARGS, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_free, ast_hangup(), ast_log(), ast_manager_event, ast_manager_event_multichan, ast_pbx_start(), AST_PBX_SUCCESS, ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_DOWN, AST_STATE_UP, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), bridge_exec_options, BRIDGE_OPT_PLAYTONE, ast_channel::context, do_bridge_masquerade(), ast_bridge_config::end_sound, EVENT_FLAG_CALL, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, LOG_WARNING, OPT_ARG_ARRAY_SIZE, OPT_ARG_DURATION_LIMIT, OPT_CALLEE_HANGUP, OPT_CALLEE_KILL, OPT_CALLEE_MONITOR, OPT_CALLEE_PARK, OPT_CALLEE_TRANSFER, OPT_CALLER_HANGUP, OPT_CALLER_MONITOR, OPT_CALLER_PARK, OPT_CALLER_TRANSFER, OPT_DURATION_LIMIT, pbx_builtin_setvar_helper(), ast_channel::priority, ast_bridge_config::start_sound, ast_bridge_config::warning_sound, and xfersound.

Referenced by ast_features_init().

07509 {
07510    struct ast_channel *current_dest_chan, *final_dest_chan, *chans[2];
07511    char *tmp_data  = NULL;
07512    struct ast_flags opts = { 0, };
07513    struct ast_bridge_config bconfig = { { 0, }, };
07514    char *opt_args[OPT_ARG_ARRAY_SIZE];
07515    struct timeval calldurationlimit = { 0, };
07516 
07517    AST_DECLARE_APP_ARGS(args,
07518       AST_APP_ARG(dest_chan);
07519       AST_APP_ARG(options);
07520    );
07521    
07522    if (ast_strlen_zero(data)) {
07523       ast_log(LOG_WARNING, "Bridge require at least 1 argument specifying the other end of the bridge\n");
07524       return -1;
07525    }
07526 
07527    tmp_data = ast_strdupa(data);
07528    AST_STANDARD_APP_ARGS(args, tmp_data);
07529    if (!ast_strlen_zero(args.options))
07530       ast_app_parse_options(bridge_exec_options, &opts, opt_args, args.options);
07531 
07532    /* avoid bridge with ourselves */
07533    if (!strcmp(ast_channel_name(chan), args.dest_chan)) {
07534       ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", ast_channel_name(chan));
07535       ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
07536                "Response: Failed\r\n"
07537                "Reason: Unable to bridge channel to itself\r\n"
07538                "Channel1: %s\r\n"
07539                "Channel2: %s\r\n",
07540                ast_channel_name(chan), args.dest_chan);
07541       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP");
07542       return 0;
07543    }
07544 
07545    /* make sure we have a valid end point */
07546    if (!(current_dest_chan = ast_channel_get_by_name_prefix(args.dest_chan,
07547          strlen(args.dest_chan)))) {
07548       ast_log(LOG_WARNING, "Bridge failed because channel %s does not exists or we "
07549          "cannot get its lock\n", args.dest_chan);
07550       ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
07551                "Response: Failed\r\n"
07552                "Reason: Cannot grab end point\r\n"
07553                "Channel1: %s\r\n"
07554                "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan);
07555       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT");
07556       return 0;
07557    }
07558 
07559    /* answer the channel if needed */
07560    if (current_dest_chan->_state != AST_STATE_UP) {
07561       ast_answer(current_dest_chan);
07562    }
07563 
07564    /* try to allocate a place holder where current_dest_chan will be placed */
07565    if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
07566       NULL, NULL, ast_channel_linkedid(current_dest_chan), 0, "Bridge/%s", ast_channel_name(current_dest_chan)))) {
07567       ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan);
07568       ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
07569                "Response: Failed\r\n"
07570                "Reason: cannot create placeholder\r\n"
07571                "Channel1: %s\r\n"
07572                "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan);
07573    }
07574 
07575    do_bridge_masquerade(current_dest_chan, final_dest_chan);
07576 
07577    chans[0] = current_dest_chan;
07578    chans[1] = final_dest_chan;
07579 
07580    /* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */
07581    /* try to make compatible, send error if we fail */
07582    if (ast_channel_make_compatible(chan, final_dest_chan) < 0) {
07583       ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", ast_channel_name(chan), ast_channel_name(final_dest_chan));
07584       ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans,
07585                "Response: Failed\r\n"
07586                "Reason: Could not make channels compatible for bridge\r\n"
07587                "Channel1: %s\r\n"
07588                "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan));
07589       ast_hangup(final_dest_chan); /* may be we should return this channel to the PBX? */
07590       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE");
07591       current_dest_chan = ast_channel_unref(current_dest_chan);
07592       return 0;
07593    }
07594 
07595    /* Report that the bridge will be successfull */
07596    ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans,
07597             "Response: Success\r\n"
07598             "Channel1: %s\r\n"
07599             "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan));
07600 
07601    /* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */  
07602    if (ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE) && !ast_strlen_zero(xfersound)) {
07603       if (!ast_streamfile(final_dest_chan, xfersound, ast_channel_language(final_dest_chan))) {
07604          if (ast_waitstream(final_dest_chan, "") < 0)
07605             ast_log(LOG_WARNING, "Failed to play courtesy tone on %s\n", ast_channel_name(final_dest_chan));
07606       }
07607    }
07608    
07609    current_dest_chan = ast_channel_unref(current_dest_chan);
07610    
07611    if (ast_test_flag(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
07612       if (ast_bridge_timelimit(chan, &bconfig, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
07613          goto done;
07614    }
07615 
07616    if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER))
07617       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_REDIRECT);
07618    if (ast_test_flag(&opts, OPT_CALLER_TRANSFER))
07619       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_REDIRECT);
07620    if (ast_test_flag(&opts, OPT_CALLEE_HANGUP))
07621       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
07622    if (ast_test_flag(&opts, OPT_CALLER_HANGUP))
07623       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
07624    if (ast_test_flag(&opts, OPT_CALLEE_MONITOR))
07625       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_AUTOMON);
07626    if (ast_test_flag(&opts, OPT_CALLER_MONITOR))
07627       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_AUTOMON);
07628    if (ast_test_flag(&opts, OPT_CALLEE_PARK))
07629       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_PARKCALL);
07630    if (ast_test_flag(&opts, OPT_CALLER_PARK))
07631       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_PARKCALL);
07632 
07633    ast_bridge_call(chan, final_dest_chan, &bconfig);
07634 
07635    /* the bridge has ended, set BRIDGERESULT to SUCCESS. If the other channel has not been hung up, return it to the PBX */
07636    pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS");
07637    if (!ast_check_hangup(final_dest_chan) && !ast_test_flag(&opts, OPT_CALLEE_KILL)) {
07638       ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n",
07639          final_dest_chan->context, final_dest_chan->exten,
07640          final_dest_chan->priority, ast_channel_name(final_dest_chan));
07641 
07642       if (ast_pbx_start(final_dest_chan) != AST_PBX_SUCCESS) {
07643          ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", ast_channel_name(final_dest_chan));
07644          ast_hangup(final_dest_chan);
07645       } else
07646          ast_debug(1, "SUCCESS continuing PBX on chan %s\n", ast_channel_name(final_dest_chan));
07647    } else {
07648       ast_debug(1, "hangup chan %s since the other endpoint has hung up or the x flag was passed\n", ast_channel_name(final_dest_chan));
07649       ast_hangup(final_dest_chan);
07650    }
07651 done:
07652    if (bconfig.warning_sound) {
07653       ast_free((char *)bconfig.warning_sound);
07654    }
07655    if (bconfig.end_sound) {
07656       ast_free((char *)bconfig.end_sound);
07657    }
07658    if (bconfig.start_sound) {
07659       ast_free((char *)bconfig.start_sound);
07660    }
07661 
07662    return 0;
07663 }

static struct parking_dp_context* build_dialplan_useage_context ( struct ast_parkinglot lot  )  [static, read]

Definition at line 6196 of file features.c.

References ast_calloc, ast_parkinglot::cfg, parking_dp_context::context, destroy_dialplan_usage_context(), dialplan_usage_add_parkinglot_data(), and parkinglot_cfg::parking_con.

Referenced by dialplan_usage_add_parkinglot().

06197 {
06198    struct parking_dp_context *ctx_node;
06199 
06200    ctx_node = ast_calloc(1, sizeof(*ctx_node) + strlen(lot->cfg.parking_con));
06201    if (!ctx_node) {
06202       return NULL;
06203    }
06204    if (dialplan_usage_add_parkinglot_data(ctx_node, lot, 0)) {
06205       destroy_dialplan_usage_context(ctx_node);
06206       return NULL;
06207    }
06208    strcpy(ctx_node->context, lot->cfg.parking_con);
06209    return ctx_node;
06210 }

static int build_dialplan_useage_map ( struct parking_dp_map usage_map,
int  complain 
) [static]

Definition at line 6268 of file features.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, dialplan_usage_add_parkinglot(), parkinglots, and status.

Referenced by load_config().

06269 {
06270    int status = 0;
06271    struct ao2_iterator iter;
06272    struct ast_parkinglot *curlot;
06273 
06274    /* For all parking lots */
06275    iter = ao2_iterator_init(parkinglots, 0);
06276    for (; (curlot = ao2_iterator_next(&iter)); ao2_ref(curlot, -1)) {
06277       /* Add the parking lot to the map. */
06278       if (dialplan_usage_add_parkinglot(usage_map, curlot, complain)) {
06279          ao2_ref(curlot, -1);
06280          status = -1;
06281          break;
06282       }
06283    }
06284    ao2_iterator_destroy(&iter);
06285 
06286    return status;
06287 }

static struct parking_dp_ramp* build_dialplan_useage_ramp ( const char *  exten,
int  exclusive 
) [static, read]

Definition at line 5959 of file features.c.

References ast_calloc, parking_dp_ramp::exclusive, and parking_dp_ramp::exten.

Referenced by usage_context_add_ramp().

05960 {
05961    struct parking_dp_ramp *ramp_node;
05962 
05963    ramp_node = ast_calloc(1, sizeof(*ramp_node) + strlen(exten));
05964    if (!ramp_node) {
05965       return NULL;
05966    }
05967    ramp_node->exclusive = exclusive;
05968    strcpy(ramp_node->exten, exten);
05969    return ramp_node;
05970 }

static struct parking_dp_spaces* build_dialplan_useage_spaces ( int  start,
int  stop 
) [static, read]

Definition at line 6040 of file features.c.

References ast_calloc, parking_dp_spaces::start, and parking_dp_spaces::stop.

Referenced by usage_context_add_spaces().

06041 {
06042    struct parking_dp_spaces *spaces_node;
06043 
06044    spaces_node = ast_calloc(1, sizeof(*spaces_node));
06045    if (!spaces_node) {
06046       return NULL;
06047    }
06048    spaces_node->start = start;
06049    spaces_node->stop = stop;
06050    return spaces_node;
06051 }

static struct ast_parkinglot* build_parkinglot ( const char *  pl_name,
struct ast_variable var 
) [static, read]

Build parkinglot from configuration and chain it in if it doesn't already exist.

Definition at line 5566 of file features.c.

References ao2_link, ao2_lock, ao2_unlock, ast_debug, AST_LIST_EMPTY, ast_log(), ast_parkinglot::cfg, create_parkinglot(), DEFAULT_PARKINGLOT, find_parkinglot(), force_reload_load, LOG_WARNING, ast_parkinglot::name, parkinglot_config_read(), parkinglot_unref(), parkinglots, ast_parkinglot::parkings, and ast_parkinglot::the_mark.

Referenced by load_config(), and process_config().

05567 {
05568    struct ast_parkinglot *parkinglot;
05569    const struct parkinglot_cfg *cfg_defaults;
05570    struct parkinglot_cfg new_cfg;
05571    int cfg_error;
05572    int oldparkinglot = 0;
05573 
05574    parkinglot = find_parkinglot(pl_name);
05575    if (parkinglot) {
05576       oldparkinglot = 1;
05577    } else {
05578       parkinglot = create_parkinglot(pl_name);
05579       if (!parkinglot) {
05580          return NULL;
05581       }
05582    }
05583    if (!strcmp(parkinglot->name, DEFAULT_PARKINGLOT)) {
05584       cfg_defaults = &parkinglot_cfg_default_default;
05585    } else {
05586       cfg_defaults = &parkinglot_cfg_default;
05587    }
05588    new_cfg = *cfg_defaults;
05589 
05590    ast_debug(1, "Building parking lot %s\n", parkinglot->name);
05591 
05592    ao2_lock(parkinglot);
05593 
05594    /* Do some config stuff */
05595    cfg_error = parkinglot_config_read(parkinglot->name, &new_cfg, var);
05596    if (oldparkinglot) {
05597       if (cfg_error) {
05598          /* Bad configuration read.  Keep using the original config. */
05599          ast_log(LOG_WARNING, "Changes to parking lot %s are discarded.\n",
05600             parkinglot->name);
05601          cfg_error = 0;
05602       } else if (!AST_LIST_EMPTY(&parkinglot->parkings)
05603          && memcmp(&new_cfg, &parkinglot->cfg, sizeof(parkinglot->cfg))) {
05604          /* Try reloading later when parking lot is empty. */
05605          ast_log(LOG_WARNING,
05606             "Parking lot %s has parked calls.  Parking lot changes discarded.\n",
05607             parkinglot->name);
05608          force_reload_load = 1;
05609       } else {
05610          /* Accept the new config */
05611          parkinglot->cfg = new_cfg;
05612       }
05613    } else {
05614       /* Load the initial parking lot config. */
05615       parkinglot->cfg = new_cfg;
05616    }
05617    parkinglot->the_mark = 0;
05618 
05619    ao2_unlock(parkinglot);
05620 
05621    if (cfg_error) {
05622       /* Only new parking lots could have config errors here. */
05623       ast_log(LOG_WARNING, "New parking lot %s is discarded.\n", parkinglot->name);
05624       parkinglot_unref(parkinglot);
05625       return NULL;
05626    }
05627 
05628    /* Move it into the list, if it wasn't already there */
05629    if (!oldparkinglot) {
05630       ao2_link(parkinglots, parkinglot);
05631    }
05632    parkinglot_unref(parkinglot);
05633 
05634    return parkinglot;
05635 }

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

Attended transfer.

Parameters:
chan transfered user
peer person transfering call
config 
code 
sense feature options
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 on failure

Definition at line 2427 of file features.c.

References ast_channel::_state, ast_app_dtget(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, AST_CEL_ATTENDEDTRANSFER, ast_cel_report_event(), ast_channel_alloc, ast_channel_connected_line_macro(), ast_channel_datastore_find(), ast_channel_language(), ast_channel_linkedid(), ast_channel_lock, ast_channel_masquerade(), ast_channel_name(), ast_channel_unlock, ast_channel_update_connected_line(), ast_check_hangup(), ast_clear_flag, ast_connected_line_copy_from_caller(), AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HOLD, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_copy_flags, ast_debug, AST_DIGIT_ANY, ast_do_masquerade(), ast_explicit_goto(), AST_FEATURE_DISCONNECT, AST_FEATURE_RETURN_SUCCESS, AST_FLAG_BRIDGE_HANGUP_DONT, AST_FLAGS_ALL, ast_hangup(), ast_indicate(), ast_log(), ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_safe_sleep(), ast_set_flag, AST_STATE_DOWN, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_test_flag, ast_waitfordigit(), atxfer_fail_cleanup(), atxfercallbackretries, atxferdropcall, atxferloopdelay, atxfernoanswertimeout, ast_bridge_thread_obj::bconfig, bridge_call_thread_launch(), ast_channel::caller, ast_bridge_thread_obj::chan, check_compat(), ast_channel::connected, ast_channel::context, ast_datastore::data, dial_features_info, ast_bridge_config::end_bridge_callback_data_fixup, ast_channel::exten, feature_request_and_dial(), ast_bridge_config::features_callee, ast_dial_features::features_caller, ast_bridge_config::features_caller, finishup(), get_parking_exten(), LOG_WARNING, ast_channel::nativeformats, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_bridge_thread_obj::peer, ast_channel::priority, ast_channel::readformat, real_ctx(), set_peers(), ast_party_connected_line::source, strsep(), transferdigittimeout, ast_channel::visible_indication, ast_channel::writeformat, xfer_park_call_helper(), xferfailsound, and xfersound.

02428 {
02429    struct ast_channel *transferer;/* Party B */
02430    struct ast_channel *transferee;/* Party A */
02431    struct ast_exten *park_exten;
02432    const char *transferer_real_context;
02433    char xferto[256] = "";
02434    int res;
02435    int outstate=0;
02436    struct ast_channel *newchan;
02437    struct ast_channel *xferchan;
02438    struct ast_bridge_thread_obj *tobj;
02439    struct ast_bridge_config bconfig;
02440    int l;
02441    struct ast_party_connected_line connected_line;
02442    struct ast_datastore *features_datastore;
02443    struct ast_dial_features *dialfeatures = NULL;
02444    char *transferer_tech;
02445    char *transferer_name;
02446    char *transferer_name_orig;
02447    char *dash;
02448 
02449    ast_debug(1, "Executing Attended Transfer %s, %s (sense=%d) \n", ast_channel_name(chan), ast_channel_name(peer), sense);
02450    set_peers(&transferer, &transferee, peer, chan, sense);
02451    transferer_real_context = real_ctx(transferer, transferee);
02452 
02453    /* Start autoservice on transferee while we talk to the transferer */
02454    ast_autoservice_start(transferee);
02455    ast_indicate(transferee, AST_CONTROL_HOLD);
02456 
02457    /* Transfer */
02458    res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
02459    if (res < 0) {
02460       finishup(transferee);
02461       return -1;
02462    }
02463    if (res > 0) { /* If they've typed a digit already, handle it */
02464       xferto[0] = (char) res;
02465    }
02466 
02467    /* this is specific of atxfer */
02468    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
02469    if (res < 0) {  /* hangup or error, (would be 0 for invalid and 1 for valid) */
02470       finishup(transferee);
02471       return -1;
02472    }
02473    l = strlen(xferto);
02474    if (res == 0) {
02475       if (l) {
02476          ast_log(LOG_WARNING, "Extension '%s' does not exist in context '%s'\n",
02477             xferto, transferer_real_context);
02478       } else {
02479          /* Does anyone care about this case? */
02480          ast_log(LOG_WARNING, "No digits dialed for atxfer.\n");
02481       }
02482       ast_stream_and_wait(transferer, "pbx-invalid", "");
02483       finishup(transferee);
02484       return AST_FEATURE_RETURN_SUCCESS;
02485    }
02486 
02487    park_exten = get_parking_exten(xferto, transferer, transferer_real_context);
02488    if (park_exten) {
02489       /* We are transfering the transferee to a parking lot. */
02490       return xfer_park_call_helper(transferee, transferer, park_exten);
02491    }
02492 
02493    /* Append context to dialed transfer number. */
02494    snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context);
02495 
02496    /* If we are performing an attended transfer and we have two channels involved then
02497       copy sound file information to play upon attended transfer completion */
02498    if (transferee) {
02499       const char *chan1_attended_sound = pbx_builtin_getvar_helper(transferer, "ATTENDED_TRANSFER_COMPLETE_SOUND");
02500       const char *chan2_attended_sound = pbx_builtin_getvar_helper(transferee, "ATTENDED_TRANSFER_COMPLETE_SOUND");
02501 
02502       if (!ast_strlen_zero(chan1_attended_sound)) {
02503          pbx_builtin_setvar_helper(transferer, "BRIDGE_PLAY_SOUND", chan1_attended_sound);
02504       }
02505       if (!ast_strlen_zero(chan2_attended_sound)) {
02506          pbx_builtin_setvar_helper(transferee, "BRIDGE_PLAY_SOUND", chan2_attended_sound);
02507       }
02508    }
02509 
02510    /* Extract redial transferer information from the channel name. */
02511    transferer_name_orig = ast_strdupa(ast_channel_name(transferer));
02512    transferer_name = ast_strdupa(transferer_name_orig);
02513    transferer_tech = strsep(&transferer_name, "/");
02514    dash = strrchr(transferer_name, '-');
02515    if (dash) {
02516       /* Trim off channel name sequence/serial number. */
02517       *dash = '\0';
02518    }
02519 
02520    /* Stop autoservice so we can monitor all parties involved in the transfer. */
02521    if (ast_autoservice_stop(transferee) < 0) {
02522       ast_indicate(transferee, AST_CONTROL_UNHOLD);
02523       return -1;
02524    }
02525 
02526    /* Save connected line info for party B about party A in case transfer fails. */
02527    ast_party_connected_line_init(&connected_line);
02528    ast_channel_lock(transferer);
02529    ast_party_connected_line_copy(&connected_line, &transferer->connected);
02530    ast_channel_unlock(transferer);
02531    connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
02532 
02533    /* Dial party C */
02534    newchan = feature_request_and_dial(transferer, transferer_name_orig, transferer,
02535       transferee, "Local", transferer->nativeformats, xferto,
02536       atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
02537    ast_debug(2, "Dial party C result: newchan:%d, outstate:%d\n", !!newchan, outstate);
02538 
02539    if (!ast_check_hangup(transferer)) {
02540       int hangup_dont = 0;
02541 
02542       /* Transferer (party B) is up */
02543       ast_debug(1, "Actually doing an attended transfer.\n");
02544 
02545       /* Start autoservice on transferee while the transferer deals with party C. */
02546       ast_autoservice_start(transferee);
02547 
02548       ast_indicate(transferer, -1);
02549       if (!newchan) {
02550          /* any reason besides user requested cancel and busy triggers the failed sound */
02551          switch (outstate) {
02552          case AST_CONTROL_UNHOLD:/* Caller requested cancel or party C answer timeout. */
02553          case AST_CONTROL_BUSY:
02554          case AST_CONTROL_CONGESTION:
02555             if (ast_stream_and_wait(transferer, xfersound, "")) {
02556                ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
02557             }
02558             break;
02559          default:
02560             if (ast_stream_and_wait(transferer, xferfailsound, "")) {
02561                ast_log(LOG_WARNING, "Failed to play transfer failed sound!\n");
02562             }
02563             break;
02564          }
02565          atxfer_fail_cleanup(transferee, transferer, &connected_line);
02566          return AST_FEATURE_RETURN_SUCCESS;
02567       }
02568 
02569       if (check_compat(transferer, newchan)) {
02570          if (ast_stream_and_wait(transferer, xferfailsound, "")) {
02571             ast_log(LOG_WARNING, "Failed to play transfer failed sound!\n");
02572          }
02573          atxfer_fail_cleanup(transferee, transferer, &connected_line);
02574          return AST_FEATURE_RETURN_SUCCESS;
02575       }
02576       memset(&bconfig,0,sizeof(struct ast_bridge_config));
02577       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
02578       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
02579 
02580       /* ast_bridge_call clears AST_FLAG_BRIDGE_HANGUP_DONT, but we don't
02581          want that to happen here because we're also in another bridge already
02582        */
02583       if (ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT)) {
02584          hangup_dont = 1;
02585       }
02586       /* Let party B and party C talk as long as they want. */
02587       ast_bridge_call(transferer, newchan, &bconfig);
02588       if (hangup_dont) {
02589          ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT);
02590       }
02591 
02592       if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) {
02593          ast_hangup(newchan);
02594          if (ast_stream_and_wait(transferer, xfersound, "")) {
02595             ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
02596          }
02597          atxfer_fail_cleanup(transferee, transferer, &connected_line);
02598          return AST_FEATURE_RETURN_SUCCESS;
02599       }
02600 
02601       /* Transferer (party B) is confirmed hung up at this point. */
02602       if (check_compat(transferee, newchan)) {
02603          finishup(transferee);
02604          ast_party_connected_line_free(&connected_line);
02605          return -1;
02606       }
02607 
02608       ast_indicate(transferee, AST_CONTROL_UNHOLD);
02609       if ((ast_autoservice_stop(transferee) < 0)
02610          || (ast_waitfordigit(transferee, 100) < 0)
02611          || (ast_waitfordigit(newchan, 100) < 0)
02612          || ast_check_hangup(transferee)
02613          || ast_check_hangup(newchan)) {
02614          ast_hangup(newchan);
02615          ast_party_connected_line_free(&connected_line);
02616          return -1;
02617       }
02618    } else if (!ast_check_hangup(transferee)) {
02619       /* Transferer (party B) has hung up at this point.  Doing blonde transfer. */
02620       ast_debug(1, "Actually doing a blonde transfer.\n");
02621 
02622       if (!newchan && !atxferdropcall) {
02623          /* Party C is not available, try to call party B back. */
02624          unsigned int tries = 0;
02625 
02626          if (ast_strlen_zero(transferer_name) || ast_strlen_zero(transferer_tech)) {
02627             ast_log(LOG_WARNING,
02628                "Transferer channel name: '%s' cannot be used for callback.\n",
02629                transferer_name_orig);
02630             ast_indicate(transferee, AST_CONTROL_UNHOLD);
02631             ast_party_connected_line_free(&connected_line);
02632             return -1;
02633          }
02634 
02635          tries = 0;
02636          for (;;) {
02637             /* Try to get party B back. */
02638             ast_debug(1, "We're trying to callback %s/%s\n",
02639                transferer_tech, transferer_name);
02640             newchan = feature_request_and_dial(transferer, transferer_name_orig,
02641                transferee, transferee, transferer_tech,
02642                transferee->nativeformats, transferer_name,
02643                atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
02644             ast_debug(2, "Dial party B result: newchan:%d, outstate:%d\n",
02645                !!newchan, outstate);
02646             if (newchan || ast_check_hangup(transferee)) {
02647                break;
02648             }
02649 
02650             ++tries;
02651             if (atxfercallbackretries <= tries) {
02652                /* No more callback tries remaining. */
02653                break;
02654             }
02655 
02656             if (atxferloopdelay) {
02657                /* Transfer failed, sleeping */
02658                ast_debug(1, "Sleeping for %d ms before retrying atxfer.\n",
02659                   atxferloopdelay);
02660                ast_safe_sleep(transferee, atxferloopdelay);
02661                if (ast_check_hangup(transferee)) {
02662                   ast_party_connected_line_free(&connected_line);
02663                   return -1;
02664                }
02665             }
02666 
02667             /* Retry dialing party C. */
02668             ast_debug(1, "We're retrying to call %s/%s\n", "Local", xferto);
02669             newchan = feature_request_and_dial(transferer, transferer_name_orig,
02670                transferer, transferee, "Local",
02671                transferee->nativeformats, xferto,
02672                atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
02673             ast_debug(2, "Redial party C result: newchan:%d, outstate:%d\n",
02674                !!newchan, outstate);
02675             if (newchan || ast_check_hangup(transferee)) {
02676                break;
02677             }
02678          }
02679       }
02680       ast_indicate(transferee, AST_CONTROL_UNHOLD);
02681       if (!newchan) {
02682          /* No party C or could not callback party B. */
02683          ast_party_connected_line_free(&connected_line);
02684          return -1;
02685       }
02686 
02687       /* newchan is up, we should prepare transferee and bridge them */
02688       if (ast_check_hangup(newchan)) {
02689          ast_hangup(newchan);
02690          ast_party_connected_line_free(&connected_line);
02691          return -1;
02692       }
02693       if (check_compat(transferee, newchan)) {
02694          ast_party_connected_line_free(&connected_line);
02695          return -1;
02696       }
02697    } else {
02698       /*
02699        * Both the transferer and transferee have hungup.  If newchan
02700        * is up, hang it up as it has no one to talk to.
02701        */
02702       ast_debug(1, "Everyone is hungup.\n");
02703       if (newchan) {
02704          ast_hangup(newchan);
02705       }
02706       ast_party_connected_line_free(&connected_line);
02707       return -1;
02708    }
02709 
02710    /* Initiate the channel transfer of party A to party C (or recalled party B). */
02711    ast_cel_report_event(transferee, AST_CEL_ATTENDEDTRANSFER, NULL, NULL, newchan);
02712 
02713    xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(transferee), 0, "Transfered/%s", ast_channel_name(transferee));
02714    if (!xferchan) {
02715       ast_hangup(newchan);
02716       ast_party_connected_line_free(&connected_line);
02717       return -1;
02718    }
02719 
02720    /* Give party A a momentary ringback tone during transfer. */
02721    xferchan->visible_indication = AST_CONTROL_RINGING;
02722 
02723    /* Make formats okay */
02724    xferchan->readformat = transferee->readformat;
02725    xferchan->writeformat = transferee->writeformat;
02726 
02727    ast_channel_masquerade(xferchan, transferee);
02728    ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
02729    xferchan->_state = AST_STATE_UP;
02730    ast_clear_flag(xferchan, AST_FLAGS_ALL);
02731 
02732    /* Do the masquerade manually to make sure that is is completed. */
02733    ast_do_masquerade(xferchan);
02734 
02735    newchan->_state = AST_STATE_UP;
02736    ast_clear_flag(newchan, AST_FLAGS_ALL);
02737    tobj = ast_calloc(1, sizeof(*tobj));
02738    if (!tobj) {
02739       ast_hangup(xferchan);
02740       ast_hangup(newchan);
02741       ast_party_connected_line_free(&connected_line);
02742       return -1;
02743    }
02744 
02745    ast_channel_lock(newchan);
02746    if ((features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL))) {
02747       dialfeatures = features_datastore->data;
02748    }
02749    ast_channel_unlock(newchan);
02750 
02751    if (dialfeatures) {
02752       /* newchan should always be the callee and shows up as callee in dialfeatures, but for some reason
02753          I don't currently understand, the abilities of newchan seem to be stored on the caller side */
02754       ast_copy_flags(&(config->features_callee), &(dialfeatures->features_caller), AST_FLAGS_ALL);
02755       dialfeatures = NULL;
02756    }
02757 
02758    ast_channel_lock(xferchan);
02759    if ((features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL))) {
02760       dialfeatures = features_datastore->data;
02761    }
02762    ast_channel_unlock(xferchan);
02763 
02764    if (dialfeatures) {
02765       ast_copy_flags(&(config->features_caller), &(dialfeatures->features_caller), AST_FLAGS_ALL);
02766    }
02767 
02768    tobj->chan = newchan;
02769    tobj->peer = xferchan;
02770    tobj->bconfig = *config;
02771 
02772    if (tobj->bconfig.end_bridge_callback_data_fixup) {
02773       tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan);
02774    }
02775 
02776    /*
02777     * xferchan is transferee, and newchan is the transfer target
02778     * So...in a transfer, who is the caller and who is the callee?
02779     *
02780     * When the call is originally made, it is clear who is caller and callee.
02781     * When a transfer occurs, it is my humble opinion that the transferee becomes
02782     * the caller, and the transfer target is the callee.
02783     *
02784     * The problem is that these macros were set with the intention of the original
02785     * caller and callee taking those roles.  A transfer can totally mess things up,
02786     * to be technical.  What sucks even more is that you can't effectively change
02787     * the macros in the dialplan during the call from the transferer to the transfer
02788     * target because the transferee is stuck with whatever role he originally had.
02789     *
02790     * I think the answer here is just to make sure that it is well documented that
02791     * during a transfer, the transferee is the "caller" and the transfer target
02792     * is the "callee."
02793     *
02794     * This means that if party B calls party A, and party B transfers party A to
02795     * party C, then A has switched roles for the call.  Now party A will have the
02796     * caller macro called on his channel instead of the callee macro.
02797     *
02798     * Luckily, the method by which the party B to party C bridge is
02799     * launched above ensures that the transferee is the "chan" on
02800     * the bridge and the transfer target is the "peer," so my idea
02801     * for the roles post-transfer does not require extensive code
02802     * changes.
02803     */
02804 
02805    /* Transfer party C connected line to party A */
02806    ast_channel_lock(transferer);
02807    /*
02808     * Due to a limitation regarding when callerID is set on a Local channel,
02809     * we use the transferer's connected line information here.
02810     */
02811    ast_party_connected_line_copy(&connected_line, &transferer->connected);
02812    ast_channel_unlock(transferer);
02813    connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
02814    if (ast_channel_connected_line_macro(newchan, xferchan, &connected_line, 1, 0)) {
02815       ast_channel_update_connected_line(xferchan, &connected_line, NULL);
02816    }
02817 
02818    /* Transfer party A connected line to party C */
02819    ast_channel_lock(xferchan);
02820    ast_connected_line_copy_from_caller(&connected_line, &xferchan->caller);
02821    ast_channel_unlock(xferchan);
02822    connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
02823    if (ast_channel_connected_line_macro(xferchan, newchan, &connected_line, 0, 0)) {
02824       ast_channel_update_connected_line(newchan, &connected_line, NULL);
02825    }
02826 
02827    if (ast_stream_and_wait(newchan, xfersound, ""))
02828       ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
02829    bridge_call_thread_launch(tobj);
02830 
02831    ast_party_connected_line_free(&connected_line);
02832    return -1;/* The transferee is masqueraded and the original bridged channels can be hungup. */
02833 }

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

Definition at line 2100 of file features.c.

References args, AST_AUDIOHOOK_TYPE_SPY, ast_autoservice_ignore(), ast_autoservice_start(), ast_autoservice_stop(), ast_channel_audiohook_count_by_source(), ast_channel_audiohook_count_by_source_running(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, AST_FEATURE_RETURN_SUCCESS, AST_FRAME_DTMF_END, ast_log(), ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_verb, ast_channel::caller, courtesytone, ast_party_caller::id, LOG_ERROR, LOG_NOTICE, LOG_WARNING, mixmonitor_app, mixmonitor_ok, mixmonitor_spy_type, ast_party_id::number, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), S_COR, S_OR, set_peers(), stopmixmonitor_app, stopmixmonitor_ok, ast_party_number::str, and ast_party_number::valid.

02101 {
02102    char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
02103    int x = 0;
02104    size_t len;
02105    struct ast_channel *caller_chan, *callee_chan;
02106    const char *mixmonitor_spy_type = "MixMonitor";
02107    int count = 0;
02108 
02109    if (!mixmonitor_ok) {
02110       ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
02111       return -1;
02112    }
02113 
02114    if (!(mixmonitor_app = pbx_findapp("MixMonitor"))) {
02115       mixmonitor_ok = 0;
02116       ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
02117       return -1;
02118    }
02119 
02120    set_peers(&caller_chan, &callee_chan, peer, chan, sense);
02121 
02122    if (!ast_strlen_zero(courtesytone)) {
02123       if (ast_autoservice_start(callee_chan))
02124          return -1;
02125       ast_autoservice_ignore(callee_chan, AST_FRAME_DTMF_END);
02126       if (ast_stream_and_wait(caller_chan, courtesytone, "")) {
02127          ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
02128          ast_autoservice_stop(callee_chan);
02129          return -1;
02130       }
02131       if (ast_autoservice_stop(callee_chan))
02132          return -1;
02133    }
02134 
02135    ast_channel_lock(callee_chan);
02136    count = ast_channel_audiohook_count_by_source(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
02137    ast_channel_unlock(callee_chan);
02138 
02139    /* This means a mixmonitor is attached to the channel, running or not is unknown. */
02140    if (count > 0) {
02141       
02142       ast_verb(3, "User hit '%s' to stop recording call.\n", code);
02143 
02144       /* Make sure they are running */
02145       ast_channel_lock(callee_chan);
02146       count = ast_channel_audiohook_count_by_source_running(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
02147       ast_channel_unlock(callee_chan);
02148       if (count > 0) {
02149          if (!stopmixmonitor_ok) {
02150             ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
02151             return -1;
02152          }
02153          if (!(stopmixmonitor_app = pbx_findapp("StopMixMonitor"))) {
02154             stopmixmonitor_ok = 0;
02155             ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
02156             return -1;
02157          } else {
02158             pbx_exec(callee_chan, stopmixmonitor_app, "");
02159             return AST_FEATURE_RETURN_SUCCESS;
02160          }
02161       }
02162       
02163       ast_log(LOG_WARNING,"Stopped MixMonitors are attached to the channel.\n"); 
02164    }        
02165 
02166    if (caller_chan && callee_chan) {
02167       const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR_FORMAT");
02168       const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR");
02169 
02170       if (!touch_format)
02171          touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR_FORMAT");
02172 
02173       if (!touch_monitor)
02174          touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR");
02175 
02176       if (touch_monitor) {
02177          len = strlen(touch_monitor) + 50;
02178          args = alloca(len);
02179          touch_filename = alloca(len);
02180          snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
02181          snprintf(args, len, "%s.%s,b", touch_filename, (touch_format) ? touch_format : "wav");
02182       } else {
02183          caller_chan_id = ast_strdupa(S_COR(caller_chan->caller.id.number.valid,
02184             caller_chan->caller.id.number.str, ast_channel_name(caller_chan)));
02185          callee_chan_id = ast_strdupa(S_COR(callee_chan->caller.id.number.valid,
02186             callee_chan->caller.id.number.str, ast_channel_name(callee_chan)));
02187          len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
02188          args = alloca(len);
02189          touch_filename = alloca(len);
02190          snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
02191          snprintf(args, len, "%s.%s,b", touch_filename, S_OR(touch_format, "wav"));
02192       }
02193 
02194       for( x = 0; x < strlen(args); x++) {
02195          if (args[x] == '/')
02196             args[x] = '-';
02197       }
02198 
02199       ast_verb(3, "User hit '%s' to record call. filename: %s\n", code, touch_filename);
02200 
02201       pbx_exec(callee_chan, mixmonitor_app, args);
02202       pbx_builtin_setvar_helper(callee_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
02203       pbx_builtin_setvar_helper(caller_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
02204       return AST_FEATURE_RETURN_SUCCESS;
02205    
02206    }
02207 
02208    ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");
02209    return -1;
02210 
02211 }

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

Monitor a channel by DTMF.

Parameters:
chan channel requesting monitor
peer channel to be monitored
config 
code 
sense feature options
data Check monitor app enabled, setup channels, both caller/callee chans not null get TOUCH_MONITOR variable for filename if exists, exec monitor app.
Return values:
AST_FEATURE_RETURN_SUCCESS on success.
-1 on error.

Definition at line 2005 of file features.c.

References args, ast_channel_name(), AST_FEATURE_RETURN_SUCCESS, ast_log(), ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::caller, courtesytone, ast_party_caller::id, LOG_ERROR, LOG_NOTICE, ast_channel::monitor, monitor_app, monitor_ok, ast_party_id::number, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), play_message_in_bridged_call(), S_COR, S_OR, set_peers(), ast_channel_monitor::stop, ast_party_number::str, and ast_party_number::valid.

02006 {
02007    char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
02008    int x = 0;
02009    size_t len;
02010    struct ast_channel *caller_chan, *callee_chan;
02011    const char *automon_message_start = NULL;
02012    const char *automon_message_stop = NULL;
02013 
02014    if (!monitor_ok) {
02015       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
02016       return -1;
02017    }
02018 
02019    if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
02020       monitor_ok = 0;
02021       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
02022       return -1;
02023    }
02024 
02025    set_peers(&caller_chan, &callee_chan, peer, chan, sense);
02026    if (caller_chan) {   /* Find extra messages */
02027       automon_message_start = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_MESSAGE_START");
02028       automon_message_stop = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_MESSAGE_STOP");
02029    }
02030 
02031    if (!ast_strlen_zero(courtesytone)) {  /* Play courtesy tone if configured */
02032       if(play_message_in_bridged_call(caller_chan, callee_chan, courtesytone) == -1) {
02033          return -1;
02034       }
02035    }
02036    
02037    if (callee_chan->monitor) {
02038       ast_verb(4, "User hit '%s' to stop recording call.\n", code);
02039       if (!ast_strlen_zero(automon_message_stop)) {
02040          play_message_in_bridged_call(caller_chan, callee_chan, automon_message_stop);
02041       }
02042       callee_chan->monitor->stop(callee_chan, 1);
02043       return AST_FEATURE_RETURN_SUCCESS;
02044    }
02045 
02046    if (caller_chan && callee_chan) {
02047       const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
02048       const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
02049       const char *touch_monitor_prefix = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_PREFIX");
02050 
02051       if (!touch_format)
02052          touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
02053 
02054       if (!touch_monitor)
02055          touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
02056    
02057       if (!touch_monitor_prefix)
02058          touch_monitor_prefix = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_PREFIX");
02059    
02060       if (touch_monitor) {
02061          len = strlen(touch_monitor) + 50;
02062          args = alloca(len);
02063          touch_filename = alloca(len);
02064          snprintf(touch_filename, len, "%s-%ld-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor);
02065          snprintf(args, len, "%s,%s,m", S_OR(touch_format, "wav"), touch_filename);
02066       } else {
02067          caller_chan_id = ast_strdupa(S_COR(caller_chan->caller.id.number.valid,
02068             caller_chan->caller.id.number.str, ast_channel_name(caller_chan)));
02069          callee_chan_id = ast_strdupa(S_COR(callee_chan->caller.id.number.valid,
02070             callee_chan->caller.id.number.str, ast_channel_name(callee_chan)));
02071          len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
02072          args = alloca(len);
02073          touch_filename = alloca(len);
02074          snprintf(touch_filename, len, "%s-%ld-%s-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), caller_chan_id, callee_chan_id);
02075          snprintf(args, len, "%s,%s,m", S_OR(touch_format, "wav"), touch_filename);
02076       }
02077 
02078       for(x = 0; x < strlen(args); x++) {
02079          if (args[x] == '/')
02080             args[x] = '-';
02081       }
02082       
02083       ast_verb(4, "User hit '%s' to record call. filename: %s\n", code, args);
02084 
02085       pbx_exec(callee_chan, monitor_app, args);
02086       pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
02087       pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
02088 
02089       if (!ast_strlen_zero(automon_message_start)) {  /* Play start message for both channels */
02090          play_message_in_bridged_call(caller_chan, callee_chan, automon_message_start);
02091       }
02092    
02093       return AST_FEATURE_RETURN_SUCCESS;
02094    }
02095    
02096    ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");  
02097    return -1;
02098 }

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

Blind transfer user to another extension.

Parameters:
chan channel to be transfered
peer channel initiated blind transfer
config 
code 
data 
sense feature options
Place chan on hold, check if transferred to parkinglot extension, otherwise check extension exists and transfer caller.
Return values:
AST_FEATURE_RETURN_SUCCESS. 
-1 on failure.

Definition at line 2256 of file features.c.

References ast_app_dtget(), ast_async_goto(), ast_autoservice_start(), ast_cdr_alloc(), ast_cdr_init(), ast_cdr_start(), AST_CEL_BLINDTRANSFER, ast_cel_report_event(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, AST_CONTROL_HOLD, ast_debug, AST_DIGIT_ANY, AST_FEATURE_RETURN_SUCCESS, AST_FEATURE_RETURN_SUCCESSBREAK, AST_FLAG_BRIDGE_HANGUP_DONT, ast_indicate(), ast_log(), ast_set_flag, ast_stream_and_wait(), ast_verb, ast_channel::cdr, ast_cdr::channel, check_goto_on_transfer(), ast_cdr::dstchannel, finishup(), get_parking_exten(), ast_cdr::lastapp, ast_cdr::lastdata, LOG_WARNING, ast_channel::pbx, pbx_builtin_setvar_helper(), real_ctx(), set_c_e_p(), set_peers(), transferdigittimeout, and xfer_park_call_helper().

02257 {
02258    struct ast_channel *transferer;
02259    struct ast_channel *transferee;
02260    struct ast_exten *park_exten;
02261    const char *transferer_real_context;
02262    char xferto[256] = "";
02263    int res;
02264 
02265    ast_debug(1, "Executing Blind Transfer %s, %s (sense=%d) \n", ast_channel_name(chan), ast_channel_name(peer), sense);
02266    set_peers(&transferer, &transferee, peer, chan, sense);
02267    transferer_real_context = real_ctx(transferer, transferee);
02268 
02269    /* Start autoservice on transferee while we talk to the transferer */
02270    ast_autoservice_start(transferee);
02271    ast_indicate(transferee, AST_CONTROL_HOLD);
02272 
02273    /* Transfer */
02274    res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
02275    if (res < 0) {
02276       finishup(transferee);
02277       return -1; /* error ? */
02278    }
02279    if (res > 0) { /* If they've typed a digit already, handle it */
02280       xferto[0] = (char) res;
02281    }
02282 
02283    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
02284    if (res < 0) {  /* hangup or error, (would be 0 for invalid and 1 for valid) */
02285       finishup(transferee);
02286       return -1;
02287    }
02288    if (res == 0) {
02289       if (xferto[0]) {
02290          ast_log(LOG_WARNING, "Extension '%s' does not exist in context '%s'\n",
02291             xferto, transferer_real_context);
02292       } else {
02293          /* Does anyone care about this case? */
02294          ast_log(LOG_WARNING, "No digits dialed.\n");
02295       }
02296       ast_stream_and_wait(transferer, "pbx-invalid", "");
02297       finishup(transferee);
02298       return AST_FEATURE_RETURN_SUCCESS;
02299    }
02300 
02301    park_exten = get_parking_exten(xferto, transferer, transferer_real_context);
02302    if (park_exten) {
02303       /* We are transfering the transferee to a parking lot. */
02304       return xfer_park_call_helper(transferee, transferer, park_exten);
02305    }
02306 
02307    /* Do blind transfer. */
02308    ast_verb(3, "Blind transferring %s to '%s' (context %s) priority 1\n",
02309       ast_channel_name(transferee), xferto, transferer_real_context);
02310    ast_cel_report_event(transferer, AST_CEL_BLINDTRANSFER, NULL, xferto, transferee);
02311    pbx_builtin_setvar_helper(transferer, "BLINDTRANSFER", ast_channel_name(transferee));
02312    pbx_builtin_setvar_helper(transferee, "BLINDTRANSFER", ast_channel_name(transferer));
02313    finishup(transferee);
02314    ast_channel_lock(transferer);
02315    if (!transferer->cdr) { /* this code should never get called (in a perfect world) */
02316       transferer->cdr = ast_cdr_alloc();
02317       if (transferer->cdr) {
02318          ast_cdr_init(transferer->cdr, transferer); /* initialize our channel's cdr */
02319          ast_cdr_start(transferer->cdr);
02320       }
02321    }
02322    ast_channel_unlock(transferer);
02323    if (transferer->cdr) {
02324       struct ast_cdr *swap = transferer->cdr;
02325 
02326       ast_debug(1,
02327          "transferer=%s; transferee=%s; lastapp=%s; lastdata=%s; chan=%s; dstchan=%s\n",
02328          ast_channel_name(transferer), ast_channel_name(transferee), transferer->cdr->lastapp,
02329          transferer->cdr->lastdata, transferer->cdr->channel,
02330          transferer->cdr->dstchannel);
02331       ast_debug(1, "TRANSFEREE; lastapp=%s; lastdata=%s, chan=%s; dstchan=%s\n",
02332          transferee->cdr->lastapp, transferee->cdr->lastdata, transferee->cdr->channel,
02333          transferee->cdr->dstchannel);
02334       ast_debug(1, "transferer_real_context=%s; xferto=%s\n",
02335          transferer_real_context, xferto);
02336       /* swap cdrs-- it will save us some time & work */
02337       transferer->cdr = transferee->cdr;
02338       transferee->cdr = swap;
02339    }
02340    if (!transferee->pbx) {
02341       /* Doh!  Use our handy async_goto functions */
02342       ast_debug(1, "About to ast_async_goto %s.\n", ast_channel_name(transferee));
02343       if (ast_async_goto(transferee, transferer_real_context, xferto, 1)) {
02344          ast_log(LOG_WARNING, "Async goto failed :-(\n");
02345       }
02346 
02347       /* The transferee is masqueraded and the original bridged channels can be hungup. */
02348       res = -1;
02349    } else {
02350       /* Set the transferee's new extension, since it exists, using transferer context */
02351       ast_debug(1, "About to explicit goto %s, it has a PBX.\n", ast_channel_name(transferee));
02352       ast_set_flag(transferee, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
02353       set_c_e_p(transferee, transferer_real_context, xferto, 0);
02354 
02355       /*
02356        * Break the bridge.  The transferee needs to resume executing
02357        * dialplan at the xferto location.
02358        */
02359       res = AST_FEATURE_RETURN_SUCCESSBREAK;
02360    }
02361    check_goto_on_transfer(transferer);
02362    return res;
02363 }

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

Definition at line 2213 of file features.c.

References AST_FEATURE_RETURN_HANGUP, and ast_verb.

02214 {
02215    ast_verb(4, "User hit '%s' to disconnect call.\n", code);
02216    return AST_FEATURE_RETURN_HANGUP;
02217 }

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

support routing for one touch call parking

Parameters:
chan channel parking call
peer channel to be parked
config unsed
code unused
sense feature options
data unused
Return values:
-1 on successful park.
-1 on chan hangup.
AST_FEATURE_RETURN_SUCCESS on error to keep the bridge connected.

Definition at line 1883 of file features.c.

References ast_channel::_state, ast_answer(), AST_FEATURE_RETURN_SUCCESS, ast_safe_sleep(), AST_STATE_UP, masq_park_call(), and set_peers().

01884 {
01885    struct ast_channel *parker;
01886    struct ast_channel *parkee;
01887    struct ast_park_call_args args = { 0, };
01888 
01889    /*
01890     * We used to set chan's exten and priority to "s" and 1 here,
01891     * but this generates (in some cases) an invalid extension, and
01892     * if "s" exists, could errantly cause execution of extensions
01893     * you don't expect.  It makes more sense to let nature take its
01894     * course when chan finishes, and let the pbx do its thing and
01895     * hang up when the park is over.
01896     */
01897 
01898    /* Answer if call is not up */
01899    if (chan->_state != AST_STATE_UP) {
01900       /*
01901        * XXX Why are we doing this?  Both of the channels should be up
01902        * since you cannot do DTMF features unless you are bridged.
01903        */
01904       if (ast_answer(chan)) {
01905          return -1;
01906       }
01907 
01908       /* Sleep to allow VoIP streams to settle down */
01909       if (ast_safe_sleep(chan, 1000)) {
01910          return -1;
01911       }
01912    }
01913 
01914    /* one direction used to call park_call.... */
01915    set_peers(&parker, &parkee, peer, chan, sense);
01916    return masq_park_call(parkee, parker, &args) ? AST_FEATURE_RETURN_SUCCESS : -1;
01917 }

static char* callback_dialoptions ( struct ast_flags features_callee,
struct ast_flags features_caller,
char *  options,
size_t  len 
) [static]

Definition at line 4519 of file features.c.

References AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, and ast_test_flag.

Referenced by manage_parked_call().

04520 {
04521    int i = 0;
04522    enum {
04523       OPT_CALLEE_REDIRECT   = 't',
04524       OPT_CALLER_REDIRECT   = 'T',
04525       OPT_CALLEE_AUTOMON    = 'w',
04526       OPT_CALLER_AUTOMON    = 'W',
04527       OPT_CALLEE_DISCONNECT = 'h',
04528       OPT_CALLER_DISCONNECT = 'H',
04529       OPT_CALLEE_PARKCALL   = 'k',
04530       OPT_CALLER_PARKCALL   = 'K',
04531    };
04532 
04533    memset(options, 0, len);
04534    if (ast_test_flag(features_caller, AST_FEATURE_REDIRECT) && i < len) {
04535       options[i++] = OPT_CALLER_REDIRECT;
04536    }
04537    if (ast_test_flag(features_caller, AST_FEATURE_AUTOMON) && i < len) {
04538       options[i++] = OPT_CALLER_AUTOMON;
04539    }
04540    if (ast_test_flag(features_caller, AST_FEATURE_DISCONNECT) && i < len) {
04541       options[i++] = OPT_CALLER_DISCONNECT;
04542    }
04543    if (ast_test_flag(features_caller, AST_FEATURE_PARKCALL) && i < len) {
04544       options[i++] = OPT_CALLER_PARKCALL;
04545    }
04546 
04547    if (ast_test_flag(features_callee, AST_FEATURE_REDIRECT) && i < len) {
04548       options[i++] = OPT_CALLEE_REDIRECT;
04549    }
04550    if (ast_test_flag(features_callee, AST_FEATURE_AUTOMON) && i < len) {
04551       options[i++] = OPT_CALLEE_AUTOMON;
04552    }
04553    if (ast_test_flag(features_callee, AST_FEATURE_DISCONNECT) && i < len) {
04554       options[i++] = OPT_CALLEE_DISCONNECT;
04555    }
04556    if (ast_test_flag(features_callee, AST_FEATURE_PARKCALL) && i < len) {
04557       options[i++] = OPT_CALLEE_PARKCALL;
04558    }
04559 
04560    return options;
04561 }

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

make channels compatible

Parameters:
c 
newchan 
Return values:
0 on success.
-1 on failure.

Definition at line 2372 of file features.c.

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

Referenced by builtin_atxfer().

02373 {
02374    if (ast_channel_make_compatible(c, newchan) < 0) {
02375       ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
02376          ast_channel_name(c), ast_channel_name(newchan));
02377       ast_hangup(newchan);
02378       return -1;
02379    }
02380    return 0;
02381 }

static void check_goto_on_transfer ( struct ast_channel chan  )  [static]

Check goto on transfer.

Parameters:
chan Check if channel has 'GOTO_ON_BLINDXFR' set, if not exit. When found make sure the types are compatible. Check if channel is valid if so start the new channel else hangup the call.

Definition at line 853 of file features.c.

References ast_channel::_state, ast_channel_alloc, ast_channel_clear_softhangup(), ast_channel_linkedid(), ast_channel_lock, ast_channel_masquerade(), ast_channel_name(), ast_channel_unlock, ast_clear_flag, ast_debug, ast_do_masquerade(), AST_FLAGS_ALL, ast_hangup(), ast_parseable_goto(), ast_pbx_start(), AST_SOFTHANGUP_ALL, AST_STATE_DOWN, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), pbx_builtin_getvar_helper(), ast_channel::readformat, and ast_channel::writeformat.

Referenced by builtin_blindtransfer().

00854 {
00855    struct ast_channel *xferchan;
00856    const char *val;
00857    char *goto_on_transfer;
00858    char *x;
00859 
00860    ast_channel_lock(chan);
00861    val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
00862    if (ast_strlen_zero(val)) {
00863       ast_channel_unlock(chan);
00864       return;
00865    }
00866    goto_on_transfer = ast_strdupa(val);
00867    ast_channel_unlock(chan);
00868 
00869    ast_debug(1, "Attempting GOTO_ON_BLINDXFR=%s for %s.\n", val, ast_channel_name(chan));
00870 
00871    xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(chan), 0,
00872       "%s", ast_channel_name(chan));
00873    if (!xferchan) {
00874       return;
00875    }
00876 
00877    /* Make formats okay */
00878    xferchan->readformat = chan->readformat;
00879    xferchan->writeformat = chan->writeformat;
00880 
00881    if (ast_channel_masquerade(xferchan, chan)) {
00882       /* Failed to setup masquerade. */
00883       ast_hangup(xferchan);
00884       return;
00885    }
00886 
00887    for (x = goto_on_transfer; *x; ++x) {
00888       if (*x == '^') {
00889          *x = ',';
00890       }
00891    }
00892    ast_parseable_goto(xferchan, goto_on_transfer);
00893    xferchan->_state = AST_STATE_UP;
00894    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
00895    ast_channel_clear_softhangup(xferchan, AST_SOFTHANGUP_ALL);
00896 
00897    if (ast_do_masquerade(xferchan) || ast_pbx_start(xferchan)) {
00898       /* Failed to do masquerade or could not start PBX. */
00899       ast_hangup(xferchan);
00900    }
00901 }

static void clear_dialed_interfaces ( struct ast_channel chan  )  [static]

Definition at line 3823 of file features.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, ast_datastore_free(), ast_log(), dialed_interface_info, LOG_DEBUG, and option_debug.

Referenced by ast_bridge_call().

03824 {
03825    struct ast_datastore *di_datastore;
03826 
03827    ast_channel_lock(chan);
03828    if ((di_datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL))) {
03829       if (option_debug) {
03830          ast_log(LOG_DEBUG, "Removing dialed interfaces datastore on %s since we're bridging\n", ast_channel_name(chan));
03831       }
03832       if (!ast_channel_datastore_remove(chan, di_datastore)) {
03833          ast_datastore_free(di_datastore);
03834       }
03835    }
03836    ast_channel_unlock(chan);
03837 }

static struct ast_parkinglot * copy_parkinglot ( const char *  name,
const struct ast_parkinglot parkinglot 
) [static, read]

Copy parkinglot and store it with new name.

Definition at line 4892 of file features.c.

References ao2_ref, ast_debug, ast_parkinglot::cfg, create_parkinglot(), and find_parkinglot().

Referenced by create_dynamic_parkinglot().

04893 {
04894    struct ast_parkinglot *copylot;
04895 
04896    if ((copylot = find_parkinglot(name))) { /* Parkinglot with that name already exists */
04897       ao2_ref(copylot, -1);
04898       return NULL;
04899    }
04900 
04901    copylot = create_parkinglot(name);
04902    if (!copylot) {
04903       return NULL;
04904    }
04905 
04906    ast_debug(1, "Building parking lot %s\n", name);
04907 
04908    /* Copy the source parking lot configuration. */
04909    copylot->cfg = parkinglot->cfg;
04910 
04911    return copylot;
04912 }

static struct ast_parkinglot* create_dynamic_parkinglot ( const char *  name,
struct ast_channel chan 
) [static, read]

Definition at line 1083 of file features.c.

References ao2_link, ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, ast_log(), ast_strdupa, ast_strlen_zero(), ast_parkinglot::cfg, copy_parkinglot(), default_parkinglot, find_parkinglot(), parkinglot_cfg::is_invalid, LOG_ERROR, LOG_WARNING, ast_parkinglot::name, parkinglot_cfg::parkext, parkinglot_cfg::parkext_exclusive, parkinglot_cfg::parking_con, parkinglot_cfg::parking_start, parkinglot_cfg::parking_stop, parkinglot_activate(), parkinglot_addref(), parkinglot_unref(), parkinglots, pbx_builtin_getvar_helper(), and S_OR.

Referenced by ast_masq_park_call_exten(), ast_park_call_exten(), park_call_exec(), park_space_reserve(), and xfer_park_call_helper().

01084 {
01085    const char *dyn_context;
01086    const char *dyn_exten;
01087    const char *dyn_range;
01088    const char *template_name;
01089    struct ast_parkinglot *template_parkinglot = NULL;
01090    struct ast_parkinglot *parkinglot;
01091    int dyn_start;
01092    int dyn_end;
01093 
01094    ast_channel_lock(chan);
01095    template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
01096    dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
01097    dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
01098    dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
01099    ast_channel_unlock(chan);
01100 
01101    if (!ast_strlen_zero(template_name)) {
01102       template_parkinglot = find_parkinglot(template_name);
01103       if (!template_parkinglot) {
01104          ast_debug(1, "PARKINGDYNAMIC lot %s does not exist.\n",
01105             template_name);
01106       } else if (template_parkinglot->cfg.is_invalid) {
01107          ast_debug(1, "PARKINGDYNAMIC lot %s has invalid config.\n",
01108             template_name);
01109          parkinglot_unref(template_parkinglot);
01110          template_parkinglot = NULL;
01111       }
01112    }
01113    if (!template_parkinglot) {
01114       template_parkinglot = parkinglot_addref(default_parkinglot);
01115       ast_debug(1, "Using default parking lot for template\n");
01116    }
01117 
01118    parkinglot = copy_parkinglot(name, template_parkinglot);
01119    if (!parkinglot) {
01120       ast_log(LOG_ERROR, "Could not build dynamic parking lot!\n");
01121    } else {
01122       /* Configure the dynamic parking lot. */
01123       if (!ast_strlen_zero(dyn_context)) {
01124          ast_copy_string(parkinglot->cfg.parking_con, dyn_context,
01125             sizeof(parkinglot->cfg.parking_con));
01126       }
01127       if (!ast_strlen_zero(dyn_exten)) {
01128          ast_copy_string(parkinglot->cfg.parkext, dyn_exten,
01129             sizeof(parkinglot->cfg.parkext));
01130       }
01131       if (!ast_strlen_zero(dyn_range)) {
01132          if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
01133             ast_log(LOG_WARNING,
01134                "Format for parking positions is a-b, where a and b are numbers\n");
01135          } else if (dyn_end < dyn_start || dyn_start <= 0 || dyn_end <= 0) {
01136             ast_log(LOG_WARNING,
01137                "Format for parking positions is a-b, where a <= b\n");
01138          } else {
01139             parkinglot->cfg.parking_start = dyn_start;
01140             parkinglot->cfg.parking_stop = dyn_end;
01141          }
01142       }
01143 
01144       /*
01145        * Sanity check for dynamic parking lot configuration.
01146        *
01147        * XXX It may be desirable to instead check if the dynamic
01148        * parking lot overlaps any existing lots like what is done for
01149        * a reload.
01150        */
01151       if (!strcmp(parkinglot->cfg.parking_con, template_parkinglot->cfg.parking_con)) {
01152          if (!strcmp(parkinglot->cfg.parkext, template_parkinglot->cfg.parkext)
01153             && parkinglot->cfg.parkext_exclusive) {
01154             ast_log(LOG_WARNING,
01155                "Parking lot '%s' conflicts with template parking lot '%s'!\n"
01156                "Change either PARKINGDYNCONTEXT or PARKINGDYNEXTEN.\n",
01157                parkinglot->name, template_parkinglot->name);
01158          }
01159          if ((template_parkinglot->cfg.parking_start <= parkinglot->cfg.parking_start
01160                && parkinglot->cfg.parking_start <= template_parkinglot->cfg.parking_stop)
01161             || (template_parkinglot->cfg.parking_start <= parkinglot->cfg.parking_stop
01162                && parkinglot->cfg.parking_stop <= template_parkinglot->cfg.parking_stop)
01163             || (parkinglot->cfg.parking_start < template_parkinglot->cfg.parking_start
01164                && template_parkinglot->cfg.parking_stop < parkinglot->cfg.parking_stop)) {
01165             ast_log(LOG_WARNING,
01166                "Parking lot '%s' parking spaces overlap template parking lot '%s'!\n"
01167                "Change PARKINGDYNPOS.\n",
01168                parkinglot->name, template_parkinglot->name);
01169          }
01170       }
01171 
01172       parkinglot_activate(parkinglot);
01173       ao2_link(parkinglots, parkinglot);
01174    }
01175    parkinglot_unref(template_parkinglot);
01176 
01177    return parkinglot;
01178 }

static struct ast_parkinglot * create_parkinglot ( const char *  name  )  [static, read]

Allocate parking lot structure.

Definition at line 5320 of file features.c.

References ao2_alloc, ast_copy_string(), AST_LIST_HEAD_INIT, ast_strlen_zero(), ast_parkinglot::cfg, parkinglot_cfg::is_invalid, ast_parkinglot::name, parkinglot_destroy(), and ast_parkinglot::parkings.

Referenced by build_parkinglot(), and copy_parkinglot().

05321 {
05322    struct ast_parkinglot *newlot;
05323 
05324    if (ast_strlen_zero(name)) { /* No name specified */
05325       return NULL;
05326    }
05327 
05328    newlot = ao2_alloc(sizeof(*newlot), parkinglot_destroy);
05329    if (!newlot)
05330       return NULL;
05331    
05332    ast_copy_string(newlot->name, name, sizeof(newlot->name));
05333    newlot->cfg.is_invalid = 1;/* No config is set yet. */
05334    AST_LIST_HEAD_INIT(&newlot->parkings);
05335 
05336    return newlot;
05337 }

static void destroy_dialplan_usage_context ( struct parking_dp_context doomed  )  [static]

Definition at line 5915 of file features.c.

References parking_dp_context::access_extens, ast_free, AST_LIST_REMOVE_HEAD, parking_dp_context::hints, and parking_dp_context::spaces.

Referenced by build_dialplan_useage_context(), and destroy_dialplan_usage_map().

05916 {
05917    struct parking_dp_ramp *ramp;
05918    struct parking_dp_spaces *spaces;
05919 
05920    while ((ramp = AST_LIST_REMOVE_HEAD(&doomed->access_extens, node))) {
05921       ast_free(ramp);
05922    }
05923    while ((spaces = AST_LIST_REMOVE_HEAD(&doomed->spaces, node))) {
05924       ast_free(spaces);
05925    }
05926    while ((spaces = AST_LIST_REMOVE_HEAD(&doomed->hints, node))) {
05927       ast_free(spaces);
05928    }
05929    ast_free(doomed);
05930 }

static void destroy_dialplan_usage_map ( struct parking_dp_map doomed  )  [static]

Definition at line 5940 of file features.c.

References AST_LIST_REMOVE_HEAD, and destroy_dialplan_usage_context().

Referenced by load_config().

05941 {
05942    struct parking_dp_context *item;
05943 
05944    while ((item = AST_LIST_REMOVE_HEAD(doomed, node))) {
05945       destroy_dialplan_usage_context(item);
05946    }
05947 }

static void destroy_space ( const char *  context,
int  space 
) [static]

Definition at line 6370 of file features.c.

References AST_MAX_EXTENSION, PRIORITY_HINT, and remove_exten_if_exist().

Referenced by remove_dead_context_usage(), and remove_dead_spaces_usage().

06371 {
06372    char exten[AST_MAX_EXTENSION];
06373 
06374    /* Destroy priorities of the parking space that we registered. */
06375    snprintf(exten, sizeof(exten), "%d", space);
06376    remove_exten_if_exist(context, exten, PRIORITY_HINT);
06377    remove_exten_if_exist(context, exten, 1);
06378 }

static void dial_features_destroy ( void *  data  )  [static]

Definition at line 747 of file features.c.

References ast_free.

00748 {
00749    struct ast_dial_features *df = data;
00750    if (df) {
00751       ast_free(df);
00752    }
00753 }

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

Definition at line 734 of file features.c.

References ast_calloc.

00735 {
00736    struct ast_dial_features *df = data, *df_copy;
00737  
00738    if (!(df_copy = ast_calloc(1, sizeof(*df)))) {
00739       return NULL;
00740    }
00741  
00742    memcpy(df_copy, df, sizeof(*df));
00743  
00744    return df_copy;
00745 }

static int dialplan_usage_add_parkinglot ( struct parking_dp_map usage_map,
struct ast_parkinglot lot,
int  complain 
) [static]

Definition at line 6223 of file features.c.

References AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, build_dialplan_useage_context(), ast_parkinglot::cfg, parking_dp_context::context, dialplan_usage_add_parkinglot_data(), and parkinglot_cfg::parking_con.

Referenced by build_dialplan_useage_map().

06224 {
06225    struct parking_dp_context *cur_ctx;
06226    struct parking_dp_context *new_ctx;
06227    int cmp;
06228 
06229    AST_LIST_TRAVERSE_SAFE_BEGIN(usage_map, cur_ctx, node) {
06230       cmp = strcmp(lot->cfg.parking_con, cur_ctx->context);
06231       if (cmp > 0) {
06232          /* The parking lot context goes after this node. */
06233          continue;
06234       }
06235       if (cmp == 0) {
06236          /* This is the node we will add parking lot spaces to the map. */
06237          return dialplan_usage_add_parkinglot_data(cur_ctx, lot, complain);
06238       }
06239       /* The new parking lot context goes before this node. */
06240       new_ctx = build_dialplan_useage_context(lot);
06241       if (!new_ctx) {
06242          return -1;
06243       }
06244       AST_LIST_INSERT_BEFORE_CURRENT(new_ctx, node);
06245       return 0;
06246    }
06247    AST_LIST_TRAVERSE_SAFE_END;
06248 
06249    /* New parking lot context goes on the end. */
06250    new_ctx = build_dialplan_useage_context(lot);
06251    if (!new_ctx) {
06252       return -1;
06253    }
06254    AST_LIST_INSERT_TAIL(usage_map, new_ctx, node);
06255    return 0;
06256 }

static int dialplan_usage_add_parkinglot_data ( struct parking_dp_context ctx_node,
struct ast_parkinglot lot,
int  complain 
) [static]

Definition at line 6169 of file features.c.

References parking_dp_context::access_extens, ast_parkinglot::cfg, parking_dp_context::hints, parkinglot_cfg::parkaddhints, parkinglot_cfg::parkext, parkinglot_cfg::parkext_exclusive, parkinglot_cfg::parking_start, parkinglot_cfg::parking_stop, parking_dp_context::spaces, usage_context_add_ramp(), and usage_context_add_spaces().

Referenced by build_dialplan_useage_context(), and dialplan_usage_add_parkinglot().

06170 {
06171    if (usage_context_add_ramp(&ctx_node->access_extens, lot->cfg.parkext,
06172       lot->cfg.parkext_exclusive, lot, complain)) {
06173       return -1;
06174    }
06175    if (usage_context_add_spaces(&ctx_node->spaces, lot->cfg.parking_start,
06176       lot->cfg.parking_stop, lot, complain)) {
06177       return -1;
06178    }
06179    if (lot->cfg.parkaddhints
06180       && usage_context_add_spaces(&ctx_node->hints, lot->cfg.parking_start,
06181          lot->cfg.parking_stop, lot, 0)) {
06182       return -1;
06183    }
06184    return 0;
06185 }

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

Actual bridge.

Parameters:
chan 
tmpchan Stop hold music, lock both channels, masq channels, after bridge return channel to next priority.

Definition at line 6810 of file features.c.

References ast_channel::_state, ast_channel_lock_both, ast_channel_masquerade(), ast_channel_unlock, ast_do_masquerade(), ast_explicit_goto(), ast_moh_stop(), ast_setstate(), ast_channel::context, ast_channel::exten, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat.

Referenced by action_bridge(), and bridge_exec().

06811 {
06812    ast_moh_stop(chan);
06813    ast_channel_lock_both(chan, tmpchan);
06814    ast_setstate(tmpchan, chan->_state);
06815    tmpchan->readformat = chan->readformat;
06816    tmpchan->writeformat = chan->writeformat;
06817    ast_channel_unlock(chan);
06818    ast_channel_unlock(tmpchan);
06819 
06820    ast_channel_masquerade(tmpchan, chan);
06821 
06822    /* must be done without any channel locks held */
06823    ast_do_masquerade(tmpchan);
06824 
06825    /* when returning from bridge, the channel will continue at the next priority */
06826    ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1);
06827 }

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

Take care of parked calls and unpark them if needed.

Parameters:
ignore unused var.
Start inf loop, lock parking lot, check if any parked channels have gone above timeout if so, remove channel from parking lot and return it to the extension that parked it. Check if parked channel decided to hangup, wait until next FD via select().

Definition at line 4842 of file features.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_free, ast_poll, manage_parkinglot(), and parkinglots.

Referenced by ast_features_init().

04843 {
04844    struct pollfd *pfds = NULL, *new_pfds = NULL;
04845    int nfds = 0, new_nfds = 0;
04846 
04847    for (;;) {
04848       struct ao2_iterator iter;
04849       struct ast_parkinglot *curlot;
04850       int ms = -1;   /* poll2 timeout, uninitialized */
04851 
04852       iter = ao2_iterator_init(parkinglots, 0);
04853       while ((curlot = ao2_iterator_next(&iter))) {
04854          manage_parkinglot(curlot, pfds, nfds, &new_pfds, &new_nfds, &ms);
04855          ao2_ref(curlot, -1);
04856       }
04857       ao2_iterator_destroy(&iter);
04858 
04859       /* Recycle */
04860       ast_free(pfds);
04861       pfds = new_pfds;
04862       nfds = new_nfds;
04863       new_pfds = NULL;
04864       new_nfds = 0;
04865 
04866       /* Wait for something to happen */
04867       ast_poll(pfds, nfds, ms);
04868       pthread_testcancel();
04869    }
04870    /* If this WERE reached, we'd need to free(pfds) */
04871    return NULL;   /* Never reached */
04872 }

static int feature_check ( struct ast_channel chan,
struct ast_flags features,
char *  code 
) [static]

Check if a feature exists.

Definition at line 3292 of file features.c.

References ast_channel_lock, ast_channel_unlock, ast_strdupa, FEATURE_INTERPRET_CHECK, feature_interpret_helper(), pbx_builtin_getvar_helper(), and S_OR.

Referenced by ast_bridge_call().

03292                                                                                            {
03293    char *chan_dynamic_features;
03294    ast_channel_lock(chan);
03295    chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
03296    ast_channel_unlock(chan);
03297 
03298    return feature_interpret_helper(chan, NULL, NULL, code, 0, chan_dynamic_features, features, FEATURE_INTERPRET_CHECK, NULL);
03299 }

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

exec an app by feature

Parameters:
chan,peer,config,code,sense,data Find a feature, determine which channel activated
Return values:
AST_FEATURE_RETURN_NO_HANGUP_PEER 
-1 error.
-2 when an application cannot be found.

Todo:
XXX should probably return res

Definition at line 3039 of file features.c.

References ast_call_feature::app, app, ast_call_feature::app_args, ast_autoservice_ignore(), ast_autoservice_start(), ast_autoservice_stop(), ast_channel_name(), AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_FLAG_ONSELF, AST_FEATURE_RETURN_KEEPTRYING, AST_FEATURE_RETURN_SUCCESS, AST_FEATURE_RETURN_SUCCESSBREAK, AST_FRAME_DTMF_END, ast_log(), ast_moh_start(), ast_moh_stop(), ast_strlen_zero(), ast_test_flag, FEATURE_SENSE_CHAN, LOG_NOTICE, LOG_WARNING, ast_call_feature::moh_class, pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), and ast_call_feature::sname.

Referenced by process_applicationmap_line().

03040 {
03041    struct ast_app *app;
03042    struct ast_call_feature *feature = data;
03043    struct ast_channel *work, *idle;
03044    int res;
03045 
03046    if (!feature) { /* shouldn't ever happen! */
03047       ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
03048       return -1; 
03049    }
03050 
03051    if (sense == FEATURE_SENSE_CHAN) {
03052       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
03053          return AST_FEATURE_RETURN_KEEPTRYING;
03054       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
03055          work = chan;
03056          idle = peer;
03057       } else {
03058          work = peer;
03059          idle = chan;
03060       }
03061    } else {
03062       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
03063          return AST_FEATURE_RETURN_KEEPTRYING;
03064       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
03065          work = peer;
03066          idle = chan;
03067       } else {
03068          work = chan;
03069          idle = peer;
03070       }
03071    }
03072 
03073    if (!(app = pbx_findapp(feature->app))) {
03074       ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
03075       return -2;
03076    }
03077 
03078    ast_autoservice_start(idle);
03079    ast_autoservice_ignore(idle, AST_FRAME_DTMF_END);
03080    
03081    if(work && idle) {
03082       pbx_builtin_setvar_helper(work, "DYNAMIC_PEERNAME", ast_channel_name(idle));
03083       pbx_builtin_setvar_helper(idle, "DYNAMIC_PEERNAME", ast_channel_name(work));
03084       pbx_builtin_setvar_helper(work, "DYNAMIC_FEATURENAME", feature->sname);
03085       pbx_builtin_setvar_helper(idle, "DYNAMIC_FEATURENAME", feature->sname);
03086    }
03087 
03088    if (!ast_strlen_zero(feature->moh_class))
03089       ast_moh_start(idle, feature->moh_class, NULL);
03090 
03091    res = pbx_exec(work, app, feature->app_args);
03092 
03093    if (!ast_strlen_zero(feature->moh_class))
03094       ast_moh_stop(idle);
03095 
03096    ast_autoservice_stop(idle);
03097 
03098    if (res) {
03099       return AST_FEATURE_RETURN_SUCCESSBREAK;
03100    }
03101    return AST_FEATURE_RETURN_SUCCESS;  /*! \todo XXX should probably return res */
03102 }

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

Check the dynamic features.

Parameters:
chan,peer,config,code,sense 
Return values:
res on success.
-1 on failure.

Definition at line 3257 of file features.c.

References ast_channel_lock, ast_channel_name(), ast_channel_unlock, ast_copy_flags, ast_debug, AST_FLAGS_ALL, ast_strdupa, FEATURE_INTERPRET_DO, feature_interpret_helper(), FEATURE_SENSE_CHAN, ast_bridge_config::features_callee, ast_bridge_config::features_caller, ast_flags::flags, pbx_builtin_getvar_helper(), and S_OR.

Referenced by ast_bridge_call().

03257                                                                                                                                                 {
03258 
03259    char dynamic_features_buf[128];
03260    const char *peer_dynamic_features, *chan_dynamic_features;
03261    struct ast_flags features;
03262    struct ast_call_feature feature;
03263    if (sense == FEATURE_SENSE_CHAN) {
03264       ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
03265    }
03266    else {
03267       ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
03268    }
03269 
03270    ast_channel_lock(peer);
03271    peer_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"),""));
03272    ast_channel_unlock(peer);
03273 
03274    ast_channel_lock(chan);
03275    chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
03276    ast_channel_unlock(chan);
03277 
03278    snprintf(dynamic_features_buf, sizeof(dynamic_features_buf), "%s%s%s", S_OR(chan_dynamic_features, ""), chan_dynamic_features && peer_dynamic_features ? "#" : "", S_OR(peer_dynamic_features,""));
03279 
03280    ast_debug(3, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d, dynamic=%s\n", ast_channel_name(chan), ast_channel_name(peer), code, sense, features.flags, dynamic_features_buf);
03281 
03282    return feature_interpret_helper(chan, peer, config, code, sense, dynamic_features_buf, &features, FEATURE_INTERPRET_DO, &feature);
03283 }

static int feature_interpret_helper ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
const char *  code,
int  sense,
char *  dynamic_features_buf,
struct ast_flags features,
feature_interpret_op  operation,
struct ast_call_feature feature 
) [static]

Helper function for feature_interpret and ast_feature_detect.

Parameters:
chan,peer,config,code,sense,dynamic_features_buf,features,operation,feature Lock features list, browse for code, unlock list If a feature is found and the operation variable is set, that feature's operation is executed. The first feature found is copied to the feature parameter.
Return values:
res on success.
-1 on failure.

Definition at line 3142 of file features.c.

References ast_debug, AST_FEATURE_RETURN_KEEPTRYING, AST_FEATURE_RETURN_PASSDIGITS, AST_FEATURE_RETURN_STOREDIGITS, AST_FEATURE_RETURN_SUCCESS, AST_LIST_TRAVERSE, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, ast_rwlock_rdlock, ast_rwlock_unlock, ast_strlen_zero(), ast_test_flag, ast_verb, ast_call_feature::exten, feature_group_exten::exten, feature_group_exten::feature, FEATURE_INTERPRET_CHECK, FEATURE_INTERPRET_DO, ast_call_feature::feature_mask, feature_group::features, FEATURES_COUNT, features_lock, find_dynamic_feature(), find_group(), ast_call_feature::fname, ast_call_feature::operation, ast_call_feature::sname, and strsep().

Referenced by ast_feature_detect(), feature_check(), and feature_interpret().

03145 {
03146    int x;
03147    struct feature_group *fg = NULL;
03148    struct feature_group_exten *fge;
03149    struct ast_call_feature *tmpfeature;
03150    char *tmp, *tok;
03151    int res = AST_FEATURE_RETURN_PASSDIGITS;
03152    int feature_detected = 0;
03153 
03154    if (!(peer && chan && config) && operation == FEATURE_INTERPRET_DO) {
03155       return -1; /* can not run feature operation */
03156    }
03157 
03158    ast_rwlock_rdlock(&features_lock);
03159    for (x = 0; x < FEATURES_COUNT; x++) {
03160       if ((ast_test_flag(features, builtin_features[x].feature_mask)) &&
03161           !ast_strlen_zero(builtin_features[x].exten)) {
03162          /* Feature is up for consideration */
03163          if (!strcmp(builtin_features[x].exten, code)) {
03164             ast_debug(3, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
03165             if (operation == FEATURE_INTERPRET_CHECK) {
03166                res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
03167             } else if (operation == FEATURE_INTERPRET_DO) {
03168                res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
03169             }
03170             if (feature) {
03171                memcpy(feature, &builtin_features[x], sizeof(feature));
03172             }
03173             feature_detected = 1;
03174             break;
03175          } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
03176             if (res == AST_FEATURE_RETURN_PASSDIGITS) {
03177                res = AST_FEATURE_RETURN_STOREDIGITS;
03178             }
03179          }
03180       }
03181    }
03182    ast_rwlock_unlock(&features_lock);
03183 
03184    if (ast_strlen_zero(dynamic_features_buf) || feature_detected) {
03185       return res;
03186    }
03187 
03188    tmp = dynamic_features_buf;
03189 
03190    while ((tok = strsep(&tmp, "#"))) {
03191       AST_RWLIST_RDLOCK(&feature_groups);
03192 
03193       fg = find_group(tok);
03194 
03195       if (fg) {
03196          AST_LIST_TRAVERSE(&fg->features, fge, entry) {
03197             if (!strcmp(fge->exten, code)) {
03198                if (operation) {
03199                   res = fge->feature->operation(chan, peer, config, code, sense, fge->feature);
03200                }
03201                memcpy(feature, fge->feature, sizeof(feature));
03202                if (res != AST_FEATURE_RETURN_KEEPTRYING) {
03203                   AST_RWLIST_UNLOCK(&feature_groups);
03204                   break;
03205                }
03206                res = AST_FEATURE_RETURN_PASSDIGITS;
03207             } else if (!strncmp(fge->exten, code, strlen(code))) {
03208                res = AST_FEATURE_RETURN_STOREDIGITS;
03209             }
03210          }
03211          if (fge) {
03212             break;
03213          }
03214       }
03215 
03216       AST_RWLIST_UNLOCK(&feature_groups);
03217 
03218       AST_RWLIST_RDLOCK(&feature_list);
03219 
03220       if (!(tmpfeature = find_dynamic_feature(tok))) {
03221          AST_RWLIST_UNLOCK(&feature_list);
03222          continue;
03223       }
03224 
03225       /* Feature is up for consideration */
03226       if (!strcmp(tmpfeature->exten, code)) {
03227          ast_verb(3, " Feature Found: %s exten: %s\n",tmpfeature->sname, tok);
03228          if (operation == FEATURE_INTERPRET_CHECK) {
03229             res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
03230          } else if (operation == FEATURE_INTERPRET_DO) {
03231             res = tmpfeature->operation(chan, peer, config, code, sense, tmpfeature);
03232          }
03233          if (feature) {
03234             memcpy(feature, tmpfeature, sizeof(feature));
03235          }
03236          if (res != AST_FEATURE_RETURN_KEEPTRYING) {
03237             AST_RWLIST_UNLOCK(&feature_list);
03238             break;
03239          }
03240          res = AST_FEATURE_RETURN_PASSDIGITS;
03241       } else if (!strncmp(tmpfeature->exten, code, strlen(code)))
03242          res = AST_FEATURE_RETURN_STOREDIGITS;
03243 
03244       AST_RWLIST_UNLOCK(&feature_list);
03245    }
03246 
03247    return res;
03248 }

static struct ast_channel * feature_request_and_dial ( struct ast_channel caller,
const char *  caller_name,
struct ast_channel requestor,
struct ast_channel transferee,
const char *  type,
struct ast_format_cap cap,
const char *  addr,
int  timeout,
int *  outstate,
const char *  language 
) [static, read]

Definition at line 3396 of file features.c.

References ast_channel::_state, ast_autoservice_start(), ast_autoservice_stop(), ast_best_codec(), ast_call(), ast_call_forward(), AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, ast_channel_call_forward(), ast_channel_connected_line_macro(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_name(), ast_channel_redirecting_macro(), ast_channel_set_connected_line(), ast_channel_unlock, ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_connected_line_parse_data(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_INCOMPLETE, AST_CONTROL_PROCEEDING, AST_CONTROL_PROGRESS, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_format_cap_add(), ast_format_cap_alloc_nolock(), ast_format_cap_destroy(), AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frfree, ast_frisolate(), ast_hangup(), ast_indicate(), ast_indicate_data(), ast_is_deferrable_frame(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_HEAD_NOLOCK, AST_LIST_INSERT_HEAD, AST_LIST_REMOVE_HEAD, ast_log(), ast_party_connected_line_free(), ast_party_connected_line_set_init(), ast_poll_channel_add(), ast_poll_channel_del(), ast_queue_frame_head(), ast_read(), ast_request(), ast_rwlock_rdlock, ast_rwlock_unlock, AST_STATE_UP, ast_strlen_zero(), ast_tvdiff_ms(), ast_tvnow(), ast_verb, ast_waitfor_n(), ast_write(), ast_channel::caller, cause, ast_channel::connected, ast_frame::data, ast_frame::datalen, ast_channel::exten, ast_call_feature::exten, f, FEATURES_COUNT, features_lock, ast_frame::frametype, ast_channel::hangupcause, ast_frame_subclass::integer, LOG_NOTICE, pbx_builtin_setvar_helper(), ast_frame::ptr, and ast_frame::subclass.

Referenced by builtin_atxfer().

03400 {
03401    int state = 0;
03402    int cause = 0;
03403    int to;
03404    int caller_hungup;
03405    int transferee_hungup;
03406    struct ast_channel *chan;
03407    struct ast_channel *monitor_chans[3];
03408    struct ast_channel *active_channel;
03409    int res;
03410    int ready = 0;
03411    struct timeval started;
03412    int x, len = 0;
03413    char *disconnect_code = NULL, *dialed_code = NULL;
03414    struct ast_format_cap *tmp_cap;
03415    struct ast_format best_audio_fmt;
03416    struct ast_frame *f;
03417    AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
03418 
03419    tmp_cap = ast_format_cap_alloc_nolock();
03420    if (!tmp_cap) {
03421       if (outstate) {
03422          *outstate = 0;
03423       }
03424       return NULL;
03425    }
03426    ast_best_codec(cap, &best_audio_fmt);
03427    ast_format_cap_add(tmp_cap, &best_audio_fmt);
03428 
03429    caller_hungup = ast_check_hangup(caller);
03430 
03431    if (!(chan = ast_request(type, tmp_cap, requestor, addr, &cause))) {
03432       ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, addr);
03433       switch (cause) {
03434       case AST_CAUSE_BUSY:
03435          state = AST_CONTROL_BUSY;
03436          break;
03437       case AST_CAUSE_CONGESTION:
03438          state = AST_CONTROL_CONGESTION;
03439          break;
03440       default:
03441          state = 0;
03442          break;
03443       }
03444       goto done;
03445    }
03446 
03447    ast_channel_language_set(chan, language);
03448    ast_channel_inherit_variables(caller, chan);
03449    pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller_name);
03450 
03451    ast_channel_lock(chan);
03452    ast_connected_line_copy_from_caller(&chan->connected, &requestor->caller);
03453    ast_channel_unlock(chan);
03454 
03455    if (ast_call(chan, addr, timeout)) {
03456       ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, addr);
03457       switch (chan->hangupcause) {
03458       case AST_CAUSE_BUSY:
03459          state = AST_CONTROL_BUSY;
03460          break;
03461       case AST_CAUSE_CONGESTION:
03462          state = AST_CONTROL_CONGESTION;
03463          break;
03464       default:
03465          state = 0;
03466          break;
03467       }
03468       goto done;
03469    }
03470 
03471    /* support dialing of the featuremap disconnect code while performing an attended tranfer */
03472    ast_rwlock_rdlock(&features_lock);
03473    for (x = 0; x < FEATURES_COUNT; x++) {
03474       if (strcasecmp(builtin_features[x].sname, "disconnect"))
03475          continue;
03476 
03477       disconnect_code = builtin_features[x].exten;
03478       len = strlen(disconnect_code) + 1;
03479       dialed_code = alloca(len);
03480       memset(dialed_code, 0, len);
03481       break;
03482    }
03483    ast_rwlock_unlock(&features_lock);
03484    x = 0;
03485    started = ast_tvnow();
03486    to = timeout;
03487    AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
03488 
03489    ast_poll_channel_add(caller, chan);
03490 
03491    transferee_hungup = 0;
03492    while (!ast_check_hangup(transferee) && (chan->_state != AST_STATE_UP)) {
03493       int num_chans = 0;
03494 
03495       monitor_chans[num_chans++] = transferee;
03496       monitor_chans[num_chans++] = chan;
03497       if (!caller_hungup) {
03498          if (ast_check_hangup(caller)) {
03499             caller_hungup = 1;
03500 
03501 #if defined(ATXFER_NULL_TECH)
03502             /* Change caller's name to ensure that it will remain unique. */
03503             set_new_chan_name(caller);
03504 
03505             /*
03506              * Get rid of caller's physical technology so it is free for
03507              * other calls.
03508              */
03509             set_kill_chan_tech(caller);
03510 #endif   /* defined(ATXFER_NULL_TECH) */
03511          } else {
03512             /* caller is not hungup so monitor it. */
03513             monitor_chans[num_chans++] = caller;
03514          }
03515       }
03516 
03517       /* see if the timeout has been violated */
03518       if (ast_tvdiff_ms(ast_tvnow(), started) > timeout) {
03519          state = AST_CONTROL_UNHOLD;
03520          ast_log(LOG_NOTICE, "We exceeded our AT-timeout for %s\n", ast_channel_name(chan));
03521          break; /*doh! timeout*/
03522       }
03523 
03524       active_channel = ast_waitfor_n(monitor_chans, num_chans, &to);
03525       if (!active_channel)
03526          continue;
03527 
03528       f = NULL;
03529       if (transferee == active_channel) {
03530          struct ast_frame *dup_f;
03531 
03532          f = ast_read(transferee);
03533          if (f == NULL) { /*doh! where'd he go?*/
03534             transferee_hungup = 1;
03535             state = 0;
03536             break;
03537          }
03538          if (ast_is_deferrable_frame(f)) {
03539             dup_f = ast_frisolate(f);
03540             if (dup_f) {
03541                if (dup_f == f) {
03542                   f = NULL;
03543                }
03544                AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list);
03545             }
03546          }
03547       } else if (chan == active_channel) {
03548          if (!ast_strlen_zero(ast_channel_call_forward(chan))) {
03549             state = 0;
03550             ast_autoservice_start(transferee);
03551             chan = ast_call_forward(caller, chan, NULL, tmp_cap, NULL, &state);
03552             ast_autoservice_stop(transferee);
03553             if (!chan) {
03554                break;
03555             }
03556             continue;
03557          }
03558          f = ast_read(chan);
03559          if (f == NULL) { /*doh! where'd he go?*/
03560             switch (chan->hangupcause) {
03561             case AST_CAUSE_BUSY:
03562                state = AST_CONTROL_BUSY;
03563                break;
03564             case AST_CAUSE_CONGESTION:
03565                state = AST_CONTROL_CONGESTION;
03566                break;
03567             default:
03568                state = 0;
03569                break;
03570             }
03571             break;
03572          }
03573 
03574          if (f->frametype == AST_FRAME_CONTROL) {
03575             if (f->subclass.integer == AST_CONTROL_RINGING) {
03576                ast_verb(3, "%s is ringing\n", ast_channel_name(chan));
03577                ast_indicate(caller, AST_CONTROL_RINGING);
03578             } else if (f->subclass.integer == AST_CONTROL_BUSY) {
03579                state = f->subclass.integer;
03580                ast_verb(3, "%s is busy\n", ast_channel_name(chan));
03581                ast_indicate(caller, AST_CONTROL_BUSY);
03582                ast_frfree(f);
03583                break;
03584             } else if (f->subclass.integer == AST_CONTROL_INCOMPLETE) {
03585                ast_verb(3, "%s dialed incomplete extension %s; ignoring\n", ast_channel_name(chan), chan->exten);
03586             } else if (f->subclass.integer == AST_CONTROL_CONGESTION) {
03587                state = f->subclass.integer;
03588                ast_verb(3, "%s is congested\n", ast_channel_name(chan));
03589                ast_indicate(caller, AST_CONTROL_CONGESTION);
03590                ast_frfree(f);
03591                break;
03592             } else if (f->subclass.integer == AST_CONTROL_ANSWER) {
03593                /* This is what we are hoping for */
03594                state = f->subclass.integer;
03595                ast_frfree(f);
03596                ready=1;
03597                break;
03598             } else if (f->subclass.integer == AST_CONTROL_CONNECTED_LINE) {
03599                if (caller_hungup) {
03600                   struct ast_party_connected_line connected;
03601 
03602                   /* Just save it for the transfer. */
03603                   ast_party_connected_line_set_init(&connected, &caller->connected);
03604                   res = ast_connected_line_parse_data(f->data.ptr, f->datalen,
03605                      &connected);
03606                   if (!res) {
03607                      ast_channel_set_connected_line(caller, &connected, NULL);
03608                   }
03609                   ast_party_connected_line_free(&connected);
03610                } else {
03611                   ast_autoservice_start(transferee);
03612                   if (ast_channel_connected_line_macro(chan, caller, f, 1, 1)) {
03613                      ast_indicate_data(caller, AST_CONTROL_CONNECTED_LINE,
03614                         f->data.ptr, f->datalen);
03615                   }
03616                   ast_autoservice_stop(transferee);
03617                }
03618             } else if (f->subclass.integer == AST_CONTROL_REDIRECTING) {
03619                if (!caller_hungup) {
03620                   ast_autoservice_start(transferee);
03621                   if (ast_channel_redirecting_macro(chan, caller, f, 1, 1)) {
03622                      ast_indicate_data(caller, AST_CONTROL_REDIRECTING,
03623                         f->data.ptr, f->datalen);
03624                   }
03625                   ast_autoservice_stop(transferee);
03626                }
03627             } else if (f->subclass.integer != -1
03628                && f->subclass.integer != AST_CONTROL_PROGRESS
03629                && f->subclass.integer != AST_CONTROL_PROCEEDING) {
03630                ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass.integer);
03631             }
03632             /* else who cares */
03633          } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
03634             ast_write(caller, f);
03635          }
03636       } else if (caller == active_channel) {
03637          f = ast_read(caller);
03638          if (f) {
03639             if (f->frametype == AST_FRAME_DTMF) {
03640                dialed_code[x++] = f->subclass.integer;
03641                dialed_code[x] = '\0';
03642                if (strlen(dialed_code) == len) {
03643                   x = 0;
03644                } else if (x && strncmp(dialed_code, disconnect_code, x)) {
03645                   x = 0;
03646                   dialed_code[x] = '\0';
03647                }
03648                if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
03649                   /* Caller Canceled the call */
03650                   state = AST_CONTROL_UNHOLD;
03651                   ast_frfree(f);
03652                   break;
03653                }
03654             } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
03655                ast_write(chan, f);
03656             }
03657          }
03658       }
03659       if (f)
03660          ast_frfree(f);
03661    } /* end while */
03662 
03663    ast_poll_channel_del(caller, chan);
03664 
03665    /*
03666     * We need to free all the deferred frames, but we only need to
03667     * queue the deferred frames if no hangup was received.
03668     */
03669    ast_channel_lock(transferee);
03670    transferee_hungup = (transferee_hungup || ast_check_hangup(transferee));
03671    while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) {
03672       if (!transferee_hungup) {
03673          ast_queue_frame_head(transferee, f);
03674       }
03675       ast_frfree(f);
03676    }
03677    ast_channel_unlock(transferee);
03678 
03679 done:
03680    ast_indicate(caller, -1);
03681    if (chan && (ready || chan->_state == AST_STATE_UP)) {
03682       state = AST_CONTROL_ANSWER;
03683    } else if (chan) {
03684       ast_hangup(chan);
03685       chan = NULL;
03686    }
03687 
03688    tmp_cap = ast_format_cap_destroy(tmp_cap);
03689 
03690    if (outstate)
03691       *outstate = state;
03692 
03693    return chan;
03694 }

static int find_channel_by_group ( void *  obj,
void *  arg,
void *  data,
int  flags 
) [static]

< Potential pickup target

< Channel wanting to pickup call

Definition at line 7215 of file features.c.

References ast_can_pickup(), ast_channel_lock, ast_channel_unlock, ast_channel::callgroup, CMP_MATCH, CMP_STOP, and ast_channel::pickupgroup.

07216 {
07217    struct ast_channel *target = obj;/*!< Potential pickup target */
07218    struct ast_channel *chan = data;/*!< Channel wanting to pickup call */
07219 
07220    ast_channel_lock(target);
07221    if (chan != target && (chan->pickupgroup & target->callgroup)
07222       && ast_can_pickup(target)) {
07223       /* Return with the channel still locked on purpose */
07224       return CMP_MATCH | CMP_STOP;
07225    }
07226    ast_channel_unlock(target);
07227 
07228    return 0;
07229 }

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

find a call feature by name

Definition at line 2959 of file features.c.

References AST_RWLIST_TRAVERSE, ast_call_feature::feature_entry, and ast_call_feature::sname.

Referenced by ast_find_call_feature(), feature_interpret_helper(), process_applicationmap_line(), process_config(), and set_config_flags().

02960 {
02961    struct ast_call_feature *tmp;
02962 
02963    AST_RWLIST_TRAVERSE(&feature_list, tmp, feature_entry) {
02964       if (!strcasecmp(tmp->sname, name)) {
02965          break;
02966       }
02967    }
02968 
02969    return tmp;
02970 }

static struct feature_group* find_group ( const char *  name  )  [static, read]

Find a group by name.

Parameters:
name feature name
Return values:
feature group on success.
NULL on failure.

Definition at line 2997 of file features.c.

References AST_LIST_TRAVERSE, and feature_group::gname.

Referenced by feature_interpret_helper().

02998 {
02999    struct feature_group *fg = NULL;
03000 
03001    AST_LIST_TRAVERSE(&feature_groups, fg, entry) {
03002       if (!strcasecmp(fg->gname, name))
03003          break;
03004    }
03005 
03006    return fg;
03007 }

static struct ast_parkinglot * find_parkinglot ( const char *  name  )  [static, read]

Find parkinglot by name.

Definition at line 4875 of file features.c.

References ao2_find, ast_debug, ast_strlen_zero(), ast_parkinglot::name, and parkinglots.

Referenced by ast_masq_park_call_exten(), ast_park_call_exten(), build_parkinglot(), copy_parkinglot(), create_dynamic_parkinglot(), manager_park(), park_call_exec(), park_space_reserve(), parked_call_exec(), and xfer_park_call_helper().

04876 {
04877    struct ast_parkinglot *parkinglot;
04878 
04879    if (ast_strlen_zero(name)) {
04880       return NULL;
04881    }
04882 
04883    parkinglot = ao2_find(parkinglots, (void *) name, 0);
04884    if (parkinglot) {
04885       ast_debug(1, "Found Parking lot: %s\n", parkinglot->name);
04886    }
04887 
04888    return parkinglot;
04889 }

static const char* findparkinglotname ( struct ast_channel chan  )  [static]

Find parking lot name from channel.

Note:
Channel needs to be locked while the returned string is in use.

Definition at line 1000 of file features.c.

References ast_channel_parkinglot(), ast_strlen_zero(), name, and pbx_builtin_getvar_helper().

Referenced by park_call_exec(), park_space_reserve(), parked_call_exec(), and xfer_park_call_helper().

01001 {
01002    const char *name;
01003 
01004    /* The channel variable overrides everything */
01005    name = pbx_builtin_getvar_helper(chan, "PARKINGLOT");
01006    if (!name && !ast_strlen_zero(ast_channel_parkinglot(chan))) {
01007       /* Use the channel's parking lot. */
01008       name = ast_channel_parkinglot(chan);
01009    }
01010    return name;
01011 }

static int finishup ( struct ast_channel chan  )  [static]

Definition at line 1777 of file features.c.

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

Referenced by atxfer_fail_cleanup(), builtin_atxfer(), builtin_blindtransfer(), and xfer_park_call_helper().

01778 {
01779    ast_indicate(chan, AST_CONTROL_UNHOLD);
01780 
01781    return ast_autoservice_stop(chan);
01782 }

static struct ast_exten* get_parking_exten ( const char *  exten_str,
struct ast_channel chan,
const char *  context 
) [static, read]

Definition at line 781 of file features.c.

References ast_get_extension_app(), E_MATCH, parkcall, pbx_find_extension(), and pbx_find_info::stacklen.

Referenced by ast_masq_park_call_exten(), ast_park_call_exten(), ast_parking_ext_valid(), builtin_atxfer(), and builtin_blindtransfer().

00782 {
00783    struct ast_exten *exten;
00784    struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
00785    const char *app_at_exten;
00786 
00787    exten = pbx_find_extension(chan, NULL, &q, context, exten_str, 1, NULL, NULL,
00788       E_MATCH);
00789    if (!exten) {
00790       return NULL;
00791    }
00792 
00793    app_at_exten = ast_get_extension_app(exten);
00794    if (!app_at_exten || strcasecmp(parkcall, app_at_exten)) {
00795       return NULL;
00796    }
00797 
00798    return exten;
00799 }

static char* handle_feature_show ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

CLI command to list configured features.

Parameters:
e 
cmd 
a 
Return values:
CLI_SUCCESS on success.
NULL when tab completion is used.

Definition at line 6672 of file features.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_cli(), AST_CLI_YESNO, AST_LIST_TRAVERSE, ast_pickup_ext(), AST_RWLIST_EMPTY, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_rwlock_rdlock, ast_rwlock_unlock, ast_parkinglot::cfg, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, parkinglot_cfg::comebackcontext, parkinglot_cfg::comebackdialtime, parkinglot_cfg::comebacktoorigin, ast_cli_entry::command, ast_call_feature::default_exten, ast_parkinglot::disabled, feature_group_exten::exten, ast_call_feature::exten, ast_cli_args::fd, feature_group_exten::feature, feature_group::features, FEATURES_COUNT, features_lock, ast_call_feature::fname, feature_group::gname, HFS_FORMAT, parkinglot_cfg::mohclass, ast_parkinglot::name, parkinglot_cfg::parkext, parkinglot_cfg::parking_con, parkinglot_cfg::parking_start, parkinglot_cfg::parking_stop, parkinglots, parkinglot_cfg::parkingtime, ast_call_feature::sname, and ast_cli_entry::usage.

06673 {
06674    int i;
06675    struct ast_call_feature *feature;
06676    struct ao2_iterator iter;
06677    struct ast_parkinglot *curlot;
06678 #define HFS_FORMAT "%-25s %-7s %-7s\n"
06679 
06680    switch (cmd) {
06681    
06682    case CLI_INIT:
06683       e->command = "features show";
06684       e->usage =
06685          "Usage: features show\n"
06686          "       Lists configured features\n";
06687       return NULL;
06688    case CLI_GENERATE:
06689       return NULL;
06690    }
06691 
06692    ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
06693    ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
06694 
06695    ast_cli(a->fd, HFS_FORMAT, "Pickup", "*8", ast_pickup_ext());          /* default hardcoded above, so we'll hardcode it here */
06696 
06697    ast_rwlock_rdlock(&features_lock);
06698    for (i = 0; i < FEATURES_COUNT; i++)
06699       ast_cli(a->fd, HFS_FORMAT, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
06700    ast_rwlock_unlock(&features_lock);
06701 
06702    ast_cli(a->fd, "\n");
06703    ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
06704    ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
06705    if (AST_RWLIST_EMPTY(&feature_list)) {
06706       ast_cli(a->fd, "(none)\n");
06707    } else {
06708       AST_RWLIST_RDLOCK(&feature_list);
06709       AST_RWLIST_TRAVERSE(&feature_list, feature, feature_entry) {
06710          ast_cli(a->fd, HFS_FORMAT, feature->sname, "no def", feature->exten);
06711       }
06712       AST_RWLIST_UNLOCK(&feature_list);
06713    }
06714 
06715    ast_cli(a->fd, "\nFeature Groups:\n");
06716    ast_cli(a->fd, "---------------\n");
06717    if (AST_RWLIST_EMPTY(&feature_groups)) {
06718       ast_cli(a->fd, "(none)\n");
06719    } else {
06720       struct feature_group *fg;
06721       struct feature_group_exten *fge;
06722 
06723       AST_RWLIST_RDLOCK(&feature_groups);
06724       AST_RWLIST_TRAVERSE(&feature_groups, fg, entry) {
06725          ast_cli(a->fd, "===> Group: %s\n", fg->gname);
06726          AST_LIST_TRAVERSE(&fg->features, fge, entry) {
06727             ast_cli(a->fd, "===> --> %s (%s)\n", fge->feature->sname, fge->exten);
06728          }
06729       }
06730       AST_RWLIST_UNLOCK(&feature_groups);
06731    }
06732 
06733    iter = ao2_iterator_init(parkinglots, 0);
06734    while ((curlot = ao2_iterator_next(&iter))) {
06735       ast_cli(a->fd, "\nCall parking (Parking lot: %s)\n", curlot->name);
06736       ast_cli(a->fd, "------------\n");
06737       ast_cli(a->fd,"%-22s:      %s\n", "Parking extension", curlot->cfg.parkext);
06738       ast_cli(a->fd,"%-22s:      %s\n", "Parking context", curlot->cfg.parking_con);
06739       ast_cli(a->fd,"%-22s:      %d-%d\n", "Parked call extensions",
06740          curlot->cfg.parking_start, curlot->cfg.parking_stop);
06741       ast_cli(a->fd,"%-22s:      %d ms\n", "Parkingtime", curlot->cfg.parkingtime);
06742       ast_cli(a->fd,"%-22s:      %s\n", "Comeback to origin",
06743             (curlot->cfg.comebacktoorigin ? "yes" : "no"));
06744       ast_cli(a->fd,"%-22s:      %s%s\n", "Comeback context",
06745             curlot->cfg.comebackcontext, (curlot->cfg.comebacktoorigin ?
06746                " (comebacktoorigin=yes, not used)" : ""));
06747       ast_cli(a->fd,"%-22s:      %d\n", "Comeback dial time",
06748             curlot->cfg.comebackdialtime);
06749       ast_cli(a->fd,"%-22s:      %s\n", "MusicOnHold class", curlot->cfg.mohclass);
06750       ast_cli(a->fd,"%-22s:      %s\n", "Enabled", AST_CLI_YESNO(!curlot->disabled));
06751       ast_cli(a->fd,"\n");
06752       ao2_ref(curlot, -1);
06753    }
06754    ao2_iterator_destroy(&iter);
06755 
06756    return CLI_SUCCESS;
06757 }

static char* handle_features_reload ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 6785 of file features.c.

References ast_features_reload(), CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, and ast_cli_entry::usage.

06786 {
06787    switch (cmd) { 
06788    case CLI_INIT:
06789       e->command = "features reload";
06790       e->usage =
06791          "Usage: features reload\n"
06792          "       Reloads configured call features from features.conf\n";
06793       return NULL;
06794    case CLI_GENERATE:
06795       return NULL;
06796    }
06797    ast_features_reload();
06798 
06799    return CLI_SUCCESS;
06800 }

static char* handle_parkedcalls ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

CLI command to list parked calls.

Parameters:
e 
cmd 
a Check right usage, lock parking lot, display parked calls, unlock parking lot list.
Return values:
CLI_SUCCESS on success.
CLI_SHOWUSAGE on incorrect number of arguments.
NULL when tab completion is used.

Definition at line 6968 of file features.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_cli_args::argc, ast_cli_entry::args, ast_channel_name(), ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, parkeduser::chan, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, parkeduser::context, default_parkinglot, ESS, parkeduser::exten, ast_cli_args::fd, ast_parkinglot::name, parkeduser::parkingexten, parkinglots, ast_parkinglot::parkings, parkeduser::parkingtime, parkeduser::priority, parkeduser::start, and ast_cli_entry::usage.

06969 {
06970    struct parkeduser *cur;
06971    int numparked = 0;
06972    struct ao2_iterator iter;
06973    struct ast_parkinglot *curlot;
06974 
06975    switch (cmd) {
06976    case CLI_INIT:
06977       e->command = "parkedcalls show";
06978       e->usage =
06979          "Usage: parkedcalls show\n"
06980          "       List currently parked calls\n";
06981       return NULL;
06982    case CLI_GENERATE:
06983       return NULL;
06984    }
06985 
06986    if (a->argc > e->args)
06987       return CLI_SHOWUSAGE;
06988 
06989    ast_cli(a->fd, "%-10s %-25s (%-15s %-12s %4s) %s\n", "Num", "Channel",
06990       "Context", "Extension", "Pri", "Timeout");
06991 
06992    iter = ao2_iterator_init(parkinglots, 0);
06993    while ((curlot = ao2_iterator_next(&iter))) {
06994       int lotparked = 0;
06995 
06996       /* subtract ref for iterator and for configured parking lot */
06997       ast_cli(a->fd, "*** Parking lot: %s (%d)\n", curlot->name,
06998          ao2_ref(curlot, 0) - 2 - (curlot == default_parkinglot));
06999 
07000       AST_LIST_LOCK(&curlot->parkings);
07001       AST_LIST_TRAVERSE(&curlot->parkings, cur, list) {
07002          ast_cli(a->fd, "%-10.10s %-25s (%-15s %-12s %4d) %6lds\n",
07003             cur->parkingexten, ast_channel_name(cur->chan), cur->context, cur->exten,
07004             cur->priority,
07005             (long) (cur->start.tv_sec + (cur->parkingtime / 1000) - time(NULL)));
07006          ++lotparked;
07007       }
07008       AST_LIST_UNLOCK(&curlot->parkings);
07009       if (lotparked) {
07010          numparked += lotparked;
07011          ast_cli(a->fd, "   %d parked call%s in parking lot %s\n", lotparked,
07012             ESS(lotparked), curlot->name);
07013       }
07014 
07015       ao2_ref(curlot, -1);
07016    }
07017    ao2_iterator_destroy(&iter);
07018 
07019    ast_cli(a->fd, "---\n%d parked call%s in total.\n", numparked, ESS(numparked));
07020 
07021    return CLI_SUCCESS;
07022 }

static int load_config ( int  reload  )  [static]

Definition at line 6589 of file features.c.

References ao2_t_callback, ast_config_destroy(), ast_config_load2(), ast_debug, AST_LIST_HEAD_NOLOCK_INIT_VALUE, ast_log(), build_dialplan_useage_map(), build_parkinglot(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, DEFAULT_PARKINGLOT, default_parkinglot, destroy_dialplan_usage_map(), force_reload_load, LOG_ERROR, LOG_WARNING, OBJ_NODATA, OBJ_UNLINK, parkinglot_activate_cb(), parkinglot_addref(), parkinglot_is_marked_cb(), parkinglot_markall_cb(), parkinglots, process_config(), and remove_dead_dialplan_useage().

06590 {
06591    struct ast_flags config_flags = {
06592       reload && !force_reload_load ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06593    struct ast_config *cfg;
06594    struct parking_dp_map old_usage_map = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
06595    struct parking_dp_map new_usage_map = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
06596 
06597    /* We are reloading now and have already determined if we will force the reload. */
06598    force_reload_load = 0;
06599 
06600    if (!default_parkinglot) {
06601       /* Must create the default default parking lot */
06602       default_parkinglot = build_parkinglot(DEFAULT_PARKINGLOT, NULL);
06603       if (!default_parkinglot) {
06604          ast_log(LOG_ERROR, "Configuration of default default parking lot failed.\n");
06605          return -1;
06606       }
06607       ast_debug(1, "Configuration of default default parking lot done.\n");
06608       parkinglot_addref(default_parkinglot);
06609    }
06610 
06611    cfg = ast_config_load2("features.conf", "features", config_flags);
06612    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06613       /* No sense in asking for reload trouble if nothing changed. */
06614       ast_debug(1, "features.conf did not change.\n");
06615       return 0;
06616    }
06617    if (cfg == CONFIG_STATUS_FILEMISSING
06618       || cfg == CONFIG_STATUS_FILEINVALID) {
06619       ast_log(LOG_WARNING, "Could not load features.conf\n");
06620       return 0;
06621    }
06622 
06623    /* Save current parking lot dialplan needs. */
06624    if (build_dialplan_useage_map(&old_usage_map, 0)) {
06625       destroy_dialplan_usage_map(&old_usage_map);
06626 
06627       /* Allow reloading later to see if conditions have improved. */
06628       force_reload_load = 1;
06629       return -1;
06630    }
06631 
06632    ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_markall_cb, NULL,
06633       "callback to mark all parking lots");
06634    process_config(cfg);
06635    ast_config_destroy(cfg);
06636    ao2_t_callback(parkinglots, OBJ_NODATA | OBJ_UNLINK, parkinglot_is_marked_cb, NULL,
06637       "callback to remove marked parking lots");
06638 
06639    /* Save updated parking lot dialplan needs. */
06640    if (build_dialplan_useage_map(&new_usage_map, 1)) {
06641       /*
06642        * Yuck, if this failure caused any parking lot dialplan items
06643        * to be lost, they will likely remain lost until Asterisk is
06644        * restarted.
06645        */
06646       destroy_dialplan_usage_map(&old_usage_map);
06647       destroy_dialplan_usage_map(&new_usage_map);
06648       return -1;
06649    }
06650 
06651    /* Remove no longer needed parking lot dialplan usage. */
06652    remove_dead_dialplan_useage(&old_usage_map, &new_usage_map);
06653 
06654    destroy_dialplan_usage_map(&old_usage_map);
06655    destroy_dialplan_usage_map(&new_usage_map);
06656 
06657    ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_activate_cb, NULL,
06658       "callback to activate all parking lots");
06659 
06660    return 0;
06661 }

static int manage_parked_call ( struct parkeduser pu,
const struct pollfd *  pfds,
int  nfds,
struct pollfd **  new_pfds,
int *  new_nfds,
int *  ms 
) [static]

Definition at line 4571 of file features.c.

References ast_add_extension(), AST_CEL_PARK_END, ast_cel_report_event(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, ast_clear_flag, ast_context_find_or_create(), AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_debug, AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, ast_free_ptr, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_EXTENSION, ast_pbx_start(), ast_read(), ast_realloc, ast_set_flag, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_tvdiff_ms(), ast_tvnow(), ast_verb, callback_dialoptions(), ast_parkinglot::cfg, parkeduser::chan, parkinglot_cfg::comebackcontext, parkinglot_cfg::comebackdialtime, parkinglot_cfg::comebacktoorigin, ast_channel::context, parkeduser::context, ast_datastore::data, dial_features_info, ast_channel::exten, parkeduser::exten, f, ast_channel::fdno, ast_channel::fds, ast_dial_features::features_callee, ast_dial_features::features_caller, ast_frame::frametype, ast_channel::generatordata, parkeduser::hold_method, ast_frame_subclass::integer, LOG_ERROR, LOG_NOTICE, LOG_WARNING, MAX_DIAL_FEATURE_OPTIONS, parkeduser::moh_trys, parkinglot_cfg::mohclass, ast_parkinglot::name, parkeduser::options_specified, parking_con_dial, parkeduser::parkinglot, parkeduser::parkingnum, parkeduser::parkingtime, pbx_builtin_setvar_helper(), parkeduser::peername, post_manager_event(), ast_channel::priority, parkeduser::priority, registrar, S_OR, set_c_e_p(), parkeduser::start, and ast_frame::subclass.

Referenced by manage_parkinglot().

04572 {
04573    struct ast_channel *chan = pu->chan;   /* shorthand */
04574    int tms;        /* timeout for this item */
04575    int x;          /* fd index in channel */
04576    int parking_complete = 0;
04577 
04578    tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
04579    if (tms > pu->parkingtime) {
04580       /*
04581        * Call has been parked too long.
04582        * Stop entertaining the caller.
04583        */
04584       switch (pu->hold_method) {
04585       case AST_CONTROL_HOLD:
04586          ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
04587          break;
04588       case AST_CONTROL_RINGING:
04589          ast_indicate(pu->chan, -1);
04590          break;
04591       default:
04592          break;
04593       }
04594       pu->hold_method = 0;
04595 
04596       /* Get chan, exten from derived kludge */
04597       if (pu->peername[0]) {
04598          char *peername;
04599          char *dash;
04600          char *peername_flat; /* using something like DAHDI/52 for an extension name is NOT a good idea */
04601          int i;
04602 
04603          peername = ast_strdupa(pu->peername);
04604          dash = strrchr(peername, '-');
04605          if (dash) {
04606             *dash = '\0';
04607          }
04608 
04609          peername_flat = ast_strdupa(peername);
04610          for (i = 0; peername_flat[i]; i++) {
04611             if (peername_flat[i] == '/') {
04612                peername_flat[i] = '_';
04613             }
04614          }
04615 
04616          if (!ast_context_find_or_create(NULL, NULL, parking_con_dial, registrar)) {
04617             ast_log(LOG_ERROR,
04618                "Parking dial context '%s' does not exist and unable to create\n",
04619                parking_con_dial);
04620          } else {
04621             char returnexten[AST_MAX_EXTENSION];
04622             char comebackdialtime[AST_MAX_EXTENSION];
04623             struct ast_datastore *features_datastore;
04624             struct ast_dial_features *dialfeatures;
04625 
04626             if (!strncmp(peername, "Parked/", 7)) {
04627                peername += 7;
04628             }
04629 
04630             ast_channel_lock(chan);
04631             features_datastore = ast_channel_datastore_find(chan, &dial_features_info,
04632                NULL);
04633             if (features_datastore && (dialfeatures = features_datastore->data)) {
04634                char buf[MAX_DIAL_FEATURE_OPTIONS] = {0,};
04635 
04636                snprintf(returnexten, sizeof(returnexten), "%s,%u,%s", peername,
04637                   pu->parkinglot->cfg.comebackdialtime,
04638                   callback_dialoptions(&(dialfeatures->features_callee),
04639                      &(dialfeatures->features_caller), buf, sizeof(buf)));
04640             } else { /* Existing default */
04641                ast_log(LOG_NOTICE, "Dial features not found on %s, using default!\n",
04642                   ast_channel_name(chan));
04643                snprintf(returnexten, sizeof(returnexten), "%s,%u,t", peername,
04644                   pu->parkinglot->cfg.comebackdialtime);
04645             }
04646             ast_channel_unlock(chan);
04647 
04648             snprintf(comebackdialtime, sizeof(comebackdialtime), "%u",
04649                   pu->parkinglot->cfg.comebackdialtime);
04650             pbx_builtin_setvar_helper(chan, "COMEBACKDIALTIME", comebackdialtime);
04651 
04652             pbx_builtin_setvar_helper(chan, "PARKER", peername);
04653 
04654             if (ast_add_extension(parking_con_dial, 1, peername_flat, 1, NULL, NULL,
04655                "Dial", ast_strdup(returnexten), ast_free_ptr, registrar)) {
04656                ast_log(LOG_ERROR,
04657                   "Could not create parking return dial exten: %s@%s\n",
04658                   peername_flat, parking_con_dial);
04659             }
04660          }
04661          if (pu->options_specified) {
04662             /*
04663              * Park() was called with overriding return arguments, respect
04664              * those arguments.
04665              */
04666             set_c_e_p(chan, pu->context, pu->exten, pu->priority);
04667          } else if (pu->parkinglot->cfg.comebacktoorigin) {
04668             set_c_e_p(chan, parking_con_dial, peername_flat, 1);
04669          } else {
04670             char parkingslot[AST_MAX_EXTENSION];
04671 
04672             snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
04673             pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parkingslot);
04674             pbx_builtin_setvar_helper(chan, "PARKEDLOT", pu->parkinglot->name);
04675             set_c_e_p(chan, pu->parkinglot->cfg.comebackcontext, peername_flat, 1);
04676          }
04677       } else {
04678          /*
04679           * They've been waiting too long, send them back to where they
04680           * came.  Theoretically they should have their original
04681           * extensions and such, but we copy to be on the safe side.
04682           */
04683          set_c_e_p(chan, pu->context, pu->exten, pu->priority);
04684       }
04685       post_manager_event("ParkedCallTimeOut", pu);
04686       ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallTimeOut", NULL);
04687 
04688       ast_verb(2, "Timeout for %s parked on %d (%s). Returning to %s,%s,%d\n",
04689          ast_channel_name(pu->chan), pu->parkingnum, pu->parkinglot->name, pu->chan->context,
04690          pu->chan->exten, pu->chan->priority);
04691 
04692       /* Start up the PBX, or hang them up */
04693       if (ast_pbx_start(chan))  {
04694          ast_log(LOG_WARNING,
04695             "Unable to restart the PBX for user on '%s', hanging them up...\n",
04696             ast_channel_name(pu->chan));
04697          ast_hangup(chan);
04698       }
04699 
04700       /* And take them out of the parking lot */
04701       parking_complete = 1;
04702    } else { /* still within parking time, process descriptors */
04703       for (x = 0; x < AST_MAX_FDS; x++) {
04704          struct ast_frame *f;
04705          int y;
04706 
04707          if (chan->fds[x] == -1) {
04708             continue;   /* nothing on this descriptor */
04709          }
04710 
04711          for (y = 0; y < nfds; y++) {
04712             if (pfds[y].fd == chan->fds[x]) {
04713                /* Found poll record! */
04714                break;
04715             }
04716          }
04717          if (y == nfds) {
04718             /* Not found */
04719             continue;
04720          }
04721 
04722          if (!(pfds[y].revents & (POLLIN | POLLERR | POLLPRI))) {
04723             /* Next x */
04724             continue;
04725          }
04726 
04727          if (pfds[y].revents & POLLPRI) {
04728             ast_set_flag(chan, AST_FLAG_EXCEPTION);
04729          } else {
04730             ast_clear_flag(chan, AST_FLAG_EXCEPTION);
04731          }
04732          chan->fdno = x;
04733 
04734          /* See if they need servicing */
04735          f = ast_read(pu->chan);
04736          /* Hangup? */
04737          if (!f || (f->frametype == AST_FRAME_CONTROL
04738             && f->subclass.integer == AST_CONTROL_HANGUP)) {
04739             if (f) {
04740                ast_frfree(f);
04741             }
04742             post_manager_event("ParkedCallGiveUp", pu);
04743             ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallGiveUp",
04744                NULL);
04745 
04746             /* There's a problem, hang them up */
04747             ast_verb(2, "%s got tired of being parked\n", ast_channel_name(chan));
04748             ast_hangup(chan);
04749 
04750             /* And take them out of the parking lot */
04751             parking_complete = 1;
04752             break;
04753          } else {
04754             /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
04755             ast_frfree(f);
04756             if (pu->hold_method == AST_CONTROL_HOLD
04757                && pu->moh_trys < 3
04758                && !chan->generatordata) {
04759                ast_debug(1,
04760                   "MOH on parked call stopped by outside source.  Restarting on channel %s.\n",
04761                   ast_channel_name(chan));
04762                ast_indicate_data(chan, AST_CONTROL_HOLD,
04763                   S_OR(pu->parkinglot->cfg.mohclass, NULL),
04764                   (!ast_strlen_zero(pu->parkinglot->cfg.mohclass)
04765                      ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0));
04766                pu->moh_trys++;
04767             }
04768             goto std;   /* XXX Ick: jumping into an else statement??? XXX */
04769          }
04770       } /* End for */
04771       if (x >= AST_MAX_FDS) {
04772 std:     for (x = 0; x < AST_MAX_FDS; x++) { /* mark fds for next round */
04773             if (chan->fds[x] > -1) {
04774                void *tmp = ast_realloc(*new_pfds,
04775                   (*new_nfds + 1) * sizeof(struct pollfd));
04776 
04777                if (!tmp) {
04778                   continue;
04779                }
04780                *new_pfds = tmp;
04781                (*new_pfds)[*new_nfds].fd = chan->fds[x];
04782                (*new_pfds)[*new_nfds].events = POLLIN | POLLERR | POLLPRI;
04783                (*new_pfds)[*new_nfds].revents = 0;
04784                (*new_nfds)++;
04785             }
04786          }
04787          /* Keep track of our shortest wait */
04788          if (tms < *ms || *ms < 0) {
04789             *ms = tms;
04790          }
04791       }
04792    }
04793 
04794    return parking_complete;
04795 }

static void manage_parkinglot ( struct ast_parkinglot curlot,
const struct pollfd *  pfds,
int  nfds,
struct pollfd **  new_pfds,
int *  new_nfds,
int *  ms 
) [static]

Run management on parkinglots, called once per parkinglot.

Definition at line 4798 of file features.c.

References ast_context_find(), ast_context_remove_extension2(), AST_DEVICE_NOT_INUSE, ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), ast_parkinglot::cfg, LOG_WARNING, manage_parked_call(), ast_parkinglot::name, notify_metermaids(), parkeduser::notquiteyet, parkinglot_cfg::parking_con, parkeduser::parkingexten, parkeduser::parkinglot, parkinglot_unref(), and ast_parkinglot::parkings.

Referenced by do_parking_thread().

04799 {
04800    struct parkeduser *pu;
04801    struct ast_context *con;
04802 
04803    /* Lock parkings list */
04804    AST_LIST_LOCK(&curlot->parkings);
04805    AST_LIST_TRAVERSE_SAFE_BEGIN(&curlot->parkings, pu, list) {
04806       if (pu->notquiteyet) { /* Pretend this one isn't here yet */
04807          continue;
04808       }
04809       if (manage_parked_call(pu, pfds, nfds, new_pfds, new_nfds, ms)) {
04810          /* Parking is complete for this call so remove it from the parking lot. */
04811          con = ast_context_find(pu->parkinglot->cfg.parking_con);
04812          if (con) {
04813             if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL, 0)) {
04814                ast_log(LOG_WARNING,
04815                   "Whoa, failed to remove the parking extension %s@%s!\n",
04816                   pu->parkingexten, pu->parkinglot->cfg.parking_con);
04817             }
04818             notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con,
04819                AST_DEVICE_NOT_INUSE);
04820          } else {
04821             ast_log(LOG_WARNING,
04822                "Whoa, parking lot '%s' context '%s' does not exist.\n",
04823                pu->parkinglot->name, pu->parkinglot->cfg.parking_con);
04824          }
04825          AST_LIST_REMOVE_CURRENT(list);
04826          parkinglot_unref(pu->parkinglot);
04827          ast_free(pu);
04828       }
04829    }
04830    AST_LIST_TRAVERSE_SAFE_END;
04831    AST_LIST_UNLOCK(&curlot->parkings);
04832 }

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

Create manager event for parked calls.

Parameters:
s 
m Get channels involved in park, create event.
Returns:
Always 0
Note:
ADSI is not compatible with this AMI action for the same reason ch2 can no longer announce the parking space.

Definition at line 7104 of file features.c.

References ast_channel_get_by_name(), ast_channel_unref, AST_PARK_OPT_SILENCE, ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), find_parkinglot(), ast_park_call_args::flags, masq_park_call(), ast_park_call_args::parkinglot, parkinglot_unref(), and ast_park_call_args::timeout.

Referenced by ast_features_init().

07105 {
07106    const char *channel = astman_get_header(m, "Channel");
07107    const char *channel2 = astman_get_header(m, "Channel2");
07108    const char *timeout = astman_get_header(m, "Timeout");
07109    const char *parkinglotname = astman_get_header(m, "Parkinglot");
07110    char buf[BUFSIZ];
07111    int res = 0;
07112    struct ast_channel *ch1, *ch2;
07113    struct ast_park_call_args args = {
07114          /*
07115           * Don't say anything to ch2 since AMI is a third party parking
07116           * a call and we will likely crash if we do.
07117           *
07118           * XXX When the AMI action was originally implemented, the
07119           * parking space was announced to ch2.  Unfortunately, grabbing
07120           * the ch2 lock and holding it while the announcement is played
07121           * was not really a good thing to do to begin with since it
07122           * could hold up the system.  Also holding the lock is no longer
07123           * possible with a masquerade.
07124           *
07125           * Restoring the announcement to ch2 is not easily doable for
07126           * the following reasons:
07127           *
07128           * 1) The AMI manager is not the thread processing ch2.
07129           *
07130           * 2) ch2 could be the same as ch1, bridged to ch1, or some
07131           * random uninvolved channel.
07132           */
07133          .flags = AST_PARK_OPT_SILENCE,
07134       };
07135 
07136    if (ast_strlen_zero(channel)) {
07137       astman_send_error(s, m, "Channel not specified");
07138       return 0;
07139    }
07140 
07141    if (ast_strlen_zero(channel2)) {
07142       astman_send_error(s, m, "Channel2 not specified");
07143       return 0;
07144    }
07145 
07146    if (!ast_strlen_zero(timeout)) {
07147       if (sscanf(timeout, "%30d", &args.timeout) != 1) {
07148          astman_send_error(s, m, "Invalid timeout value.");
07149          return 0;
07150       }
07151    }
07152 
07153    if (!(ch1 = ast_channel_get_by_name(channel))) {
07154       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
07155       astman_send_error(s, m, buf);
07156       return 0;
07157    }
07158 
07159    if (!(ch2 = ast_channel_get_by_name(channel2))) {
07160       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
07161       astman_send_error(s, m, buf);
07162       ast_channel_unref(ch1);
07163       return 0;
07164    }
07165 
07166    if (!ast_strlen_zero(parkinglotname)) {
07167       args.parkinglot = find_parkinglot(parkinglotname);
07168    }
07169 
07170    res = masq_park_call(ch1, ch2, &args);
07171    if (!res) {
07172       ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
07173       astman_send_ack(s, m, "Park successful");
07174    } else {
07175       astman_send_error(s, m, "Park failure");
07176    }
07177 
07178    if (args.parkinglot) {
07179       parkinglot_unref(args.parkinglot);
07180    }
07181    ch1 = ast_channel_unref(ch1);
07182    ch2 = ast_channel_unref(ch2);
07183 
07184    return 0;
07185 }

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

Dump parking lot status.

Parameters:
s 
m Lock parking lot, iterate list and append parked calls status, unlock parking lot.
Returns:
Always RESULT_SUCCESS

Definition at line 7038 of file features.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_name(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), ast_channel::caller, parkeduser::chan, ast_channel::connected, ast_party_connected_line::id, ast_party_caller::id, ast_party_id::name, ast_parkinglot::name, ast_party_id::number, parkinglots, parkeduser::parkingnum, ast_parkinglot::parkings, parkeduser::parkingtime, parkeduser::peername, RESULT_SUCCESS, S_COR, parkeduser::start, ast_party_name::str, ast_party_number::str, ast_party_name::valid, and ast_party_number::valid.

Referenced by ast_features_init().

07039 {
07040    struct parkeduser *cur;
07041    const char *id = astman_get_header(m, "ActionID");
07042    char idText[256] = "";
07043    struct ao2_iterator iter;
07044    struct ast_parkinglot *curlot;
07045    int numparked = 0;
07046 
07047    if (!ast_strlen_zero(id))
07048       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07049 
07050    astman_send_ack(s, m, "Parked calls will follow");
07051 
07052    iter = ao2_iterator_init(parkinglots, 0);
07053    while ((curlot = ao2_iterator_next(&iter))) {
07054       AST_LIST_LOCK(&curlot->parkings);
07055       AST_LIST_TRAVERSE(&curlot->parkings, cur, list) {
07056          astman_append(s, "Event: ParkedCall\r\n"
07057             "Parkinglot: %s\r\n"
07058             "Exten: %d\r\n"
07059             "Channel: %s\r\n"
07060             "From: %s\r\n"
07061             "Timeout: %ld\r\n"
07062             "CallerIDNum: %s\r\n"
07063             "CallerIDName: %s\r\n"
07064             "ConnectedLineNum: %s\r\n"
07065             "ConnectedLineName: %s\r\n"
07066             "%s"
07067             "\r\n",
07068             curlot->name,
07069             cur->parkingnum, ast_channel_name(cur->chan), cur->peername,
07070             (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
07071             S_COR(cur->chan->caller.id.number.valid, cur->chan->caller.id.number.str, ""),   /* XXX in other places it is <unknown> */
07072             S_COR(cur->chan->caller.id.name.valid, cur->chan->caller.id.name.str, ""),
07073             S_COR(cur->chan->connected.id.number.valid, cur->chan->connected.id.number.str, ""),   /* XXX in other places it is <unknown> */
07074             S_COR(cur->chan->connected.id.name.valid, cur->chan->connected.id.name.str, ""),
07075             idText);
07076          ++numparked;
07077       }
07078       AST_LIST_UNLOCK(&curlot->parkings);
07079       ao2_ref(curlot, -1);
07080    }
07081    ao2_iterator_destroy(&iter);
07082 
07083    astman_append(s,
07084       "Event: ParkedCallsComplete\r\n"
07085       "Total: %d\r\n"
07086       "%s"
07087       "\r\n",
07088       numparked, idText);
07089 
07090    return RESULT_SUCCESS;
07091 }

static int masq_park_call ( struct ast_channel rchan,
struct ast_channel peer,
struct ast_park_call_args args 
) [static]

Park call via masqueraded channel and announce parking spot on peer channel.

Parameters:
rchan the real channel to be parked
peer the channel to have the parking read to.
args Additional parking options when parking a call.
Return values:
0 on success.
-1 on failure.

Definition at line 1633 of file features.c.

References ast_channel::amaflags, ast_channel_accountcode(), ast_channel_alloc, ast_channel_linkedid(), ast_channel_masquerade(), ast_channel_name(), ast_copy_string(), ast_do_masquerade(), ast_hangup(), ast_log(), AST_PARK_OPT_SILENCE, AST_STATE_DOWN, ast_stream_and_wait(), ast_test_flag, ast_channel::context, ast_channel::exten, LOG_WARNING, ast_channel::macrocontext, ast_channel::macroexten, ast_channel::macropriority, park_call_full(), park_space_abort(), park_space_reserve(), play_message_on_chan(), ast_channel::priority, ast_park_call_args::pu, ast_channel::readformat, set_c_e_p(), and ast_channel::writeformat.

Referenced by ast_masq_park_call(), ast_masq_park_call_exten(), builtin_parkcall(), manager_park(), park_call_exec(), and xfer_park_call_helper().

01634 {
01635    struct ast_channel *chan;
01636 
01637    /* Make a new, channel that we'll use to masquerade in the real one */
01638    chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(rchan), rchan->exten,
01639       rchan->context, ast_channel_linkedid(rchan), rchan->amaflags, "Parked/%s", ast_channel_name(rchan));
01640    if (!chan) {
01641       ast_log(LOG_WARNING, "Unable to create parked channel\n");
01642       if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
01643          if (peer == rchan) {
01644             /* Only have one channel to worry about. */
01645             ast_stream_and_wait(peer, "pbx-parkingfailed", "");
01646          } else if (peer) {
01647             /* Have two different channels to worry about. */
01648             play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
01649          }
01650       }
01651       return -1;
01652    }
01653 
01654    args->pu = park_space_reserve(rchan, peer, args);
01655    if (!args->pu) {
01656       ast_hangup(chan);
01657       if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
01658          if (peer == rchan) {
01659             /* Only have one channel to worry about. */
01660             ast_stream_and_wait(peer, "pbx-parkingfailed", "");
01661          } else if (peer) {
01662             /* Have two different channels to worry about. */
01663             play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
01664          }
01665       }
01666       return -1;
01667    }
01668 
01669    /* Make formats okay */
01670    chan->readformat = rchan->readformat;
01671    chan->writeformat = rchan->writeformat;
01672 
01673    if (ast_channel_masquerade(chan, rchan)) {
01674       park_space_abort(args->pu);
01675       args->pu = NULL;
01676       ast_hangup(chan);
01677       if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
01678          if (peer == rchan) {
01679             /* Only have one channel to worry about. */
01680             ast_stream_and_wait(peer, "pbx-parkingfailed", "");
01681          } else if (peer) {
01682             /* Have two different channels to worry about. */
01683             play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
01684          }
01685       }
01686       return -1;
01687    }
01688 
01689    /* Setup the extensions and such */
01690    set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
01691 
01692    /* Setup the macro extension and such */
01693    ast_copy_string(chan->macrocontext,rchan->macrocontext,sizeof(chan->macrocontext));
01694    ast_copy_string(chan->macroexten,rchan->macroexten,sizeof(chan->macroexten));
01695    chan->macropriority = rchan->macropriority;
01696 
01697    /* Manually do the masquerade to make sure it is complete. */
01698    ast_do_masquerade(chan);
01699 
01700    if (peer == rchan) {
01701       peer = chan;
01702    }
01703 
01704    /* parking space reserved, return code check unnecessary */
01705    park_call_full(chan, peer, args);
01706 
01707    return 0;
01708 }

static enum ast_device_state metermaidstate ( const char *  data  )  [static]

metermaids callback from devicestate.c

Definition at line 1023 of file features.c.

References ast_debug, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, ast_exists_extension(), ast_strdupa, and strsep().

Referenced by ast_features_init().

01024 {
01025    char *context;
01026    char *exten;
01027 
01028    context = ast_strdupa(data);
01029 
01030    exten = strsep(&context, "@");
01031    if (!context)
01032       return AST_DEVICE_INVALID;
01033 
01034    ast_debug(4, "Checking state of exten %s in context %s\n", exten, context);
01035 
01036    if (!ast_exists_extension(NULL, context, exten, 1, NULL))
01037       return AST_DEVICE_NOT_INUSE;
01038 
01039    return AST_DEVICE_INUSE;
01040 }

static void notify_metermaids ( const char *  exten,
char *  context,
enum ast_device_state  state 
) [static]

Notify metermaids that we've changed an extension.

Definition at line 1014 of file features.c.

References ast_debug, ast_devstate2str(), and ast_devstate_changed().

Referenced by manage_parkinglot(), park_call_full(), parked_call_exec(), and parkinglot_activate().

01015 {
01016    ast_debug(4, "Notification of state change to metermaids %s@%s\n to state '%s'",
01017       exten, context, ast_devstate2str(state));
01018 
01019    ast_devstate_changed(state, "park:%s@%s", exten, context);
01020 }

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

Add parking hints for all defined parking spaces.

Parameters:
context Dialplan context to add the hints.
start Starting space in parkinglot.
stop Ending space in parkinglot.

Definition at line 5345 of file features.c.

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

Referenced by parkinglot_activate().

05346 {
05347    int numext;
05348    char device[AST_MAX_EXTENSION];
05349    char exten[10];
05350 
05351    for (numext = start; numext <= stop; numext++) {
05352       snprintf(exten, sizeof(exten), "%d", numext);
05353       snprintf(device, sizeof(device), "park:%s@%s", exten, context);
05354       ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
05355    }
05356 }

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

Park a call.

Definition at line 4921 of file features.c.

References ast_channel::_state, ast_answer(), ast_app_parse_options(), ast_channel_name(), ast_copy_string(), ast_log(), AST_MAX_EXTENSION, AST_PARK_OPT_SILENCE, ast_safe_sleep(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_test_flag, create_dynamic_parkinglot(), default_parkinglot, ast_channel::exten, find_parkinglot(), findparkinglotname(), ast_flags::flags, ast_park_call_args::flags, LOG_WARNING, masq_park_call(), park_app_args::options, ast_park_call_args::orig_chan_name, orig_exten(), park_call_options, parkeddynamic, ast_park_call_args::parkinglot, parkinglot_addref(), parkinglot_unref(), parse(), park_app_args::pl_name, ast_channel::priority, ast_park_call_args::return_con, park_app_args::return_con, ast_park_call_args::return_ext, park_app_args::return_ext, ast_park_call_args::return_pri, park_app_args::return_pri, ast_park_call_args::timeout, and park_app_args::timeout.

Referenced by ast_features_init().

04922 {
04923    /* Cache the original channel name in case we get masqueraded in the middle
04924     * of a park--it is still theoretically possible for a transfer to happen before
04925     * we get here, but it is _really_ unlikely */
04926    char *orig_chan_name = ast_strdupa(ast_channel_name(chan));
04927    struct ast_park_call_args args = {
04928       .orig_chan_name = orig_chan_name,
04929    };
04930    struct ast_flags flags = { 0 };
04931    char orig_exten[AST_MAX_EXTENSION];
04932    int orig_priority;
04933    int res;
04934    const char *pl_name;
04935    char *parse;
04936    struct park_app_args app_args;
04937 
04938    /* Answer if call is not up */
04939    if (chan->_state != AST_STATE_UP) {
04940       if (ast_answer(chan)) {
04941          return -1;
04942       }
04943 
04944       /* Sleep to allow VoIP streams to settle down */
04945       if (ast_safe_sleep(chan, 1000)) {
04946          return -1;
04947       }
04948    }
04949 
04950    /* Process the dialplan application options. */
04951    parse = ast_strdupa(data);
04952    AST_STANDARD_APP_ARGS(app_args, parse);
04953 
04954    if (!ast_strlen_zero(app_args.timeout)) {
04955       if (sscanf(app_args.timeout, "%30d", &args.timeout) != 1) {
04956          ast_log(LOG_WARNING, "Invalid timeout '%s' provided\n", app_args.timeout);
04957          args.timeout = 0;
04958       }
04959    }
04960    if (!ast_strlen_zero(app_args.return_con)) {
04961       args.return_con = app_args.return_con;
04962    }
04963    if (!ast_strlen_zero(app_args.return_ext)) {
04964       args.return_ext = app_args.return_ext;
04965    }
04966    if (!ast_strlen_zero(app_args.return_pri)) {
04967       if (sscanf(app_args.return_pri, "%30d", &args.return_pri) != 1) {
04968          ast_log(LOG_WARNING, "Invalid priority '%s' specified\n", app_args.return_pri);
04969          args.return_pri = 0;
04970       }
04971    }
04972 
04973    ast_app_parse_options(park_call_options, &flags, NULL, app_args.options);
04974    args.flags = flags.flags;
04975 
04976    /*
04977     * Setup the exten/priority to be s/1 since we don't know where
04978     * this call should return.
04979     */
04980    ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten));
04981    orig_priority = chan->priority;
04982    strcpy(chan->exten, "s");
04983    chan->priority = 1;
04984 
04985    /* Park the call */
04986    if (!ast_strlen_zero(app_args.pl_name)) {
04987       pl_name = app_args.pl_name;
04988    } else {
04989       pl_name = findparkinglotname(chan);
04990    }
04991    if (ast_strlen_zero(pl_name)) {
04992       /* Parking lot is not specified, so use the default parking lot. */
04993       args.parkinglot = parkinglot_addref(default_parkinglot);
04994    } else {
04995       args.parkinglot = find_parkinglot(pl_name);
04996       if (!args.parkinglot && parkeddynamic) {
04997          args.parkinglot = create_dynamic_parkinglot(pl_name, chan);
04998       }
04999    }
05000    if (args.parkinglot) {
05001       res = masq_park_call(chan, chan, &args);
05002       parkinglot_unref(args.parkinglot);
05003    } else {
05004       /* Parking failed because the parking lot does not exist. */
05005       if (!ast_test_flag(&args, AST_PARK_OPT_SILENCE)) {
05006          ast_stream_and_wait(chan, "pbx-parkingfailed", "");
05007       }
05008       res = -1;
05009    }
05010    if (res) {
05011       /* Park failed, try to continue in the dialplan. */
05012       ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
05013       chan->priority = orig_priority;
05014       res = 0;
05015    } else {
05016       /* Park succeeded. */
05017       res = -1;
05018    }
05019 
05020    return res;
05021 }

static int park_call_full ( struct ast_channel chan,
struct ast_channel peer,
struct ast_park_call_args args 
) [static]

Definition at line 1388 of file features.c.

References adsi_announce_park(), adsipark, ast_channel::appl, ast_add_extension(), ast_adsi_available(), ast_adsi_unload_session(), ast_bridged_channel(), AST_CEL_PARK_START, ast_cel_report_event(), ast_channel_get_by_name(), ast_channel_language(), ast_channel_lock, ast_channel_name(), AST_CHANNEL_NAME, ast_channel_uniqueid(), ast_channel_unlock, ast_channel_unref, ast_clear_flag, AST_CONTROL_HOLD, AST_CONTROL_RINGING, ast_copy_string(), AST_DEVICE_INUSE, AST_FLAG_MASQ_NOSTREAM, ast_free_ptr, ast_indicate(), ast_indicate_data(), AST_LIST_UNLOCK, ast_log(), ast_manager_event, AST_MAX_CONTEXT, AST_MAX_EXTENSION, AST_PARK_OPT_RINGING, AST_PARK_OPT_SILENCE, ast_say_digits(), ast_set_flag, ast_strdup, ast_strlen_zero(), ast_test_flag, ast_tvnow(), ast_verb, ast_channel::caller, ast_parkinglot::cfg, parkeduser::chan, ast_channel::connected, ast_channel::context, parkeduser::context, ast_channel::data, EVENT_FLAG_CALL, ast_channel::exten, parkeduser::exten, ast_park_call_args::extout, parkeduser::hold_method, ast_party_connected_line::id, ast_party_caller::id, LOG_ERROR, ast_channel::macrocontext, ast_channel::macroexten, ast_channel::macropriority, parkinglot_cfg::mohclass, ast_party_id::name, ast_parkinglot::name, notify_metermaids(), parkeduser::notquiteyet, ast_party_id::number, parkeduser::options_specified, ast_park_call_args::orig_chan_name, park_space_reserve(), parkinglot_cfg::parking_con, parking_thread, parkeduser::parkingexten, parkeduser::parkinglot, parkeduser::parkingnum, ast_parkinglot::parkings, parkinglot_cfg::parkingtime, parkeduser::parkingtime, pbx_builtin_getvar_helper(), parkeduser::peername, ast_channel::priority, parkeduser::priority, ast_park_call_args::pu, registrar, ast_park_call_args::return_con, ast_park_call_args::return_ext, ast_park_call_args::return_pri, S_COR, S_OR, parkeduser::start, ast_party_name::str, ast_party_number::str, ast_channel::tech, ast_park_call_args::timeout, ast_channel_tech::type, ast_party_name::valid, and ast_party_number::valid.

Referenced by ast_park_call(), ast_park_call_exten(), and masq_park_call().

01389 {
01390    struct parkeduser *pu = args->pu;
01391    const char *event_from;
01392    char app_data[AST_MAX_EXTENSION + AST_MAX_CONTEXT];
01393 
01394    if (pu == NULL) {
01395       args->pu = pu = park_space_reserve(chan, peer, args);
01396       if (pu == NULL) {
01397          return -1;
01398       }
01399    }
01400 
01401    chan->appl = "Parked Call";
01402    chan->data = NULL;
01403 
01404    pu->chan = chan;
01405 
01406    /* Put the parked channel on hold if we have two different channels */
01407    if (chan != peer) {
01408       if (ast_test_flag(args, AST_PARK_OPT_RINGING)) {
01409          pu->hold_method = AST_CONTROL_RINGING;
01410          ast_indicate(pu->chan, AST_CONTROL_RINGING);
01411       } else {
01412          pu->hold_method = AST_CONTROL_HOLD;
01413          ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
01414             S_OR(pu->parkinglot->cfg.mohclass, NULL),
01415             !ast_strlen_zero(pu->parkinglot->cfg.mohclass) ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0);
01416       }
01417    }
01418    
01419    pu->start = ast_tvnow();
01420    pu->parkingtime = (args->timeout > 0) ? args->timeout : pu->parkinglot->cfg.parkingtime;
01421    if (args->extout)
01422       *(args->extout) = pu->parkingnum;
01423 
01424    if (peer) { 
01425       /*
01426        * This is so ugly that it hurts, but implementing
01427        * get_base_channel() on local channels could have ugly side
01428        * effects.  We could have
01429        * transferer<->local,1<->local,2<->parking and we need the
01430        * callback name to be that of transferer.  Since local,1/2 have
01431        * the same name we can be tricky and just grab the bridged
01432        * channel from the other side of the local.
01433        */
01434       if (!strcasecmp(peer->tech->type, "Local")) {
01435          struct ast_channel *tmpchan, *base_peer;
01436          char other_side[AST_CHANNEL_NAME];
01437          char *c;
01438 
01439          ast_copy_string(other_side, S_OR(args->orig_chan_name, ast_channel_name(peer)), sizeof(other_side));
01440          if ((c = strrchr(other_side, ';'))) {
01441             *++c = '1';
01442          }
01443          if ((tmpchan = ast_channel_get_by_name(other_side))) {
01444             ast_channel_lock(tmpchan);
01445             if ((base_peer = ast_bridged_channel(tmpchan))) {
01446                ast_copy_string(pu->peername, ast_channel_name(base_peer), sizeof(pu->peername));
01447             }
01448             ast_channel_unlock(tmpchan);
01449             tmpchan = ast_channel_unref(tmpchan);
01450          }
01451       } else {
01452          ast_copy_string(pu->peername, S_OR(args->orig_chan_name, ast_channel_name(peer)), sizeof(pu->peername));
01453       }
01454    }
01455 
01456    /*
01457     * Remember what had been dialed, so that if the parking
01458     * expires, we try to come back to the same place
01459     */
01460    pu->options_specified = (!ast_strlen_zero(args->return_con) || !ast_strlen_zero(args->return_ext) || args->return_pri);
01461 
01462    /*
01463     * If extension has options specified, they override all other
01464     * possibilities such as the returntoorigin flag and transferred
01465     * context.  Information on extension options is lost here, so
01466     * we set a flag
01467     */
01468    ast_copy_string(pu->context, 
01469       S_OR(args->return_con, S_OR(chan->macrocontext, chan->context)), 
01470       sizeof(pu->context));
01471    ast_copy_string(pu->exten, 
01472       S_OR(args->return_ext, S_OR(chan->macroexten, chan->exten)), 
01473       sizeof(pu->exten));
01474    pu->priority = args->return_pri ? args->return_pri : 
01475       (chan->macropriority ? chan->macropriority : chan->priority);
01476 
01477    /*
01478     * If parking a channel directly, don't quite yet get parking
01479     * running on it.  All parking lot entries are put into the
01480     * parking lot with notquiteyet on.
01481     */
01482    if (peer != chan) {
01483       pu->notquiteyet = 0;
01484    }
01485 
01486    /* Wake up the (presumably select()ing) thread */
01487    pthread_kill(parking_thread, SIGURG);
01488    ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %d seconds\n",
01489       ast_channel_name(pu->chan), pu->parkingnum, pu->parkinglot->name,
01490       pu->context, pu->exten, pu->priority, (pu->parkingtime / 1000));
01491 
01492    ast_cel_report_event(pu->chan, AST_CEL_PARK_START, NULL, pu->parkinglot->name, peer);
01493 
01494    if (peer) {
01495       event_from = ast_channel_name(peer);
01496    } else {
01497       event_from = pbx_builtin_getvar_helper(chan, "BLINDTRANSFER");
01498    }
01499 
01500    ast_manager_event(pu->chan, EVENT_FLAG_CALL, "ParkedCall",
01501       "Exten: %s\r\n"
01502       "Channel: %s\r\n"
01503       "Parkinglot: %s\r\n"
01504       "From: %s\r\n"
01505       "Timeout: %ld\r\n"
01506       "CallerIDNum: %s\r\n"
01507       "CallerIDName: %s\r\n"
01508       "ConnectedLineNum: %s\r\n"
01509       "ConnectedLineName: %s\r\n"
01510       "Uniqueid: %s\r\n",
01511       pu->parkingexten, ast_channel_name(pu->chan), pu->parkinglot->name, event_from ? event_from : "",
01512       (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
01513       S_COR(pu->chan->caller.id.number.valid, pu->chan->caller.id.number.str, "<unknown>"),
01514       S_COR(pu->chan->caller.id.name.valid, pu->chan->caller.id.name.str, "<unknown>"),
01515       S_COR(pu->chan->connected.id.number.valid, pu->chan->connected.id.number.str, "<unknown>"),
01516       S_COR(pu->chan->connected.id.name.valid, pu->chan->connected.id.name.str, "<unknown>"),
01517       ast_channel_uniqueid(pu->chan)
01518       );
01519 
01520    if (peer && adsipark && ast_adsi_available(peer)) {
01521       adsi_announce_park(peer, pu->parkingexten);  /* Only supports parking numbers */
01522       ast_adsi_unload_session(peer);
01523    }
01524 
01525    snprintf(app_data, sizeof(app_data), "%s,%s", pu->parkingexten,
01526       pu->parkinglot->name);
01527    if (ast_add_extension(pu->parkinglot->cfg.parking_con, 1, pu->parkingexten, 1,
01528       NULL, NULL, parkedcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
01529       ast_log(LOG_ERROR, "Could not create parked call exten: %s@%s\n",
01530          pu->parkingexten, pu->parkinglot->cfg.parking_con);
01531    } else {
01532       notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con, AST_DEVICE_INUSE);
01533    }
01534 
01535    AST_LIST_UNLOCK(&pu->parkinglot->parkings);
01536 
01537    /* Only say number if it's a number and the channel hasn't been masqueraded away */
01538    if (peer && !ast_test_flag(args, AST_PARK_OPT_SILENCE)
01539       && (ast_strlen_zero(args->orig_chan_name) || !strcasecmp(ast_channel_name(peer), args->orig_chan_name))) {
01540       /*
01541        * If a channel is masqueraded into peer while playing back the
01542        * parking space number do not continue playing it back.  This
01543        * is the case if an attended transfer occurs.
01544        */
01545       ast_set_flag(peer, AST_FLAG_MASQ_NOSTREAM);
01546       /* Tell the peer channel the number of the parking space */
01547       ast_say_digits(peer, pu->parkingnum, "", ast_channel_language(peer));
01548       ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM);
01549    }
01550    if (peer == chan) { /* pu->notquiteyet = 1 */
01551       /* Wake up parking thread if we're really done */
01552       pu->hold_method = AST_CONTROL_HOLD;
01553       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
01554          S_OR(pu->parkinglot->cfg.mohclass, NULL),
01555          !ast_strlen_zero(pu->parkinglot->cfg.mohclass) ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0);
01556       pu->notquiteyet = 0;
01557       pthread_kill(parking_thread, SIGURG);
01558    }
01559    return 0;
01560 }

static void park_space_abort ( struct parkeduser pu  )  [static]

Definition at line 1190 of file features.c.

References ast_free, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_parkinglot::next_parking_space, parkeduser::parkinglot, parkinglot_unref(), and ast_parkinglot::parkings.

Referenced by masq_park_call().

01191 {
01192    struct ast_parkinglot *parkinglot;
01193 
01194    parkinglot = pu->parkinglot;
01195 
01196    /* Put back the parking space just allocated. */
01197    --parkinglot->next_parking_space;
01198 
01199    AST_LIST_REMOVE(&parkinglot->parkings, pu, list);
01200 
01201    AST_LIST_UNLOCK(&parkinglot->parkings);
01202    parkinglot_unref(parkinglot);
01203    ast_free(pu);
01204 }

static struct parkeduser* park_space_reserve ( struct ast_channel park_me,
struct ast_channel parker,
struct ast_park_call_args args 
) [static, read]

Note:
The API forces us to specify a numeric parking slot, even though the architecture would tend to support non-numeric extensions (as are possible with SIP, for example). Hence, we enforce that limitation here. If extout was not numeric, we could permit arbitrary non-numeric extensions.

Definition at line 1217 of file features.c.

References ast_calloc, ast_channel_name(), ast_debug, ast_free, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_PARK_OPT_RANDOMIZE, ast_random(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_parkinglot::cfg, create_dynamic_parkinglot(), default_parkinglot, ast_parkinglot::disabled, find_parkinglot(), findparkinglotname(), parkinglot_cfg::is_invalid, LOG_WARNING, ast_parkinglot::name, ast_parkinglot::next_parking_space, parkeduser::notquiteyet, parkeddynamic, parkinglot_cfg::parkfindnext, parkinglot_cfg::parking_start, parkinglot_cfg::parking_stop, parkeduser::parkingexten, parkeduser::parkinglot, ast_park_call_args::parkinglot, parkinglot_addref(), parkinglot_unref(), parkeduser::parkingnum, ast_parkinglot::parkings, pbx_builtin_getvar_helper(), and S_OR.

Referenced by masq_park_call(), and park_call_full().

01218 {
01219    struct parkeduser *pu;
01220    int i;
01221    int parking_space = -1;
01222    const char *parkinglotname;
01223    const char *parkingexten;
01224    struct parkeduser *cur;
01225    struct ast_parkinglot *parkinglot = NULL;
01226 
01227    if (args->parkinglot) {
01228       parkinglot = parkinglot_addref(args->parkinglot);
01229       parkinglotname = parkinglot->name;
01230    } else {
01231       if (parker) {
01232          parkinglotname = findparkinglotname(parker);
01233       } else { /* parker was NULL, check park_me (ParkAndAnnounce / res_agi) */
01234          parkinglotname = findparkinglotname(park_me);
01235       }
01236       if (!ast_strlen_zero(parkinglotname)) {
01237          parkinglot = find_parkinglot(parkinglotname);
01238       } else {
01239          /* Parking lot is not specified, so use the default parking lot. */
01240          ast_debug(4, "This could be an indication channel driver needs updating, using default lot.\n");
01241          parkinglot = parkinglot_addref(default_parkinglot);
01242       }
01243    }
01244 
01245    /* Dynamically create parkinglot */
01246    if (!parkinglot && parkeddynamic && !ast_strlen_zero(parkinglotname)) {
01247       parkinglot = create_dynamic_parkinglot(parkinglotname, park_me);
01248    }
01249 
01250    if (!parkinglot) {
01251       ast_log(LOG_WARNING, "Parking lot not available to park %s.\n", ast_channel_name(park_me));
01252       return NULL;
01253    }
01254 
01255    ast_debug(1, "Parking lot: %s\n", parkinglot->name);
01256    if (parkinglot->disabled || parkinglot->cfg.is_invalid) {
01257       ast_log(LOG_WARNING, "Parking lot %s is not in a useable state.\n",
01258          parkinglot->name);
01259       parkinglot_unref(parkinglot);
01260       return NULL;
01261    }
01262 
01263    /* Allocate memory for parking data */
01264    if (!(pu = ast_calloc(1, sizeof(*pu)))) {
01265       parkinglot_unref(parkinglot);
01266       return NULL;
01267    }
01268 
01269    /* Lock parking list */
01270    AST_LIST_LOCK(&parkinglot->parkings);
01271 
01272    /* Check for channel variable PARKINGEXTEN */
01273    parkingexten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(park_me, "PARKINGEXTEN"), ""));
01274    if (!ast_strlen_zero(parkingexten)) {
01275       /*!
01276        * \note The API forces us to specify a numeric parking slot, even
01277        * though the architecture would tend to support non-numeric extensions
01278        * (as are possible with SIP, for example).  Hence, we enforce that
01279        * limitation here.  If extout was not numeric, we could permit
01280        * arbitrary non-numeric extensions.
01281        */
01282       if (sscanf(parkingexten, "%30d", &parking_space) != 1 || parking_space <= 0) {
01283          ast_log(LOG_WARNING, "PARKINGEXTEN='%s' is not a valid parking space.\n",
01284             parkingexten);
01285          AST_LIST_UNLOCK(&parkinglot->parkings);
01286          parkinglot_unref(parkinglot);
01287          ast_free(pu);
01288          return NULL;
01289       }
01290 
01291       if (parking_space < parkinglot->cfg.parking_start
01292          || parkinglot->cfg.parking_stop < parking_space) {
01293          /*
01294           * Cannot allow park because parking lots are not setup for
01295           * spaces outside of the lot.  (Things like dialplan hints don't
01296           * exist for outside lot space.)
01297           */
01298          ast_log(LOG_WARNING, "PARKINGEXTEN=%d is not in %s (%d-%d).\n",
01299             parking_space, parkinglot->name, parkinglot->cfg.parking_start,
01300             parkinglot->cfg.parking_stop);
01301          AST_LIST_UNLOCK(&parkinglot->parkings);
01302          parkinglot_unref(parkinglot);
01303          ast_free(pu);
01304          return NULL;
01305       }
01306 
01307       /* Check if requested parking space is in use. */
01308       AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
01309          if (cur->parkingnum == parking_space) {
01310             ast_log(LOG_WARNING, "PARKINGEXTEN=%d is already in use in %s\n",
01311                parking_space, parkinglot->name);
01312             AST_LIST_UNLOCK(&parkinglot->parkings);
01313             parkinglot_unref(parkinglot);
01314             ast_free(pu);
01315             return NULL;
01316          }
01317       }
01318    } else {
01319       /* PARKINGEXTEN is empty, so find a usable extension in the lot to park the call */
01320       int start; /* The first slot we look in the parkinglot. It can be randomized. */
01321       int start_checked = 0; /* flag raised once the first slot is checked */
01322 
01323       /* If using randomize mode, set start to random position on parking range */
01324       if (ast_test_flag(args, AST_PARK_OPT_RANDOMIZE)) {
01325          start = ast_random() % (parkinglot->cfg.parking_stop - parkinglot->cfg.parking_start + 1);
01326          start += parkinglot->cfg.parking_start;
01327       } else if (parkinglot->cfg.parkfindnext
01328          && parkinglot->cfg.parking_start <= parkinglot->next_parking_space
01329          && parkinglot->next_parking_space <= parkinglot->cfg.parking_stop) {
01330          /* Start looking with the next parking space in the lot. */
01331          start = parkinglot->next_parking_space;
01332       } else {
01333          /* Otherwise, just set it to the start position. */
01334          start = parkinglot->cfg.parking_start;
01335       }
01336 
01337       /* free parking extension linear search: O(n^2) */
01338       for (i = start; ; i++) {
01339          /* If we are past the end, wrap around to the first parking slot*/
01340          if (i == parkinglot->cfg.parking_stop + 1) {
01341             i = parkinglot->cfg.parking_start;
01342          }
01343 
01344          if (i == start) {
01345             /* At this point, if start_checked, we've exhausted all the possible slots. */
01346             if (start_checked) {
01347                break;
01348             } else {
01349                start_checked = 1;
01350             }
01351          }
01352 
01353          /* Search the list of parked calls already in use for i. If we find it, it's in use. */
01354          AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
01355             if (cur->parkingnum == i) {
01356                break;
01357             }
01358          }
01359          if (!cur) {
01360             /* We found a parking space. */
01361             parking_space = i;
01362             break;
01363          }
01364       }
01365       if (parking_space == -1) {
01366          /* We did not find a parking space.  Lot is full. */
01367          ast_log(LOG_WARNING, "No more parking spaces in %s\n", parkinglot->name);
01368          AST_LIST_UNLOCK(&parkinglot->parkings);
01369          parkinglot_unref(parkinglot);
01370          ast_free(pu);
01371          return NULL;
01372       }
01373    }
01374 
01375    /* Prepare for next parking space search. */
01376    parkinglot->next_parking_space = parking_space + 1;
01377 
01378    snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space);
01379    pu->notquiteyet = 1;
01380    pu->parkingnum = parking_space;
01381    pu->parkinglot = parkinglot;
01382    AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list);
01383 
01384    return pu;
01385 }

static int parked_call_exec ( struct ast_channel chan,
const char *  data 
) [static]

Pickup parked call.

< Parking lot space to retrieve if present.

< Parking lot name to use if present.

< Place to put any remaining args string.

Definition at line 5024 of file features.c.

References ast_channel::_state, ast_answer(), AST_APP_ARG, ast_bridge_call(), ast_cdr_setdestchan(), AST_CEL_PARK_END, ast_cel_report_event(), ast_channel_connected_line_macro(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_name(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_update_connected_line(), ast_connected_line_copy_from_caller(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, ast_context_find(), ast_context_remove_extension2(), AST_CONTROL_HOLD, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_copy_flags, AST_DECLARE_APP_ARGS, AST_DEVICE_NOT_INUSE, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_FLAG_BYBOTH, AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, AST_FLAGS_ALL, ast_free, ast_hangup(), ast_indicate(), AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), ast_manager_event, ast_party_connected_line_free(), ast_party_connected_line_init(), ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_verb, ast_channel::caller, ast_channel::cdr, ast_parkinglot::cfg, parkeduser::chan, ast_channel::connected, courtesytone, ast_datastore::data, default_parkinglot, dial_features_info, EVENT_FLAG_CALL, ast_bridge_config::features_callee, ast_bridge_config::features_caller, ast_dial_features::features_caller, find_parkinglot(), findparkinglotname(), parkeduser::hold_method, ast_party_connected_line::id, ast_party_caller::id, LOG_WARNING, ast_party_id::name, ast_parkinglot::name, notify_metermaids(), parkeduser::notquiteyet, ast_party_id::number, parkinglot_cfg::parkedcallhangup, parkinglot_cfg::parkedcallrecording, parkinglot_cfg::parkedcallreparking, parkinglot_cfg::parkedcalltransfers, parkedplay, parkinglot_cfg::parking_con, parkeduser::parkingexten, parkeduser::parkinglot, parkinglot_addref(), parkinglot_unref(), parkeduser::parkingnum, ast_parkinglot::parkings, parse(), ast_channel::pbx, pbx_builtin_setvar_helper(), play_message_to_chans(), S_COR, ast_party_connected_line::source, ast_party_name::str, ast_party_number::str, ast_party_name::valid, and ast_party_number::valid.

Referenced by ast_features_init().

05025 {
05026    int res = 0;
05027    struct ast_channel *peer = NULL;
05028    struct parkeduser *pu;
05029    struct ast_context *con;
05030    char *parse;
05031    const char *pl_name;
05032    int park = 0;
05033    struct ast_bridge_config config;
05034    struct ast_parkinglot *parkinglot;
05035    AST_DECLARE_APP_ARGS(app_args,
05036       AST_APP_ARG(pl_space);  /*!< Parking lot space to retrieve if present. */
05037       AST_APP_ARG(pl_name);   /*!< Parking lot name to use if present. */
05038       AST_APP_ARG(dummy);     /*!< Place to put any remaining args string. */
05039    );
05040 
05041    parse = ast_strdupa(data);
05042    AST_STANDARD_APP_ARGS(app_args, parse);
05043 
05044    if (!ast_strlen_zero(app_args.pl_space)) {
05045       if (sscanf(app_args.pl_space, "%30u", &park) != 1) {
05046          ast_log(LOG_WARNING, "Specified parking extension not a number: %s\n",
05047             app_args.pl_space);
05048          park = -1;
05049       }
05050    }
05051 
05052    if (!ast_strlen_zero(app_args.pl_name)) {
05053       pl_name = app_args.pl_name;
05054    } else {
05055       pl_name = findparkinglotname(chan);
05056    }
05057    if (ast_strlen_zero(pl_name)) {
05058       /* Parking lot is not specified, so use the default parking lot. */
05059       parkinglot = parkinglot_addref(default_parkinglot);
05060    } else {
05061       parkinglot = find_parkinglot(pl_name);
05062       if (!parkinglot) {
05063          /* It helps to answer the channel if not already up. :) */
05064          if (chan->_state != AST_STATE_UP) {
05065             ast_answer(chan);
05066          }
05067          if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) {
05068             ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n",
05069                "pbx-invalidpark", ast_channel_name(chan));
05070          }
05071          ast_log(LOG_WARNING,
05072             "Channel %s tried to retrieve parked call from unknown parking lot '%s'\n",
05073             ast_channel_name(chan), pl_name);
05074          return -1;
05075       }
05076    }
05077 
05078    AST_LIST_LOCK(&parkinglot->parkings);
05079    AST_LIST_TRAVERSE_SAFE_BEGIN(&parkinglot->parkings, pu, list) {
05080       if ((ast_strlen_zero(app_args.pl_space) || pu->parkingnum == park)
05081          && !pu->notquiteyet && !pu->chan->pbx) {
05082          /* The parking space has a call and can be picked up now. */
05083          AST_LIST_REMOVE_CURRENT(list);
05084          break;
05085       }
05086    }
05087    AST_LIST_TRAVERSE_SAFE_END;
05088    if (pu) {
05089       /* Found a parked call to pickup. */
05090       peer = pu->chan;
05091       con = ast_context_find(parkinglot->cfg.parking_con);
05092       if (con) {
05093          if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL, 0)) {
05094             ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
05095          } else {
05096             notify_metermaids(pu->parkingexten, parkinglot->cfg.parking_con, AST_DEVICE_NOT_INUSE);
05097          }
05098       } else {
05099          ast_log(LOG_WARNING, "Whoa, no parking context?\n");
05100       }
05101 
05102       ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "UnParkedCall", chan);
05103       ast_manager_event(pu->chan, EVENT_FLAG_CALL, "UnParkedCall",
05104          "Exten: %s\r\n"
05105          "Channel: %s\r\n"
05106          "Parkinglot: %s\r\n"
05107          "From: %s\r\n"
05108          "CallerIDNum: %s\r\n"
05109          "CallerIDName: %s\r\n"
05110          "ConnectedLineNum: %s\r\n"
05111          "ConnectedLineName: %s\r\n"
05112          "Uniqueid: %s\r\n",
05113          pu->parkingexten, ast_channel_name(pu->chan), pu->parkinglot->name,
05114          ast_channel_name(chan),
05115          S_COR(pu->chan->caller.id.number.valid, pu->chan->caller.id.number.str, "<unknown>"),
05116          S_COR(pu->chan->caller.id.name.valid, pu->chan->caller.id.name.str, "<unknown>"),
05117          S_COR(pu->chan->connected.id.number.valid, pu->chan->connected.id.number.str, "<unknown>"),
05118          S_COR(pu->chan->connected.id.name.valid, pu->chan->connected.id.name.str, "<unknown>"),
05119          ast_channel_uniqueid(pu->chan)
05120          );
05121 
05122       /* Stop entertaining the caller. */
05123       switch (pu->hold_method) {
05124       case AST_CONTROL_HOLD:
05125          ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
05126          break;
05127       case AST_CONTROL_RINGING:
05128          ast_indicate(pu->chan, -1);
05129          break;
05130       default:
05131          break;
05132       }
05133       pu->hold_method = 0;
05134 
05135       parkinglot_unref(pu->parkinglot);
05136       ast_free(pu);
05137    }
05138    AST_LIST_UNLOCK(&parkinglot->parkings);
05139 
05140    if (peer) {
05141       /* Update connected line between retrieving call and parked call. */
05142       struct ast_party_connected_line connected;
05143 
05144       ast_party_connected_line_init(&connected);
05145 
05146       /* Send our caller-id to peer. */
05147       ast_channel_lock(chan);
05148       ast_connected_line_copy_from_caller(&connected, &chan->caller);
05149       ast_channel_unlock(chan);
05150       connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
05151       if (ast_channel_connected_line_macro(chan, peer, &connected, 0, 0)) {
05152          ast_channel_update_connected_line(peer, &connected, NULL);
05153       }
05154 
05155       /*
05156        * Get caller-id from peer.
05157        *
05158        * Update the retrieving call before it is answered if possible
05159        * for best results.  Some phones do not support updating the
05160        * connected line information after connection.
05161        */
05162       ast_channel_lock(peer);
05163       ast_connected_line_copy_from_caller(&connected, &peer->caller);
05164       ast_channel_unlock(peer);
05165       connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
05166       if (ast_channel_connected_line_macro(peer, chan, &connected, 1, 0)) {
05167          ast_channel_update_connected_line(chan, &connected, NULL);
05168       }
05169 
05170       ast_party_connected_line_free(&connected);
05171    }
05172 
05173    /* JK02: it helps to answer the channel if not already up */
05174    if (chan->_state != AST_STATE_UP) {
05175       ast_answer(chan);
05176    }
05177 
05178    if (peer) {
05179       struct ast_datastore *features_datastore;
05180       struct ast_dial_features *dialfeatures = NULL;
05181 
05182       /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
05183       if (!ast_strlen_zero(courtesytone)) {
05184          static const char msg[] = "courtesy tone";
05185 
05186          switch (parkedplay) {
05187          case 0:/* Courtesy tone to pickup chan */
05188             res = play_message_to_chans(chan, peer, -1, msg, courtesytone);
05189             break;
05190          case 1:/* Courtesy tone to parked chan */
05191             res = play_message_to_chans(chan, peer, 1, msg, courtesytone);
05192             break;
05193          case 2:/* Courtesy tone to both chans */
05194             res = play_message_to_chans(chan, peer, 0, msg, courtesytone);
05195             break;
05196          default:
05197             res = 0;
05198             break;
05199          }
05200          if (res) {
05201             ast_hangup(peer);
05202             parkinglot_unref(parkinglot);
05203             return -1;
05204          }
05205       }
05206 
05207       res = ast_channel_make_compatible(chan, peer);
05208       if (res < 0) {
05209          ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", ast_channel_name(chan), ast_channel_name(peer));
05210          ast_hangup(peer);
05211          parkinglot_unref(parkinglot);
05212          return -1;
05213       }
05214       /* This runs sorta backwards, since we give the incoming channel control, as if it
05215          were the person called. */
05216       ast_verb(3, "Channel %s connected to parked call %d\n", ast_channel_name(chan), park);
05217 
05218       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", ast_channel_name(peer));
05219       ast_cdr_setdestchan(chan->cdr, ast_channel_name(peer));
05220       memset(&config, 0, sizeof(struct ast_bridge_config));
05221 
05222       /* Get datastore for peer and apply it's features to the callee side of the bridge config */
05223       ast_channel_lock(peer);
05224       if ((features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL))) {
05225          dialfeatures = features_datastore->data;
05226       }
05227 
05228       /*
05229        * When the datastores for both caller and callee are created,
05230        * both the callee and caller channels use the features_caller
05231        * flag variable to represent themselves.  With that said, the
05232        * config.features_callee flags should be copied from the
05233        * datastore's caller feature flags regardless if peer was a
05234        * callee or caller.
05235        */
05236       if (dialfeatures) {
05237          ast_copy_flags(&(config.features_callee), &(dialfeatures->features_caller), AST_FLAGS_ALL);
05238       }
05239       ast_channel_unlock(peer);
05240 
05241       if ((parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
05242          ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
05243       }
05244       if ((parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
05245          ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
05246       }
05247       if ((parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
05248          ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
05249       }
05250       if ((parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
05251          ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
05252       }
05253       if ((parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
05254          ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
05255       }
05256       if ((parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
05257          ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
05258       }
05259       if ((parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
05260          ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
05261       }
05262       if ((parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
05263          ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
05264       }
05265 
05266       res = ast_bridge_call(chan, peer, &config);
05267 
05268       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", ast_channel_name(peer));
05269       ast_cdr_setdestchan(chan->cdr, ast_channel_name(peer));
05270 
05271       /* Simulate the PBX hanging up */
05272       ast_hangup(peer);
05273    } else {
05274       if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) {
05275          ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark",
05276             ast_channel_name(chan));
05277       }
05278       ast_verb(3, "Channel %s tried to retrieve nonexistent parked call %d\n",
05279          ast_channel_name(chan), park);
05280    }
05281 
05282    parkinglot_unref(parkinglot);
05283    return -1;
05284 }

static int parkinglot_activate ( struct ast_parkinglot parkinglot  )  [static]

Definition at line 5517 of file features.c.

References ast_add_extension(), ast_context_find_or_create(), AST_DEVICE_INUSE, ast_free_ptr, ast_log(), AST_MAX_CONTEXT, ast_strdup, ast_parkinglot::cfg, ast_parkinglot::disabled, LOG_ERROR, ast_parkinglot::name, notify_metermaids(), park_add_hints(), parkinglot_cfg::parkaddhints, parkcall, parkinglot_cfg::parkext, parkinglot_cfg::parkext_exclusive, parkinglot_cfg::parking_con, parkinglot_cfg::parking_start, parkinglot_cfg::parking_stop, and registrar.

Referenced by create_dynamic_parkinglot(), and parkinglot_activate_cb().

05518 {
05519    int disabled = 0;
05520    char app_data[5 + AST_MAX_CONTEXT];
05521 
05522    /* Create Park option list.  Must match with struct park_app_args options. */
05523    if (parkinglot->cfg.parkext_exclusive) {
05524       /* Specify the parking lot this parking extension parks calls. */
05525       snprintf(app_data, sizeof(app_data), ",,,,,%s", parkinglot->name);
05526    } else {
05527       /* The dialplan must specify which parking lot to use. */
05528       app_data[0] = '\0';
05529    }
05530 
05531    /* Create context */
05532    if (!ast_context_find_or_create(NULL, NULL, parkinglot->cfg.parking_con, registrar)) {
05533       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n",
05534          parkinglot->cfg.parking_con);
05535       disabled = 1;
05536 
05537    /* Add a parking extension into the context */
05538    } else if (ast_add_extension(parkinglot->cfg.parking_con, 1, parkinglot->cfg.parkext,
05539       1, NULL, NULL, parkcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
05540       ast_log(LOG_ERROR, "Could not create parking lot %s access exten %s@%s\n",
05541          parkinglot->name, parkinglot->cfg.parkext, parkinglot->cfg.parking_con);
05542       disabled = 1;
05543    } else {
05544       /* Add parking hints */
05545       if (parkinglot->cfg.parkaddhints) {
05546          park_add_hints(parkinglot->cfg.parking_con, parkinglot->cfg.parking_start,
05547             parkinglot->cfg.parking_stop);
05548       }
05549 
05550       /*
05551        * XXX Not sure why we should need to notify the metermaids for
05552        * this exten.  It was originally done for the default parking
05553        * lot entry exten only but should be done for all entry extens
05554        * if we do it for one.
05555        */
05556       /* Notify metermaids about parking lot entry exten state. */
05557       notify_metermaids(parkinglot->cfg.parkext, parkinglot->cfg.parking_con,
05558          AST_DEVICE_INUSE);
05559    }
05560 
05561    parkinglot->disabled = disabled;
05562    return disabled ? -1 : 0;
05563 }

static int parkinglot_activate_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 6561 of file features.c.

References ast_debug, ast_log(), ast_parkinglot::cfg, force_reload_load, LOG_WARNING, ast_parkinglot::name, parkinglot_cfg::parking_start, parkinglot_cfg::parking_stop, parkinglot_activate(), and ast_parkinglot::the_mark.

Referenced by load_config().

06562 {
06563    struct ast_parkinglot *parkinglot = obj;
06564 
06565    if (parkinglot->the_mark) {
06566       /*
06567        * Don't activate a parking lot that still bears the_mark since
06568        * it is effectively deleted.
06569        */
06570       return 0;
06571    }
06572 
06573    if (parkinglot_activate(parkinglot)) {
06574       /*
06575        * The parking lot failed to activate.  Allow reloading later to
06576        * see if that fixes it.
06577        */
06578       force_reload_load = 1;
06579       ast_log(LOG_WARNING, "Parking lot %s not open for business.\n", parkinglot->name);
06580    } else {
06581       ast_debug(1, "Parking lot %s now open for business. (parkpos %d-%d)\n",
06582          parkinglot->name, parkinglot->cfg.parking_start,
06583          parkinglot->cfg.parking_stop);
06584    }
06585 
06586    return 0;
06587 }

static struct ast_parkinglot * parkinglot_addref ( struct ast_parkinglot parkinglot  )  [static, read]

Definition at line 5296 of file features.c.

References ao2_ref, ast_debug, and ast_parkinglot::name.

Referenced by create_dynamic_parkinglot(), load_config(), park_call_exec(), park_space_reserve(), parked_call_exec(), and xfer_park_call_helper().

05297 {
05298    int refcount;
05299 
05300    refcount = ao2_ref(parkinglot, +1);
05301    ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1);
05302    return parkinglot;
05303 }

static int parkinglot_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 826 of file features.c.

References CMP_MATCH, CMP_STOP, and ast_parkinglot::name.

Referenced by ast_features_init().

00827 {
00828    struct ast_parkinglot *parkinglot = obj;
00829    struct ast_parkinglot *parkinglot2 = arg;
00830 
00831    return !strcasecmp(parkinglot->name, parkinglot2->name) ? CMP_MATCH | CMP_STOP : 0;
00832 }

static int parkinglot_config_read ( const char *  pl_name,
struct parkinglot_cfg cfg,
struct ast_variable var 
) [static]

Definition at line 5413 of file features.c.

References ast_copy_string(), ast_log(), ast_strlen_zero(), ast_true(), parkinglot_cfg::comebackcontext, parkinglot_cfg::comebackdialtime, parkinglot_cfg::comebacktoorigin, DEFAULT_COMEBACK_DIAL_TIME, ast_variable::file, parkinglot_cfg::is_invalid, ast_variable::lineno, LOG_WARNING, parkinglot_cfg::mohclass, ast_variable::name, ast_variable::next, parkinglot_cfg::parkaddhints, parkinglot_cfg::parkedcallhangup, parkinglot_cfg::parkedcallrecording, parkinglot_cfg::parkedcallreparking, parkinglot_cfg::parkedcalltransfers, parkinglot_cfg::parkext, parkinglot_cfg::parkext_exclusive, parkinglot_cfg::parkfindnext, parkinglot_cfg::parking_con, parkinglot_cfg::parking_start, parkinglot_cfg::parking_stop, parkinglot_feature_flag_cfg(), parkinglot_cfg::parkingtime, and ast_variable::value.

Referenced by build_parkinglot().

05414 {
05415    int error = 0;
05416 
05417    while (var) {
05418       if (!strcasecmp(var->name, "context")) {
05419          ast_copy_string(cfg->parking_con, var->value, sizeof(cfg->parking_con));
05420       } else if (!strcasecmp(var->name, "parkext")) {
05421          ast_copy_string(cfg->parkext, var->value, sizeof(cfg->parkext));
05422       } else if (!strcasecmp(var->name, "parkext_exclusive")) {
05423          cfg->parkext_exclusive = ast_true(var->value);
05424       } else if (!strcasecmp(var->name, "parkinghints")) {
05425          cfg->parkaddhints = ast_true(var->value);
05426       } else if (!strcasecmp(var->name, "parkedmusicclass")) {
05427          ast_copy_string(cfg->mohclass, var->value, sizeof(cfg->mohclass));
05428       } else if (!strcasecmp(var->name, "parkingtime")) {
05429          int parkingtime = 0;
05430 
05431          if ((sscanf(var->value, "%30d", &parkingtime) != 1) || parkingtime < 1) {
05432             ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
05433             error = -1;
05434          } else {
05435             cfg->parkingtime = parkingtime * 1000;
05436          }
05437       } else if (!strcasecmp(var->name, "parkpos")) {
05438          int start = 0;
05439          int end = 0;
05440 
05441          if (sscanf(var->value, "%30d-%30d", &start, &end) != 2) {
05442             ast_log(LOG_WARNING,
05443                "Format for parking positions is a-b, where a and b are numbers at line %d of %s\n",
05444                var->lineno, var->file);
05445             error = -1;
05446          } else if (end < start || start <= 0 || end <= 0) {
05447             ast_log(LOG_WARNING, "Parking range is invalid. Must be a <= b, at line %d of %s\n",
05448                var->lineno, var->file);
05449             error = -1;
05450          } else {
05451             cfg->parking_start = start;
05452             cfg->parking_stop = end;
05453          }
05454       } else if (!strcasecmp(var->name, "findslot")) {
05455          cfg->parkfindnext = (!strcasecmp(var->value, "next"));
05456       } else if (!strcasecmp(var->name, "parkedcalltransfers")) {
05457          parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcalltransfers, var);
05458       } else if (!strcasecmp(var->name, "parkedcallreparking")) {
05459          parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallreparking, var);
05460       } else if (!strcasecmp(var->name, "parkedcallhangup")) {
05461          parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallhangup, var);
05462       } else if (!strcasecmp(var->name, "parkedcallrecording")) {
05463          parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallrecording, var);
05464       } else if (!strcasecmp(var->name, "comebackcontext")) {
05465          ast_copy_string(cfg->comebackcontext, var->value, sizeof(cfg->comebackcontext));
05466       } else if (!strcasecmp(var->name, "comebacktoorigin")) {
05467          cfg->comebacktoorigin = ast_true(var->value);
05468       } else if (!strcasecmp(var->name, "comebackdialtime")) {
05469          if ((sscanf(var->value, "%30u", &cfg->comebackdialtime) != 1)
05470                || (cfg->comebackdialtime < 1)) {
05471             ast_log(LOG_WARNING, "%s is not a valid comebackdialtime\n", var->value);
05472             cfg->parkingtime = DEFAULT_COMEBACK_DIAL_TIME;
05473          }
05474       }
05475       var = var->next;
05476    }
05477 
05478    /* Check for configuration errors */
05479    if (ast_strlen_zero(cfg->parking_con)) {
05480       ast_log(LOG_WARNING, "Parking lot %s needs context\n", pl_name);
05481       error = -1;
05482    }
05483    if (ast_strlen_zero(cfg->parkext)) {
05484       ast_log(LOG_WARNING, "Parking lot %s needs parkext\n", pl_name);
05485       error = -1;
05486    }
05487    if (!cfg->parking_start) {
05488       ast_log(LOG_WARNING, "Parking lot %s needs parkpos\n", pl_name);
05489       error = -1;
05490    }
05491    if (!cfg->comebacktoorigin && ast_strlen_zero(cfg->comebackcontext)) {
05492       ast_log(LOG_WARNING, "Parking lot %s has comebacktoorigin set false"
05493             "but has no comebackcontext.\n",
05494             pl_name);
05495       error = -1;
05496    }
05497    if (error) {
05498       cfg->is_invalid = 1;
05499    }
05500 
05501    return error;
05502 }

static void parkinglot_destroy ( void *  obj  )  [static]

Destroy a parking lot.

Definition at line 5306 of file features.c.

References ast_assert, AST_LIST_EMPTY, AST_LIST_HEAD_DESTROY, and ast_parkinglot::parkings.

Referenced by create_parkinglot().

05307 {
05308    struct ast_parkinglot *doomed = obj;
05309 
05310    /*
05311     * No need to destroy parked calls here because any parked call
05312     * holds a parking lot reference.  Therefore the parkings list
05313     * must be empty.
05314     */
05315    ast_assert(AST_LIST_EMPTY(&doomed->parkings));
05316    AST_LIST_HEAD_DESTROY(&doomed->parkings);
05317 }

static void parkinglot_feature_flag_cfg ( const char *  pl_name,
int *  param,
struct ast_variable var 
) [static]

Definition at line 5390 of file features.c.

References ast_debug, AST_FEATURE_FLAG_BYBOTH, AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, ast_variable::name, and ast_variable::value.

Referenced by parkinglot_config_read().

05391 {
05392    ast_debug(1, "Setting parking lot %s %s to %s\n", pl_name, var->name, var->value);
05393    if (!strcasecmp(var->value, "both")) {
05394       *param = AST_FEATURE_FLAG_BYBOTH;
05395    } else if (!strcasecmp(var->value, "caller")) {
05396       *param = AST_FEATURE_FLAG_BYCALLER;
05397    } else if (!strcasecmp(var->value, "callee")) {
05398       *param = AST_FEATURE_FLAG_BYCALLEE;
05399    }
05400 }

static int parkinglot_hash_cb ( const void *  obj,
const int  flags 
) [static]

Definition at line 819 of file features.c.

References ast_str_case_hash(), and ast_parkinglot::name.

Referenced by ast_features_init().

00820 {
00821    const struct ast_parkinglot *parkinglot = obj;
00822 
00823    return ast_str_case_hash(parkinglot->name);
00824 }

static int parkinglot_is_marked_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 6541 of file features.c.

References AST_LIST_EMPTY, ast_log(), CMP_MATCH, ast_parkinglot::disabled, force_reload_load, LOG_WARNING, ast_parkinglot::name, ast_parkinglot::parkings, and ast_parkinglot::the_mark.

Referenced by load_config().

06542 {
06543    struct ast_parkinglot *parkinglot = obj;
06544 
06545    if (parkinglot->the_mark) {
06546       if (AST_LIST_EMPTY(&parkinglot->parkings)) {
06547          /* This parking lot can actually be deleted. */
06548          return CMP_MATCH;
06549       }
06550       /* Try reloading later when parking lot is empty. */
06551       ast_log(LOG_WARNING,
06552          "Parking lot %s has parked calls.  Could not remove.\n",
06553          parkinglot->name);
06554       parkinglot->disabled = 1;
06555       force_reload_load = 1;
06556    }
06557 
06558    return 0;
06559 }

static int parkinglot_markall_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 6533 of file features.c.

References ast_parkinglot::the_mark.

Referenced by load_config().

06534 {
06535    struct ast_parkinglot *parkinglot = obj;
06536 
06537    parkinglot->the_mark = 1;
06538    return 0;
06539 }

static void parkinglot_unref ( struct ast_parkinglot parkinglot  )  [static]

Unreference parkinglot object.

Definition at line 5289 of file features.c.

References ao2_ref, ast_debug, and ast_parkinglot::name.

Referenced by ast_masq_park_call_exten(), ast_park_call_exten(), build_parkinglot(), create_dynamic_parkinglot(), manage_parkinglot(), manager_park(), park_call_exec(), park_space_abort(), park_space_reserve(), parked_call_exec(), and xfer_park_call_helper().

05290 {
05291    ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name,
05292       ao2_ref(parkinglot, 0) - 1);
05293    ao2_ref(parkinglot, -1);
05294 }

static struct ast_cdr* pick_unlocked_cdr ( struct ast_cdr cdr  )  [static, read]

return the first unlocked cdr in a possible chain

Definition at line 3722 of file features.c.

References AST_CDR_FLAG_LOCKED, ast_test_flag, and ast_cdr::next.

Referenced by ast_bridge_call().

03723 {
03724    struct ast_cdr *cdr_orig = cdr;
03725    while (cdr) {
03726       if (!ast_test_flag(cdr,AST_CDR_FLAG_LOCKED))
03727          return cdr;
03728       cdr = cdr->next;
03729    }
03730    return cdr_orig; /* everybody LOCKED or some other weirdness, like a NULL */
03731 }

static int play_message_in_bridged_call ( struct ast_channel caller_chan,
struct ast_channel callee_chan,
const char *  audiofile 
) [static]

Play message to both caller and callee in bridged call, plays synchronously, autoservicing the other channel during the message, so please don't use this for very long messages.

Definition at line 1985 of file features.c.

References play_message_to_chans().

Referenced by builtin_automonitor().

01986 {
01987    return play_message_to_chans(caller_chan, callee_chan, 0, "automon message",
01988       audiofile);
01989 }

static int play_message_on_chan ( struct ast_channel play_to,
struct ast_channel other,
const char *  msg,
const char *  audiofile 
) [static]

Definition at line 1931 of file features.c.

References ast_autoservice_ignore(), ast_autoservice_start(), ast_autoservice_stop(), AST_FRAME_DTMF_BEGIN, AST_FRAME_DTMF_END, ast_log(), ast_stream_and_wait(), and LOG_WARNING.

Referenced by masq_park_call(), and play_message_to_chans().

01932 {
01933    /* Put other channel in autoservice. */
01934    if (ast_autoservice_start(other)) {
01935       return -1;
01936    }
01937    ast_autoservice_ignore(other, AST_FRAME_DTMF_BEGIN);
01938    ast_autoservice_ignore(other, AST_FRAME_DTMF_END);
01939    if (ast_stream_and_wait(play_to, audiofile, "")) {
01940       ast_log(LOG_WARNING, "Failed to play %s '%s'!\n", msg, audiofile);
01941       ast_autoservice_stop(other);
01942       return -1;
01943    }
01944    if (ast_autoservice_stop(other)) {
01945       return -1;
01946    }
01947    return 0;
01948 }

static int play_message_to_chans ( struct ast_channel left,
struct ast_channel right,
int  which,
const char *  msg,
const char *  audiofile 
) [static]

Definition at line 1966 of file features.c.

References play_message_on_chan().

Referenced by parked_call_exec(), and play_message_in_bridged_call().

01967 {
01968    /* First play the file to the left channel if requested. */
01969    if (which <= 0 && play_message_on_chan(left, right, msg, audiofile)) {
01970       return -1;
01971    }
01972 
01973    /* Then play the file to the right channel if requested. */
01974    if (which >= 0 && play_message_on_chan(right, left, msg, audiofile)) {
01975       return -1;
01976    }
01977 
01978    return 0;
01979 }

static void post_manager_event ( const char *  s,
struct parkeduser pu 
) [static]

Output parking event to manager.

Definition at line 4497 of file features.c.

References ast_channel_name(), ast_channel_uniqueid(), ast_channel::caller, parkeduser::chan, ast_channel::connected, EVENT_FLAG_CALL, ast_party_connected_line::id, ast_party_caller::id, manager_event, ast_party_id::name, ast_parkinglot::name, ast_party_id::number, parkeduser::parkingexten, parkeduser::parkinglot, S_COR, ast_party_name::str, ast_party_number::str, ast_party_name::valid, and ast_party_number::valid.

Referenced by manage_parked_call().

04498 {
04499    manager_event(EVENT_FLAG_CALL, s,
04500       "Exten: %s\r\n"
04501       "Channel: %s\r\n"
04502       "Parkinglot: %s\r\n"
04503       "CallerIDNum: %s\r\n"
04504       "CallerIDName: %s\r\n"
04505       "ConnectedLineNum: %s\r\n"
04506       "ConnectedLineName: %s\r\n"
04507       "UniqueID: %s\r\n",
04508       pu->parkingexten, 
04509       ast_channel_name(pu->chan),
04510       pu->parkinglot->name,
04511       S_COR(pu->chan->caller.id.number.valid, pu->chan->caller.id.number.str, "<unknown>"),
04512       S_COR(pu->chan->caller.id.name.valid, pu->chan->caller.id.name.str, "<unknown>"),
04513       S_COR(pu->chan->connected.id.number.valid, pu->chan->connected.id.number.str, "<unknown>"),
04514       S_COR(pu->chan->connected.id.name.valid, pu->chan->connected.id.name.str, "<unknown>"),
04515       ast_channel_uniqueid(pu->chan)
04516       );
04517 }

static void process_applicationmap_line ( struct ast_variable var  )  [static]

Todo:
XXX var_name or app_args ?

Definition at line 5645 of file features.c.

References ast_call_feature::app, app, ast_call_feature::app_args, args, AST_APP_ARG, ast_calloc, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_FEATURE_FLAG_BYBOTH, AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_FLAG_NEEDSDTMF, AST_FEATURE_FLAG_ONPEER, AST_FEATURE_FLAG_ONSELF, ast_log(), ast_register_feature(), AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, ast_set_flag, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, ast_call_feature::exten, FEATURE_APP_ARGS_LEN, FEATURE_APP_LEN, feature_exec_app(), FEATURE_EXTEN_LEN, FEATURE_MOH_LEN, FEATURE_SNAME_LEN, find_dynamic_feature(), LOG_NOTICE, LOG_WARNING, ast_call_feature::moh_class, ast_variable::name, ast_call_feature::operation, ast_call_feature::sname, strsep(), and ast_variable::value.

Referenced by process_config().

05646 {
05647    char *tmp_val = ast_strdupa(var->value);
05648    char *activateon;
05649    struct ast_call_feature *feature;
05650    AST_DECLARE_APP_ARGS(args,
05651       AST_APP_ARG(exten);
05652       AST_APP_ARG(activatedby);
05653       AST_APP_ARG(app);
05654       AST_APP_ARG(app_args);
05655       AST_APP_ARG(moh_class);
05656    );
05657 
05658    AST_STANDARD_APP_ARGS(args, tmp_val);
05659    if (strchr(args.app, '(')) {
05660       /* New syntax */
05661       args.moh_class = args.app_args;
05662       args.app_args = strchr(args.app, '(');
05663       *args.app_args++ = '\0';
05664       if (args.app_args[strlen(args.app_args) - 1] == ')') {
05665          args.app_args[strlen(args.app_args) - 1] = '\0';
05666       }
05667    }
05668 
05669    activateon = strsep(&args.activatedby, "/");
05670 
05671    /*! \todo XXX var_name or app_args ? */
05672    if (ast_strlen_zero(args.app)
05673       || ast_strlen_zero(args.exten)
05674       || ast_strlen_zero(activateon)
05675       || ast_strlen_zero(var->name)) {
05676       ast_log(LOG_NOTICE,
05677          "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
05678          args.app, args.exten, activateon, var->name);
05679       return;
05680    }
05681 
05682    AST_RWLIST_RDLOCK(&feature_list);
05683    if (find_dynamic_feature(var->name)) {
05684       AST_RWLIST_UNLOCK(&feature_list);
05685       ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n",
05686          var->name);
05687       return;
05688    }
05689    AST_RWLIST_UNLOCK(&feature_list);
05690 
05691    if (!(feature = ast_calloc(1, sizeof(*feature)))) {
05692       return;
05693    }
05694 
05695    ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
05696    ast_copy_string(feature->app, args.app, FEATURE_APP_LEN);
05697    ast_copy_string(feature->exten, args.exten, FEATURE_EXTEN_LEN);
05698 
05699    if (args.app_args) {
05700       ast_copy_string(feature->app_args, args.app_args, FEATURE_APP_ARGS_LEN);
05701    }
05702 
05703    if (args.moh_class) {
05704       ast_copy_string(feature->moh_class, args.moh_class, FEATURE_MOH_LEN);
05705    }
05706 
05707    ast_copy_string(feature->exten, args.exten, sizeof(feature->exten));
05708    feature->operation = feature_exec_app;
05709    ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
05710 
05711    /* Allow caller and callee to be specified for backwards compatability */
05712    if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller")) {
05713       ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
05714    } else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee")) {
05715       ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
05716    } else {
05717       ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
05718          " must be 'self', or 'peer'\n", var->name);
05719       return;
05720    }
05721 
05722    if (ast_strlen_zero(args.activatedby)) {
05723       ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
05724    } else if (!strcasecmp(args.activatedby, "caller")) {
05725       ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
05726    } else if (!strcasecmp(args.activatedby, "callee")) {
05727       ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
05728    } else if (!strcasecmp(args.activatedby, "both")) {
05729       ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
05730    } else {
05731       ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
05732          " must be 'caller', or 'callee', or 'both'\n", var->name);
05733       return;
05734    }
05735 
05736    ast_register_feature(feature);
05737 
05738    ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n",
05739       var->name, args.app, args.app_args, args.exten);
05740 }

static int process_config ( struct ast_config cfg  )  [static]

Definition at line 5742 of file features.c.

References adsipark, ARRAY_LEN, ast_category_browse(), ast_copy_string(), ast_debug, ast_find_call_feature(), ast_log(), AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_true(), ast_unregister_features(), ast_unregister_groups(), ast_variable_browse(), atxfercallbackretries, atxferdropcall, atxferloopdelay, atxfernoanswertimeout, build_parkinglot(), courtesytone, DEFAULT_ATXFER_CALLBACK_RETRIES, DEFAULT_ATXFER_DROP_CALL, DEFAULT_ATXFER_LOOP_DELAY, DEFAULT_FEATURE_DIGIT_TIMEOUT, DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER, DEFAULT_PARKINGLOT, DEFAULT_TRANSFER_DIGIT_TIMEOUT, featuredigittimeout, find_dynamic_feature(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, parkeddynamic, parkedplay, pickupfailsound, pickupsound, process_applicationmap_line(), register_group(), register_group_feature(), remap_feature(), transferdigittimeout, unmap_features(), ast_variable::value, var, xferfailsound, and xfersound.

Referenced by load_config().

05743 {
05744    int i;
05745    struct ast_variable *var = NULL;
05746    struct feature_group *fg = NULL;
05747    char *ctg; 
05748    static const char * const categories[] = { 
05749       /* Categories in features.conf that are not
05750        * to be parsed as group categories
05751        */
05752       "general",
05753       "featuremap",
05754       "applicationmap"
05755    };
05756 
05757    /* Set general features global defaults. */
05758    featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
05759 
05760    /* Set global call pickup defaults. */
05761    strcpy(pickup_ext, "*8");
05762    pickupsound[0] = '\0';
05763    pickupfailsound[0] = '\0';
05764 
05765    /* Set global call transfer defaults. */
05766    strcpy(xfersound, "beep");
05767    strcpy(xferfailsound, "beeperr");
05768    transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
05769    atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
05770    atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
05771    atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
05772    atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
05773 
05774    /* Set global call parking defaults. */
05775    courtesytone[0] = '\0';
05776    parkedplay = 0;
05777    adsipark = 0;
05778    parkeddynamic = 0;
05779 
05780    var = ast_variable_browse(cfg, "general");
05781    build_parkinglot(DEFAULT_PARKINGLOT, var);
05782    for (; var; var = var->next) {
05783       if (!strcasecmp(var->name, "parkeddynamic")) {
05784          parkeddynamic = ast_true(var->value);
05785       } else if (!strcasecmp(var->name, "adsipark")) {
05786          adsipark = ast_true(var->value);
05787       } else if (!strcasecmp(var->name, "transferdigittimeout")) {
05788          if ((sscanf(var->value, "%30d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
05789             ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
05790             transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
05791          } else {
05792             transferdigittimeout = transferdigittimeout * 1000;
05793          }
05794       } else if (!strcasecmp(var->name, "featuredigittimeout")) {
05795          if ((sscanf(var->value, "%30d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
05796             ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
05797             featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
05798          }
05799       } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
05800          if ((sscanf(var->value, "%30d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
05801             ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
05802             atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
05803          } else {
05804             atxfernoanswertimeout = atxfernoanswertimeout * 1000;
05805          }
05806       } else if (!strcasecmp(var->name, "atxferloopdelay")) {
05807          if ((sscanf(var->value, "%30u", &atxferloopdelay) != 1)) {
05808             ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
05809             atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
05810          } else {
05811             atxferloopdelay *= 1000;
05812          }
05813       } else if (!strcasecmp(var->name, "atxferdropcall")) {
05814          atxferdropcall = ast_true(var->value);
05815       } else if (!strcasecmp(var->name, "atxfercallbackretries")) {
05816          if ((sscanf(var->value, "%30u", &atxfercallbackretries) != 1)) {
05817             ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
05818             atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
05819          }
05820       } else if (!strcasecmp(var->name, "courtesytone")) {
05821          ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
05822       }  else if (!strcasecmp(var->name, "parkedplay")) {
05823          if (!strcasecmp(var->value, "both")) {
05824             parkedplay = 2;
05825          } else if (!strcasecmp(var->value, "parked")) {
05826             parkedplay = 1;
05827          } else {
05828             parkedplay = 0;
05829          }
05830       } else if (!strcasecmp(var->name, "xfersound")) {
05831          ast_copy_string(xfersound, var->value, sizeof(xfersound));
05832       } else if (!strcasecmp(var->name, "xferfailsound")) {
05833          ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
05834       } else if (!strcasecmp(var->name, "pickupexten")) {
05835          ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
05836       } else if (!strcasecmp(var->name, "pickupsound")) {
05837          ast_copy_string(pickupsound, var->value, sizeof(pickupsound));
05838       } else if (!strcasecmp(var->name, "pickupfailsound")) {
05839          ast_copy_string(pickupfailsound, var->value, sizeof(pickupfailsound));
05840       }
05841    }
05842 
05843    unmap_features();
05844    for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
05845       if (remap_feature(var->name, var->value)) {
05846          ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
05847       }
05848    }
05849 
05850    /* Map a key combination to an application */
05851    ast_unregister_features();
05852    for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
05853       process_applicationmap_line(var);
05854    }
05855 
05856    ast_unregister_groups();
05857    AST_RWLIST_WRLOCK(&feature_groups);
05858 
05859    ctg = NULL;
05860    while ((ctg = ast_category_browse(cfg, ctg))) {
05861       /* Is this a parkinglot definition ? */
05862       if (!strncasecmp(ctg, "parkinglot_", strlen("parkinglot_"))) {
05863          ast_debug(2, "Found configuration section %s, assume parking context\n", ctg);
05864          if (!build_parkinglot(ctg, ast_variable_browse(cfg, ctg))) {
05865             ast_log(LOG_ERROR, "Could not build parking lot %s. Configuration error.\n", ctg);
05866          } else {
05867             ast_debug(1, "Configured parking context %s\n", ctg);
05868          }
05869          continue;
05870       }
05871 
05872       /* No, check if it's a group */
05873       for (i = 0; i < ARRAY_LEN(categories); i++) {
05874          if (!strcasecmp(categories[i], ctg)) {
05875             break;
05876          }
05877       }
05878       if (i < ARRAY_LEN(categories)) {
05879          continue;
05880       }
05881 
05882       if (!(fg = register_group(ctg))) {
05883          continue;
05884       }
05885 
05886       for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
05887          struct ast_call_feature *feature;
05888 
05889          AST_RWLIST_RDLOCK(&feature_list);
05890          if (!(feature = find_dynamic_feature(var->name)) && 
05891              !(feature = ast_find_call_feature(var->name))) {
05892             AST_RWLIST_UNLOCK(&feature_list);
05893             ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
05894             continue;
05895          }
05896          AST_RWLIST_UNLOCK(&feature_list);
05897 
05898          register_group_feature(fg, var->value, feature);
05899       }
05900    }
05901 
05902    AST_RWLIST_UNLOCK(&feature_groups);
05903 
05904    return 0;
05905 }

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

Find the context for the transfer.

Parameters:
transferer 
transferee Grab the TRANSFER_CONTEXT, if fails try grabbing macrocontext.
Returns:
a context string

Definition at line 2227 of file features.c.

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

Referenced by builtin_atxfer(), and builtin_blindtransfer().

02228 {
02229    const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
02230    if (ast_strlen_zero(s)) {
02231       s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
02232    }
02233    if (ast_strlen_zero(s)) { /* Use the non-macro context to transfer the call XXX ? */
02234       s = transferer->macrocontext;
02235    }
02236    if (ast_strlen_zero(s)) {
02237       s = transferer->context;
02238    }
02239    return s;  
02240 }

static struct feature_group* register_group ( const char *  fgname  )  [static, read]

Add new feature group.

Parameters:
fgname feature group name.
Add new feature group to the feature group list insert at head of list.
Note:
This function MUST be called while feature_groups is locked.

Definition at line 2874 of file features.c.

References ast_calloc_with_stringfields, AST_LIST_INSERT_HEAD, ast_log(), ast_string_field_set, ast_verb, feature_group::gname, and LOG_NOTICE.

Referenced by process_config().

02875 {
02876    struct feature_group *fg;
02877 
02878    if (!fgname) {
02879       ast_log(LOG_NOTICE, "You didn't pass a new group name!\n");
02880       return NULL;
02881    }
02882 
02883    if (!(fg = ast_calloc_with_stringfields(1, struct feature_group, 128))) {
02884       return NULL;
02885    }
02886 
02887    ast_string_field_set(fg, gname, fgname);
02888 
02889    AST_LIST_INSERT_HEAD(&feature_groups, fg, entry);
02890 
02891    ast_verb(2, "Registered group '%s'\n", fg->gname);
02892 
02893    return fg;
02894 }

static void register_group_feature ( struct feature_group fg,
const char *  exten,
struct ast_call_feature feature 
) [static]

Add feature to group.

Parameters:
fg feature group
exten 
feature feature to add.
Check fg and feature specified, add feature to list
Note:
This function MUST be called while feature_groups is locked.

Definition at line 2905 of file features.c.

References ast_calloc_with_stringfields, AST_LIST_INSERT_HEAD, ast_log(), ast_string_field_set, ast_verb, feature_group_exten::exten, ast_call_feature::exten, feature_group_exten::feature, feature_group::features, feature_group::gname, LOG_NOTICE, S_OR, and ast_call_feature::sname.

Referenced by process_config().

02906 {
02907    struct feature_group_exten *fge;
02908 
02909    if (!fg) {
02910       ast_log(LOG_NOTICE, "You didn't pass a group!\n");
02911       return;
02912    }
02913 
02914    if (!feature) {
02915       ast_log(LOG_NOTICE, "You didn't pass a feature!\n");
02916       return;
02917    }
02918 
02919    if (!(fge = ast_calloc_with_stringfields(1, struct feature_group_exten, 128))) {
02920       return;
02921    }
02922 
02923    ast_string_field_set(fge, exten, S_OR(exten, feature->exten));
02924 
02925    fge->feature = feature;
02926 
02927    AST_LIST_INSERT_HEAD(&fg->features, fge, entry);
02928 
02929    ast_verb(2, "Registered feature '%s' for group '%s' at exten '%s'\n",
02930                feature->sname, fg->gname, fge->exten);
02931 }

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

Definition at line 3114 of file features.c.

References ast_copy_string(), ast_rwlock_unlock, ast_rwlock_wrlock, FEATURES_COUNT, and features_lock.

Referenced by process_config().

03115 {
03116    int x, res = -1;
03117 
03118    ast_rwlock_wrlock(&features_lock);
03119    for (x = 0; x < FEATURES_COUNT; x++) {
03120       if (strcasecmp(builtin_features[x].sname, name))
03121          continue;
03122 
03123       ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
03124       res = 0;
03125       break;
03126    }
03127    ast_rwlock_unlock(&features_lock);
03128 
03129    return res;
03130 }

static void remove_dead_context_usage ( const char *  context,
struct parking_dp_context old_ctx,
struct parking_dp_context new_ctx 
) [static]

Definition at line 6466 of file features.c.

References parking_dp_context::access_extens, destroy_space(), parking_dp_context::hints, remove_dead_ramp_usage(), remove_dead_spaces_usage(), and parking_dp_context::spaces.

Referenced by remove_dead_dialplan_useage().

06467 {
06468    remove_dead_ramp_usage(context, &old_ctx->access_extens, &new_ctx->access_extens);
06469    remove_dead_spaces_usage(context, &old_ctx->spaces, &new_ctx->spaces, destroy_space);
06470 #if 0
06471    /* I don't think we should destroy hints if the parking space still exists. */
06472    remove_dead_spaces_usage(context, &old_ctx->hints, &new_ctx->hints, destroy_space_hint);
06473 #endif
06474 }

static void remove_dead_dialplan_useage ( struct parking_dp_map old_map,
struct parking_dp_map new_map 
) [static]

Definition at line 6489 of file features.c.

References ast_context_destroy(), ast_context_find(), AST_LIST_FIRST, AST_LIST_NEXT, parking_dp_context::context, registrar, and remove_dead_context_usage().

Referenced by load_config().

06490 {
06491    struct parking_dp_context *old_ctx;
06492    struct parking_dp_context *new_ctx;
06493    struct ast_context *con;
06494    int cmp;
06495 
06496    old_ctx = AST_LIST_FIRST(old_map);
06497    new_ctx = AST_LIST_FIRST(new_map);
06498 
06499    while (new_ctx) {
06500       if (!old_ctx) {
06501          /* No old contexts left, so no dead stuff can remain. */
06502          return;
06503       }
06504       cmp = strcmp(old_ctx->context, new_ctx->context);
06505       if (cmp < 0) {
06506          /* New map does not have old map context. */
06507          con = ast_context_find(old_ctx->context);
06508          if (con) {
06509             ast_context_destroy(con, registrar);
06510          }
06511          old_ctx = AST_LIST_NEXT(old_ctx, node);
06512          continue;
06513       }
06514       if (cmp == 0) {
06515          /* Old and new map have this context. */
06516          remove_dead_context_usage(old_ctx->context, old_ctx, new_ctx);
06517          old_ctx = AST_LIST_NEXT(old_ctx, node);
06518       } else {
06519          /* Old map does not have new map context. */
06520       }
06521       new_ctx = AST_LIST_NEXT(new_ctx, node);
06522    }
06523 
06524    /* Any old contexts left must be dead. */
06525    for (; old_ctx; old_ctx = AST_LIST_NEXT(old_ctx, node)) {
06526       con = ast_context_find(old_ctx->context);
06527       if (con) {
06528          ast_context_destroy(con, registrar);
06529       }
06530    }
06531 }

static void remove_dead_ramp_usage ( const char *  context,
struct parking_dp_ramp_map old_ramps,
struct parking_dp_ramp_map new_ramps 
) [static]

Definition at line 6325 of file features.c.

References AST_LIST_FIRST, AST_LIST_NEXT, parking_dp_ramp::exten, and remove_exten_if_exist().

Referenced by remove_dead_context_usage().

06326 {
06327    struct parking_dp_ramp *old_ramp;
06328    struct parking_dp_ramp *new_ramp;
06329    int cmp;
06330 
06331    old_ramp = AST_LIST_FIRST(old_ramps);
06332    new_ramp = AST_LIST_FIRST(new_ramps);
06333 
06334    while (new_ramp) {
06335       if (!old_ramp) {
06336          /* No old ramps left, so no dead ramps can remain. */
06337          return;
06338       }
06339       cmp = strcmp(old_ramp->exten, new_ramp->exten);
06340       if (cmp < 0) {
06341          /* New map does not have old ramp. */
06342          remove_exten_if_exist(context, old_ramp->exten, 1);
06343          old_ramp = AST_LIST_NEXT(old_ramp, node);
06344          continue;
06345       }
06346       if (cmp == 0) {
06347          /* Old and new map have this ramp. */
06348          old_ramp = AST_LIST_NEXT(old_ramp, node);
06349       } else {
06350          /* Old map does not have new ramp. */
06351       }
06352       new_ramp = AST_LIST_NEXT(new_ramp, node);
06353    }
06354 
06355    /* Any old ramps left must be dead. */
06356    for (; old_ramp; old_ramp = AST_LIST_NEXT(old_ramp, node)) {
06357       remove_exten_if_exist(context, old_ramp->exten, 1);
06358    }
06359 }

static void remove_dead_spaces_usage ( const char *  context,
struct parking_dp_space_map old_spaces,
struct parking_dp_space_map new_spaces,
void(*)(const char *context, int space)  destroy_space 
) [static]

< Current position in the current old range.

Definition at line 6395 of file features.c.

References AST_LIST_FIRST, AST_LIST_NEXT, destroy_space(), parking_dp_spaces::start, and parking_dp_spaces::stop.

Referenced by remove_dead_context_usage().

06398 {
06399    struct parking_dp_spaces *old_range;
06400    struct parking_dp_spaces *new_range;
06401    int space;/*!< Current position in the current old range. */
06402    int stop;
06403 
06404    old_range = AST_LIST_FIRST(old_spaces);
06405    new_range = AST_LIST_FIRST(new_spaces);
06406    space = -1;
06407 
06408    while (old_range) {
06409       if (space < old_range->start) {
06410          space = old_range->start;
06411       }
06412       if (new_range) {
06413          if (space < new_range->start) {
06414             /* Current position in old range starts before new range. */
06415             if (old_range->stop < new_range->start) {
06416                /* Old range ends before new range. */
06417                stop = old_range->stop;
06418                old_range = AST_LIST_NEXT(old_range, node);
06419             } else {
06420                /* Tail of old range overlaps new range. */
06421                stop = new_range->start - 1;
06422             }
06423          } else if (/* new_range->start <= space && */ space <= new_range->stop) {
06424             /* Current position in old range overlaps new range. */
06425             if (old_range->stop <= new_range->stop) {
06426                /* Old range ends at or before new range. */
06427                old_range = AST_LIST_NEXT(old_range, node);
06428             } else {
06429                /* Old range extends beyond end of new range. */
06430                space = new_range->stop + 1;
06431                new_range = AST_LIST_NEXT(new_range, node);
06432             }
06433             continue;
06434          } else /* if (new_range->stop < space) */ {
06435             /* Current position in old range starts after new range. */
06436             new_range = AST_LIST_NEXT(new_range, node);
06437             continue;
06438          }
06439       } else {
06440          /* No more new ranges.  All remaining old spaces are dead. */
06441          stop = old_range->stop;
06442          old_range = AST_LIST_NEXT(old_range, node);
06443       }
06444 
06445       /* Destroy dead parking spaces. */
06446       for (; space <= stop; ++space) {
06447          destroy_space(context, space);
06448       }
06449    }
06450 }

static void remove_exten_if_exist ( const char *  context,
const char *  exten,
int  priority 
) [static]

Definition at line 6299 of file features.c.

References ast_context_remove_extension(), ast_debug, E_MATCH, pbx_find_extension(), registrar, and pbx_find_info::stacklen.

Referenced by destroy_space(), and remove_dead_ramp_usage().

06300 {
06301    struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
06302 
06303    if (pbx_find_extension(NULL, NULL, &q, context, exten, priority, NULL, NULL,
06304       E_MATCH)) {
06305       ast_debug(1, "Removing unneeded parking lot exten: %s@%s priority:%d\n",
06306          context, exten, priority);
06307       ast_context_remove_extension(context, exten, priority, registrar);
06308    }
06309 }

static void set_bridge_features_on_config ( struct ast_bridge_config config,
const char *  features 
) [static]

Definition at line 3733 of file features.c.

References AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_log(), ast_set_flag, ast_strlen_zero(), ast_bridge_config::features_caller, and LOG_WARNING.

Referenced by ast_bridge_call().

03734 {
03735    const char *feature;
03736 
03737    if (ast_strlen_zero(features)) {
03738       return;
03739    }
03740 
03741    for (feature = features; *feature; feature++) {
03742       switch (*feature) {
03743       case 'T' :
03744       case 't' :
03745          ast_set_flag(&(config->features_caller), AST_FEATURE_REDIRECT);
03746          break;
03747       case 'K' :
03748       case 'k' :
03749          ast_set_flag(&(config->features_caller), AST_FEATURE_PARKCALL);
03750          break;
03751       case 'H' :
03752       case 'h' :
03753          ast_set_flag(&(config->features_caller), AST_FEATURE_DISCONNECT);
03754          break;
03755       case 'W' :
03756       case 'w' :
03757          ast_set_flag(&(config->features_caller), AST_FEATURE_AUTOMON);
03758          break;
03759       default :
03760          ast_log(LOG_WARNING, "Skipping unknown feature code '%c'\n", *feature);
03761       }
03762    }
03763 }

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

store context, extension and priority

Parameters:
chan,context,ext,pri 

Definition at line 838 of file features.c.

References ast_copy_string(), ast_channel::context, ast_channel::exten, and ast_channel::priority.

Referenced by builtin_blindtransfer(), manage_parked_call(), and masq_park_call().

00839 {
00840    ast_copy_string(chan->context, context, sizeof(chan->context));
00841    ast_copy_string(chan->exten, ext, sizeof(chan->exten));
00842    chan->priority = pri;
00843 }

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

Definition at line 3301 of file 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_TRAVERSE, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_rwlock_rdlock, ast_rwlock_unlock, ast_set_flag, ast_strdupa, ast_test_flag, feature_group_exten::feature, ast_call_feature::feature_mask, feature_group::features, ast_bridge_config::features_callee, ast_bridge_config::features_caller, FEATURES_COUNT, features_lock, find_dynamic_feature(), pbx_builtin_getvar_helper(), and strsep().

Referenced by ast_bridge_call().

03302 {
03303    int x;
03304 
03305    ast_clear_flag(config, AST_FLAGS_ALL);
03306 
03307    ast_rwlock_rdlock(&features_lock);
03308    for (x = 0; x < FEATURES_COUNT; x++) {
03309       if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
03310          continue;
03311 
03312       if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
03313          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
03314 
03315       if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
03316          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
03317    }
03318    ast_rwlock_unlock(&features_lock);
03319 
03320    if (!(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
03321       const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
03322 
03323       if (dynamic_features) {
03324          char *tmp = ast_strdupa(dynamic_features);
03325          char *tok;
03326          struct ast_call_feature *feature;
03327 
03328          /* while we have a feature */
03329          while ((tok = strsep(&tmp, "#"))) {
03330             struct feature_group *fg;
03331 
03332             AST_RWLIST_RDLOCK(&feature_groups);
03333             AST_RWLIST_TRAVERSE(&feature_groups, fg, entry) {
03334                struct feature_group_exten *fge;
03335 
03336                AST_LIST_TRAVERSE(&fg->features, fge, entry) {
03337                   if (ast_test_flag(fge->feature, AST_FEATURE_FLAG_BYCALLER)) {
03338                      ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
03339                   }
03340                   if (ast_test_flag(fge->feature, AST_FEATURE_FLAG_BYCALLEE)) {
03341                      ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
03342                   }
03343                }
03344             }
03345             AST_RWLIST_UNLOCK(&feature_groups);
03346 
03347             AST_RWLIST_RDLOCK(&feature_list);
03348             if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
03349                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER)) {
03350                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
03351                }
03352                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE)) {
03353                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
03354                }
03355             }
03356             AST_RWLIST_UNLOCK(&feature_list);
03357          }
03358       }
03359    }
03360 }

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

Parameters:
caller,callee,peer,chan,sense Detect who triggered feature and set callee/caller variables accordingly

Definition at line 1858 of file features.c.

References FEATURE_SENSE_PEER.

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

01860 {
01861    if (sense == FEATURE_SENSE_PEER) {
01862       *caller = peer;
01863       *callee = chan;
01864    } else {
01865       *callee = peer;
01866       *caller = chan;
01867    }
01868 }

static void unmap_features ( void   )  [static]

Definition at line 3104 of file features.c.

References ast_rwlock_unlock, ast_rwlock_wrlock, FEATURES_COUNT, and features_lock.

Referenced by process_config().

03105 {
03106    int x;
03107 
03108    ast_rwlock_wrlock(&features_lock);
03109    for (x = 0; x < FEATURES_COUNT; x++)
03110       strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
03111    ast_rwlock_unlock(&features_lock);
03112 }

static int usage_context_add_ramp ( struct parking_dp_ramp_map ramp_map,
const char *  exten,
int  exclusive,
struct ast_parkinglot lot,
int  complain 
) [static]

Definition at line 5985 of file features.c.

References AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), build_dialplan_useage_ramp(), ast_parkinglot::cfg, parking_dp_ramp::exclusive, parking_dp_ramp::exten, LOG_WARNING, ast_parkinglot::name, and parkinglot_cfg::parking_con.

Referenced by dialplan_usage_add_parkinglot_data().

05986 {
05987    struct parking_dp_ramp *cur_ramp;
05988    struct parking_dp_ramp *new_ramp;
05989    int cmp;
05990 
05991    /* Make sure that exclusive is only 0 or 1 */
05992    if (exclusive) {
05993       exclusive = 1;
05994    }
05995 
05996    AST_LIST_TRAVERSE_SAFE_BEGIN(ramp_map, cur_ramp, node) {
05997       cmp = strcmp(exten, cur_ramp->exten);
05998       if (cmp > 0) {
05999          /* The parking lot ramp goes after this node. */
06000          continue;
06001       }
06002       if (cmp == 0) {
06003          /* The ramp is already in the map. */
06004          if (complain && (cur_ramp->exclusive || exclusive)) {
06005             ast_log(LOG_WARNING,
06006                "Parking lot '%s' parkext %s@%s used by another parking lot.\n",
06007                lot->name, exten, lot->cfg.parking_con);
06008          }
06009          return 0;
06010       }
06011       /* The new parking lot ramp goes before this node. */
06012       new_ramp = build_dialplan_useage_ramp(exten, exclusive);
06013       if (!new_ramp) {
06014          return -1;
06015       }
06016       AST_LIST_INSERT_BEFORE_CURRENT(new_ramp, node);
06017       return 0;
06018    }
06019    AST_LIST_TRAVERSE_SAFE_END;
06020 
06021    /* New parking lot access ramp goes on the end. */
06022    new_ramp = build_dialplan_useage_ramp(exten, exclusive);
06023    if (!new_ramp) {
06024       return -1;
06025    }
06026    AST_LIST_INSERT_TAIL(ramp_map, new_ramp, node);
06027    return 0;
06028 }

static int usage_context_add_spaces ( struct parking_dp_space_map space_map,
int  start,
int  stop,
struct ast_parkinglot lot,
int  complain 
) [static]

Definition at line 6066 of file features.c.

References ast_free, AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), build_dialplan_useage_spaces(), ast_parkinglot::cfg, LOG_WARNING, ast_parkinglot::name, parkinglot_cfg::parking_con, parking_dp_spaces::start, and parking_dp_spaces::stop.

Referenced by dialplan_usage_add_parkinglot_data().

06067 {
06068    struct parking_dp_spaces *cur_node;
06069    struct parking_dp_spaces *expand_node;
06070    struct parking_dp_spaces *new_node;
06071 
06072    expand_node = NULL;
06073    AST_LIST_TRAVERSE_SAFE_BEGIN(space_map, cur_node, node) {
06074       /* NOTE: stop + 1 to combine immediately adjacent nodes into one. */
06075       if (expand_node) {
06076          /* The previous node is expanding to possibly eat following nodes. */
06077          if (expand_node->stop + 1 < cur_node->start) {
06078             /* Current node is completely after expanding node. */
06079             return 0;
06080          }
06081 
06082          if (complain
06083             && ((cur_node->start <= start && start <= cur_node->stop)
06084                || (cur_node->start <= stop && stop <= cur_node->stop)
06085                || (start < cur_node->start && cur_node->stop < stop))) {
06086             /* Only complain once per range add. */
06087             complain = 0;
06088             ast_log(LOG_WARNING,
06089                "Parking lot '%s' parkpos %d-%d@%s overlaps another parking lot.\n",
06090                lot->name, start, stop, lot->cfg.parking_con);
06091          }
06092 
06093          /* Current node is eaten by the expanding node. */
06094          if (expand_node->stop < cur_node->stop) {
06095             expand_node->stop = cur_node->stop;
06096          }
06097          AST_LIST_REMOVE_CURRENT(node);
06098          ast_free(cur_node);
06099          continue;
06100       }
06101 
06102       if (cur_node->stop + 1 < start) {
06103          /* New range is completely after current node. */
06104          continue;
06105       }
06106       if (stop + 1 < cur_node->start) {
06107          /* New range is completely before current node. */
06108          new_node = build_dialplan_useage_spaces(start, stop);
06109          if (!new_node) {
06110             return -1;
06111          }
06112          AST_LIST_INSERT_BEFORE_CURRENT(new_node, node);
06113          return 0;
06114       }
06115 
06116       if (complain
06117          && ((cur_node->start <= start && start <= cur_node->stop)
06118             || (cur_node->start <= stop && stop <= cur_node->stop)
06119             || (start < cur_node->start && cur_node->stop < stop))) {
06120          /* Only complain once per range add. */
06121          complain = 0;
06122          ast_log(LOG_WARNING,
06123             "Parking lot '%s' parkpos %d-%d@%s overlaps another parking lot.\n",
06124             lot->name, start, stop, lot->cfg.parking_con);
06125       }
06126 
06127       /* Current node range overlaps or is immediately adjacent to new range. */
06128       if (start < cur_node->start) {
06129          /* Expand the current node in the front. */
06130          cur_node->start = start;
06131       }
06132       if (stop <= cur_node->stop) {
06133          /* Current node is not expanding in the rear. */
06134          return 0;
06135       }
06136       cur_node->stop = stop;
06137       expand_node = cur_node;
06138    }
06139    AST_LIST_TRAVERSE_SAFE_END;
06140 
06141    if (expand_node) {
06142       /*
06143        * The previous node expanded and either ate all following nodes
06144        * or it was the last node.
06145        */
06146       return 0;
06147    }
06148 
06149    /* New range goes on the end. */
06150    new_node = build_dialplan_useage_spaces(start, stop);
06151    if (!new_node) {
06152       return -1;
06153    }
06154    AST_LIST_INSERT_TAIL(space_map, new_node, node);
06155    return 0;
06156 }

static int xfer_park_call_helper ( struct ast_channel park_me,
struct ast_channel parker,
struct ast_exten park_exten 
) [static]

Definition at line 1798 of file features.c.

References AST_FEATURE_RETURN_SUCCESS, ast_get_extension_app_data(), AST_PARK_OPT_SILENCE, AST_STANDARD_APP_ARGS, ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_test_flag, create_dynamic_parkinglot(), default_parkinglot, find_parkinglot(), findparkinglotname(), finishup(), masq_park_call(), parkeddynamic, ast_park_call_args::parkinglot, parkinglot_addref(), parkinglot_unref(), parse(), and park_app_args::pl_name.

Referenced by builtin_atxfer(), and builtin_blindtransfer().

01799 {
01800    char *parse;
01801    const char *app_data;
01802    const char *pl_name;
01803    struct ast_park_call_args args = { 0, };
01804    struct park_app_args app_args;
01805    int res;
01806 
01807    app_data = ast_get_extension_app_data(park_exten);
01808    if (!app_data) {
01809       app_data = "";
01810    }
01811    parse = ast_strdupa(app_data);
01812    AST_STANDARD_APP_ARGS(app_args, parse);
01813 
01814    /* Find the parking lot */
01815    if (!ast_strlen_zero(app_args.pl_name)) {
01816       pl_name = app_args.pl_name;
01817    } else {
01818       pl_name = findparkinglotname(parker);
01819    }
01820    if (ast_strlen_zero(pl_name)) {
01821       /* Parking lot is not specified, so use the default parking lot. */
01822       args.parkinglot = parkinglot_addref(default_parkinglot);
01823    } else {
01824       args.parkinglot = find_parkinglot(pl_name);
01825       if (!args.parkinglot && parkeddynamic) {
01826          args.parkinglot = create_dynamic_parkinglot(pl_name, park_me);
01827       }
01828    }
01829 
01830    if (args.parkinglot) {
01831       /* Park the call */
01832       res = finishup(park_me);
01833       if (res) {
01834          /* park_me hungup on us. */
01835          parkinglot_unref(args.parkinglot);
01836          return -1;
01837       }
01838       res = masq_park_call(park_me, parker, &args);
01839       parkinglot_unref(args.parkinglot);
01840    } else {
01841       /* Parking failed because parking lot does not exist. */
01842       if (!ast_test_flag(&args, AST_PARK_OPT_SILENCE)) {
01843          ast_stream_and_wait(parker, "pbx-parkingfailed", "");
01844       }
01845       finishup(park_me);
01846       res = -1;
01847    }
01848 
01849    return res ? AST_FEATURE_RETURN_SUCCESS : -1;
01850 }


Variable Documentation

int adsipark [static]

Definition at line 617 of file features.c.

Referenced by park_call_full(), and process_config().

char* app_bridge = "Bridge" [static]

Definition at line 7351 of file features.c.

unsigned int atxfercallbackretries [static]

Definition at line 625 of file features.c.

Referenced by builtin_atxfer(), and process_config().

unsigned int atxferdropcall [static]

Definition at line 623 of file features.c.

Referenced by builtin_atxfer(), and process_config().

unsigned int atxferloopdelay [static]

Definition at line 624 of file features.c.

Referenced by builtin_atxfer(), and process_config().

int atxfernoanswertimeout [static]

Definition at line 622 of file features.c.

Referenced by builtin_atxfer(), and process_config().

struct ast_app_option bridge_exec_options[128] = { [ 'p' ] = { .flag = BRIDGE_OPT_PLAYTONE }, [ 'h' ] = { .flag = OPT_CALLEE_HANGUP }, [ 'H' ] = { .flag = OPT_CALLER_HANGUP }, [ 'k' ] = { .flag = OPT_CALLEE_PARK }, [ 'K' ] = { .flag = OPT_CALLER_PARK }, [ 'L' ] = { .flag = OPT_DURATION_LIMIT , .arg_index = OPT_ARG_DURATION_LIMIT + 1 }, [ 'S' ] = { .flag = OPT_DURATION_STOP , .arg_index = OPT_ARG_DURATION_STOP + 1 }, [ 't' ] = { .flag = OPT_CALLEE_TRANSFER }, [ 'T' ] = { .flag = OPT_CALLER_TRANSFER }, [ 'w' ] = { .flag = OPT_CALLEE_MONITOR }, [ 'W' ] = { .flag = OPT_CALLER_MONITOR }, [ 'x' ] = { .flag = OPT_CALLEE_KILL }, } [static]

Definition at line 7388 of file features.c.

Referenced by bridge_exec().

Definition at line 2840 of file features.c.

struct ast_cli_entry cli_features[] [static]

Initial value:

 {
   AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
   AST_CLI_DEFINE(handle_features_reload, "Reloads configured features"),
   AST_CLI_DEFINE(handle_parkedcalls, "List currently parked calls"),
}

Definition at line 7024 of file features.c.

char courtesytone[256] [static]

Courtesy tone used to pickup parked calls and on-touch-record

Definition at line 598 of file features.c.

Referenced by builtin_automixmonitor(), builtin_automonitor(), parked_call_exec(), and process_config().

Default parking lot.

Note:
Holds a parkinglot reference.

Will not be NULL while running.

Definition at line 591 of file features.c.

Initial value:

 {
   .type = "dial-features",
   .destroy = dial_features_destroy,
   .duplicate = dial_features_duplicate,
}

Definition at line 755 of file features.c.

Referenced by add_features_datastores(), builtin_atxfer(), manage_parked_call(), and parked_call_exec().

int featuredigittimeout [static]

Definition at line 620 of file features.c.

ast_rwlock_t features_lock = { {0} , NULL, 1 } [static]

ast_mutex_t features_reload_lock = { PTHREAD_MUTEX_INITIALIZER , NULL, 1 } [static]

Ensure that features.conf reloads on one thread at a time.

Definition at line 615 of file features.c.

Referenced by ast_features_reload().

int force_reload_load [static]

Force a config reload to reload regardless of config file timestamp.

Definition at line 594 of file features.c.

Referenced by build_parkinglot(), load_config(), parkinglot_activate_cb(), and parkinglot_is_marked_cb().

struct ast_app* mixmonitor_app = NULL [static]

Definition at line 646 of file features.c.

Referenced by builtin_automixmonitor().

int mixmonitor_ok = 1 [static]

Definition at line 647 of file features.c.

Referenced by builtin_automixmonitor().

struct ast_app* monitor_app = NULL [static]

Definition at line 643 of file features.c.

Referenced by ast_bridge_call(), and builtin_automonitor().

int monitor_ok = 1 [static]

Definition at line 644 of file features.c.

Referenced by ast_bridge_call(), and builtin_automonitor().

struct ast_app_option park_call_options[128] = { [ 'r' ] = { .flag = AST_PARK_OPT_RINGING }, [ 'R' ] = { .flag = AST_PARK_OPT_RANDOMIZE }, [ 's' ] = { .flag = AST_PARK_OPT_SILENCE }, } [static]

Definition at line 4918 of file features.c.

Referenced by park_call_exec().

const char* parkcall = "Park" [static]

Definition at line 641 of file features.c.

Referenced by ast_features_init(), get_parking_exten(), and parkinglot_activate().

const char* parkedcall = "ParkedCall" [static]

Definition at line 422 of file features.c.

int parkeddynamic = 0 [static]

Enable creation of parkinglots dynamically

Definition at line 597 of file features.c.

Referenced by ast_masq_park_call_exten(), ast_park_call_exten(), park_call_exec(), park_space_reserve(), process_config(), and xfer_park_call_helper().

int parkedplay = 0 [static]

Who to play courtesytone to when someone picks up a parked call.

Definition at line 596 of file features.c.

Referenced by parked_call_exec(), and process_config().

char parking_con_dial[] = "park-dial" [static]

Context for parking dialback to parker.

Note:
The need for the context is a KLUDGE.
Todo:
Might be able to eliminate the parking_con_dial context kludge by running app_dial directly in its own thread to simulate a PBX.

Definition at line 612 of file features.c.

Referenced by ast_features_reload(), and manage_parked_call().

pthread_t parking_thread [static]

Definition at line 652 of file features.c.

Referenced by ast_features_init(), and park_call_full().

Default configuration for normal parking lots.

Definition at line 5372 of file features.c.

Default configuration for default parking lot.

Definition at line 5359 of file features.c.

struct ao2_container* parkinglots [static]

The configured parking lots container. Always at least one - the default parking lot.

Definition at line 584 of file features.c.

Referenced by ast_features_init(), build_dialplan_useage_map(), build_parkinglot(), create_dynamic_parkinglot(), do_parking_thread(), find_parkinglot(), handle_feature_show(), handle_parkedcalls(), load_config(), and manager_parking_status().

Initial value:

 {
   .type = "pickup-active",
}
The presence of this datastore on the channel indicates that someone is attemting to pickup or has picked up the channel. The purpose is to prevent a race between two channels attempting to pickup the same channel.

Definition at line 7193 of file features.c.

char pickup_ext[AST_MAX_EXTENSION] [static]

Call pickup extension

Definition at line 424 of file features.c.

Referenced by analog_canmatch_featurecode(), and canmatch_featurecode().

char pickupfailsound[256] [static]

Pickup failure sound

Definition at line 602 of file features.c.

Referenced by ast_pickup_call(), and process_config().

char pickupsound[256] [static]

Pickup sound

Definition at line 601 of file features.c.

Referenced by ast_pickup_call(), and process_config().

char* registrar = "features" [static]

struct ast_app* stopmixmonitor_app = NULL [static]

Definition at line 649 of file features.c.

Referenced by builtin_automixmonitor().

int stopmixmonitor_ok = 1 [static]

Definition at line 650 of file features.c.

Referenced by builtin_automixmonitor().

int transferdigittimeout [static]

Definition at line 619 of file features.c.

Referenced by builtin_atxfer(), builtin_blindtransfer(), and process_config().

char xferfailsound[256] [static]

Call transfer failure sound

Definition at line 600 of file features.c.

Referenced by builtin_atxfer(), and process_config().

char xfersound[256] [static]

Call transfer sound

Definition at line 599 of file features.c.

Referenced by action_bridge(), bridge_exec(), builtin_atxfer(), local_attended_transfer(), and process_config().


Generated on Fri Feb 10 06:36:04 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6