Tue Aug 20 16:35:02 2013

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"

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  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
struct  parkinglot_parklist

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_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 int action_bridge (struct mansession *s, const struct message *m)
 Bridge channels together.
static int add_features_datastore (struct ast_channel *chan, const struct ast_flags *my_features, const struct ast_flags *peer_features)
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
void ast_bridge_end_dtmf (struct ast_channel *chan, char digit, struct timeval start, const char *why)
 Simulate a DTMF end on a broken bridge channel.
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 (struct ast_bridge_thread_obj *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 int 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, format_t format, void *data, int timeout, int *outstate, const char *language)
static void features_shutdown (void)
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 int set_chan_app_data (struct ast_channel *chan, const char *src_app_data)
static void set_config_flags (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
static void set_peers (struct ast_channel **caller, struct ast_channel **callee, struct ast_channel *peer, struct ast_channel *chan, int sense)
 set caller and callee according to the direction
static 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_datastore_info channel_app_data_datastore
static struct ast_cli_entry cli_features []
static int comebacktoorigin = 1
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 = { PTHREAD_RWLOCK_INITIALIZER , NULL, 1 }
static ast_mutex_t features_reload_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , 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 392 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_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 393 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 7449 of file features.c.

07449      {
07450    BRIDGE_OPT_PLAYTONE = (1 << 0),
07451    OPT_CALLEE_HANGUP =  (1 << 1),
07452    OPT_CALLER_HANGUP =  (1 << 2),
07453    OPT_DURATION_LIMIT = (1 << 3),
07454    OPT_DURATION_STOP =  (1 << 4),
07455    OPT_CALLEE_TRANSFER = (1 << 5),
07456    OPT_CALLER_TRANSFER = (1 << 6),
07457    OPT_CALLEE_MONITOR = (1 << 7),
07458    OPT_CALLER_MONITOR = (1 << 8),
07459    OPT_CALLEE_PARK = (1 << 9),
07460    OPT_CALLER_PARK = (1 << 10),
07461    OPT_CALLEE_KILL = (1 << 11),
07462 };

anonymous enum
Enumerator:
OPT_ARG_DURATION_LIMIT 
OPT_ARG_DURATION_STOP 
OPT_ARG_ARRAY_SIZE 

Definition at line 7464 of file features.c.

07464      {
07465    OPT_ARG_DURATION_LIMIT = 0,
07466    OPT_ARG_DURATION_STOP,
07467    /* note: this entry _MUST_ be the last one in the enum */
07468    OPT_ARG_ARRAY_SIZE,
07469 };

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

01121                            {
01122    /*! Provide ringing to the parked caller instead of music on hold */
01123    AST_PARK_OPT_RINGING =   (1 << 0),
01124    /*! Randomly choose a parking spot for the caller instead of choosing
01125     *  the first one that is available. */
01126    AST_PARK_OPT_RANDOMIZE = (1 << 1),
01127    /*! Do not announce the parking number */
01128    AST_PARK_OPT_SILENCE = (1 << 2),
01129 };

Enumerator:
FEATURE_INTERPRET_DETECT 
FEATURE_INTERPRET_DO 
FEATURE_INTERPRET_CHECK 

Definition at line 413 of file features.c.

00413              {
00414    FEATURE_INTERPRET_DETECT, /* Used by ast_feature_detect */
00415    FEATURE_INTERPRET_DO,     /* Used by feature_interpret */
00416    FEATURE_INTERPRET_CHECK,  /* Used by feature_check */
00417 } feature_interpret_op;


Function Documentation

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 

Definition at line 6929 of file features.c.

References ast_channel::_state, ast_answer(), ast_calloc, ast_channel_alloc, ast_channel_get_by_name_prefix(), ast_channel_make_compatible(), 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().

06930 {
06931    const char *channela = astman_get_header(m, "Channel1");
06932    const char *channelb = astman_get_header(m, "Channel2");
06933    const char *playtone = astman_get_header(m, "Tone");
06934    struct ast_channel *chana = NULL, *chanb = NULL, *chans[2];
06935    struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
06936    struct ast_bridge_thread_obj *tobj = NULL;
06937    char buf[256];
06938 
06939    /* make sure valid channels were specified */
06940    if (ast_strlen_zero(channela) || ast_strlen_zero(channelb)) {
06941       astman_send_error(s, m, "Missing channel parameter in request");
06942       return 0;
06943    }
06944 
06945    /* Start with chana */
06946    chana = ast_channel_get_by_name_prefix(channela, strlen(channela));
06947    if (!chana) {
06948       snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
06949       astman_send_error(s, m, buf);
06950       return 0;
06951    }
06952 
06953    /* Answer the channels if needed */
06954    if (chana->_state != AST_STATE_UP)
06955       ast_answer(chana);
06956 
06957    /* create the placeholder channels and grab the other channels */
06958    if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
06959       NULL, NULL, chana->linkedid, 0, "Bridge/%s", chana->name))) {
06960       astman_send_error(s, m, "Unable to create temporary channel!");
06961       chana = ast_channel_unref(chana);
06962       return 0;
06963    }
06964 
06965    if (do_bridge_masquerade(chana, tmpchana)) {
06966       snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channela);
06967       astman_send_error(s, m, buf);
06968       ast_hangup(tmpchana);
06969       chana = ast_channel_unref(chana);
06970       return 0;
06971    }
06972 
06973    chana = ast_channel_unref(chana);
06974 
06975    /* now do chanb */
06976    chanb = ast_channel_get_by_name_prefix(channelb, strlen(channelb));
06977    if (!chanb) {
06978       snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
06979       astman_send_error(s, m, buf);
06980       ast_hangup(tmpchana);
06981       return 0;
06982    }
06983 
06984    /* Answer the channels if needed */
06985    if (chanb->_state != AST_STATE_UP)
06986       ast_answer(chanb);
06987 
06988    /* create the placeholder channels and grab the other channels */
06989    if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
06990       NULL, NULL, chanb->linkedid, 0, "Bridge/%s", chanb->name))) {
06991       astman_send_error(s, m, "Unable to create temporary channels!");
06992       ast_hangup(tmpchana);
06993       chanb = ast_channel_unref(chanb);
06994       return 0;
06995    }
06996 
06997    if (do_bridge_masquerade(chanb, tmpchanb)) {
06998       snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channelb);
06999       astman_send_error(s, m, buf);
07000       ast_hangup(tmpchana);
07001       ast_hangup(tmpchanb);
07002       chanb = ast_channel_unref(chanb);
07003       return 0;
07004    }
07005 
07006    chanb = ast_channel_unref(chanb);
07007 
07008    /* make the channels compatible, send error if we fail doing so */
07009    if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
07010       ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name);
07011       astman_send_error(s, m, "Could not make channels compatible for manager bridge");
07012       ast_hangup(tmpchana);
07013       ast_hangup(tmpchanb);
07014       return 0;
07015    }
07016 
07017    /* setup the bridge thread object and start the bridge */
07018    if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
07019       ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno));
07020       astman_send_error(s, m, "Unable to spawn a new bridge thread");
07021       ast_hangup(tmpchana);
07022       ast_hangup(tmpchanb);
07023       return 0;
07024    }
07025 
07026    tobj->chan = tmpchana;
07027    tobj->peer = tmpchanb;
07028    tobj->return_to_pbx = 1;
07029 
07030    if (ast_true(playtone)) {
07031       if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) {
07032          if (ast_waitstream(tmpchanb, "") < 0)
07033             ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", tmpchanb->name);
07034       }
07035    }
07036 
07037    chans[0] = tmpchana;
07038    chans[1] = tmpchanb;
07039 
07040    ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeAction", 2, chans,
07041             "Response: Success\r\n"
07042             "Channel1: %s\r\n"
07043             "Channel2: %s\r\n", tmpchana->name, tmpchanb->name);
07044 
07045    bridge_call_thread_launch(tobj);
07046 
07047    astman_send_ack(s, m, "Launched bridge thread with success");
07048 
07049    return 0;
07050 }

static int add_features_datastore ( struct ast_channel chan,
const struct ast_flags my_features,
const struct ast_flags peer_features 
) [static]

Definition at line 764 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_datastore::inheritance, LOG_WARNING, ast_dial_features::my_features, and ast_dial_features::peer_features.

Referenced by add_features_datastores(), and builtin_atxfer().

00765 {
00766    struct ast_datastore *datastore;
00767    struct ast_dial_features *dialfeatures;
00768 
00769    ast_channel_lock(chan);
00770    datastore = ast_channel_datastore_find(chan, &dial_features_info, NULL);
00771    ast_channel_unlock(chan);
00772    if (datastore) {
00773       /* Already exists. */
00774       return 1;
00775    }
00776 
00777    /* Create a new datastore with specified feature flags. */
00778    datastore = ast_datastore_alloc(&dial_features_info, NULL);
00779    if (!datastore) {
00780       ast_log(LOG_WARNING, "Unable to create channel features datastore.\n");
00781       return 0;
00782    }
00783    dialfeatures = ast_calloc(1, sizeof(*dialfeatures));
00784    if (!dialfeatures) {
00785       ast_log(LOG_WARNING, "Unable to allocate memory for feature flags.\n");
00786       ast_datastore_free(datastore);
00787       return 0;
00788    }
00789    ast_copy_flags(&dialfeatures->my_features, my_features, AST_FLAGS_ALL);
00790    ast_copy_flags(&dialfeatures->peer_features, peer_features, AST_FLAGS_ALL);
00791    datastore->inheritance = DATASTORE_INHERIT_FOREVER;
00792    datastore->data = dialfeatures;
00793    ast_channel_lock(chan);
00794    ast_channel_datastore_add(chan, datastore);
00795    ast_channel_unlock(chan);
00796    return 0;
00797 }

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

Definition at line 3875 of file features.c.

References add_features_datastore(), ast_bridge_config::features_callee, and ast_bridge_config::features_caller.

Referenced by ast_bridge_call().

03876 {
03877    if (add_features_datastore(caller, &config->features_caller, &config->features_callee)) {
03878       /*
03879        * If we don't return here, then when we do a builtin_atxfer we
03880        * will copy the disconnect flags over from the atxfer to the
03881        * callee (Party C).
03882        */
03883       return;
03884    }
03885 
03886    add_features_datastore(callee, &config->features_callee, &config->features_caller);
03887 }

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

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

Referenced by park_call_full().

01060 {
01061    int res;
01062    int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
01063    char tmp[256];
01064    char *message[5] = {NULL, NULL, NULL, NULL, NULL};
01065 
01066    snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
01067    message[0] = tmp;
01068    res = ast_adsi_load_session(chan, NULL, 0, 1);
01069    if (res == -1)
01070       return res;
01071    return ast_adsi_print(chan, message, justify, 1);
01072 }

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 3938 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_end_dtmf(), AST_BRIDGE_RETRY, ast_bridged_channel(), ast_cdr_alloc(), ast_cdr_answer(), AST_CDR_ANSWERED, ast_cdr_appenduserfield(), ast_cdr_copy_vars(), 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_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_redirecting_macro(), ast_channel_set_linkgroup(), ast_channel_setoption(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), 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_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_DUAL_REDIRECT_WAIT, 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, 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, ast_channel::sending_dtmf_digit, ast_channel::sending_dtmf_tv, 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().

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

void ast_bridge_end_dtmf ( struct ast_channel chan,
char  digit,
struct timeval  start,
const char *  why 
)

Simulate a DTMF end on a broken bridge channel.

Parameters:
chan Channel sending DTMF that has not ended.
digit DTMF digit to stop.
start DTMF digit start time.
why Reason bridge broken.
Returns:
Nothing

Definition at line 3905 of file features.c.

References ast_channel::_softhangup, ast_channel_lock, ast_channel_unlock, AST_FLAG_ZOMBIE, ast_log(), ast_senddigit_end(), AST_SOFTHANGUP_ASYNCGOTO, AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, ast_tvdiff_ms(), ast_tvnow(), and LOG_DTMF.

Referenced by ast_bridge_call(), and ast_do_masquerade().

03906 {
03907    int dead;
03908    long duration;
03909 
03910    ast_channel_lock(chan);
03911    dead = ast_test_flag(chan, AST_FLAG_ZOMBIE)
03912       || (chan->_softhangup
03913          & ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE));
03914    ast_channel_unlock(chan);
03915    if (dead) {
03916       /* Channel is a zombie or a real hangup. */
03917       return;
03918    }
03919 
03920    duration = ast_tvdiff_ms(ast_tvnow(), start);
03921    ast_senddigit_end(chan, digit, duration);
03922    ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n",
03923       digit, chan->name, why, duration);
03924 }

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 7486 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, ast_bridge_config::timelimit, var, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound.

Referenced by bridge_exec(), and dial_exec_full().

07488 {
07489    char *stringp = ast_strdupa(parse);
07490    char *limit_str, *warning_str, *warnfreq_str;
07491    const char *var;
07492    int play_to_caller = 0, play_to_callee = 0;
07493    int delta;
07494 
07495    limit_str = strsep(&stringp, ":");
07496    warning_str = strsep(&stringp, ":");
07497    warnfreq_str = strsep(&stringp, ":");
07498 
07499    config->timelimit = atol(limit_str);
07500    if (warning_str)
07501       config->play_warning = atol(warning_str);
07502    if (warnfreq_str)
07503       config->warning_freq = atol(warnfreq_str);
07504 
07505    if (!config->timelimit) {
07506       ast_log(LOG_WARNING, "Bridge does not accept L(%s), hanging up.\n", limit_str);
07507       config->timelimit = config->play_warning = config->warning_freq = 0;
07508       config->warning_sound = NULL;
07509       return -1; /* error */
07510    } else if ( (delta = config->play_warning - config->timelimit) > 0) {
07511       int w = config->warning_freq;
07512 
07513       /*
07514        * If the first warning is requested _after_ the entire call
07515        * would end, and no warning frequency is requested, then turn
07516        * off the warning. If a warning frequency is requested, reduce
07517        * the 'first warning' time by that frequency until it falls
07518        * within the call's total time limit.
07519        *
07520        * Graphically:
07521        *                timelim->|    delta        |<-playwarning
07522        *      0__________________|_________________|
07523        *                       | w  |    |    |    |
07524        *
07525        * so the number of intervals to cut is 1+(delta-1)/w
07526        */
07527       if (w == 0) {
07528          config->play_warning = 0;
07529       } else {
07530          config->play_warning -= w * ( 1 + (delta-1)/w );
07531          if (config->play_warning < 1)
07532             config->play_warning = config->warning_freq = 0;
07533       }
07534    }
07535    
07536    ast_channel_lock(chan);
07537 
07538    var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLER");
07539    play_to_caller = var ? ast_true(var) : 1;
07540 
07541    var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLEE");
07542    play_to_callee = var ? ast_true(var) : 0;
07543 
07544    if (!play_to_caller && !play_to_callee)
07545       play_to_caller = 1;
07546 
07547    var = pbx_builtin_getvar_helper(chan, "LIMIT_WARNING_FILE");
07548    config->warning_sound = !ast_strlen_zero(var) ? ast_strdup(var) : ast_strdup("timeleft");
07549 
07550    /* The code looking at config wants a NULL, not just "", to decide
07551     * that the message should not be played, so we replace "" with NULL.
07552     * Note, pbx_builtin_getvar_helper _can_ return NULL if the variable is
07553     * not found.
07554     */
07555 
07556    var = pbx_builtin_getvar_helper(chan, "LIMIT_TIMEOUT_FILE");
07557    config->end_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
07558 
07559    var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
07560    config->start_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
07561 
07562    ast_channel_unlock(chan);
07563 
07564    /* undo effect of S(x) in case they are both used */
07565    calldurationlimit->tv_sec = 0;
07566    calldurationlimit->tv_usec = 0;
07567 
07568    /* more efficient to do it like S(x) does since no advanced opts */
07569    if (!config->play_warning && !config->start_sound && !config->end_sound && config->timelimit) {
07570       calldurationlimit->tv_sec = config->timelimit / 1000;
07571       calldurationlimit->tv_usec = (config->timelimit % 1000) * 1000;
07572       ast_verb(3, "Setting call duration limit to %.3lf seconds.\n",
07573          calldurationlimit->tv_sec + calldurationlimit->tv_usec / 1000000.0);
07574       config->timelimit = play_to_caller = play_to_callee =
07575       config->play_warning = config->warning_freq = 0;
07576    } else {
07577       ast_verb(4, "Limit Data for this call:\n");
07578       ast_verb(4, "timelimit      = %ld ms (%.3lf s)\n", config->timelimit, config->timelimit / 1000.0);
07579       ast_verb(4, "play_warning   = %ld ms (%.3lf s)\n", config->play_warning, config->play_warning / 1000.0);
07580       ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
07581       ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
07582       ast_verb(4, "warning_freq   = %ld ms (%.3lf s)\n", config->warning_freq, config->warning_freq / 1000.0);
07583       ast_verb(4, "start_sound    = %s\n", S_OR(config->start_sound, ""));
07584       ast_verb(4, "warning_sound  = %s\n", config->warning_sound);
07585       ast_verb(4, "end_sound      = %s\n", S_OR(config->end_sound, ""));
07586    }
07587    if (play_to_caller)
07588       ast_set_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
07589    if (play_to_callee)
07590       ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
07591    return 0;
07592 }

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

07293 {
07294    if (!chan->pbx && !chan->masq && !ast_test_flag(chan, AST_FLAG_ZOMBIE)
07295       && (chan->_state == AST_STATE_RINGING
07296          || chan->_state == AST_STATE_RING
07297          /*
07298           * Check the down state as well because some SIP devices do not
07299           * give 180 ringing when they can just give 183 session progress
07300           * instead.  Issue 14005.  (Some ISDN switches as well for that
07301           * matter.)
07302           */
07303          || chan->_state == AST_STATE_DOWN)
07304       && !ast_channel_datastore_find(chan, &pickup_active, NULL)) {
07305       return 1;
07306    }
07307    return 0;
07308 }

void ast_channel_log ( char *  title,
struct ast_channel chan 
)

Definition at line 3810 of file features.c.

References ast_log(), and LOG_NOTICE.

Referenced by ast_bridge_call().

03811 {
03812        ast_log(LOG_NOTICE, "______ %s (%lx)______\n", title, (unsigned long)chan);
03813        ast_log(LOG_NOTICE, "CHAN: name: %s;  appl: %s; data: %s; contxt: %s;  exten: %s; pri: %d;\n",
03814                        chan->name, chan->appl, chan->data, chan->context, chan->exten, chan->priority);
03815        ast_log(LOG_NOTICE, "CHAN: acctcode: %s;  dialcontext: %s; amaflags: %x; maccontxt: %s;  macexten: %s; macpri: %d;\n",
03816                        chan->accountcode, chan->dialcontext, chan->amaflags, chan->macrocontext, chan->macroexten, chan->macropriority);
03817        ast_log(LOG_NOTICE, "CHAN: masq: %p;  masqr: %p; _bridge: %p; uniqueID: %s; linkedID:%s\n",
03818                        chan->masq, chan->masqr,
03819                        chan->_bridge, chan->uniqueid, chan->linkedid);
03820        if (chan->masqr)
03821                ast_log(LOG_NOTICE, "CHAN: masquerading as: %s;  cdr: %p;\n",
03822                                chan->masqr->name, chan->masqr->cdr);
03823        if (chan->_bridge)
03824                ast_log(LOG_NOTICE, "CHAN: Bridged to %s\n", chan->_bridge->name);
03825 
03826    ast_log(LOG_NOTICE, "===== done ====\n");
03827 }

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 7368 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_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().

07369 {
07370    struct ast_party_connected_line connected_caller;
07371    struct ast_channel *chans[2] = { chan, target };
07372    struct ast_datastore *ds_pickup;
07373    const char *chan_name;/*!< A masquerade changes channel names. */
07374    const char *target_name;/*!< A masquerade changes channel names. */
07375    int res = -1;
07376 
07377    target_name = ast_strdupa(target->name);
07378    ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, chan->name);
07379 
07380    /* Mark the target to block any call pickup race. */
07381    ds_pickup = ast_datastore_alloc(&pickup_active, NULL);
07382    if (!ds_pickup) {
07383       ast_log(LOG_WARNING,
07384          "Unable to create channel datastore on '%s' for call pickup\n", target_name);
07385       return -1;
07386    }
07387    ast_channel_datastore_add(target, ds_pickup);
07388 
07389    ast_party_connected_line_init(&connected_caller);
07390    ast_party_connected_line_copy(&connected_caller, &target->connected);
07391    ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */
07392    connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
07393    if (ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
07394       ast_channel_update_connected_line(chan, &connected_caller, NULL);
07395    }
07396    ast_party_connected_line_free(&connected_caller);
07397 
07398    ast_channel_lock(chan);
07399    chan_name = ast_strdupa(chan->name);
07400    ast_connected_line_copy_from_caller(&connected_caller, &chan->caller);
07401    ast_channel_unlock(chan);
07402    connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
07403 
07404    ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan);
07405 
07406    if (ast_answer(chan)) {
07407       ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
07408       goto pickup_failed;
07409    }
07410 
07411    if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
07412       ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name);
07413       goto pickup_failed;
07414    }
07415    
07416    ast_channel_queue_connected_line_update(chan, &connected_caller, NULL);
07417 
07418    /* setting this flag to generate a reason header in the cancel message to the ringing channel */
07419    ast_set_flag(chan, AST_FLAG_ANSWERED_ELSEWHERE);
07420 
07421    if (ast_channel_masquerade(target, chan)) {
07422       ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name,
07423          target_name);
07424       goto pickup_failed;
07425    }
07426 
07427    /* If you want UniqueIDs, set channelvars in manager.conf to CHANNEL(uniqueid) */
07428    ast_manager_event_multichan(EVENT_FLAG_CALL, "Pickup", 2, chans,
07429       "Channel: %s\r\n"
07430       "TargetChannel: %s\r\n",
07431       chan_name, target_name);
07432 
07433    /* Do the masquerade manually to make sure that it is completed. */
07434    ast_do_masquerade(target);
07435    res = 0;
07436 
07437 pickup_failed:
07438    ast_channel_lock(target);
07439    if (!ast_channel_datastore_remove(target, ds_pickup)) {
07440       ast_datastore_free(ds_pickup);
07441    }
07442    ast_party_connected_line_free(&connected_caller);
07443 
07444    return res;
07445 }

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

References FEATURE_INTERPRET_DETECT, and feature_interpret_helper().

Referenced by detect_disconnect().

03412                                                                                                                                  {
03413 
03414    return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_DETECT, feature);
03415 }

int ast_features_init ( void   ) 

Provided by features.c

Definition at line 8282 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_register_atexit(), AST_TEST_REGISTER, bridge_exec(), do_parking_thread(), EVENT_FLAG_CALL, features_shutdown(), 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().

08283 {
08284    int res;
08285 
08286    parkinglots = ao2_container_alloc(7, parkinglot_hash_cb, parkinglot_cmp_cb);
08287    if (!parkinglots) {
08288       return -1;
08289    }
08290 
08291    res = load_config(0);
08292    if (res) {
08293       return res;
08294    }
08295    ast_cli_register_multiple(cli_features, ARRAY_LEN(cli_features));
08296    if (ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL)) {
08297       return -1;
08298    }
08299    ast_register_application2(app_bridge, bridge_exec, NULL, NULL, NULL);
08300    res = ast_register_application2(parkedcall, parked_call_exec, NULL, NULL, NULL);
08301    if (!res)
08302       res = ast_register_application2(parkcall, park_call_exec, NULL, NULL, NULL);
08303    if (!res) {
08304       ast_manager_register_xml("ParkedCalls", 0, manager_parking_status);
08305       ast_manager_register_xml("Park", EVENT_FLAG_CALL, manager_park);
08306       ast_manager_register_xml("Bridge", EVENT_FLAG_CALL, action_bridge);
08307    }
08308 
08309    res |= ast_devstate_prov_add("Park", metermaidstate);
08310 #if defined(TEST_FRAMEWORK)
08311    res |= AST_TEST_REGISTER(features_test);
08312 #endif   /* defined(TEST_FRAMEWORK) */
08313 
08314    ast_register_atexit(features_shutdown);
08315 
08316    return res;
08317 }

int ast_features_reload ( void   ) 

Reload call features from features.conf.

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

06834 {
06835    struct ast_context *con;
06836    int res;
06837 
06838    ast_mutex_lock(&features_reload_lock);/* Searialize reloading features.conf */
06839 
06840    /*
06841     * Always destroy the parking_con_dial context to remove buildup
06842     * of recalled extensions in the context.  At worst, the parked
06843     * call gets hungup attempting to run an invalid extension when
06844     * we are trying to callback the parker or the preset return
06845     * extension.  This is a small window of opportunity on an
06846     * execution chain that is not expected to happen very often.
06847     */
06848    con = ast_context_find(parking_con_dial);
06849    if (con) {
06850       ast_context_destroy(con, registrar);
06851    }
06852 
06853    res = load_config(1);
06854    ast_mutex_unlock(&features_reload_lock);
06855 
06856    return res;
06857 }

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

References FEATURES_COUNT, and ast_call_feature::sname.

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

03145 {
03146    int x;
03147    for (x = 0; x < FEATURES_COUNT; x++) {
03148       if (!strcasecmp(name, builtin_features[x].sname))
03149          return &builtin_features[x];
03150    }
03151    return NULL;
03152 }

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

References 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(), and parkandannounce_exec().

01851 {
01852    struct ast_park_call_args args = {
01853       .timeout = timeout,
01854       .extout = extout,
01855    };
01856 
01857    if (peer) {
01858       args.orig_chan_name = ast_strdupa(peer->name);
01859    }
01860    return masq_park_call(rchan, peer, &args);
01861 }

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 1796 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(), 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().

01797 {
01798    int res;
01799    char *parse;
01800    const char *app_data;
01801    struct ast_exten *exten;
01802    struct park_app_args app_args;
01803    struct ast_park_call_args args = {
01804       .timeout = timeout,
01805       .extout = extout,
01806    };
01807 
01808    if (parker) {
01809       args.orig_chan_name = ast_strdupa(parker->name);
01810    }
01811    if (!park_exten || !park_context) {
01812       return masq_park_call(park_me, parker, &args);
01813    }
01814 
01815    /*
01816     * Determiine if the specified park extension has an exclusive
01817     * parking lot to use.
01818     */
01819    if (parker && parker != park_me) {
01820       ast_autoservice_start(park_me);
01821    }
01822    exten = get_parking_exten(park_exten, parker, park_context);
01823    if (exten) {
01824       app_data = ast_get_extension_app_data(exten);
01825       if (!app_data) {
01826          app_data = "";
01827       }
01828       parse = ast_strdupa(app_data);
01829       AST_STANDARD_APP_ARGS(app_args, parse);
01830    
01831       if (!ast_strlen_zero(app_args.pl_name)) {
01832          /* Find the specified exclusive parking lot */
01833          args.parkinglot = find_parkinglot(app_args.pl_name);
01834          if (!args.parkinglot && parkeddynamic) {
01835             args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
01836          }
01837       }
01838    }
01839    if (parker && parker != park_me) {
01840       ast_autoservice_stop(park_me);
01841    }
01842 
01843    res = masq_park_call(park_me, parker, &args);
01844    if (args.parkinglot) {
01845       parkinglot_unref(args.parkinglot);
01846    }
01847    return res;
01848 }

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

References park_call_full(), and ast_park_call_args::timeout.

01700 {
01701    struct ast_park_call_args args = {
01702       .timeout = timeout,
01703       .extout = extout,
01704    };
01705 
01706    return park_call_full(park_me, parker, &args);
01707 }

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

01649 {
01650    int res;
01651    char *parse;
01652    const char *app_data;
01653    struct ast_exten *exten;
01654    struct park_app_args app_args;
01655    struct ast_park_call_args args = {
01656       .timeout = timeout,
01657       .extout = extout,
01658    };
01659 
01660    if (!park_exten || !park_context) {
01661       return park_call_full(park_me, parker, &args);
01662    }
01663 
01664    /*
01665     * Determiine if the specified park extension has an exclusive
01666     * parking lot to use.
01667     */
01668    if (parker && parker != park_me) {
01669       ast_autoservice_start(park_me);
01670    }
01671    exten = get_parking_exten(park_exten, parker, park_context);
01672    if (exten) {
01673       app_data = ast_get_extension_app_data(exten);
01674       if (!app_data) {
01675          app_data = "";
01676       }
01677       parse = ast_strdupa(app_data);
01678       AST_STANDARD_APP_ARGS(app_args, parse);
01679    
01680       if (!ast_strlen_zero(app_args.pl_name)) {
01681          /* Find the specified exclusive parking lot */
01682          args.parkinglot = find_parkinglot(app_args.pl_name);
01683          if (!args.parkinglot && parkeddynamic) {
01684             args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
01685          }
01686       }
01687    }
01688    if (parker && parker != park_me) {
01689       ast_autoservice_stop(park_me);
01690    }
01691 
01692    res = park_call_full(park_me, parker, &args);
01693    if (args.parkinglot) {
01694       parkinglot_unref(args.parkinglot);
01695    }
01696    return res;
01697 }

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

00841 {
00842    return get_parking_exten(exten_str, chan, context) ? 1 : 0;
00843 }

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

References ast_answer(), ast_channel_callback(), 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().

07335 {
07336    struct ast_channel *target;/*!< Potential pickup target */
07337    int res = -1;
07338    ast_debug(1, "pickup attempt by %s\n", chan->name);
07339 
07340    /* The found channel is already locked. */
07341    target = ast_channel_callback(find_channel_by_group, NULL, chan, 0);
07342    if (target) {
07343       ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", target->name, chan->name);
07344 
07345       res = ast_do_pickup(chan, target);
07346       ast_channel_unlock(target);
07347       if (!res) {
07348          if (!ast_strlen_zero(pickupsound)) {
07349             pbx_builtin_setvar_helper(target, "BRIDGE_PLAY_SOUND", pickupsound);
07350          }
07351       } else {
07352          ast_log(LOG_WARNING, "pickup %s failed by %s\n", target->name, chan->name);
07353       }
07354       target = ast_channel_unref(target);
07355    }
07356 
07357    if (res < 0) {
07358       ast_debug(1, "No call pickup possible... for %s\n", chan->name);
07359       if (!ast_strlen_zero(pickupfailsound)) {
07360          ast_answer(chan);
07361          ast_stream_and_wait(chan, pickupfailsound, "");
07362       }
07363    }
07364 
07365    return res;
07366 }

const char* ast_pickup_ext ( void   ) 
void ast_rdlock_call_features ( void   ) 

Definition at line 3134 of file features.c.

References ast_rwlock_rdlock, and features_lock.

Referenced by handle_request_info().

03135 {
03136    ast_rwlock_rdlock(&features_lock);
03137 }

void ast_register_feature ( struct ast_call_feature feature  ) 

register new feature into feature_list

register new feature into feature_set

Definition at line 2978 of file features.c.

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

Referenced by process_applicationmap_line().

02979 {
02980    if (!feature) {
02981       ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
02982       return;
02983    }
02984   
02985    AST_RWLIST_WRLOCK(&feature_list);
02986    AST_RWLIST_INSERT_HEAD(&feature_list,feature,feature_entry);
02987    AST_RWLIST_UNLOCK(&feature_list);
02988 
02989    ast_verb(2, "Registered Feature '%s'\n",feature->sname);
02990 }

void ast_unlock_call_features ( void   ) 

Definition at line 3139 of file features.c.

References ast_rwlock_unlock, and features_lock.

Referenced by handle_request_info().

03140 {
03141    ast_rwlock_unlock(&features_lock);
03142 }

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

References ast_free, AST_RWLIST_REMOVE, AST_RWLIST_UNLOCK, and AST_RWLIST_WRLOCK.

03059 {
03060    if (!feature) {
03061       return;
03062    }
03063 
03064    AST_RWLIST_WRLOCK(&feature_list);
03065    AST_RWLIST_REMOVE(&feature_list, feature, feature_entry);
03066    AST_RWLIST_UNLOCK(&feature_list);
03067 
03068    ast_free(feature);
03069 }

static void ast_unregister_features ( void   )  [static]

Remove all features in the list.

Definition at line 3072 of file features.c.

References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, and AST_RWLIST_WRLOCK.

Referenced by process_config().

03073 {
03074    struct ast_call_feature *feature;
03075 
03076    AST_RWLIST_WRLOCK(&feature_list);
03077    while ((feature = AST_RWLIST_REMOVE_HEAD(&feature_list, feature_entry))) {
03078       ast_free(feature);
03079    }
03080    AST_RWLIST_UNLOCK(&feature_list);
03081 }

static void ast_unregister_groups ( void   )  [static]

Remove all feature groups in the list.

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

03099 {
03100    struct feature_group *fg;
03101    struct feature_group_exten *fge;
03102 
03103    AST_RWLIST_WRLOCK(&feature_groups);
03104    while ((fg = AST_LIST_REMOVE_HEAD(&feature_groups, entry))) {
03105       while ((fge = AST_LIST_REMOVE_HEAD(&fg->features, entry))) {
03106          ast_string_field_free_memory(fge);
03107          ast_free(fge);
03108       }
03109 
03110       ast_string_field_free_memory(fg);
03111       ast_free(fg);
03112    }
03113    AST_RWLIST_UNLOCK(&feature_groups);
03114 }

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

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

02475 {
02476    finishup(transferee);
02477 
02478    /*
02479     * Restore party B connected line info about party A.
02480     *
02481     * Party B was the caller to party C and is the last known mode
02482     * for party B.
02483     */
02484    if (ast_channel_connected_line_macro(transferee, transferer, connected_line, 1, 0)) {
02485       ast_channel_update_connected_line(transferer, connected_line, NULL);
02486    }
02487    ast_party_connected_line_free(connected_line);
02488 }

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

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

Referenced by bridge_call_thread_launch().

00984 {
00985    struct ast_bridge_thread_obj *tobj = data;
00986 
00987    tobj->chan->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge";
00988    if (set_chan_app_data(tobj->chan, tobj->peer->name)) {
00989       tobj->chan->data = "(Empty)";
00990    }
00991    tobj->peer->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge";
00992    if (set_chan_app_data(tobj->peer, tobj->chan->name)) {
00993       tobj->peer->data = "(Empty)";
00994    }
00995 
00996    ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
00997 
00998    if (tobj->return_to_pbx) {
00999       if (!ast_check_hangup(tobj->peer)) {
01000          ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", tobj->peer->name);
01001          if (ast_pbx_start(tobj->peer)) {
01002             ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", tobj->peer->name);
01003             ast_hangup(tobj->peer);
01004          }
01005       } else {
01006          ast_hangup(tobj->peer);
01007       }
01008       if (!ast_check_hangup(tobj->chan)) {
01009          ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", tobj->chan->name);
01010          if (ast_pbx_start(tobj->chan)) {
01011             ast_log(LOG_WARNING, "FAILED continuing PBX on chan %s\n", tobj->chan->name);
01012             ast_hangup(tobj->chan);
01013          }
01014       } else {
01015          ast_hangup(tobj->chan);
01016       }
01017    } else {
01018       ast_hangup(tobj->chan);
01019       ast_hangup(tobj->peer);
01020    }
01021 
01022    ast_free(tobj);
01023 
01024    return NULL;
01025 }

static void bridge_call_thread_launch ( struct ast_bridge_thread_obj data  )  [static]

create thread for the parked call

Parameters:
data Create thread and attributes, call bridge_call_thread

Definition at line 1033 of file features.c.

References ast_hangup(), ast_log(), ast_pthread_create, bridge_call_thread(), ast_bridge_thread_obj::chan, LOG_ERROR, ast_bridge_thread_obj::peer, and thread.

Referenced by action_bridge(), and builtin_atxfer().

01034 {
01035    pthread_t thread;
01036    pthread_attr_t attr;
01037    struct sched_param sched;
01038 
01039    pthread_attr_init(&attr);
01040    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01041    if (ast_pthread_create(&thread, &attr, bridge_call_thread, data)) {
01042       ast_hangup(data->chan);
01043       ast_hangup(data->peer);
01044       ast_log(LOG_ERROR, "Failed to create bridge_call_thread.\n");
01045    }
01046    pthread_attr_destroy(&attr);
01047    memset(&sched, 0, sizeof(sched));
01048    pthread_setschedparam(thread, SCHED_RR, &sched);
01049 }

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 7604 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_make_compatible(), 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_FLAG_BRIDGE_HANGUP_DONT, ast_free, ast_hangup(), ast_log(), ast_manager_event, ast_manager_event_multichan, ast_pbx_start(), 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().

07605 {
07606    struct ast_channel *current_dest_chan, *final_dest_chan, *chans[2];
07607    char *tmp_data  = NULL;
07608    struct ast_flags opts = { 0, };
07609    struct ast_bridge_config bconfig = { { 0, }, };
07610    char *opt_args[OPT_ARG_ARRAY_SIZE];
07611    struct timeval calldurationlimit = { 0, };
07612 
07613    AST_DECLARE_APP_ARGS(args,
07614       AST_APP_ARG(dest_chan);
07615       AST_APP_ARG(options);
07616    );
07617 
07618    if (ast_strlen_zero(data)) {
07619       ast_log(LOG_WARNING, "Bridge require at least 1 argument specifying the other end of the bridge\n");
07620       return -1;
07621    }
07622 
07623    tmp_data = ast_strdupa(data);
07624    AST_STANDARD_APP_ARGS(args, tmp_data);
07625    if (!ast_strlen_zero(args.options))
07626       ast_app_parse_options(bridge_exec_options, &opts, opt_args, args.options);
07627 
07628    /* avoid bridge with ourselves */
07629    if (!strcmp(chan->name, args.dest_chan)) {
07630       ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", chan->name);
07631       ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
07632          "Response: Failed\r\n"
07633          "Reason: Unable to bridge channel to itself\r\n"
07634          "Channel1: %s\r\n"
07635          "Channel2: %s\r\n",
07636          chan->name, args.dest_chan);
07637       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP");
07638       return 0;
07639    }
07640 
07641    /* make sure we have a valid end point */
07642    if (!(current_dest_chan = ast_channel_get_by_name_prefix(args.dest_chan,
07643          strlen(args.dest_chan)))) {
07644       ast_log(LOG_WARNING, "Bridge failed because channel %s does not exist\n",
07645          args.dest_chan);
07646       ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
07647          "Response: Failed\r\n"
07648          "Reason: Channel2 does not exist\r\n"
07649          "Channel1: %s\r\n"
07650          "Channel2: %s\r\n", chan->name, args.dest_chan);
07651       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT");
07652       return 0;
07653    }
07654 
07655    /* try to allocate a place holder where current_dest_chan will be placed */
07656    if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
07657       NULL, NULL, current_dest_chan->linkedid, 0, "Bridge/%s", current_dest_chan->name))) {
07658       ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan);
07659       ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
07660          "Response: Failed\r\n"
07661          "Reason: Cannot create placeholder channel\r\n"
07662          "Channel1: %s\r\n"
07663          "Channel2: %s\r\n", chan->name, args.dest_chan);
07664       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE");
07665       ast_channel_unref(current_dest_chan);
07666       return 0;
07667    }
07668 
07669    if (ast_test_flag(&opts, OPT_DURATION_LIMIT)
07670       && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])
07671       && ast_bridge_timelimit(chan, &bconfig, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit)) {
07672       ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
07673          "Response: Failed\r\n"
07674          "Reason: Cannot setup bridge time limit\r\n"
07675          "Channel1: %s\r\n"
07676          "Channel2: %s\r\n", chan->name, args.dest_chan);
07677       ast_hangup(final_dest_chan);
07678       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE");
07679       current_dest_chan = ast_channel_unref(current_dest_chan);
07680       goto done;
07681    }
07682 
07683    if (do_bridge_masquerade(current_dest_chan, final_dest_chan)) {
07684       ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
07685          "Response: Failed\r\n"
07686          "Reason: Cannot masquerade channels\r\n"
07687          "Channel1: %s\r\n"
07688          "Channel2: %s\r\n", chan->name, args.dest_chan);
07689       ast_hangup(final_dest_chan);
07690       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE");
07691       current_dest_chan = ast_channel_unref(current_dest_chan);
07692       goto done;
07693    }
07694 
07695    /* answer the channel if needed */
07696    if (final_dest_chan->_state != AST_STATE_UP) {
07697       ast_answer(final_dest_chan);
07698    }
07699 
07700    chans[0] = current_dest_chan;
07701    chans[1] = final_dest_chan;
07702 
07703    /* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */
07704    /* try to make compatible, send error if we fail */
07705    if (ast_channel_make_compatible(chan, final_dest_chan) < 0) {
07706       ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, final_dest_chan->name);
07707       ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans,
07708          "Response: Failed\r\n"
07709          "Reason: Could not make channels compatible for bridge\r\n"
07710          "Channel1: %s\r\n"
07711          "Channel2: %s\r\n", chan->name, final_dest_chan->name);
07712 
07713       /* Maybe we should return this channel to the PBX? */
07714       ast_hangup(final_dest_chan);
07715 
07716       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE");
07717       current_dest_chan = ast_channel_unref(current_dest_chan);
07718       goto done;
07719    }
07720 
07721    /* Report that the bridge will be successfull */
07722    ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans,
07723       "Response: Success\r\n"
07724       "Channel1: %s\r\n"
07725       "Channel2: %s\r\n", chan->name, final_dest_chan->name);
07726 
07727    current_dest_chan = ast_channel_unref(current_dest_chan);
07728 
07729    /* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */  
07730    if (ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE) && !ast_strlen_zero(xfersound)) {
07731       if (!ast_streamfile(final_dest_chan, xfersound, final_dest_chan->language)) {
07732          if (ast_waitstream(final_dest_chan, "") < 0)
07733             ast_log(LOG_WARNING, "Failed to play courtesy tone on %s\n", final_dest_chan->name);
07734       }
07735    }
07736 
07737    if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER))
07738       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_REDIRECT);
07739    if (ast_test_flag(&opts, OPT_CALLER_TRANSFER))
07740       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_REDIRECT);
07741    if (ast_test_flag(&opts, OPT_CALLEE_HANGUP))
07742       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
07743    if (ast_test_flag(&opts, OPT_CALLER_HANGUP))
07744       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
07745    if (ast_test_flag(&opts, OPT_CALLEE_MONITOR))
07746       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_AUTOMON);
07747    if (ast_test_flag(&opts, OPT_CALLER_MONITOR))
07748       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_AUTOMON);
07749    if (ast_test_flag(&opts, OPT_CALLEE_PARK))
07750       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_PARKCALL);
07751    if (ast_test_flag(&opts, OPT_CALLER_PARK))
07752       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_PARKCALL);
07753 
07754    /*
07755     * Don't let the after-bridge code run the h-exten.  We want to
07756     * continue in the dialplan.
07757     */
07758    ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT);
07759    ast_bridge_call(chan, final_dest_chan, &bconfig);
07760 
07761    /* The bridge has ended, set BRIDGERESULT to SUCCESS. */
07762    pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS");
07763 
07764    /* If the other channel has not been hung up, return it to the PBX */
07765    if (!ast_check_hangup(final_dest_chan)) {
07766       if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) {
07767          ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n",
07768             final_dest_chan->context, final_dest_chan->exten,
07769             final_dest_chan->priority, final_dest_chan->name);
07770 
07771          if (ast_pbx_start(final_dest_chan)) {
07772             ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", final_dest_chan->name);
07773             ast_hangup(final_dest_chan);
07774          } else {
07775             ast_debug(1, "SUCCESS continuing PBX on chan %s\n", final_dest_chan->name);
07776          }
07777       } else {
07778          ast_hangup(final_dest_chan);
07779       }
07780    } else {
07781       ast_debug(1, "chan %s was hungup\n", final_dest_chan->name);
07782       ast_hangup(final_dest_chan);
07783    }
07784 done:
07785    ast_free((char *) bconfig.warning_sound);
07786    ast_free((char *) bconfig.end_sound);
07787    ast_free((char *) bconfig.start_sound);
07788 
07789    return 0;
07790 }

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

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

06279 {
06280    struct parking_dp_context *ctx_node;
06281 
06282    ctx_node = ast_calloc(1, sizeof(*ctx_node) + strlen(lot->cfg.parking_con));
06283    if (!ctx_node) {
06284       return NULL;
06285    }
06286    if (dialplan_usage_add_parkinglot_data(ctx_node, lot, 0)) {
06287       destroy_dialplan_usage_context(ctx_node);
06288       return NULL;
06289    }
06290    strcpy(ctx_node->context, lot->cfg.parking_con);
06291    return ctx_node;
06292 }

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

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

06351 {
06352    int status = 0;
06353    struct ao2_iterator iter;
06354    struct ast_parkinglot *curlot;
06355 
06356    /* For all parking lots */
06357    iter = ao2_iterator_init(parkinglots, 0);
06358    for (; (curlot = ao2_iterator_next(&iter)); ao2_ref(curlot, -1)) {
06359       /* Add the parking lot to the map. */
06360       if (dialplan_usage_add_parkinglot(usage_map, curlot, complain)) {
06361          ao2_ref(curlot, -1);
06362          status = -1;
06363          break;
06364       }
06365    }
06366    ao2_iterator_destroy(&iter);
06367 
06368    return status;
06369 }

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

Definition at line 6041 of file features.c.

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

Referenced by usage_context_add_ramp().

06042 {
06043    struct parking_dp_ramp *ramp_node;
06044 
06045    ramp_node = ast_calloc(1, sizeof(*ramp_node) + strlen(exten));
06046    if (!ramp_node) {
06047       return NULL;
06048    }
06049    ramp_node->exclusive = exclusive;
06050    strcpy(ramp_node->exten, exten);
06051    return ramp_node;
06052 }

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

Definition at line 6122 of file features.c.

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

Referenced by usage_context_add_spaces().

06123 {
06124    struct parking_dp_spaces *spaces_node;
06125 
06126    spaces_node = ast_calloc(1, sizeof(*spaces_node));
06127    if (!spaces_node) {
06128       return NULL;
06129    }
06130    spaces_node->start = start;
06131    spaces_node->stop = stop;
06132    return spaces_node;
06133 }

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

05644 {
05645    struct ast_parkinglot *parkinglot;
05646    const struct parkinglot_cfg *cfg_defaults;
05647    struct parkinglot_cfg new_cfg;
05648    int cfg_error;
05649    int oldparkinglot = 0;
05650 
05651    parkinglot = find_parkinglot(pl_name);
05652    if (parkinglot) {
05653       oldparkinglot = 1;
05654    } else {
05655       parkinglot = create_parkinglot(pl_name);
05656       if (!parkinglot) {
05657          return NULL;
05658       }
05659    }
05660    if (!strcmp(parkinglot->name, DEFAULT_PARKINGLOT)) {
05661       cfg_defaults = &parkinglot_cfg_default_default;
05662    } else {
05663       cfg_defaults = &parkinglot_cfg_default;
05664    }
05665    new_cfg = *cfg_defaults;
05666 
05667    ast_debug(1, "Building parking lot %s\n", parkinglot->name);
05668 
05669    ao2_lock(parkinglot);
05670 
05671    /* Do some config stuff */
05672    cfg_error = parkinglot_config_read(parkinglot->name, &new_cfg, var);
05673    if (oldparkinglot) {
05674       if (cfg_error) {
05675          /* Bad configuration read.  Keep using the original config. */
05676          ast_log(LOG_WARNING, "Changes to parking lot %s are discarded.\n",
05677             parkinglot->name);
05678          cfg_error = 0;
05679       } else if (!AST_LIST_EMPTY(&parkinglot->parkings)
05680          && memcmp(&new_cfg, &parkinglot->cfg, sizeof(parkinglot->cfg))) {
05681          /* Try reloading later when parking lot is empty. */
05682          ast_log(LOG_WARNING,
05683             "Parking lot %s has parked calls.  Parking lot changes discarded.\n",
05684             parkinglot->name);
05685          force_reload_load = 1;
05686       } else {
05687          /* Accept the new config */
05688          parkinglot->cfg = new_cfg;
05689       }
05690    } else {
05691       /* Load the initial parking lot config. */
05692       parkinglot->cfg = new_cfg;
05693    }
05694    parkinglot->the_mark = 0;
05695 
05696    ao2_unlock(parkinglot);
05697 
05698    if (cfg_error) {
05699       /* Only new parking lots could have config errors here. */
05700       ast_log(LOG_WARNING, "New parking lot %s is discarded.\n", parkinglot->name);
05701       parkinglot_unref(parkinglot);
05702       return NULL;
05703    }
05704 
05705    /* Move it into the list, if it wasn't already there */
05706    if (!oldparkinglot) {
05707       ao2_link(parkinglots, parkinglot);
05708    }
05709    parkinglot_unref(parkinglot);
05710 
05711    return parkinglot;
05712 }

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

References ast_channel::_state, add_features_datastore(), ast_app_dtget(), ast_autoservice_start(), ast_autoservice_stop(), ast_best_codec(), 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_lock, ast_channel_masquerade(), 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_datastore::data, dial_features_info, ast_bridge_config::end_bridge_callback_data_fixup, feature_request_and_dial(), ast_bridge_config::features_callee, ast_bridge_config::features_caller, finishup(), get_parking_exten(), LOG_WARNING, ast_dial_features::my_features, ast_channel::nativeformats, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_bridge_thread_obj::peer, ast_dial_features::peer_features, ast_channel::readformat, real_ctx(), set_peers(), ast_party_connected_line::source, transferdigittimeout, ast_channel::visible_indication, ast_channel::writeformat, xfer_park_call_helper(), xferfailsound, and xfersound.

02506 {
02507    struct ast_channel *transferer;/* Party B */
02508    struct ast_channel *transferee;/* Party A */
02509    struct ast_exten *park_exten;
02510    const char *chan1_attended_sound;
02511    const char *chan2_attended_sound;
02512    const char *transferer_real_context;
02513    char xferto[256] = "";
02514    int res;
02515    int outstate=0;
02516    struct ast_channel *newchan;
02517    struct ast_channel *xferchan;
02518    struct ast_bridge_thread_obj *tobj;
02519    struct ast_bridge_config bconfig;
02520    int l;
02521    struct ast_party_connected_line connected_line;
02522    struct ast_datastore *features_datastore;
02523    struct ast_dial_features *dialfeatures;
02524    char *transferer_tech;
02525    char *transferer_name;
02526    char *transferer_name_orig;
02527    char *dash;
02528 
02529    ast_debug(1, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
02530    set_peers(&transferer, &transferee, peer, chan, sense);
02531    transferer_real_context = real_ctx(transferer, transferee);
02532 
02533    /* Start autoservice on transferee while we talk to the transferer */
02534    ast_autoservice_start(transferee);
02535    ast_indicate(transferee, AST_CONTROL_HOLD);
02536 
02537    /* Transfer */
02538    res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
02539    if (res < 0) {
02540       finishup(transferee);
02541       return -1;
02542    }
02543    if (res > 0) { /* If they've typed a digit already, handle it */
02544       xferto[0] = (char) res;
02545    }
02546 
02547    /* this is specific of atxfer */
02548    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
02549    if (res < 0) {  /* hangup or error, (would be 0 for invalid and 1 for valid) */
02550       finishup(transferee);
02551       return -1;
02552    }
02553    l = strlen(xferto);
02554    if (res == 0) {
02555       if (l) {
02556          ast_log(LOG_WARNING, "Extension '%s' does not exist in context '%s'\n",
02557             xferto, transferer_real_context);
02558       } else {
02559          /* Does anyone care about this case? */
02560          ast_log(LOG_WARNING, "No digits dialed for atxfer.\n");
02561       }
02562       ast_stream_and_wait(transferer, "pbx-invalid", "");
02563       finishup(transferee);
02564       return AST_FEATURE_RETURN_SUCCESS;
02565    }
02566 
02567    park_exten = get_parking_exten(xferto, transferer, transferer_real_context);
02568    if (park_exten) {
02569       /* We are transfering the transferee to a parking lot. */
02570       return xfer_park_call_helper(transferee, transferer, park_exten);
02571    }
02572 
02573    /*
02574     * Append context to dialed transfer number.
02575     *
02576     * NOTE: The local channel needs the /n flag so party C will use
02577     * the feature flags set by the dialplan when calling that
02578     * party.
02579     */
02580    snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context);
02581 
02582    /* If we are performing an attended transfer and we have two channels involved then
02583       copy sound file information to play upon attended transfer completion */
02584    chan1_attended_sound = pbx_builtin_getvar_helper(transferer, "ATTENDED_TRANSFER_COMPLETE_SOUND");
02585    chan2_attended_sound = pbx_builtin_getvar_helper(transferee, "ATTENDED_TRANSFER_COMPLETE_SOUND");
02586    if (!ast_strlen_zero(chan1_attended_sound)) {
02587       pbx_builtin_setvar_helper(transferer, "BRIDGE_PLAY_SOUND", chan1_attended_sound);
02588    }
02589    if (!ast_strlen_zero(chan2_attended_sound)) {
02590       pbx_builtin_setvar_helper(transferee, "BRIDGE_PLAY_SOUND", chan2_attended_sound);
02591    }
02592 
02593    /* Extract redial transferer information from the channel name. */
02594    transferer_name_orig = ast_strdupa(transferer->name);
02595    transferer_name = ast_strdupa(transferer_name_orig);
02596    transferer_tech = strsep(&transferer_name, "/");
02597    dash = strrchr(transferer_name, '-');
02598    if (dash) {
02599       /* Trim off channel name sequence/serial number. */
02600       *dash = '\0';
02601    }
02602 
02603    /* Stop autoservice so we can monitor all parties involved in the transfer. */
02604    if (ast_autoservice_stop(transferee) < 0) {
02605       ast_indicate(transferee, AST_CONTROL_UNHOLD);
02606       return -1;
02607    }
02608 
02609    /* Save connected line info for party B about party A in case transfer fails. */
02610    ast_party_connected_line_init(&connected_line);
02611    ast_channel_lock(transferer);
02612    ast_party_connected_line_copy(&connected_line, &transferer->connected);
02613    ast_channel_unlock(transferer);
02614    connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
02615 
02616    /* Dial party C */
02617    newchan = feature_request_and_dial(transferer, transferer_name_orig, transferer,
02618       transferee, "Local", ast_best_codec(transferer->nativeformats), xferto,
02619       atxfernoanswertimeout, &outstate, transferer->language);
02620    ast_debug(2, "Dial party C result: newchan:%d, outstate:%d\n", !!newchan, outstate);
02621 
02622    if (!ast_check_hangup(transferer)) {
02623       int hangup_dont = 0;
02624 
02625       /* Transferer (party B) is up */
02626       ast_debug(1, "Actually doing an attended transfer.\n");
02627 
02628       /* Start autoservice on transferee while the transferer deals with party C. */
02629       ast_autoservice_start(transferee);
02630 
02631       ast_indicate(transferer, -1);
02632       if (!newchan) {
02633          /* any reason besides user requested cancel and busy triggers the failed sound */
02634          switch (outstate) {
02635          case AST_CONTROL_UNHOLD:/* Caller requested cancel or party C answer timeout. */
02636          case AST_CONTROL_BUSY:
02637          case AST_CONTROL_CONGESTION:
02638             if (ast_stream_and_wait(transferer, xfersound, "")) {
02639                ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
02640             }
02641             break;
02642          default:
02643             if (ast_stream_and_wait(transferer, xferfailsound, "")) {
02644                ast_log(LOG_WARNING, "Failed to play transfer failed sound!\n");
02645             }
02646             break;
02647          }
02648          atxfer_fail_cleanup(transferee, transferer, &connected_line);
02649          return AST_FEATURE_RETURN_SUCCESS;
02650       }
02651 
02652       if (check_compat(transferer, newchan)) {
02653          if (ast_stream_and_wait(transferer, xferfailsound, "")) {
02654             ast_log(LOG_WARNING, "Failed to play transfer failed sound!\n");
02655          }
02656          atxfer_fail_cleanup(transferee, transferer, &connected_line);
02657          return AST_FEATURE_RETURN_SUCCESS;
02658       }
02659       memset(&bconfig,0,sizeof(struct ast_bridge_config));
02660       ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
02661       ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
02662 
02663       /*
02664        * ast_bridge_call clears AST_FLAG_BRIDGE_HANGUP_DONT, but we
02665        * don't want that to happen here because the transferer is in
02666        * another bridge already.
02667        */
02668       if (ast_test_flag(transferer, AST_FLAG_BRIDGE_HANGUP_DONT)) {
02669          hangup_dont = 1;
02670       }
02671 
02672       /*
02673        * Don't let the after-bridge code run the h-exten.  It is the
02674        * wrong bridge to run the h-exten after.
02675        */
02676       ast_set_flag(transferer, AST_FLAG_BRIDGE_HANGUP_DONT);
02677 
02678       /*
02679        * Let party B and C talk as long as they want while party A
02680        * languishes in autoservice listening to MOH.
02681        */
02682       ast_bridge_call(transferer, newchan, &bconfig);
02683 
02684       if (hangup_dont) {
02685          /* Restore the AST_FLAG_BRIDGE_HANGUP_DONT flag */
02686          ast_set_flag(transferer, AST_FLAG_BRIDGE_HANGUP_DONT);
02687       }
02688 
02689       if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) {
02690          ast_hangup(newchan);
02691          if (ast_stream_and_wait(transferer, xfersound, "")) {
02692             ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
02693          }
02694          atxfer_fail_cleanup(transferee, transferer, &connected_line);
02695          return AST_FEATURE_RETURN_SUCCESS;
02696       }
02697 
02698       /* Transferer (party B) is confirmed hung up at this point. */
02699       if (check_compat(transferee, newchan)) {
02700          finishup(transferee);
02701          ast_party_connected_line_free(&connected_line);
02702          return -1;
02703       }
02704 
02705       ast_indicate(transferee, AST_CONTROL_UNHOLD);
02706       if ((ast_autoservice_stop(transferee) < 0)
02707          || (ast_waitfordigit(transferee, 100) < 0)
02708          || (ast_waitfordigit(newchan, 100) < 0)
02709          || ast_check_hangup(transferee)
02710          || ast_check_hangup(newchan)) {
02711          ast_hangup(newchan);
02712          ast_party_connected_line_free(&connected_line);
02713          return -1;
02714       }
02715    } else if (!ast_check_hangup(transferee)) {
02716       /* Transferer (party B) has hung up at this point.  Doing blonde transfer. */
02717       ast_debug(1, "Actually doing a blonde transfer.\n");
02718 
02719       if (!newchan && !atxferdropcall) {
02720          /* Party C is not available, try to call party B back. */
02721          unsigned int tries = 0;
02722 
02723          if (ast_strlen_zero(transferer_name) || ast_strlen_zero(transferer_tech)) {
02724             ast_log(LOG_WARNING,
02725                "Transferer channel name: '%s' cannot be used for callback.\n",
02726                transferer_name_orig);
02727             ast_indicate(transferee, AST_CONTROL_UNHOLD);
02728             ast_party_connected_line_free(&connected_line);
02729             return -1;
02730          }
02731 
02732          tries = 0;
02733          for (;;) {
02734             /* Try to get party B back. */
02735             ast_debug(1, "We're trying to callback %s/%s\n",
02736                transferer_tech, transferer_name);
02737             newchan = feature_request_and_dial(transferer, transferer_name_orig,
02738                transferee, transferee, transferer_tech,
02739                ast_best_codec(transferee->nativeformats), transferer_name,
02740                atxfernoanswertimeout, &outstate, transferer->language);
02741             ast_debug(2, "Dial party B result: newchan:%d, outstate:%d\n",
02742                !!newchan, outstate);
02743             if (newchan) {
02744                /*
02745                 * We have recalled party B (newchan).  We need to give this
02746                 * call leg the same feature flags as the original party B call
02747                 * leg.
02748                 */
02749                ast_channel_lock(transferer);
02750                features_datastore = ast_channel_datastore_find(transferer,
02751                   &dial_features_info, NULL);
02752                if (features_datastore && (dialfeatures = features_datastore->data)) {
02753                   struct ast_flags my_features = { 0 };
02754                   struct ast_flags peer_features = { 0 };
02755 
02756                   ast_copy_flags(&my_features, &dialfeatures->my_features,
02757                      AST_FLAGS_ALL);
02758                   ast_copy_flags(&peer_features, &dialfeatures->peer_features,
02759                      AST_FLAGS_ALL);
02760                   ast_channel_unlock(transferer);
02761                   add_features_datastore(newchan, &my_features, &peer_features);
02762                } else {
02763                   ast_channel_unlock(transferer);
02764                }
02765                break;
02766             }
02767             if (ast_check_hangup(transferee)) {
02768                break;
02769             }
02770 
02771             ++tries;
02772             if (atxfercallbackretries <= tries) {
02773                /* No more callback tries remaining. */
02774                break;
02775             }
02776 
02777             if (atxferloopdelay) {
02778                /* Transfer failed, sleeping */
02779                ast_debug(1, "Sleeping for %d ms before retrying atxfer.\n",
02780                   atxferloopdelay);
02781                ast_safe_sleep(transferee, atxferloopdelay);
02782                if (ast_check_hangup(transferee)) {
02783                   ast_party_connected_line_free(&connected_line);
02784                   return -1;
02785                }
02786             }
02787 
02788             /* Retry dialing party C. */
02789             ast_debug(1, "We're retrying to call %s/%s\n", "Local", xferto);
02790             newchan = feature_request_and_dial(transferer, transferer_name_orig,
02791                transferer, transferee, "Local",
02792                ast_best_codec(transferee->nativeformats), xferto,
02793                atxfernoanswertimeout, &outstate, transferer->language);
02794             ast_debug(2, "Redial party C result: newchan:%d, outstate:%d\n",
02795                !!newchan, outstate);
02796             if (newchan || ast_check_hangup(transferee)) {
02797                break;
02798             }
02799          }
02800       }
02801       ast_indicate(transferee, AST_CONTROL_UNHOLD);
02802       if (!newchan) {
02803          /* No party C or could not callback party B. */
02804          ast_party_connected_line_free(&connected_line);
02805          return -1;
02806       }
02807 
02808       /* newchan is up, we should prepare transferee and bridge them */
02809       if (ast_check_hangup(newchan)) {
02810          ast_hangup(newchan);
02811          ast_party_connected_line_free(&connected_line);
02812          return -1;
02813       }
02814       if (check_compat(transferee, newchan)) {
02815          ast_party_connected_line_free(&connected_line);
02816          return -1;
02817       }
02818    } else {
02819       /*
02820        * Both the transferer and transferee have hungup.  If newchan
02821        * is up, hang it up as it has no one to talk to.
02822        */
02823       ast_debug(1, "Everyone is hungup.\n");
02824       if (newchan) {
02825          ast_hangup(newchan);
02826       }
02827       ast_party_connected_line_free(&connected_line);
02828       return -1;
02829    }
02830 
02831    /* Initiate the channel transfer of party A to party C (or recalled party B). */
02832    ast_cel_report_event(transferee, AST_CEL_ATTENDEDTRANSFER, NULL, NULL, newchan);
02833 
02834    xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", transferee->linkedid, 0, "Transfered/%s", transferee->name);
02835    if (!xferchan) {
02836       ast_hangup(newchan);
02837       ast_party_connected_line_free(&connected_line);
02838       return -1;
02839    }
02840 
02841    /* Give party A a momentary ringback tone during transfer. */
02842    xferchan->visible_indication = AST_CONTROL_RINGING;
02843 
02844    /* Make formats okay */
02845    xferchan->readformat = transferee->readformat;
02846    xferchan->writeformat = transferee->writeformat;
02847 
02848    if (ast_channel_masquerade(xferchan, transferee)) {
02849       ast_hangup(xferchan);
02850       ast_hangup(newchan);
02851       ast_party_connected_line_free(&connected_line);
02852       return -1;
02853    }
02854 
02855    dash = strrchr(xferto, '@');
02856    if (dash) {
02857       /* Trim off the context. */
02858       *dash = '\0';
02859    }
02860    ast_explicit_goto(xferchan, transferer_real_context, xferto, 1);
02861    xferchan->_state = AST_STATE_UP;
02862    ast_clear_flag(xferchan, AST_FLAGS_ALL);
02863 
02864    /* Do the masquerade manually to make sure that is is completed. */
02865    ast_do_masquerade(xferchan);
02866 
02867    newchan->_state = AST_STATE_UP;
02868    ast_clear_flag(newchan, AST_FLAGS_ALL);
02869    tobj = ast_calloc(1, sizeof(*tobj));
02870    if (!tobj) {
02871       ast_hangup(xferchan);
02872       ast_hangup(newchan);
02873       ast_party_connected_line_free(&connected_line);
02874       return -1;
02875    }
02876 
02877    tobj->chan = newchan;
02878    tobj->peer = xferchan;
02879    tobj->bconfig = *config;
02880 
02881    ast_channel_lock(newchan);
02882    features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL);
02883    if (features_datastore && (dialfeatures = features_datastore->data)) {
02884       ast_copy_flags(&tobj->bconfig.features_callee, &dialfeatures->my_features,
02885          AST_FLAGS_ALL);
02886    }
02887    ast_channel_unlock(newchan);
02888 
02889    ast_channel_lock(xferchan);
02890    features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL);
02891    if (features_datastore && (dialfeatures = features_datastore->data)) {
02892       ast_copy_flags(&tobj->bconfig.features_caller, &dialfeatures->my_features,
02893          AST_FLAGS_ALL);
02894    }
02895    ast_channel_unlock(xferchan);
02896 
02897    if (tobj->bconfig.end_bridge_callback_data_fixup) {
02898       tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan);
02899    }
02900 
02901    /*
02902     * xferchan is transferee, and newchan is the transfer target
02903     * So...in a transfer, who is the caller and who is the callee?
02904     *
02905     * When the call is originally made, it is clear who is caller and callee.
02906     * When a transfer occurs, it is my humble opinion that the transferee becomes
02907     * the caller, and the transfer target is the callee.
02908     *
02909     * The problem is that these macros were set with the intention of the original
02910     * caller and callee taking those roles.  A transfer can totally mess things up,
02911     * to be technical.  What sucks even more is that you can't effectively change
02912     * the macros in the dialplan during the call from the transferer to the transfer
02913     * target because the transferee is stuck with whatever role he originally had.
02914     *
02915     * I think the answer here is just to make sure that it is well documented that
02916     * during a transfer, the transferee is the "caller" and the transfer target
02917     * is the "callee."
02918     *
02919     * This means that if party B calls party A, and party B transfers party A to
02920     * party C, then A has switched roles for the call.  Now party A will have the
02921     * caller macro called on his channel instead of the callee macro.
02922     *
02923     * Luckily, the method by which the party B to party C bridge is
02924     * launched above ensures that the transferee is the "chan" on
02925     * the bridge and the transfer target is the "peer," so my idea
02926     * for the roles post-transfer does not require extensive code
02927     * changes.
02928     */
02929 
02930    /* Transfer party C connected line to party A */
02931    ast_channel_lock(transferer);
02932    /*
02933     * Due to a limitation regarding when callerID is set on a Local channel,
02934     * we use the transferer's connected line information here.
02935     */
02936    ast_party_connected_line_copy(&connected_line, &transferer->connected);
02937    ast_channel_unlock(transferer);
02938    connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
02939    if (ast_channel_connected_line_macro(newchan, xferchan, &connected_line, 1, 0)) {
02940       ast_channel_update_connected_line(xferchan, &connected_line, NULL);
02941    }
02942 
02943    /* Transfer party A connected line to party C */
02944    ast_channel_lock(xferchan);
02945    ast_connected_line_copy_from_caller(&connected_line, &xferchan->caller);
02946    ast_channel_unlock(xferchan);
02947    connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
02948    if (ast_channel_connected_line_macro(xferchan, newchan, &connected_line, 0, 0)) {
02949       ast_channel_update_connected_line(newchan, &connected_line, NULL);
02950    }
02951 
02952    if (ast_stream_and_wait(newchan, xfersound, ""))
02953       ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
02954    bridge_call_thread_launch(tobj);
02955 
02956    ast_party_connected_line_free(&connected_line);
02957    return -1;/* The transferee is masqueraded and the original bridged channels can be hungup. */
02958 }

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

References args, ast_alloca, 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_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_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.

02185 {
02186    char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
02187    int x = 0;
02188    size_t len;
02189    struct ast_channel *caller_chan, *callee_chan;
02190    const char *mixmonitor_spy_type = "MixMonitor";
02191    const char *touch_format;
02192    const char *touch_monitor;
02193    int count = 0;
02194 
02195    if (!mixmonitor_ok) {
02196       ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
02197       return -1;
02198    }
02199 
02200    if (!(mixmonitor_app = pbx_findapp("MixMonitor"))) {
02201       mixmonitor_ok = 0;
02202       ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
02203       return -1;
02204    }
02205 
02206    set_peers(&caller_chan, &callee_chan, peer, chan, sense);
02207 
02208    if (!ast_strlen_zero(courtesytone)) {
02209       if (ast_autoservice_start(callee_chan))
02210          return -1;
02211       ast_autoservice_ignore(callee_chan, AST_FRAME_DTMF_END);
02212       if (ast_stream_and_wait(caller_chan, courtesytone, "")) {
02213          ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
02214          ast_autoservice_stop(callee_chan);
02215          return -1;
02216       }
02217       if (ast_autoservice_stop(callee_chan))
02218          return -1;
02219    }
02220 
02221    ast_channel_lock(callee_chan);
02222    count = ast_channel_audiohook_count_by_source(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
02223    ast_channel_unlock(callee_chan);
02224 
02225    /* This means a mixmonitor is attached to the channel, running or not is unknown. */
02226    if (count > 0) {
02227       ast_verb(3, "User hit '%s' to stop recording call.\n", code);
02228 
02229       /* Make sure they are running */
02230       ast_channel_lock(callee_chan);
02231       count = ast_channel_audiohook_count_by_source_running(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
02232       ast_channel_unlock(callee_chan);
02233       if (count > 0) {
02234          if (!stopmixmonitor_ok) {
02235             ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
02236             return -1;
02237          }
02238          if (!(stopmixmonitor_app = pbx_findapp("StopMixMonitor"))) {
02239             stopmixmonitor_ok = 0;
02240             ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
02241             return -1;
02242          } else {
02243             pbx_exec(callee_chan, stopmixmonitor_app, "");
02244             return AST_FEATURE_RETURN_SUCCESS;
02245          }
02246       }
02247       
02248       ast_log(LOG_WARNING,"Stopped MixMonitors are attached to the channel.\n"); 
02249    }        
02250 
02251    touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR_FORMAT");
02252    touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR");
02253 
02254    if (!touch_format)
02255       touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR_FORMAT");
02256 
02257    if (!touch_monitor)
02258       touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR");
02259 
02260    if (touch_monitor) {
02261       len = strlen(touch_monitor) + 50;
02262       args = ast_alloca(len);
02263       touch_filename = ast_alloca(len);
02264       snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
02265       snprintf(args, len, "%s.%s,b", touch_filename, (touch_format) ? touch_format : "wav");
02266    } else {
02267       caller_chan_id = ast_strdupa(S_COR(caller_chan->caller.id.number.valid,
02268          caller_chan->caller.id.number.str, caller_chan->name));
02269       callee_chan_id = ast_strdupa(S_COR(callee_chan->caller.id.number.valid,
02270          callee_chan->caller.id.number.str, callee_chan->name));
02271       len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
02272       args = ast_alloca(len);
02273       touch_filename = ast_alloca(len);
02274       snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
02275       snprintf(args, len, "%s.%s,b", touch_filename, S_OR(touch_format, "wav"));
02276    }
02277 
02278    for( x = 0; x < strlen(args); x++) {
02279       if (args[x] == '/')
02280          args[x] = '-';
02281    }
02282 
02283    ast_verb(3, "User hit '%s' to record call. filename: %s\n", code, touch_filename);
02284 
02285    pbx_exec(callee_chan, mixmonitor_app, args);
02286    pbx_builtin_setvar_helper(callee_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
02287    pbx_builtin_setvar_helper(caller_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
02288    return AST_FEATURE_RETURN_SUCCESS;
02289 }

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

References args, ast_alloca, AST_FEATURE_RETURN_SUCCESS, ast_log(), ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::caller, courtesytone, ast_party_caller::id, LOG_ERROR, 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.

02092 {
02093    char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
02094    int x = 0;
02095    size_t len;
02096    struct ast_channel *caller_chan, *callee_chan;
02097    const char *automon_message_start = NULL;
02098    const char *automon_message_stop = NULL;
02099    const char *touch_format = NULL;
02100    const char *touch_monitor = NULL;
02101    const char *touch_monitor_prefix = NULL;
02102 
02103    if (!monitor_ok) {
02104       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
02105       return -1;
02106    }
02107 
02108    if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
02109       monitor_ok = 0;
02110       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
02111       return -1;
02112    }
02113 
02114    set_peers(&caller_chan, &callee_chan, peer, chan, sense);
02115 
02116    /* Find extra messages */
02117    automon_message_start = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_MESSAGE_START");
02118    automon_message_stop = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_MESSAGE_STOP");
02119 
02120    if (!ast_strlen_zero(courtesytone)) {  /* Play courtesy tone if configured */
02121       if(play_message_in_bridged_call(caller_chan, callee_chan, courtesytone) == -1) {
02122          return -1;
02123       }
02124    }
02125    
02126    if (callee_chan->monitor) {
02127       ast_verb(4, "User hit '%s' to stop recording call.\n", code);
02128       if (!ast_strlen_zero(automon_message_stop)) {
02129          play_message_in_bridged_call(caller_chan, callee_chan, automon_message_stop);
02130       }
02131       callee_chan->monitor->stop(callee_chan, 1);
02132       return AST_FEATURE_RETURN_SUCCESS;
02133    }
02134 
02135    touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
02136    touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
02137    touch_monitor_prefix = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_PREFIX");
02138 
02139    if (!touch_format)
02140       touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
02141 
02142    if (!touch_monitor)
02143       touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
02144 
02145    if (!touch_monitor_prefix)
02146       touch_monitor_prefix = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_PREFIX");
02147 
02148    if (touch_monitor) {
02149       len = strlen(touch_monitor) + 50;
02150       args = ast_alloca(len);
02151       touch_filename = ast_alloca(len);
02152       snprintf(touch_filename, len, "%s-%ld-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor);
02153       snprintf(args, len, "%s,%s,m", S_OR(touch_format, "wav"), touch_filename);
02154    } else {
02155       caller_chan_id = ast_strdupa(S_COR(caller_chan->caller.id.number.valid,
02156          caller_chan->caller.id.number.str, caller_chan->name));
02157       callee_chan_id = ast_strdupa(S_COR(callee_chan->caller.id.number.valid,
02158          callee_chan->caller.id.number.str, callee_chan->name));
02159       len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
02160       args = ast_alloca(len);
02161       touch_filename = ast_alloca(len);
02162       snprintf(touch_filename, len, "%s-%ld-%s-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), caller_chan_id, callee_chan_id);
02163       snprintf(args, len, "%s,%s,m", S_OR(touch_format, "wav"), touch_filename);
02164    }
02165 
02166    for(x = 0; x < strlen(args); x++) {
02167       if (args[x] == '/')
02168          args[x] = '-';
02169    }
02170    
02171    ast_verb(4, "User hit '%s' to record call. filename: %s\n", code, args);
02172 
02173    pbx_exec(callee_chan, monitor_app, args);
02174    pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
02175    pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
02176 
02177    if (!ast_strlen_zero(automon_message_start)) {  /* Play start message for both channels */
02178       play_message_in_bridged_call(caller_chan, callee_chan, automon_message_start);
02179    }
02180 
02181    return AST_FEATURE_RETURN_SUCCESS;
02182 }

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 2334 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_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().

02335 {
02336    struct ast_channel *transferer;
02337    struct ast_channel *transferee;
02338    struct ast_exten *park_exten;
02339    const char *transferer_real_context;
02340    char xferto[256] = "";
02341    int res;
02342 
02343    ast_debug(1, "Executing Blind Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
02344    set_peers(&transferer, &transferee, peer, chan, sense);
02345    transferer_real_context = real_ctx(transferer, transferee);
02346 
02347    /* Start autoservice on transferee while we talk to the transferer */
02348    ast_autoservice_start(transferee);
02349    ast_indicate(transferee, AST_CONTROL_HOLD);
02350 
02351    /* Transfer */
02352    res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
02353    if (res < 0) {
02354       finishup(transferee);
02355       return -1; /* error ? */
02356    }
02357    if (res > 0) { /* If they've typed a digit already, handle it */
02358       xferto[0] = (char) res;
02359    }
02360 
02361    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
02362    if (res < 0) {  /* hangup or error, (would be 0 for invalid and 1 for valid) */
02363       finishup(transferee);
02364       return -1;
02365    }
02366    if (res == 0) {
02367       if (xferto[0]) {
02368          ast_log(LOG_WARNING, "Extension '%s' does not exist in context '%s'\n",
02369             xferto, transferer_real_context);
02370       } else {
02371          /* Does anyone care about this case? */
02372          ast_log(LOG_WARNING, "No digits dialed.\n");
02373       }
02374       ast_stream_and_wait(transferer, "pbx-invalid", "");
02375       finishup(transferee);
02376       return AST_FEATURE_RETURN_SUCCESS;
02377    }
02378 
02379    park_exten = get_parking_exten(xferto, transferer, transferer_real_context);
02380    if (park_exten) {
02381       /* We are transfering the transferee to a parking lot. */
02382       return xfer_park_call_helper(transferee, transferer, park_exten);
02383    }
02384 
02385    /* Do blind transfer. */
02386    ast_verb(3, "Blind transferring %s to '%s' (context %s) priority 1\n",
02387       transferee->name, xferto, transferer_real_context);
02388    ast_cel_report_event(transferer, AST_CEL_BLINDTRANSFER, NULL, xferto, transferee);
02389    pbx_builtin_setvar_helper(transferer, "BLINDTRANSFER", transferee->name);
02390    pbx_builtin_setvar_helper(transferee, "BLINDTRANSFER", transferer->name);
02391    finishup(transferee);
02392    ast_channel_lock(transferer);
02393    if (!transferer->cdr) { /* this code should never get called (in a perfect world) */
02394       transferer->cdr = ast_cdr_alloc();
02395       if (transferer->cdr) {
02396          ast_cdr_init(transferer->cdr, transferer); /* initialize our channel's cdr */
02397          ast_cdr_start(transferer->cdr);
02398       }
02399    }
02400    ast_channel_unlock(transferer);
02401    if (transferer->cdr) {
02402       struct ast_cdr *swap = transferer->cdr;
02403 
02404       ast_debug(1,
02405          "transferer=%s; transferee=%s; lastapp=%s; lastdata=%s; chan=%s; dstchan=%s\n",
02406          transferer->name, transferee->name, transferer->cdr->lastapp,
02407          transferer->cdr->lastdata, transferer->cdr->channel,
02408          transferer->cdr->dstchannel);
02409       ast_debug(1, "TRANSFEREE; lastapp=%s; lastdata=%s, chan=%s; dstchan=%s\n",
02410          transferee->cdr->lastapp, transferee->cdr->lastdata, transferee->cdr->channel,
02411          transferee->cdr->dstchannel);
02412       ast_debug(1, "transferer_real_context=%s; xferto=%s\n",
02413          transferer_real_context, xferto);
02414       /* swap cdrs-- it will save us some time & work */
02415       transferer->cdr = transferee->cdr;
02416       transferee->cdr = swap;
02417    }
02418    if (!transferee->pbx) {
02419       /* Doh!  Use our handy async_goto functions */
02420       ast_debug(1, "About to ast_async_goto %s.\n", transferee->name);
02421       if (ast_async_goto(transferee, transferer_real_context, xferto, 1)) {
02422          ast_log(LOG_WARNING, "Async goto failed :-(\n");
02423       }
02424 
02425       /* The transferee is masqueraded and the original bridged channels can be hungup. */
02426       res = -1;
02427    } else {
02428       /* Set the transferee's new extension, since it exists, using transferer context */
02429       ast_debug(1, "About to explicit goto %s, it has a PBX.\n", transferee->name);
02430       ast_set_flag(transferee, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
02431       set_c_e_p(transferee, transferer_real_context, xferto, 0);
02432 
02433       /*
02434        * Break the bridge.  The transferee needs to resume executing
02435        * dialplan at the xferto location.
02436        */
02437       res = AST_FEATURE_RETURN_SUCCESSBREAK;
02438    }
02439    check_goto_on_transfer(transferer);
02440    return res;
02441 }

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

References AST_FEATURE_RETURN_HANGUP, and ast_verb.

02292 {
02293    ast_verb(4, "User hit '%s' to disconnect call.\n", code);
02294    return AST_FEATURE_RETURN_HANGUP;
02295 }

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

01970 {
01971    struct ast_channel *parker;
01972    struct ast_channel *parkee;
01973    struct ast_park_call_args args = { 0, };
01974 
01975    /*
01976     * We used to set chan's exten and priority to "s" and 1 here,
01977     * but this generates (in some cases) an invalid extension, and
01978     * if "s" exists, could errantly cause execution of extensions
01979     * you don't expect.  It makes more sense to let nature take its
01980     * course when chan finishes, and let the pbx do its thing and
01981     * hang up when the park is over.
01982     */
01983 
01984    /* Answer if call is not up */
01985    if (chan->_state != AST_STATE_UP) {
01986       /*
01987        * XXX Why are we doing this?  Both of the channels should be up
01988        * since you cannot do DTMF features unless you are bridged.
01989        */
01990       if (ast_answer(chan)) {
01991          return -1;
01992       }
01993 
01994       /* Sleep to allow VoIP streams to settle down */
01995       if (ast_safe_sleep(chan, 1000)) {
01996          return -1;
01997       }
01998    }
01999 
02000    /* one direction used to call park_call.... */
02001    set_peers(&parker, &parkee, peer, chan, sense);
02002    return masq_park_call(parkee, parker, &args) ? AST_FEATURE_RETURN_SUCCESS : -1;
02003 }

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

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

04630 {
04631    int i = 0;
04632    enum {
04633       OPT_CALLEE_REDIRECT   = 't',
04634       OPT_CALLER_REDIRECT   = 'T',
04635       OPT_CALLEE_AUTOMON    = 'w',
04636       OPT_CALLER_AUTOMON    = 'W',
04637       OPT_CALLEE_DISCONNECT = 'h',
04638       OPT_CALLER_DISCONNECT = 'H',
04639       OPT_CALLEE_PARKCALL   = 'k',
04640       OPT_CALLER_PARKCALL   = 'K',
04641    };
04642 
04643    memset(options, 0, len);
04644    if (ast_test_flag(features_caller, AST_FEATURE_REDIRECT) && i < len) {
04645       options[i++] = OPT_CALLER_REDIRECT;
04646    }
04647    if (ast_test_flag(features_caller, AST_FEATURE_AUTOMON) && i < len) {
04648       options[i++] = OPT_CALLER_AUTOMON;
04649    }
04650    if (ast_test_flag(features_caller, AST_FEATURE_DISCONNECT) && i < len) {
04651       options[i++] = OPT_CALLER_DISCONNECT;
04652    }
04653    if (ast_test_flag(features_caller, AST_FEATURE_PARKCALL) && i < len) {
04654       options[i++] = OPT_CALLER_PARKCALL;
04655    }
04656 
04657    if (ast_test_flag(features_callee, AST_FEATURE_REDIRECT) && i < len) {
04658       options[i++] = OPT_CALLEE_REDIRECT;
04659    }
04660    if (ast_test_flag(features_callee, AST_FEATURE_AUTOMON) && i < len) {
04661       options[i++] = OPT_CALLEE_AUTOMON;
04662    }
04663    if (ast_test_flag(features_callee, AST_FEATURE_DISCONNECT) && i < len) {
04664       options[i++] = OPT_CALLEE_DISCONNECT;
04665    }
04666    if (ast_test_flag(features_callee, AST_FEATURE_PARKCALL) && i < len) {
04667       options[i++] = OPT_CALLEE_PARKCALL;
04668    }
04669 
04670    return options;
04671 }

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

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

Referenced by builtin_atxfer().

02451 {
02452    if (ast_channel_make_compatible(c, newchan) < 0) {
02453       ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
02454          c->name, newchan->name);
02455       ast_hangup(newchan);
02456       return -1;
02457    }
02458    return 0;
02459 }

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

References ast_channel::_state, ast_channel_alloc, ast_channel_clear_softhangup(), ast_channel_lock, ast_channel_masquerade(), 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().

00893 {
00894    struct ast_channel *xferchan;
00895    const char *val;
00896    char *goto_on_transfer;
00897    char *x;
00898 
00899    ast_channel_lock(chan);
00900    val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
00901    if (ast_strlen_zero(val)) {
00902       ast_channel_unlock(chan);
00903       return;
00904    }
00905    goto_on_transfer = ast_strdupa(val);
00906    ast_channel_unlock(chan);
00907 
00908    ast_debug(1, "Attempting GOTO_ON_BLINDXFR=%s for %s.\n", val, chan->name);
00909 
00910    xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", chan->linkedid, 0,
00911       "%s", chan->name);
00912    if (!xferchan) {
00913       return;
00914    }
00915 
00916    /* Make formats okay */
00917    xferchan->readformat = chan->readformat;
00918    xferchan->writeformat = chan->writeformat;
00919 
00920    if (ast_channel_masquerade(xferchan, chan)) {
00921       /* Failed to setup masquerade. */
00922       ast_hangup(xferchan);
00923       return;
00924    }
00925 
00926    for (x = goto_on_transfer; *x; ++x) {
00927       if (*x == '^') {
00928          *x = ',';
00929       }
00930    }
00931    ast_parseable_goto(xferchan, goto_on_transfer);
00932    xferchan->_state = AST_STATE_UP;
00933    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
00934    ast_channel_clear_softhangup(xferchan, AST_SOFTHANGUP_ALL);
00935 
00936    ast_do_masquerade(xferchan);
00937    if (ast_pbx_start(xferchan)) {
00938       /* Failed to start PBX. */
00939       ast_hangup(xferchan);
00940    }
00941 }

static void clear_dialed_interfaces ( struct ast_channel chan  )  [static]

Definition at line 3889 of file features.c.

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

Referenced by ast_bridge_call().

03890 {
03891    struct ast_datastore *di_datastore;
03892 
03893    ast_channel_lock(chan);
03894    if ((di_datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL))) {
03895       if (option_debug) {
03896          ast_log(LOG_DEBUG, "Removing dialed interfaces datastore on %s since we're bridging\n", chan->name);
03897       }
03898       if (!ast_channel_datastore_remove(chan, di_datastore)) {
03899          ast_datastore_free(di_datastore);
03900       }
03901    }
03902    ast_channel_unlock(chan);
03903 }

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

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

Referenced by create_dynamic_parkinglot().

04995 {
04996    struct ast_parkinglot *copylot;
04997 
04998    if ((copylot = find_parkinglot(name))) { /* Parkinglot with that name already exists */
04999       ao2_ref(copylot, -1);
05000       return NULL;
05001    }
05002 
05003    copylot = create_parkinglot(name);
05004    if (!copylot) {
05005       return NULL;
05006    }
05007 
05008    ast_debug(1, "Building parking lot %s\n", name);
05009 
05010    /* Copy the source parking lot configuration. */
05011    copylot->cfg = parkinglot->cfg;
05012 
05013    return copylot;
05014 }

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

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

01162 {
01163    const char *dyn_context;
01164    const char *dyn_exten;
01165    const char *dyn_range;
01166    const char *template_name;
01167    struct ast_parkinglot *template_parkinglot = NULL;
01168    struct ast_parkinglot *parkinglot;
01169    int dyn_start;
01170    int dyn_end;
01171 
01172    ast_channel_lock(chan);
01173    template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
01174    dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
01175    dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
01176    dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
01177    ast_channel_unlock(chan);
01178 
01179    if (!ast_strlen_zero(template_name)) {
01180       template_parkinglot = find_parkinglot(template_name);
01181       if (!template_parkinglot) {
01182          ast_debug(1, "PARKINGDYNAMIC lot %s does not exist.\n",
01183             template_name);
01184       } else if (template_parkinglot->cfg.is_invalid) {
01185          ast_debug(1, "PARKINGDYNAMIC lot %s has invalid config.\n",
01186             template_name);
01187          parkinglot_unref(template_parkinglot);
01188          template_parkinglot = NULL;
01189       }
01190    }
01191    if (!template_parkinglot) {
01192       template_parkinglot = parkinglot_addref(default_parkinglot);
01193       ast_debug(1, "Using default parking lot for template\n");
01194    }
01195 
01196    parkinglot = copy_parkinglot(name, template_parkinglot);
01197    if (!parkinglot) {
01198       ast_log(LOG_ERROR, "Could not build dynamic parking lot!\n");
01199    } else {
01200       /* Configure the dynamic parking lot. */
01201       if (!ast_strlen_zero(dyn_context)) {
01202          ast_copy_string(parkinglot->cfg.parking_con, dyn_context,
01203             sizeof(parkinglot->cfg.parking_con));
01204       }
01205       if (!ast_strlen_zero(dyn_exten)) {
01206          ast_copy_string(parkinglot->cfg.parkext, dyn_exten,
01207             sizeof(parkinglot->cfg.parkext));
01208       }
01209       if (!ast_strlen_zero(dyn_range)) {
01210          if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
01211             ast_log(LOG_WARNING,
01212                "Format for parking positions is a-b, where a and b are numbers\n");
01213          } else if (dyn_end < dyn_start || dyn_start <= 0 || dyn_end <= 0) {
01214             ast_log(LOG_WARNING,
01215                "Format for parking positions is a-b, where a <= b\n");
01216          } else {
01217             parkinglot->cfg.parking_start = dyn_start;
01218             parkinglot->cfg.parking_stop = dyn_end;
01219          }
01220       }
01221 
01222       /*
01223        * Sanity check for dynamic parking lot configuration.
01224        *
01225        * XXX It may be desirable to instead check if the dynamic
01226        * parking lot overlaps any existing lots like what is done for
01227        * a reload.
01228        */
01229       if (!strcmp(parkinglot->cfg.parking_con, template_parkinglot->cfg.parking_con)) {
01230          if (!strcmp(parkinglot->cfg.parkext, template_parkinglot->cfg.parkext)
01231             && parkinglot->cfg.parkext_exclusive) {
01232             ast_log(LOG_WARNING,
01233                "Parking lot '%s' conflicts with template parking lot '%s'!\n"
01234                "Change either PARKINGDYNCONTEXT or PARKINGDYNEXTEN.\n",
01235                parkinglot->name, template_parkinglot->name);
01236          }
01237          if ((template_parkinglot->cfg.parking_start <= parkinglot->cfg.parking_start
01238                && parkinglot->cfg.parking_start <= template_parkinglot->cfg.parking_stop)
01239             || (template_parkinglot->cfg.parking_start <= parkinglot->cfg.parking_stop
01240                && parkinglot->cfg.parking_stop <= template_parkinglot->cfg.parking_stop)
01241             || (parkinglot->cfg.parking_start < template_parkinglot->cfg.parking_start
01242                && template_parkinglot->cfg.parking_stop < parkinglot->cfg.parking_stop)) {
01243             ast_log(LOG_WARNING,
01244                "Parking lot '%s' parking spaces overlap template parking lot '%s'!\n"
01245                "Change PARKINGDYNPOS.\n",
01246                parkinglot->name, template_parkinglot->name);
01247          }
01248       }
01249 
01250       parkinglot_activate(parkinglot);
01251       ao2_link(parkinglots, parkinglot);
01252    }
01253    parkinglot_unref(template_parkinglot);
01254 
01255    return parkinglot;
01256 }

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

Allocate parking lot structure.

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

05420 {
05421    struct ast_parkinglot *newlot;
05422 
05423    if (ast_strlen_zero(name)) { /* No name specified */
05424       return NULL;
05425    }
05426 
05427    newlot = ao2_alloc(sizeof(*newlot), parkinglot_destroy);
05428    if (!newlot)
05429       return NULL;
05430    
05431    ast_copy_string(newlot->name, name, sizeof(newlot->name));
05432    newlot->cfg.is_invalid = 1;/* No config is set yet. */
05433    AST_LIST_HEAD_INIT(&newlot->parkings);
05434 
05435    return newlot;
05436 }

static void destroy_dialplan_usage_context ( struct parking_dp_context doomed  )  [static]

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

05998 {
05999    struct parking_dp_ramp *ramp;
06000    struct parking_dp_spaces *spaces;
06001 
06002    while ((ramp = AST_LIST_REMOVE_HEAD(&doomed->access_extens, node))) {
06003       ast_free(ramp);
06004    }
06005    while ((spaces = AST_LIST_REMOVE_HEAD(&doomed->spaces, node))) {
06006       ast_free(spaces);
06007    }
06008    while ((spaces = AST_LIST_REMOVE_HEAD(&doomed->hints, node))) {
06009       ast_free(spaces);
06010    }
06011    ast_free(doomed);
06012 }

static void destroy_dialplan_usage_map ( struct parking_dp_map doomed  )  [static]

Definition at line 6022 of file features.c.

References AST_LIST_REMOVE_HEAD, and destroy_dialplan_usage_context().

Referenced by load_config().

06023 {
06024    struct parking_dp_context *item;
06025 
06026    while ((item = AST_LIST_REMOVE_HEAD(doomed, node))) {
06027       destroy_dialplan_usage_context(item);
06028    }
06029 }

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

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

06453 {
06454    char exten[AST_MAX_EXTENSION];
06455 
06456    /* Destroy priorities of the parking space that we registered. */
06457    snprintf(exten, sizeof(exten), "%d", space);
06458    remove_exten_if_exist(context, exten, PRIORITY_HINT);
06459    remove_exten_if_exist(context, exten, 1);
06460 }

static void dial_features_destroy ( void *  data  )  [static]

Definition at line 740 of file features.c.

References ast_free.

00741 {
00742    struct ast_dial_features *df = data;
00743    if (df) {
00744       ast_free(df);
00745    }
00746 }

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

Definition at line 727 of file features.c.

References ast_calloc.

00728 {
00729    struct ast_dial_features *df = data, *df_copy;
00730  
00731    if (!(df_copy = ast_calloc(1, sizeof(*df)))) {
00732       return NULL;
00733    }
00734  
00735    memcpy(df_copy, df, sizeof(*df));
00736  
00737    return df_copy;
00738 }

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

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

06306 {
06307    struct parking_dp_context *cur_ctx;
06308    struct parking_dp_context *new_ctx;
06309    int cmp;
06310 
06311    AST_LIST_TRAVERSE_SAFE_BEGIN(usage_map, cur_ctx, node) {
06312       cmp = strcmp(lot->cfg.parking_con, cur_ctx->context);
06313       if (cmp > 0) {
06314          /* The parking lot context goes after this node. */
06315          continue;
06316       }
06317       if (cmp == 0) {
06318          /* This is the node we will add parking lot spaces to the map. */
06319          return dialplan_usage_add_parkinglot_data(cur_ctx, lot, complain);
06320       }
06321       /* The new parking lot context goes before this node. */
06322       new_ctx = build_dialplan_useage_context(lot);
06323       if (!new_ctx) {
06324          return -1;
06325       }
06326       AST_LIST_INSERT_BEFORE_CURRENT(new_ctx, node);
06327       return 0;
06328    }
06329    AST_LIST_TRAVERSE_SAFE_END;
06330 
06331    /* New parking lot context goes on the end. */
06332    new_ctx = build_dialplan_useage_context(lot);
06333    if (!new_ctx) {
06334       return -1;
06335    }
06336    AST_LIST_INSERT_TAIL(usage_map, new_ctx, node);
06337    return 0;
06338 }

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

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

06252 {
06253    if (usage_context_add_ramp(&ctx_node->access_extens, lot->cfg.parkext,
06254       lot->cfg.parkext_exclusive, lot, complain)) {
06255       return -1;
06256    }
06257    if (usage_context_add_spaces(&ctx_node->spaces, lot->cfg.parking_start,
06258       lot->cfg.parking_stop, lot, complain)) {
06259       return -1;
06260    }
06261    if (lot->cfg.parkaddhints
06262       && usage_context_add_spaces(&ctx_node->hints, lot->cfg.parking_start,
06263          lot->cfg.parking_stop, lot, 0)) {
06264       return -1;
06265    }
06266    return 0;
06267 }

static int 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.
Return values:
0 on success.
-1 on error.

Definition at line 6887 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_strdupa, ast_channel::context, ast_channel::exten, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat.

Referenced by action_bridge(), and bridge_exec().

06888 {
06889    const char *context;
06890    const char *exten;
06891    int priority;
06892 
06893    ast_moh_stop(chan);
06894    ast_channel_lock_both(chan, tmpchan);
06895    context = ast_strdupa(chan->context);
06896    exten = ast_strdupa(chan->exten);
06897    priority = chan->priority;
06898    ast_setstate(tmpchan, chan->_state);
06899    tmpchan->readformat = chan->readformat;
06900    tmpchan->writeformat = chan->writeformat;
06901    ast_channel_unlock(chan);
06902    ast_channel_unlock(tmpchan);
06903 
06904    /* Masquerade setup and execution must be done without any channel locks held */
06905    if (ast_channel_masquerade(tmpchan, chan)) {
06906       return -1;
06907    }
06908    ast_do_masquerade(tmpchan);
06909 
06910    /* when returning from bridge, the channel will continue at the next priority */
06911    ast_explicit_goto(tmpchan, context, exten, priority + 1);
06912 
06913    return 0;
06914 }

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

04945 {
04946    struct pollfd *pfds = NULL, *new_pfds = NULL;
04947    int nfds = 0, new_nfds = 0;
04948 
04949    for (;;) {
04950       struct ao2_iterator iter;
04951       struct ast_parkinglot *curlot;
04952       int ms = -1;   /* poll2 timeout, uninitialized */
04953 
04954       iter = ao2_iterator_init(parkinglots, 0);
04955       while ((curlot = ao2_iterator_next(&iter))) {
04956          manage_parkinglot(curlot, pfds, nfds, &new_pfds, &new_nfds, &ms);
04957          ao2_ref(curlot, -1);
04958       }
04959       ao2_iterator_destroy(&iter);
04960 
04961       /* Recycle */
04962       ast_free(pfds);
04963       pfds = new_pfds;
04964       nfds = new_nfds;
04965       new_pfds = NULL;
04966       new_nfds = 0;
04967 
04968       /* Wait for something to happen */
04969       ast_poll(pfds, nfds, ms);
04970       pthread_testcancel();
04971    }
04972    /* If this WERE reached, we'd need to free(pfds) */
04973    return NULL;   /* Never reached */
04974 }

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

Check if a feature exists.

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

03418                                                                                            {
03419    char *chan_dynamic_features;
03420    ast_channel_lock(chan);
03421    chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
03422    ast_channel_unlock(chan);
03423 
03424    return feature_interpret_helper(chan, NULL, NULL, code, 0, chan_dynamic_features, features, FEATURE_INTERPRET_CHECK, NULL);
03425 }

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 3163 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_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().

03164 {
03165    struct ast_app *app;
03166    struct ast_call_feature *feature = data;
03167    struct ast_channel *work, *idle;
03168    int res;
03169 
03170    if (!feature) { /* shouldn't ever happen! */
03171       ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
03172       return -1; 
03173    }
03174 
03175    if (sense == FEATURE_SENSE_CHAN) {
03176       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
03177          return AST_FEATURE_RETURN_KEEPTRYING;
03178       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
03179          work = chan;
03180          idle = peer;
03181       } else {
03182          work = peer;
03183          idle = chan;
03184       }
03185    } else {
03186       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
03187          return AST_FEATURE_RETURN_KEEPTRYING;
03188       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
03189          work = peer;
03190          idle = chan;
03191       } else {
03192          work = chan;
03193          idle = peer;
03194       }
03195    }
03196 
03197    if (!(app = pbx_findapp(feature->app))) {
03198       ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
03199       return -2;
03200    }
03201 
03202    ast_autoservice_start(idle);
03203    ast_autoservice_ignore(idle, AST_FRAME_DTMF_END);
03204    
03205    pbx_builtin_setvar_helper(work, "DYNAMIC_PEERNAME", idle->name);
03206    pbx_builtin_setvar_helper(idle, "DYNAMIC_PEERNAME", work->name);
03207    pbx_builtin_setvar_helper(work, "DYNAMIC_FEATURENAME", feature->sname);
03208    pbx_builtin_setvar_helper(idle, "DYNAMIC_FEATURENAME", feature->sname);
03209 
03210    if (!ast_strlen_zero(feature->moh_class))
03211       ast_moh_start(idle, feature->moh_class, NULL);
03212 
03213    res = pbx_exec(work, app, feature->app_args);
03214 
03215    if (!ast_strlen_zero(feature->moh_class))
03216       ast_moh_stop(idle);
03217 
03218    ast_autoservice_stop(idle);
03219 
03220    if (res) {
03221       return AST_FEATURE_RETURN_SUCCESSBREAK;
03222    }
03223    return AST_FEATURE_RETURN_SUCCESS;  /*! \todo XXX should probably return res */
03224 }

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

References ast_channel_lock, 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().

03381                                                                                                                                                 {
03382 
03383    char dynamic_features_buf[128];
03384    const char *peer_dynamic_features, *chan_dynamic_features;
03385    struct ast_flags features;
03386    struct ast_call_feature feature;
03387    if (sense == FEATURE_SENSE_CHAN) {
03388       /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
03389       ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
03390    }
03391    else {
03392       /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
03393       ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
03394    }
03395 
03396    ast_channel_lock(peer);
03397    peer_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"),""));
03398    ast_channel_unlock(peer);
03399 
03400    ast_channel_lock(chan);
03401    chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
03402    ast_channel_unlock(chan);
03403 
03404    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,""));
03405 
03406    ast_debug(3, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d, dynamic=%s\n", chan->name, peer->name, code, sense, features.flags, dynamic_features_buf);
03407 
03408    return feature_interpret_helper(chan, peer, config, code, sense, dynamic_features_buf, &features, FEATURE_INTERPRET_DO, &feature);
03409 }

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 3264 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, and ast_call_feature::sname.

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

03267 {
03268    int x;
03269    struct feature_group *fg = NULL;
03270    struct feature_group_exten *fge;
03271    struct ast_call_feature *tmpfeature;
03272    char *tmp, *tok;
03273    int res = AST_FEATURE_RETURN_PASSDIGITS;
03274    int feature_detected = 0;
03275 
03276    if (!(peer && chan && config) && operation == FEATURE_INTERPRET_DO) {
03277       return -1; /* can not run feature operation */
03278    }
03279 
03280    ast_rwlock_rdlock(&features_lock);
03281    for (x = 0; x < FEATURES_COUNT; x++) {
03282       if ((ast_test_flag(features, builtin_features[x].feature_mask)) &&
03283           !ast_strlen_zero(builtin_features[x].exten)) {
03284          /* Feature is up for consideration */
03285          if (!strcmp(builtin_features[x].exten, code)) {
03286             ast_debug(3, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
03287             if (operation == FEATURE_INTERPRET_CHECK) {
03288                res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
03289             } else if (operation == FEATURE_INTERPRET_DO) {
03290                res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
03291             }
03292             if (feature) {
03293                memcpy(feature, &builtin_features[x], sizeof(*feature));
03294             }
03295             feature_detected = 1;
03296             break;
03297          } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
03298             if (res == AST_FEATURE_RETURN_PASSDIGITS) {
03299                res = AST_FEATURE_RETURN_STOREDIGITS;
03300             }
03301          }
03302       }
03303    }
03304    ast_rwlock_unlock(&features_lock);
03305 
03306    if (ast_strlen_zero(dynamic_features_buf) || feature_detected) {
03307       return res;
03308    }
03309 
03310    tmp = dynamic_features_buf;
03311 
03312    while ((tok = strsep(&tmp, "#"))) {
03313       AST_RWLIST_RDLOCK(&feature_groups);
03314 
03315       fg = find_group(tok);
03316 
03317       if (fg) {
03318          AST_LIST_TRAVERSE(&fg->features, fge, entry) {
03319             if (!strcmp(fge->exten, code)) {
03320                if (operation) {
03321                   res = fge->feature->operation(chan, peer, config, code, sense, fge->feature);
03322                }
03323                if (feature) {
03324                   memcpy(feature, fge->feature, sizeof(*feature));
03325                }
03326                if (res != AST_FEATURE_RETURN_KEEPTRYING) {
03327                   AST_RWLIST_UNLOCK(&feature_groups);
03328                   break;
03329                }
03330                res = AST_FEATURE_RETURN_PASSDIGITS;
03331             } else if (!strncmp(fge->exten, code, strlen(code))) {
03332                res = AST_FEATURE_RETURN_STOREDIGITS;
03333             }
03334          }
03335          if (fge) {
03336             break;
03337          }
03338       }
03339 
03340       AST_RWLIST_UNLOCK(&feature_groups);
03341 
03342       AST_RWLIST_RDLOCK(&feature_list);
03343 
03344       if (!(tmpfeature = find_dynamic_feature(tok))) {
03345          AST_RWLIST_UNLOCK(&feature_list);
03346          continue;
03347       }
03348 
03349       /* Feature is up for consideration */
03350       if (!strcmp(tmpfeature->exten, code)) {
03351          ast_verb(3, " Feature Found: %s exten: %s\n",tmpfeature->sname, tok);
03352          if (operation == FEATURE_INTERPRET_CHECK) {
03353             res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
03354          } else if (operation == FEATURE_INTERPRET_DO) {
03355             res = tmpfeature->operation(chan, peer, config, code, sense, tmpfeature);
03356          }
03357          if (feature) {
03358             memcpy(feature, tmpfeature, sizeof(*feature));
03359          }
03360          if (res != AST_FEATURE_RETURN_KEEPTRYING) {
03361             AST_RWLIST_UNLOCK(&feature_list);
03362             break;
03363          }
03364          res = AST_FEATURE_RETURN_PASSDIGITS;
03365       } else if (!strncmp(tmpfeature->exten, code, strlen(code)))
03366          res = AST_FEATURE_RETURN_STOREDIGITS;
03367 
03368       AST_RWLIST_UNLOCK(&feature_list);
03369    }
03370 
03371    return res;
03372 }

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,
format_t  format,
void *  data,
int  timeout,
int *  outstate,
const char *  language 
) [static, read]

Definition at line 3522 of file features.c.

References ast_channel::_state, ast_alloca, ast_autoservice_start(), ast_autoservice_stop(), ast_call(), ast_call_forward(), AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, ast_channel_connected_line_macro(), ast_channel_inherit_variables(), ast_channel_lock, 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_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_string_field_set, ast_strlen_zero(), ast_tvdiff_ms(), ast_tvnow(), ast_verb, ast_waitfor_n(), ast_write(), ast_channel::caller, 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().

03526 {
03527    int state = 0;
03528    int cause = 0;
03529    int to;
03530    int caller_hungup;
03531    int transferee_hungup;
03532    struct ast_channel *chan;
03533    struct ast_channel *monitor_chans[3];
03534    struct ast_channel *active_channel;
03535    int res;
03536    int ready = 0;
03537    struct timeval started;
03538    int x, len = 0;
03539    char *disconnect_code = NULL, *dialed_code = NULL;
03540    struct ast_frame *f;
03541    AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
03542 
03543    caller_hungup = ast_check_hangup(caller);
03544 
03545    if (!(chan = ast_request(type, format, requestor, data, &cause))) {
03546       ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
03547       switch (cause) {
03548       case AST_CAUSE_BUSY:
03549          state = AST_CONTROL_BUSY;
03550          break;
03551       case AST_CAUSE_CONGESTION:
03552          state = AST_CONTROL_CONGESTION;
03553          break;
03554       default:
03555          state = 0;
03556          break;
03557       }
03558       goto done;
03559    }
03560 
03561    ast_string_field_set(chan, language, language);
03562    ast_channel_inherit_variables(caller, chan);
03563    pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller_name);
03564 
03565    ast_channel_lock(chan);
03566    ast_connected_line_copy_from_caller(&chan->connected, &requestor->caller);
03567    ast_channel_unlock(chan);
03568 
03569    if (ast_call(chan, data, timeout)) {
03570       ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
03571       switch (chan->hangupcause) {
03572       case AST_CAUSE_BUSY:
03573          state = AST_CONTROL_BUSY;
03574          break;
03575       case AST_CAUSE_CONGESTION:
03576          state = AST_CONTROL_CONGESTION;
03577          break;
03578       default:
03579          state = 0;
03580          break;
03581       }
03582       goto done;
03583    }
03584 
03585    /* support dialing of the featuremap disconnect code while performing an attended tranfer */
03586    ast_rwlock_rdlock(&features_lock);
03587    for (x = 0; x < FEATURES_COUNT; x++) {
03588       if (strcasecmp(builtin_features[x].sname, "disconnect"))
03589          continue;
03590 
03591       disconnect_code = builtin_features[x].exten;
03592       len = strlen(disconnect_code) + 1;
03593       dialed_code = ast_alloca(len);
03594       memset(dialed_code, 0, len);
03595       break;
03596    }
03597    ast_rwlock_unlock(&features_lock);
03598    x = 0;
03599    started = ast_tvnow();
03600    to = timeout;
03601    AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
03602 
03603    ast_poll_channel_add(caller, chan);
03604 
03605    transferee_hungup = 0;
03606    while (!ast_check_hangup(transferee) && (chan->_state != AST_STATE_UP)) {
03607       int num_chans = 0;
03608 
03609       monitor_chans[num_chans++] = transferee;
03610       monitor_chans[num_chans++] = chan;
03611       if (!caller_hungup) {
03612          if (ast_check_hangup(caller)) {
03613             caller_hungup = 1;
03614 
03615 #if defined(ATXFER_NULL_TECH)
03616             /* Change caller's name to ensure that it will remain unique. */
03617             set_new_chan_name(caller);
03618 
03619             /*
03620              * Get rid of caller's physical technology so it is free for
03621              * other calls.
03622              */
03623             set_kill_chan_tech(caller);
03624 #endif   /* defined(ATXFER_NULL_TECH) */
03625          } else {
03626             /* caller is not hungup so monitor it. */
03627             monitor_chans[num_chans++] = caller;
03628          }
03629       }
03630 
03631       /* see if the timeout has been violated */
03632       if (ast_tvdiff_ms(ast_tvnow(), started) > timeout) {
03633          state = AST_CONTROL_UNHOLD;
03634          ast_log(LOG_NOTICE, "We exceeded our AT-timeout for %s\n", chan->name);
03635          break; /*doh! timeout*/
03636       }
03637 
03638       active_channel = ast_waitfor_n(monitor_chans, num_chans, &to);
03639       if (!active_channel)
03640          continue;
03641 
03642       f = NULL;
03643       if (transferee == active_channel) {
03644          struct ast_frame *dup_f;
03645 
03646          f = ast_read(transferee);
03647          if (f == NULL) { /*doh! where'd he go?*/
03648             transferee_hungup = 1;
03649             state = 0;
03650             break;
03651          }
03652          if (ast_is_deferrable_frame(f)) {
03653             dup_f = ast_frisolate(f);
03654             if (dup_f) {
03655                if (dup_f == f) {
03656                   f = NULL;
03657                }
03658                AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list);
03659             }
03660          }
03661       } else if (chan == active_channel) {
03662          if (!ast_strlen_zero(chan->call_forward)) {
03663             state = 0;
03664             ast_autoservice_start(transferee);
03665             chan = ast_call_forward(caller, chan, NULL, format, NULL, &state);
03666             ast_autoservice_stop(transferee);
03667             if (!chan) {
03668                break;
03669             }
03670             continue;
03671          }
03672          f = ast_read(chan);
03673          if (f == NULL) { /*doh! where'd he go?*/
03674             switch (chan->hangupcause) {
03675             case AST_CAUSE_BUSY:
03676                state = AST_CONTROL_BUSY;
03677                break;
03678             case AST_CAUSE_CONGESTION:
03679                state = AST_CONTROL_CONGESTION;
03680                break;
03681             default:
03682                state = 0;
03683                break;
03684             }
03685             break;
03686          }
03687 
03688          if (f->frametype == AST_FRAME_CONTROL) {
03689             if (f->subclass.integer == AST_CONTROL_RINGING) {
03690                ast_verb(3, "%s is ringing\n", chan->name);
03691                ast_indicate(caller, AST_CONTROL_RINGING);
03692             } else if (f->subclass.integer == AST_CONTROL_BUSY) {
03693                state = f->subclass.integer;
03694                ast_verb(3, "%s is busy\n", chan->name);
03695                ast_indicate(caller, AST_CONTROL_BUSY);
03696                ast_frfree(f);
03697                break;
03698             } else if (f->subclass.integer == AST_CONTROL_INCOMPLETE) {
03699                ast_verb(3, "%s dialed incomplete extension %s; ignoring\n", chan->name, chan->exten);
03700             } else if (f->subclass.integer == AST_CONTROL_CONGESTION) {
03701                state = f->subclass.integer;
03702                ast_verb(3, "%s is congested\n", chan->name);
03703                ast_indicate(caller, AST_CONTROL_CONGESTION);
03704                ast_frfree(f);
03705                break;
03706             } else if (f->subclass.integer == AST_CONTROL_ANSWER) {
03707                /* This is what we are hoping for */
03708                state = f->subclass.integer;
03709                ast_frfree(f);
03710                ready=1;
03711                break;
03712             } else if (f->subclass.integer == AST_CONTROL_CONNECTED_LINE) {
03713                if (caller_hungup) {
03714                   struct ast_party_connected_line connected;
03715 
03716                   /* Just save it for the transfer. */
03717                   ast_party_connected_line_set_init(&connected, &caller->connected);
03718                   res = ast_connected_line_parse_data(f->data.ptr, f->datalen,
03719                      &connected);
03720                   if (!res) {
03721                      ast_channel_set_connected_line(caller, &connected, NULL);
03722                   }
03723                   ast_party_connected_line_free(&connected);
03724                } else {
03725                   ast_autoservice_start(transferee);
03726                   if (ast_channel_connected_line_macro(chan, caller, f, 1, 1)) {
03727                      ast_indicate_data(caller, AST_CONTROL_CONNECTED_LINE,
03728                         f->data.ptr, f->datalen);
03729                   }
03730                   ast_autoservice_stop(transferee);
03731                }
03732             } else if (f->subclass.integer == AST_CONTROL_REDIRECTING) {
03733                if (!caller_hungup) {
03734                   ast_autoservice_start(transferee);
03735                   if (ast_channel_redirecting_macro(chan, caller, f, 1, 1)) {
03736                      ast_indicate_data(caller, AST_CONTROL_REDIRECTING,
03737                         f->data.ptr, f->datalen);
03738                   }
03739                   ast_autoservice_stop(transferee);
03740                }
03741             } else if (f->subclass.integer != -1
03742                && f->subclass.integer != AST_CONTROL_PROGRESS
03743                && f->subclass.integer != AST_CONTROL_PROCEEDING) {
03744                ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass.integer);
03745             }
03746             /* else who cares */
03747          } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
03748             ast_write(caller, f);
03749          }
03750       } else if (caller == active_channel) {
03751          f = ast_read(caller);
03752          if (f) {
03753             if (f->frametype == AST_FRAME_DTMF) {
03754                dialed_code[x++] = f->subclass.integer;
03755                dialed_code[x] = '\0';
03756                if (strlen(dialed_code) == len) {
03757                   x = 0;
03758                } else if (x && strncmp(dialed_code, disconnect_code, x)) {
03759                   x = 0;
03760                   dialed_code[x] = '\0';
03761                }
03762                if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
03763                   /* Caller Canceled the call */
03764                   state = AST_CONTROL_UNHOLD;
03765                   ast_frfree(f);
03766                   break;
03767                }
03768             } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
03769                ast_write(chan, f);
03770             }
03771          }
03772       }
03773       if (f)
03774          ast_frfree(f);
03775    } /* end while */
03776 
03777    ast_poll_channel_del(caller, chan);
03778 
03779    /*
03780     * We need to free all the deferred frames, but we only need to
03781     * queue the deferred frames if no hangup was received.
03782     */
03783    ast_channel_lock(transferee);
03784    transferee_hungup = (transferee_hungup || ast_check_hangup(transferee));
03785    while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) {
03786       if (!transferee_hungup) {
03787          ast_queue_frame_head(transferee, f);
03788       }
03789       ast_frfree(f);
03790    }
03791    ast_channel_unlock(transferee);
03792 
03793 done:
03794    ast_indicate(caller, -1);
03795    if (chan && (ready || chan->_state == AST_STATE_UP)) {
03796       state = AST_CONTROL_ANSWER;
03797    } else if (chan) {
03798       ast_hangup(chan);
03799       chan = NULL;
03800    }
03801 
03802    if (outstate)
03803       *outstate = state;
03804 
03805    return chan;
03806 }

static void features_shutdown ( void   )  [static]
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 7310 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.

Referenced by ast_pickup_call().

07311 {
07312    struct ast_channel *target = obj;/*!< Potential pickup target */
07313    struct ast_channel *chan = data;/*!< Channel wanting to pickup call */
07314 
07315    ast_channel_lock(target);
07316    if (chan != target && (chan->pickupgroup & target->callgroup)
07317       && ast_can_pickup(target)) {
07318       /* Return with the channel still locked on purpose */
07319       return CMP_MATCH | CMP_STOP;
07320    }
07321    ast_channel_unlock(target);
07322 
07323    return 0;
07324 }

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

find a call feature by name

Definition at line 3084 of file features.c.

References AST_RWLIST_TRAVERSE, and ast_call_feature::sname.

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

03085 {
03086    struct ast_call_feature *tmp;
03087 
03088    AST_RWLIST_TRAVERSE(&feature_list, tmp, feature_entry) {
03089       if (!strcasecmp(tmp->sname, name)) {
03090          break;
03091       }
03092    }
03093 
03094    return tmp;
03095 }

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

References AST_LIST_TRAVERSE, and feature_group::gname.

Referenced by feature_interpret_helper().

03123 {
03124    struct feature_group *fg = NULL;
03125 
03126    AST_LIST_TRAVERSE(&feature_groups, fg, entry) {
03127       if (!strcasecmp(fg->gname, name))
03128          break;
03129    }
03130 
03131    return fg;
03132 }

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

Find parkinglot by name.

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

04978 {
04979    struct ast_parkinglot *parkinglot;
04980 
04981    if (ast_strlen_zero(name)) {
04982       return NULL;
04983    }
04984 
04985    parkinglot = ao2_find(parkinglots, (void *) name, 0);
04986    if (parkinglot) {
04987       ast_debug(1, "Found Parking lot: %s\n", parkinglot->name);
04988    }
04989 
04990    return parkinglot;
04991 }

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

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

01079 {
01080    const char *name;
01081 
01082    /* The channel variable overrides everything */
01083    name = pbx_builtin_getvar_helper(chan, "PARKINGLOT");
01084    if (!name && !ast_strlen_zero(chan->parkinglot)) {
01085       /* Use the channel's parking lot. */
01086       name = chan->parkinglot;
01087    }
01088    return name;
01089 }

static int finishup ( struct ast_channel chan  )  [static]

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

01864 {
01865    ast_indicate(chan, AST_CONTROL_UNHOLD);
01866 
01867    return ast_autoservice_stop(chan);
01868 }

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

Definition at line 819 of file features.c.

References ast_debug, 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().

00820 {
00821    struct ast_exten *exten;
00822    struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
00823    const char *app_at_exten;
00824 
00825    ast_debug(4, "Checking if %s@%s is a parking exten\n", exten_str, context);
00826    exten = pbx_find_extension(chan, NULL, &q, context, exten_str, 1, NULL, NULL,
00827       E_MATCH);
00828    if (!exten) {
00829       return NULL;
00830    }
00831 
00832    app_at_exten = ast_get_extension_app(exten);
00833    if (!app_at_exten || strcasecmp(parkcall, app_at_exten)) {
00834       return NULL;
00835    }
00836 
00837    return exten;
00838 }

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 6753 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, 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.

06754 {
06755    int i;
06756    struct ast_call_feature *feature;
06757    struct ao2_iterator iter;
06758    struct ast_parkinglot *curlot;
06759 #define HFS_FORMAT "%-25s %-7s %-7s\n"
06760 
06761    switch (cmd) {
06762    
06763    case CLI_INIT:
06764       e->command = "features show";
06765       e->usage =
06766          "Usage: features show\n"
06767          "       Lists configured features\n";
06768       return NULL;
06769    case CLI_GENERATE:
06770       return NULL;
06771    }
06772 
06773    ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
06774    ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
06775 
06776    ast_cli(a->fd, HFS_FORMAT, "Pickup", "*8", ast_pickup_ext());          /* default hardcoded above, so we'll hardcode it here */
06777 
06778    ast_rwlock_rdlock(&features_lock);
06779    for (i = 0; i < FEATURES_COUNT; i++)
06780       ast_cli(a->fd, HFS_FORMAT, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
06781    ast_rwlock_unlock(&features_lock);
06782 
06783    ast_cli(a->fd, "\n");
06784    ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
06785    ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
06786    if (AST_RWLIST_EMPTY(&feature_list)) {
06787       ast_cli(a->fd, "(none)\n");
06788    } else {
06789       AST_RWLIST_RDLOCK(&feature_list);
06790       AST_RWLIST_TRAVERSE(&feature_list, feature, feature_entry) {
06791          ast_cli(a->fd, HFS_FORMAT, feature->sname, "no def", feature->exten);
06792       }
06793       AST_RWLIST_UNLOCK(&feature_list);
06794    }
06795 
06796    ast_cli(a->fd, "\nFeature Groups:\n");
06797    ast_cli(a->fd, "---------------\n");
06798    if (AST_RWLIST_EMPTY(&feature_groups)) {
06799       ast_cli(a->fd, "(none)\n");
06800    } else {
06801       struct feature_group *fg;
06802       struct feature_group_exten *fge;
06803 
06804       AST_RWLIST_RDLOCK(&feature_groups);
06805       AST_RWLIST_TRAVERSE(&feature_groups, fg, entry) {
06806          ast_cli(a->fd, "===> Group: %s\n", fg->gname);
06807          AST_LIST_TRAVERSE(&fg->features, fge, entry) {
06808             ast_cli(a->fd, "===> --> %s (%s)\n", fge->feature->sname, fge->exten);
06809          }
06810       }
06811       AST_RWLIST_UNLOCK(&feature_groups);
06812    }
06813 
06814    iter = ao2_iterator_init(parkinglots, 0);
06815    while ((curlot = ao2_iterator_next(&iter))) {
06816       ast_cli(a->fd, "\nCall parking (Parking lot: %s)\n", curlot->name);
06817       ast_cli(a->fd, "------------\n");
06818       ast_cli(a->fd,"%-22s:      %s\n", "Parking extension", curlot->cfg.parkext);
06819       ast_cli(a->fd,"%-22s:      %s\n", "Parking context", curlot->cfg.parking_con);
06820       ast_cli(a->fd,"%-22s:      %d-%d\n", "Parked call extensions",
06821          curlot->cfg.parking_start, curlot->cfg.parking_stop);
06822       ast_cli(a->fd,"%-22s:      %d ms\n", "Parkingtime", curlot->cfg.parkingtime);
06823       ast_cli(a->fd,"%-22s:      %s\n", "MusicOnHold class", curlot->cfg.mohclass);
06824       ast_cli(a->fd,"%-22s:      %s\n", "Enabled", AST_CLI_YESNO(!curlot->disabled));
06825       ast_cli(a->fd,"\n");
06826       ao2_ref(curlot, -1);
06827    }
06828    ao2_iterator_destroy(&iter);
06829 
06830    return CLI_SUCCESS;
06831 }

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

Definition at line 6859 of file features.c.

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

06860 {
06861    switch (cmd) { 
06862    case CLI_INIT:
06863       e->command = "features reload";
06864       e->usage =
06865          "Usage: features reload\n"
06866          "       Reloads configured call features from features.conf\n";
06867       return NULL;
06868    case CLI_GENERATE:
06869       return NULL;
06870    }
06871    ast_features_reload();
06872 
06873    return CLI_SUCCESS;
06874 }

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 7063 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_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.

07064 {
07065    struct parkeduser *cur;
07066    int numparked = 0;
07067    struct ao2_iterator iter;
07068    struct ast_parkinglot *curlot;
07069 
07070    switch (cmd) {
07071    case CLI_INIT:
07072       e->command = "parkedcalls show";
07073       e->usage =
07074          "Usage: parkedcalls show\n"
07075          "       List currently parked calls\n";
07076       return NULL;
07077    case CLI_GENERATE:
07078       return NULL;
07079    }
07080 
07081    if (a->argc > e->args)
07082       return CLI_SHOWUSAGE;
07083 
07084    ast_cli(a->fd, "%-10s %-25s (%-15s %-12s %4s) %s\n", "Num", "Channel",
07085       "Context", "Extension", "Pri", "Timeout");
07086 
07087    iter = ao2_iterator_init(parkinglots, 0);
07088    while ((curlot = ao2_iterator_next(&iter))) {
07089       int lotparked = 0;
07090 
07091       /* subtract ref for iterator and for configured parking lot */
07092       ast_cli(a->fd, "*** Parking lot: %s (%d)\n", curlot->name,
07093          ao2_ref(curlot, 0) - 2 - (curlot == default_parkinglot));
07094 
07095       AST_LIST_LOCK(&curlot->parkings);
07096       AST_LIST_TRAVERSE(&curlot->parkings, cur, list) {
07097          ast_cli(a->fd, "%-10.10s %-25s (%-15s %-12s %4d) %6lds\n",
07098             cur->parkingexten, cur->chan->name, cur->context, cur->exten,
07099             cur->priority,
07100             (long) (cur->start.tv_sec + (cur->parkingtime / 1000) - time(NULL)));
07101          ++lotparked;
07102       }
07103       AST_LIST_UNLOCK(&curlot->parkings);
07104       if (lotparked) {
07105          numparked += lotparked;
07106          ast_cli(a->fd, "   %d parked call%s in parking lot %s\n", lotparked,
07107             ESS(lotparked), curlot->name);
07108       }
07109 
07110       ao2_ref(curlot, -1);
07111    }
07112    ao2_iterator_destroy(&iter);
07113 
07114    ast_cli(a->fd, "---\n%d parked call%s in total.\n", numparked, ESS(numparked));
07115 
07116    return CLI_SUCCESS;
07117 }

static int load_config ( int  reload  )  [static]

Definition at line 6671 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_is_marked_cb(), parkinglot_markall_cb(), parkinglots, process_config(), and remove_dead_dialplan_useage().

Referenced by ast_features_init(), and ast_features_reload().

06672 {
06673    struct ast_flags config_flags = {
06674       reload && !force_reload_load ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06675    struct ast_config *cfg;
06676    struct parking_dp_map old_usage_map = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
06677    struct parking_dp_map new_usage_map = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
06678 
06679    /* We are reloading now and have already determined if we will force the reload. */
06680    force_reload_load = 0;
06681 
06682    if (!default_parkinglot) {
06683       /* Must create the default default parking lot */
06684       default_parkinglot = build_parkinglot(DEFAULT_PARKINGLOT, NULL);
06685       if (!default_parkinglot) {
06686          ast_log(LOG_ERROR, "Configuration of default default parking lot failed.\n");
06687          return -1;
06688       }
06689       ast_debug(1, "Configuration of default default parking lot done.\n");
06690    }
06691 
06692    cfg = ast_config_load2("features.conf", "features", config_flags);
06693    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06694       /* No sense in asking for reload trouble if nothing changed. */
06695       ast_debug(1, "features.conf did not change.\n");
06696       return 0;
06697    }
06698    if (cfg == CONFIG_STATUS_FILEMISSING
06699       || cfg == CONFIG_STATUS_FILEINVALID) {
06700       ast_log(LOG_WARNING, "Could not load features.conf\n");
06701       return 0;
06702    }
06703 
06704    /* Save current parking lot dialplan needs. */
06705    if (build_dialplan_useage_map(&old_usage_map, 0)) {
06706       destroy_dialplan_usage_map(&old_usage_map);
06707 
06708       /* Allow reloading later to see if conditions have improved. */
06709       force_reload_load = 1;
06710       return -1;
06711    }
06712 
06713    ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_markall_cb, NULL,
06714       "callback to mark all parking lots");
06715    process_config(cfg);
06716    ast_config_destroy(cfg);
06717    ao2_t_callback(parkinglots, OBJ_NODATA | OBJ_UNLINK, parkinglot_is_marked_cb, NULL,
06718       "callback to remove marked parking lots");
06719 
06720    /* Save updated parking lot dialplan needs. */
06721    if (build_dialplan_useage_map(&new_usage_map, 1)) {
06722       /*
06723        * Yuck, if this failure caused any parking lot dialplan items
06724        * to be lost, they will likely remain lost until Asterisk is
06725        * restarted.
06726        */
06727       destroy_dialplan_usage_map(&old_usage_map);
06728       destroy_dialplan_usage_map(&new_usage_map);
06729       return -1;
06730    }
06731 
06732    /* Remove no longer needed parking lot dialplan usage. */
06733    remove_dead_dialplan_useage(&old_usage_map, &new_usage_map);
06734 
06735    destroy_dialplan_usage_map(&old_usage_map);
06736    destroy_dialplan_usage_map(&new_usage_map);
06737 
06738    ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_activate_cb, NULL,
06739       "callback to activate all parking lots");
06740 
06741    return 0;
06742 }

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 4681 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_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, 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_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_dial_features::my_features, ast_parkinglot::name, parkeduser::options_specified, parking_con_dial, parkeduser::parkinglot, parkeduser::parkingnum, parkeduser::parkingtime, pbx_builtin_setvar_helper(), ast_dial_features::peer_features, 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().

04682 {
04683    struct ast_channel *chan = pu->chan;   /* shorthand */
04684    int tms;        /* timeout for this item */
04685    int x;          /* fd index in channel */
04686 
04687    tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
04688    if (tms > pu->parkingtime) {
04689       /*
04690        * Call has been parked too long.
04691        * Stop entertaining the caller.
04692        */
04693       switch (pu->hold_method) {
04694       case AST_CONTROL_HOLD:
04695          ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
04696          break;
04697       case AST_CONTROL_RINGING:
04698          ast_indicate(pu->chan, -1);
04699          break;
04700       default:
04701          break;
04702       }
04703       pu->hold_method = 0;
04704 
04705       /* Get chan, exten from derived kludge */
04706       if (pu->peername[0]) {
04707          char *peername;
04708          char *dash;
04709          char *peername_flat; /* using something like DAHDI/52 for an extension name is NOT a good idea */
04710          int i;
04711 
04712          peername = ast_strdupa(pu->peername);
04713          dash = strrchr(peername, '-');
04714          if (dash) {
04715             *dash = '\0';
04716          }
04717 
04718          peername_flat = ast_strdupa(peername);
04719          for (i = 0; peername_flat[i]; i++) {
04720             if (peername_flat[i] == '/') {
04721                peername_flat[i] = '_';
04722             }
04723          }
04724 
04725          if (!ast_context_find_or_create(NULL, NULL, parking_con_dial, registrar)) {
04726             ast_log(LOG_ERROR,
04727                "Parking dial context '%s' does not exist and unable to create\n",
04728                parking_con_dial);
04729          } else {
04730             char returnexten[AST_MAX_EXTENSION];
04731             struct ast_datastore *features_datastore;
04732             struct ast_dial_features *dialfeatures;
04733 
04734             if (!strncmp(peername, "Parked/", 7)) {
04735                peername += 7;
04736             }
04737 
04738             ast_channel_lock(chan);
04739             features_datastore = ast_channel_datastore_find(chan, &dial_features_info,
04740                NULL);
04741             if (features_datastore && (dialfeatures = features_datastore->data)) {
04742                char buf[MAX_DIAL_FEATURE_OPTIONS] = {0,};
04743 
04744                snprintf(returnexten, sizeof(returnexten), "%s,30,%s", peername,
04745                   callback_dialoptions(&dialfeatures->peer_features,
04746                      &dialfeatures->my_features, buf, sizeof(buf)));
04747             } else { /* Existing default */
04748                ast_log(LOG_NOTICE, "Dial features not found on %s, using default!\n",
04749                   chan->name);
04750                snprintf(returnexten, sizeof(returnexten), "%s,30,t", peername);
04751             }
04752             ast_channel_unlock(chan);
04753 
04754             if (ast_add_extension(parking_con_dial, 1, peername_flat, 1, NULL, NULL,
04755                "Dial", ast_strdup(returnexten), ast_free_ptr, registrar)) {
04756                ast_log(LOG_ERROR,
04757                   "Could not create parking return dial exten: %s@%s\n",
04758                   peername_flat, parking_con_dial);
04759             }
04760          }
04761          if (pu->options_specified) {
04762             /*
04763              * Park() was called with overriding return arguments, respect
04764              * those arguments.
04765              */
04766             set_c_e_p(chan, pu->context, pu->exten, pu->priority);
04767          } else if (comebacktoorigin) {
04768             set_c_e_p(chan, parking_con_dial, peername_flat, 1);
04769          } else {
04770             char parkingslot[AST_MAX_EXTENSION];
04771 
04772             snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
04773             pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parkingslot);
04774             set_c_e_p(chan, "parkedcallstimeout", peername_flat, 1);
04775          }
04776       } else {
04777          /*
04778           * They've been waiting too long, send them back to where they
04779           * came.  Theoretically they should have their original
04780           * extensions and such, but we copy to be on the safe side.
04781           */
04782          set_c_e_p(chan, pu->context, pu->exten, pu->priority);
04783       }
04784       post_manager_event("ParkedCallTimeOut", pu);
04785       ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallTimeOut", NULL);
04786 
04787       ast_verb(2, "Timeout for %s parked on %d (%s). Returning to %s,%s,%d\n",
04788          pu->chan->name, pu->parkingnum, pu->parkinglot->name, pu->chan->context,
04789          pu->chan->exten, pu->chan->priority);
04790 
04791       /* Start up the PBX, or hang them up */
04792       if (ast_pbx_start(chan))  {
04793          ast_log(LOG_WARNING,
04794             "Unable to restart the PBX for user on '%s', hanging them up...\n",
04795             pu->chan->name);
04796          ast_hangup(chan);
04797       }
04798 
04799       /* And take them out of the parking lot */
04800       return 1;
04801    }
04802 
04803    /* still within parking time, process descriptors */
04804    if (pfds) {
04805       for (x = 0; x < AST_MAX_FDS; x++) {
04806          struct ast_frame *f;
04807          int y;
04808 
04809          if (chan->fds[x] == -1) {
04810             continue;   /* nothing on this descriptor */
04811          }
04812 
04813          for (y = 0; y < nfds; y++) {
04814             if (pfds[y].fd == chan->fds[x]) {
04815                /* Found poll record! */
04816                break;
04817             }
04818          }
04819          if (y == nfds) {
04820             /* Not found */
04821             continue;
04822          }
04823 
04824          if (!(pfds[y].revents & (POLLIN | POLLERR | POLLPRI))) {
04825             /* Next x */
04826             continue;
04827          }
04828 
04829          if (pfds[y].revents & POLLPRI) {
04830             ast_set_flag(chan, AST_FLAG_EXCEPTION);
04831          } else {
04832             ast_clear_flag(chan, AST_FLAG_EXCEPTION);
04833          }
04834          chan->fdno = x;
04835 
04836          /* See if they need servicing */
04837          f = ast_read(pu->chan);
04838          /* Hangup? */
04839          if (!f || (f->frametype == AST_FRAME_CONTROL
04840             && f->subclass.integer == AST_CONTROL_HANGUP)) {
04841             if (f) {
04842                ast_frfree(f);
04843             }
04844             post_manager_event("ParkedCallGiveUp", pu);
04845             ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallGiveUp",
04846                NULL);
04847 
04848             /* There's a problem, hang them up */
04849             ast_verb(2, "%s got tired of being parked\n", chan->name);
04850             ast_hangup(chan);
04851 
04852             /* And take them out of the parking lot */
04853             return 1;
04854          } else {
04855             /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
04856             ast_frfree(f);
04857             if (pu->hold_method == AST_CONTROL_HOLD
04858                && pu->moh_trys < 3
04859                && !chan->generatordata) {
04860                ast_debug(1,
04861                   "MOH on parked call stopped by outside source.  Restarting on channel %s.\n",
04862                   chan->name);
04863                ast_indicate_data(chan, AST_CONTROL_HOLD,
04864                   S_OR(pu->parkinglot->cfg.mohclass, NULL),
04865                   (!ast_strlen_zero(pu->parkinglot->cfg.mohclass)
04866                      ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0));
04867                pu->moh_trys++;
04868             }
04869             break;
04870          }
04871       } /* End for */
04872    }
04873 
04874    /* mark fds for next round */
04875    for (x = 0; x < AST_MAX_FDS; x++) {
04876       if (chan->fds[x] > -1) {
04877          void *tmp = ast_realloc(*new_pfds,
04878             (*new_nfds + 1) * sizeof(struct pollfd));
04879 
04880          if (!tmp) {
04881             continue;
04882          }
04883          *new_pfds = tmp;
04884          (*new_pfds)[*new_nfds].fd = chan->fds[x];
04885          (*new_pfds)[*new_nfds].events = POLLIN | POLLERR | POLLPRI;
04886          (*new_pfds)[*new_nfds].revents = 0;
04887          (*new_nfds)++;
04888       }
04889    }
04890    /* Keep track of our shortest wait */
04891    if (tms < *ms || *ms < 0) {
04892       *ms = tms;
04893    }
04894 
04895    /* Stay in the parking lot. */
04896    return 0;
04897 }

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

04901 {
04902    struct parkeduser *pu;
04903    struct ast_context *con;
04904 
04905    /* Lock parkings list */
04906    AST_LIST_LOCK(&curlot->parkings);
04907    AST_LIST_TRAVERSE_SAFE_BEGIN(&curlot->parkings, pu, list) {
04908       if (pu->notquiteyet) { /* Pretend this one isn't here yet */
04909          continue;
04910       }
04911       if (manage_parked_call(pu, pfds, nfds, new_pfds, new_nfds, ms)) {
04912          /* Parking is complete for this call so remove it from the parking lot. */
04913          con = ast_context_find(pu->parkinglot->cfg.parking_con);
04914          if (con) {
04915             if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL, 0)) {
04916                ast_log(LOG_WARNING,
04917                   "Whoa, failed to remove the parking extension %s@%s!\n",
04918                   pu->parkingexten, pu->parkinglot->cfg.parking_con);
04919             }
04920             notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con,
04921                AST_DEVICE_NOT_INUSE);
04922          } else {
04923             ast_log(LOG_WARNING,
04924                "Whoa, parking lot '%s' context '%s' does not exist.\n",
04925                pu->parkinglot->name, pu->parkinglot->cfg.parking_con);
04926          }
04927          AST_LIST_REMOVE_CURRENT(list);
04928          parkinglot_unref(pu->parkinglot);
04929          ast_free(pu);
04930       }
04931    }
04932    AST_LIST_TRAVERSE_SAFE_END;
04933    AST_LIST_UNLOCK(&curlot->parkings);
04934 }

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

07200 {
07201    const char *channel = astman_get_header(m, "Channel");
07202    const char *channel2 = astman_get_header(m, "Channel2");
07203    const char *timeout = astman_get_header(m, "Timeout");
07204    const char *parkinglotname = astman_get_header(m, "Parkinglot");
07205    char buf[BUFSIZ];
07206    int res = 0;
07207    struct ast_channel *ch1, *ch2;
07208    struct ast_park_call_args args = {
07209          /*
07210           * Don't say anything to ch2 since AMI is a third party parking
07211           * a call and we will likely crash if we do.
07212           *
07213           * XXX When the AMI action was originally implemented, the
07214           * parking space was announced to ch2.  Unfortunately, grabbing
07215           * the ch2 lock and holding it while the announcement is played
07216           * was not really a good thing to do to begin with since it
07217           * could hold up the system.  Also holding the lock is no longer
07218           * possible with a masquerade.
07219           *
07220           * Restoring the announcement to ch2 is not easily doable for
07221           * the following reasons:
07222           *
07223           * 1) The AMI manager is not the thread processing ch2.
07224           *
07225           * 2) ch2 could be the same as ch1, bridged to ch1, or some
07226           * random uninvolved channel.
07227           */
07228          .flags = AST_PARK_OPT_SILENCE,
07229       };
07230 
07231    if (ast_strlen_zero(channel)) {
07232       astman_send_error(s, m, "Channel not specified");
07233       return 0;
07234    }
07235 
07236    if (ast_strlen_zero(channel2)) {
07237       astman_send_error(s, m, "Channel2 not specified");
07238       return 0;
07239    }
07240 
07241    if (!ast_strlen_zero(timeout)) {
07242       if (sscanf(timeout, "%30d", &args.timeout) != 1) {
07243          astman_send_error(s, m, "Invalid timeout value.");
07244          return 0;
07245       }
07246    }
07247 
07248    if (!(ch1 = ast_channel_get_by_name(channel))) {
07249       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
07250       astman_send_error(s, m, buf);
07251       return 0;
07252    }
07253 
07254    if (!(ch2 = ast_channel_get_by_name(channel2))) {
07255       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
07256       astman_send_error(s, m, buf);
07257       ast_channel_unref(ch1);
07258       return 0;
07259    }
07260 
07261    if (!ast_strlen_zero(parkinglotname)) {
07262       args.parkinglot = find_parkinglot(parkinglotname);
07263    }
07264 
07265    res = masq_park_call(ch1, ch2, &args);
07266    if (!res) {
07267       ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
07268       astman_send_ack(s, m, "Park successful");
07269    } else {
07270       astman_send_error(s, m, "Park failure");
07271    }
07272 
07273    if (args.parkinglot) {
07274       parkinglot_unref(args.parkinglot);
07275    }
07276    ch1 = ast_channel_unref(ch1);
07277    ch2 = ast_channel_unref(ch2);
07278 
07279    return 0;
07280 }

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

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, 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().

07134 {
07135    struct parkeduser *cur;
07136    const char *id = astman_get_header(m, "ActionID");
07137    char idText[256] = "";
07138    struct ao2_iterator iter;
07139    struct ast_parkinglot *curlot;
07140    int numparked = 0;
07141 
07142    if (!ast_strlen_zero(id))
07143       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07144 
07145    astman_send_ack(s, m, "Parked calls will follow");
07146 
07147    iter = ao2_iterator_init(parkinglots, 0);
07148    while ((curlot = ao2_iterator_next(&iter))) {
07149       AST_LIST_LOCK(&curlot->parkings);
07150       AST_LIST_TRAVERSE(&curlot->parkings, cur, list) {
07151          astman_append(s, "Event: ParkedCall\r\n"
07152             "Parkinglot: %s\r\n"
07153             "Exten: %d\r\n"
07154             "Channel: %s\r\n"
07155             "From: %s\r\n"
07156             "Timeout: %ld\r\n"
07157             "CallerIDNum: %s\r\n"
07158             "CallerIDName: %s\r\n"
07159             "ConnectedLineNum: %s\r\n"
07160             "ConnectedLineName: %s\r\n"
07161             "%s"
07162             "\r\n",
07163             curlot->name,
07164             cur->parkingnum, cur->chan->name, cur->peername,
07165             (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
07166             S_COR(cur->chan->caller.id.number.valid, cur->chan->caller.id.number.str, ""),   /* XXX in other places it is <unknown> */
07167             S_COR(cur->chan->caller.id.name.valid, cur->chan->caller.id.name.str, ""),
07168             S_COR(cur->chan->connected.id.number.valid, cur->chan->connected.id.number.str, ""),   /* XXX in other places it is <unknown> */
07169             S_COR(cur->chan->connected.id.name.valid, cur->chan->connected.id.name.str, ""),
07170             idText);
07171          ++numparked;
07172       }
07173       AST_LIST_UNLOCK(&curlot->parkings);
07174       ao2_ref(curlot, -1);
07175    }
07176    ao2_iterator_destroy(&iter);
07177 
07178    astman_append(s,
07179       "Event: ParkedCallsComplete\r\n"
07180       "Total: %d\r\n"
07181       "%s"
07182       "\r\n",
07183       numparked, idText);
07184 
07185    return RESULT_SUCCESS;
07186 }

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

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

01720 {
01721    struct ast_channel *chan;
01722 
01723    /* Make a new, channel that we'll use to masquerade in the real one */
01724    chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten,
01725       rchan->context, rchan->linkedid, rchan->amaflags, "Parked/%s", rchan->name);
01726    if (!chan) {
01727       ast_log(LOG_WARNING, "Unable to create parked channel\n");
01728       if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
01729          if (peer == rchan) {
01730             /* Only have one channel to worry about. */
01731             ast_stream_and_wait(peer, "pbx-parkingfailed", "");
01732          } else if (peer) {
01733             /* Have two different channels to worry about. */
01734             play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
01735          }
01736       }
01737       return -1;
01738    }
01739 
01740    args->pu = park_space_reserve(rchan, peer, args);
01741    if (!args->pu) {
01742       ast_hangup(chan);
01743       if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
01744          if (peer == rchan) {
01745             /* Only have one channel to worry about. */
01746             ast_stream_and_wait(peer, "pbx-parkingfailed", "");
01747          } else if (peer) {
01748             /* Have two different channels to worry about. */
01749             play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
01750          }
01751       }
01752       return -1;
01753    }
01754 
01755    /* Make formats okay */
01756    chan->readformat = rchan->readformat;
01757    chan->writeformat = rchan->writeformat;
01758 
01759    if (ast_channel_masquerade(chan, rchan)) {
01760       park_space_abort(args->pu);
01761       args->pu = NULL;
01762       ast_hangup(chan);
01763       if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
01764          if (peer == rchan) {
01765             /* Only have one channel to worry about. */
01766             ast_stream_and_wait(peer, "pbx-parkingfailed", "");
01767          } else if (peer) {
01768             /* Have two different channels to worry about. */
01769             play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
01770          }
01771       }
01772       return -1;
01773    }
01774 
01775    /* Setup the extensions and such */
01776    set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
01777 
01778    /* Setup the macro extension and such */
01779    ast_copy_string(chan->macrocontext,rchan->macrocontext,sizeof(chan->macrocontext));
01780    ast_copy_string(chan->macroexten,rchan->macroexten,sizeof(chan->macroexten));
01781    chan->macropriority = rchan->macropriority;
01782 
01783    /* Manually do the masquerade to make sure it is complete. */
01784    ast_do_masquerade(chan);
01785 
01786    if (peer == rchan) {
01787       peer = chan;
01788    }
01789 
01790    /* parking space reserved, return code check unnecessary */
01791    park_call_full(chan, peer, args);
01792 
01793    return 0;
01794 }

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

metermaids callback from devicestate.c

Definition at line 1101 of file features.c.

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

Referenced by ast_features_init().

01102 {
01103    char *context;
01104    char *exten;
01105 
01106    context = ast_strdupa(data);
01107 
01108    exten = strsep(&context, "@");
01109    if (!context)
01110       return AST_DEVICE_INVALID;
01111    
01112    ast_debug(4, "Checking state of exten %s in context %s\n", exten, context);
01113 
01114    if (!ast_exists_extension(NULL, context, exten, 1, NULL))
01115       return AST_DEVICE_NOT_INUSE;
01116 
01117    return AST_DEVICE_INUSE;
01118 }

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

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

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

01093 {
01094    ast_debug(4, "Notification of state change to metermaids %s@%s\n to state '%s'", 
01095       exten, context, ast_devstate2str(state));
01096 
01097    ast_devstate_changed(state, AST_DEVSTATE_CACHABLE, "park:%s@%s", exten, context);
01098 }

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

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

Referenced by parkinglot_activate().

05445 {
05446    int numext;
05447    char device[AST_MAX_EXTENSION];
05448    char exten[10];
05449 
05450    for (numext = start; numext <= stop; numext++) {
05451       snprintf(exten, sizeof(exten), "%d", numext);
05452       snprintf(device, sizeof(device), "park:%s@%s", exten, context);
05453       ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
05454    }
05455 }

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

Park a call.

Definition at line 5023 of file features.c.

References ast_channel::_state, ast_answer(), ast_app_parse_options(), 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(), pbx_builtin_getvar_helper(), 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, S_OR, ast_park_call_args::timeout, and park_app_args::timeout.

Referenced by ast_features_init().

05024 {
05025    struct ast_park_call_args args = { 0, };
05026    struct ast_flags flags = { 0 };
05027    char orig_exten[AST_MAX_EXTENSION];
05028    int orig_priority;
05029    int res;
05030    const char *pl_name;
05031    char *parse;
05032    struct park_app_args app_args;
05033 
05034    /*
05035     * Cache the original channel name because we are going to
05036     * masquerade the channel.  Prefer the BLINDTRANSFER channel
05037     * name over this channel name.  BLINDTRANSFER could be set if
05038     * the parking access extension did not get detected and we are
05039     * executing the Park application from the dialplan.
05040     *
05041     * The orig_chan_name is used to return the call to the
05042     * originator on parking timeout.
05043     */
05044    args.orig_chan_name = ast_strdupa(S_OR(
05045       pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"), chan->name));
05046 
05047    /* Answer if call is not up */
05048    if (chan->_state != AST_STATE_UP) {
05049       if (ast_answer(chan)) {
05050          return -1;
05051       }
05052 
05053       /* Sleep to allow VoIP streams to settle down */
05054       if (ast_safe_sleep(chan, 1000)) {
05055          return -1;
05056       }
05057    }
05058 
05059    /* Process the dialplan application options. */
05060    parse = ast_strdupa(data);
05061    AST_STANDARD_APP_ARGS(app_args, parse);
05062 
05063    if (!ast_strlen_zero(app_args.timeout)) {
05064       if (sscanf(app_args.timeout, "%30d", &args.timeout) != 1) {
05065          ast_log(LOG_WARNING, "Invalid timeout '%s' provided\n", app_args.timeout);
05066          args.timeout = 0;
05067       }
05068    }
05069    if (!ast_strlen_zero(app_args.return_con)) {
05070       args.return_con = app_args.return_con;
05071    }
05072    if (!ast_strlen_zero(app_args.return_ext)) {
05073       args.return_ext = app_args.return_ext;
05074    }
05075    if (!ast_strlen_zero(app_args.return_pri)) {
05076       if (sscanf(app_args.return_pri, "%30d", &args.return_pri) != 1) {
05077          ast_log(LOG_WARNING, "Invalid priority '%s' specified\n", app_args.return_pri);
05078          args.return_pri = 0;
05079       }
05080    }
05081 
05082    ast_app_parse_options(park_call_options, &flags, NULL, app_args.options);
05083    args.flags = flags.flags;
05084 
05085    /*
05086     * Setup the exten/priority to be s/1 since we don't know where
05087     * this call should return.
05088     */
05089    ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten));
05090    orig_priority = chan->priority;
05091    strcpy(chan->exten, "s");
05092    chan->priority = 1;
05093 
05094    /* Park the call */
05095    if (!ast_strlen_zero(app_args.pl_name)) {
05096       pl_name = app_args.pl_name;
05097    } else {
05098       pl_name = findparkinglotname(chan);
05099    }
05100    if (ast_strlen_zero(pl_name)) {
05101       /* Parking lot is not specified, so use the default parking lot. */
05102       args.parkinglot = parkinglot_addref(default_parkinglot);
05103    } else {
05104       args.parkinglot = find_parkinglot(pl_name);
05105       if (!args.parkinglot && parkeddynamic) {
05106          args.parkinglot = create_dynamic_parkinglot(pl_name, chan);
05107       }
05108    }
05109    if (args.parkinglot) {
05110       res = masq_park_call(chan, chan, &args);
05111       parkinglot_unref(args.parkinglot);
05112    } else {
05113       /* Parking failed because the parking lot does not exist. */
05114       if (!ast_test_flag(&args, AST_PARK_OPT_SILENCE)) {
05115          ast_stream_and_wait(chan, "pbx-parkingfailed", "");
05116       }
05117       res = -1;
05118    }
05119    if (res) {
05120       /* Park failed, try to continue in the dialplan. */
05121       ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
05122       chan->priority = orig_priority;
05123       res = 0;
05124    } else {
05125       /* Park succeeded. */
05126       res = -1;
05127    }
05128 
05129    return res;
05130 }

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

< Channel name that is parking the call.

Definition at line 1466 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_lock, AST_CHANNEL_NAME, ast_channel_unlock, ast_channel_unref, ast_clear_flag, AST_CONTROL_HOLD, AST_CONTROL_RINGING, ast_copy_string(), ast_debug, 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().

01467 {
01468    struct parkeduser *pu = args->pu;
01469    const char *event_from;    /*!< Channel name that is parking the call. */
01470    char app_data[AST_MAX_EXTENSION + AST_MAX_CONTEXT];
01471 
01472    if (pu == NULL) {
01473       args->pu = pu = park_space_reserve(chan, peer, args);
01474       if (pu == NULL) {
01475          return -1;
01476       }
01477    }
01478 
01479    chan->appl = "Parked Call";
01480    chan->data = NULL;
01481 
01482    pu->chan = chan;
01483 
01484    /* Put the parked channel on hold if we have two different channels */
01485    if (chan != peer) {
01486       if (ast_test_flag(args, AST_PARK_OPT_RINGING)) {
01487          pu->hold_method = AST_CONTROL_RINGING;
01488          ast_indicate(chan, AST_CONTROL_RINGING);
01489       } else {
01490          pu->hold_method = AST_CONTROL_HOLD;
01491          ast_indicate_data(chan, AST_CONTROL_HOLD,
01492             S_OR(pu->parkinglot->cfg.mohclass, NULL),
01493             !ast_strlen_zero(pu->parkinglot->cfg.mohclass) ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0);
01494       }
01495    }
01496    
01497    pu->start = ast_tvnow();
01498    pu->parkingtime = (args->timeout > 0) ? args->timeout : pu->parkinglot->cfg.parkingtime;
01499    if (args->extout)
01500       *(args->extout) = pu->parkingnum;
01501 
01502    if (peer) {
01503       event_from = S_OR(args->orig_chan_name, peer->name);
01504 
01505       /*
01506        * This is so ugly that it hurts, but implementing
01507        * get_base_channel() on local channels could have ugly side
01508        * effects.  We could have
01509        * transferer<->local,1<->local,2<->parking and we need the
01510        * callback name to be that of transferer.  Since local,1/2 have
01511        * the same name we can be tricky and just grab the bridged
01512        * channel from the other side of the local.
01513        */
01514       if (!strcasecmp(peer->tech->type, "Local")) {
01515          struct ast_channel *tmpchan, *base_peer;
01516          char other_side[AST_CHANNEL_NAME];
01517          char *c;
01518 
01519          ast_copy_string(other_side, event_from, sizeof(other_side));
01520          if ((c = strrchr(other_side, ';'))) {
01521             *++c = '1';
01522          }
01523          if ((tmpchan = ast_channel_get_by_name(other_side))) {
01524             ast_channel_lock(tmpchan);
01525             if ((base_peer = ast_bridged_channel(tmpchan))) {
01526                ast_copy_string(pu->peername, base_peer->name, sizeof(pu->peername));
01527             }
01528             ast_channel_unlock(tmpchan);
01529             tmpchan = ast_channel_unref(tmpchan);
01530          }
01531       } else {
01532          ast_copy_string(pu->peername, event_from, sizeof(pu->peername));
01533       }
01534    } else {
01535       event_from = S_OR(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"), chan->name);
01536    }
01537 
01538    /*
01539     * Remember what had been dialed, so that if the parking
01540     * expires, we try to come back to the same place
01541     */
01542    pu->options_specified = (!ast_strlen_zero(args->return_con) || !ast_strlen_zero(args->return_ext) || args->return_pri);
01543 
01544    /*
01545     * If extension has options specified, they override all other
01546     * possibilities such as the returntoorigin flag and transferred
01547     * context.  Information on extension options is lost here, so
01548     * we set a flag
01549     */
01550    ast_copy_string(pu->context, 
01551       S_OR(args->return_con, S_OR(chan->macrocontext, chan->context)), 
01552       sizeof(pu->context));
01553    ast_copy_string(pu->exten, 
01554       S_OR(args->return_ext, S_OR(chan->macroexten, chan->exten)), 
01555       sizeof(pu->exten));
01556    pu->priority = args->return_pri ? args->return_pri : 
01557       (chan->macropriority ? chan->macropriority : chan->priority);
01558 
01559    /*
01560     * If parking a channel directly, don't quite yet get parking
01561     * running on it.  All parking lot entries are put into the
01562     * parking lot with notquiteyet on.
01563     */
01564    if (peer != chan) {
01565       pu->notquiteyet = 0;
01566    }
01567 
01568    /* Wake up the (presumably select()ing) thread */
01569    pthread_kill(parking_thread, SIGURG);
01570    ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %d seconds\n",
01571       chan->name, pu->parkingnum, pu->parkinglot->name,
01572       pu->context, pu->exten, pu->priority, (pu->parkingtime / 1000));
01573 
01574    ast_cel_report_event(chan, AST_CEL_PARK_START, NULL, pu->parkinglot->name, peer);
01575 
01576    ast_manager_event(chan, EVENT_FLAG_CALL, "ParkedCall",
01577       "Exten: %s\r\n"
01578       "Channel: %s\r\n"
01579       "Parkinglot: %s\r\n"
01580       "From: %s\r\n"
01581       "Timeout: %ld\r\n"
01582       "CallerIDNum: %s\r\n"
01583       "CallerIDName: %s\r\n"
01584       "ConnectedLineNum: %s\r\n"
01585       "ConnectedLineName: %s\r\n"
01586       "Uniqueid: %s\r\n",
01587       pu->parkingexten, chan->name, pu->parkinglot->name, event_from,
01588       (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
01589       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, "<unknown>"),
01590       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, "<unknown>"),
01591       S_COR(chan->connected.id.number.valid, chan->connected.id.number.str, "<unknown>"),
01592       S_COR(chan->connected.id.name.valid, chan->connected.id.name.str, "<unknown>"),
01593       chan->uniqueid
01594       );
01595    ast_debug(4, "peer->name: %s\n", peer ? peer->name : "-No peer-");
01596    ast_debug(4, "args->orig_chan_name: %s\n", args->orig_chan_name ? args->orig_chan_name : "-none-");
01597    ast_debug(4, "pu->peername: %s\n", pu->peername);
01598    ast_debug(4, "AMI ParkedCall Channel: %s\n", chan->name);
01599    ast_debug(4, "AMI ParkedCall From: %s\n", event_from);
01600 
01601    if (peer && adsipark && ast_adsi_available(peer)) {
01602       adsi_announce_park(peer, pu->parkingexten);  /* Only supports parking numbers */
01603       ast_adsi_unload_session(peer);
01604    }
01605 
01606    snprintf(app_data, sizeof(app_data), "%s,%s", pu->parkingexten,
01607       pu->parkinglot->name);
01608    if (ast_add_extension(pu->parkinglot->cfg.parking_con, 1, pu->parkingexten, 1,
01609       NULL, NULL, parkedcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
01610       ast_log(LOG_ERROR, "Could not create parked call exten: %s@%s\n",
01611          pu->parkingexten, pu->parkinglot->cfg.parking_con);
01612    } else {
01613       notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con, AST_DEVICE_INUSE);
01614    }
01615 
01616    AST_LIST_UNLOCK(&pu->parkinglot->parkings);
01617 
01618    /* Only say number if it's a number and the channel hasn't been masqueraded away */
01619    if (peer && !ast_test_flag(args, AST_PARK_OPT_SILENCE)
01620       && (ast_strlen_zero(args->orig_chan_name) || !strcasecmp(peer->name, args->orig_chan_name))) {
01621       /*
01622        * If a channel is masqueraded into peer while playing back the
01623        * parking space number do not continue playing it back.  This
01624        * is the case if an attended transfer occurs.
01625        */
01626       ast_set_flag(peer, AST_FLAG_MASQ_NOSTREAM);
01627       /* Tell the peer channel the number of the parking space */
01628       ast_say_digits(peer, pu->parkingnum, "", peer->language);
01629       ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM);
01630    }
01631    if (peer == chan) { /* pu->notquiteyet = 1 */
01632       /* Wake up parking thread if we're really done */
01633       if (ast_test_flag(args, AST_PARK_OPT_RINGING)) {
01634          pu->hold_method = AST_CONTROL_RINGING;
01635          ast_indicate(chan, AST_CONTROL_RINGING);
01636       } else {
01637          pu->hold_method = AST_CONTROL_HOLD;
01638          ast_indicate_data(chan, AST_CONTROL_HOLD,
01639             S_OR(pu->parkinglot->cfg.mohclass, NULL),
01640             !ast_strlen_zero(pu->parkinglot->cfg.mohclass) ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0);
01641       }
01642       pu->notquiteyet = 0;
01643       pthread_kill(parking_thread, SIGURG);
01644    }
01645    return 0;
01646 }

static void park_space_abort ( struct parkeduser pu  )  [static]

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

01269 {
01270    struct ast_parkinglot *parkinglot;
01271 
01272    parkinglot = pu->parkinglot;
01273 
01274    /* Put back the parking space just allocated. */
01275    --parkinglot->next_parking_space;
01276 
01277    AST_LIST_REMOVE(&parkinglot->parkings, pu, list);
01278 
01279    AST_LIST_UNLOCK(&parkinglot->parkings);
01280    parkinglot_unref(parkinglot);
01281    ast_free(pu);
01282 }

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

References ast_calloc, 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().

01296 {
01297    struct parkeduser *pu;
01298    int i;
01299    int parking_space = -1;
01300    const char *parkinglotname;
01301    const char *parkingexten;
01302    struct parkeduser *cur;
01303    struct ast_parkinglot *parkinglot = NULL;
01304 
01305    if (args->parkinglot) {
01306       parkinglot = parkinglot_addref(args->parkinglot);
01307       parkinglotname = parkinglot->name;
01308    } else {
01309       if (parker) {
01310          parkinglotname = findparkinglotname(parker);
01311       } else { /* parker was NULL, check park_me (ParkAndAnnounce / res_agi) */
01312          parkinglotname = findparkinglotname(park_me);
01313       }
01314       if (!ast_strlen_zero(parkinglotname)) {
01315          parkinglot = find_parkinglot(parkinglotname);
01316       } else {
01317          /* Parking lot is not specified, so use the default parking lot. */
01318          ast_debug(4, "This could be an indication channel driver needs updating, using default lot.\n");
01319          parkinglot = parkinglot_addref(default_parkinglot);
01320       }
01321    }
01322 
01323    /* Dynamically create parkinglot */
01324    if (!parkinglot && parkeddynamic && !ast_strlen_zero(parkinglotname)) {
01325       parkinglot = create_dynamic_parkinglot(parkinglotname, park_me);
01326    }
01327 
01328    if (!parkinglot) {
01329       ast_log(LOG_WARNING, "Parking lot not available to park %s.\n", park_me->name);
01330       return NULL;
01331    }
01332 
01333    ast_debug(1, "Parking lot: %s\n", parkinglot->name);
01334    if (parkinglot->disabled || parkinglot->cfg.is_invalid) {
01335       ast_log(LOG_WARNING, "Parking lot %s is not in a useable state.\n",
01336          parkinglot->name);
01337       parkinglot_unref(parkinglot);
01338       return NULL;
01339    }
01340 
01341    /* Allocate memory for parking data */
01342    if (!(pu = ast_calloc(1, sizeof(*pu)))) {
01343       parkinglot_unref(parkinglot);
01344       return NULL;
01345    }
01346 
01347    /* Lock parking list */
01348    AST_LIST_LOCK(&parkinglot->parkings);
01349 
01350    /* Check for channel variable PARKINGEXTEN */
01351    parkingexten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(park_me, "PARKINGEXTEN"), ""));
01352    if (!ast_strlen_zero(parkingexten)) {
01353       /*!
01354        * \note The API forces us to specify a numeric parking slot, even
01355        * though the architecture would tend to support non-numeric extensions
01356        * (as are possible with SIP, for example).  Hence, we enforce that
01357        * limitation here.  If extout was not numeric, we could permit
01358        * arbitrary non-numeric extensions.
01359        */
01360       if (sscanf(parkingexten, "%30d", &parking_space) != 1 || parking_space <= 0) {
01361          ast_log(LOG_WARNING, "PARKINGEXTEN='%s' is not a valid parking space.\n",
01362             parkingexten);
01363          AST_LIST_UNLOCK(&parkinglot->parkings);
01364          parkinglot_unref(parkinglot);
01365          ast_free(pu);
01366          return NULL;
01367       }
01368 
01369       if (parking_space < parkinglot->cfg.parking_start
01370          || parkinglot->cfg.parking_stop < parking_space) {
01371          /*
01372           * Cannot allow park because parking lots are not setup for
01373           * spaces outside of the lot.  (Things like dialplan hints don't
01374           * exist for outside lot space.)
01375           */
01376          ast_log(LOG_WARNING, "PARKINGEXTEN=%d is not in %s (%d-%d).\n",
01377             parking_space, parkinglot->name, parkinglot->cfg.parking_start,
01378             parkinglot->cfg.parking_stop);
01379          AST_LIST_UNLOCK(&parkinglot->parkings);
01380          parkinglot_unref(parkinglot);
01381          ast_free(pu);
01382          return NULL;
01383       }
01384 
01385       /* Check if requested parking space is in use. */
01386       AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
01387          if (cur->parkingnum == parking_space) {
01388             ast_log(LOG_WARNING, "PARKINGEXTEN=%d is already in use in %s\n",
01389                parking_space, parkinglot->name);
01390             AST_LIST_UNLOCK(&parkinglot->parkings);
01391             parkinglot_unref(parkinglot);
01392             ast_free(pu);
01393             return NULL;
01394          }
01395       }
01396    } else {
01397       /* PARKINGEXTEN is empty, so find a usable extension in the lot to park the call */
01398       int start; /* The first slot we look in the parkinglot. It can be randomized. */
01399       int start_checked = 0; /* flag raised once the first slot is checked */
01400 
01401       /* If using randomize mode, set start to random position on parking range */
01402       if (ast_test_flag(args, AST_PARK_OPT_RANDOMIZE)) {
01403          start = ast_random() % (parkinglot->cfg.parking_stop - parkinglot->cfg.parking_start + 1);
01404          start += parkinglot->cfg.parking_start;
01405       } else if (parkinglot->cfg.parkfindnext
01406          && parkinglot->cfg.parking_start <= parkinglot->next_parking_space
01407          && parkinglot->next_parking_space <= parkinglot->cfg.parking_stop) {
01408          /* Start looking with the next parking space in the lot. */
01409          start = parkinglot->next_parking_space;
01410       } else {
01411          /* Otherwise, just set it to the start position. */
01412          start = parkinglot->cfg.parking_start;
01413       }
01414 
01415       /* free parking extension linear search: O(n^2) */
01416       for (i = start; ; i++) {
01417          /* If we are past the end, wrap around to the first parking slot*/
01418          if (i == parkinglot->cfg.parking_stop + 1) {
01419             i = parkinglot->cfg.parking_start;
01420          }
01421 
01422          if (i == start) {
01423             /* At this point, if start_checked, we've exhausted all the possible slots. */
01424             if (start_checked) {
01425                break;
01426             } else {
01427                start_checked = 1;
01428             }
01429          }
01430 
01431          /* Search the list of parked calls already in use for i. If we find it, it's in use. */
01432          AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
01433             if (cur->parkingnum == i) {
01434                break;
01435             }
01436          }
01437          if (!cur) {
01438             /* We found a parking space. */
01439             parking_space = i;
01440             break;
01441          }
01442       }
01443       if (parking_space == -1) {
01444          /* We did not find a parking space.  Lot is full. */
01445          ast_log(LOG_WARNING, "No more parking spaces in %s\n", parkinglot->name);
01446          AST_LIST_UNLOCK(&parkinglot->parkings);
01447          parkinglot_unref(parkinglot);
01448          ast_free(pu);
01449          return NULL;
01450       }
01451    }
01452 
01453    /* Prepare for next parking space search. */
01454    parkinglot->next_parking_space = parking_space + 1;
01455 
01456    snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space);
01457    pu->notquiteyet = 1;
01458    pu->parkingnum = parking_space;
01459    pu->parkinglot = parkinglot;
01460    AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list);
01461 
01462    return pu;
01463 }

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 5133 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_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, find_parkinglot(), findparkinglotname(), parkeduser::hold_method, ast_party_connected_line::id, ast_party_caller::id, LOG_WARNING, ast_dial_features::my_features, 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().

05134 {
05135    int res;
05136    struct ast_channel *peer = NULL;
05137    struct parkeduser *pu;
05138    struct ast_context *con;
05139    char *parse;
05140    const char *pl_name;
05141    int park = 0;
05142    struct ast_bridge_config config;
05143    struct ast_parkinglot *parkinglot;
05144    AST_DECLARE_APP_ARGS(app_args,
05145       AST_APP_ARG(pl_space);  /*!< Parking lot space to retrieve if present. */
05146       AST_APP_ARG(pl_name);   /*!< Parking lot name to use if present. */
05147       AST_APP_ARG(dummy);     /*!< Place to put any remaining args string. */
05148    );
05149 
05150    parse = ast_strdupa(data);
05151    AST_STANDARD_APP_ARGS(app_args, parse);
05152 
05153    if (!ast_strlen_zero(app_args.pl_space)) {
05154       if (sscanf(app_args.pl_space, "%30u", &park) != 1) {
05155          ast_log(LOG_WARNING, "Specified parking extension not a number: %s\n",
05156             app_args.pl_space);
05157          park = -1;
05158       }
05159    }
05160 
05161    if (!ast_strlen_zero(app_args.pl_name)) {
05162       pl_name = app_args.pl_name;
05163    } else {
05164       pl_name = findparkinglotname(chan);
05165    }
05166    if (ast_strlen_zero(pl_name)) {
05167       /* Parking lot is not specified, so use the default parking lot. */
05168       parkinglot = parkinglot_addref(default_parkinglot);
05169    } else {
05170       parkinglot = find_parkinglot(pl_name);
05171       if (!parkinglot) {
05172          /* It helps to answer the channel if not already up. :) */
05173          if (chan->_state != AST_STATE_UP) {
05174             ast_answer(chan);
05175          }
05176          if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) {
05177             ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n",
05178                "pbx-invalidpark", chan->name);
05179          }
05180          ast_log(LOG_WARNING,
05181             "Channel %s tried to retrieve parked call from unknown parking lot '%s'\n",
05182             chan->name, pl_name);
05183          return -1;
05184       }
05185    }
05186 
05187    AST_LIST_LOCK(&parkinglot->parkings);
05188    AST_LIST_TRAVERSE_SAFE_BEGIN(&parkinglot->parkings, pu, list) {
05189       if ((ast_strlen_zero(app_args.pl_space) || pu->parkingnum == park)
05190          && !pu->notquiteyet && !pu->chan->pbx) {
05191          /* The parking space has a call and can be picked up now. */
05192          AST_LIST_REMOVE_CURRENT(list);
05193          break;
05194       }
05195    }
05196    AST_LIST_TRAVERSE_SAFE_END;
05197    if (pu) {
05198       /* Found a parked call to pickup. */
05199       peer = pu->chan;
05200       con = ast_context_find(parkinglot->cfg.parking_con);
05201       if (con) {
05202          if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL, 0)) {
05203             ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
05204          } else {
05205             notify_metermaids(pu->parkingexten, parkinglot->cfg.parking_con, AST_DEVICE_NOT_INUSE);
05206          }
05207       } else {
05208          ast_log(LOG_WARNING, "Whoa, no parking context?\n");
05209       }
05210 
05211       ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "UnParkedCall", chan);
05212       ast_manager_event(pu->chan, EVENT_FLAG_CALL, "UnParkedCall",
05213          "Exten: %s\r\n"
05214          "Channel: %s\r\n"
05215          "Parkinglot: %s\r\n"
05216          "From: %s\r\n"
05217          "CallerIDNum: %s\r\n"
05218          "CallerIDName: %s\r\n"
05219          "ConnectedLineNum: %s\r\n"
05220          "ConnectedLineName: %s\r\n"
05221          "Uniqueid: %s\r\n",
05222          pu->parkingexten, pu->chan->name, pu->parkinglot->name, chan->name,
05223          S_COR(pu->chan->caller.id.number.valid, pu->chan->caller.id.number.str, "<unknown>"),
05224          S_COR(pu->chan->caller.id.name.valid, pu->chan->caller.id.name.str, "<unknown>"),
05225          S_COR(pu->chan->connected.id.number.valid, pu->chan->connected.id.number.str, "<unknown>"),
05226          S_COR(pu->chan->connected.id.name.valid, pu->chan->connected.id.name.str, "<unknown>"),
05227          pu->chan->uniqueid
05228          );
05229 
05230       /* Stop entertaining the caller. */
05231       switch (pu->hold_method) {
05232       case AST_CONTROL_HOLD:
05233          ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
05234          break;
05235       case AST_CONTROL_RINGING:
05236          ast_indicate(pu->chan, -1);
05237          break;
05238       default:
05239          break;
05240       }
05241       pu->hold_method = 0;
05242 
05243       parkinglot_unref(pu->parkinglot);
05244       ast_free(pu);
05245    }
05246    AST_LIST_UNLOCK(&parkinglot->parkings);
05247 
05248    if (peer) {
05249       /* Update connected line between retrieving call and parked call. */
05250       struct ast_party_connected_line connected;
05251 
05252       ast_party_connected_line_init(&connected);
05253 
05254       /* Send our caller-id to peer. */
05255       ast_channel_lock(chan);
05256       ast_connected_line_copy_from_caller(&connected, &chan->caller);
05257       ast_channel_unlock(chan);
05258       connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
05259       if (ast_channel_connected_line_macro(chan, peer, &connected, 0, 0)) {
05260          ast_channel_update_connected_line(peer, &connected, NULL);
05261       }
05262 
05263       /*
05264        * Get caller-id from peer.
05265        *
05266        * Update the retrieving call before it is answered if possible
05267        * for best results.  Some phones do not support updating the
05268        * connected line information after connection.
05269        */
05270       ast_channel_lock(peer);
05271       ast_connected_line_copy_from_caller(&connected, &peer->caller);
05272       ast_channel_unlock(peer);
05273       connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
05274       if (ast_channel_connected_line_macro(peer, chan, &connected, 1, 0)) {
05275          ast_channel_update_connected_line(chan, &connected, NULL);
05276       }
05277 
05278       ast_party_connected_line_free(&connected);
05279    }
05280 
05281    /* JK02: it helps to answer the channel if not already up */
05282    if (chan->_state != AST_STATE_UP) {
05283       ast_answer(chan);
05284    }
05285 
05286    if (peer) {
05287       struct ast_datastore *features_datastore;
05288       struct ast_dial_features *dialfeatures;
05289 
05290       /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
05291       if (!ast_strlen_zero(courtesytone)) {
05292          static const char msg[] = "courtesy tone";
05293 
05294          switch (parkedplay) {
05295          case 0:/* Courtesy tone to pickup chan */
05296             res = play_message_to_chans(chan, peer, -1, msg, courtesytone);
05297             break;
05298          case 1:/* Courtesy tone to parked chan */
05299             res = play_message_to_chans(chan, peer, 1, msg, courtesytone);
05300             break;
05301          case 2:/* Courtesy tone to both chans */
05302             res = play_message_to_chans(chan, peer, 0, msg, courtesytone);
05303             break;
05304          default:
05305             res = 0;
05306             break;
05307          }
05308          if (res) {
05309             ast_hangup(peer);
05310             parkinglot_unref(parkinglot);
05311             return -1;
05312          }
05313       }
05314 
05315       res = ast_channel_make_compatible(chan, peer);
05316       if (res < 0) {
05317          ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
05318          ast_hangup(peer);
05319          parkinglot_unref(parkinglot);
05320          return -1;
05321       }
05322       /* This runs sorta backwards, since we give the incoming channel control, as if it
05323          were the person called. */
05324       ast_verb(3, "Channel %s connected to parked call %d\n", chan->name, park);
05325 
05326       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
05327       ast_cdr_setdestchan(chan->cdr, peer->name);
05328       memset(&config, 0, sizeof(struct ast_bridge_config));
05329 
05330       /* Get datastore for peer and apply it's features to the callee side of the bridge config */
05331       ast_channel_lock(peer);
05332       features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL);
05333       if (features_datastore && (dialfeatures = features_datastore->data)) {
05334          ast_copy_flags(&config.features_callee, &dialfeatures->my_features,
05335             AST_FLAGS_ALL);
05336       }
05337       ast_channel_unlock(peer);
05338 
05339       if ((parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
05340          ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
05341       }
05342       if ((parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
05343          ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
05344       }
05345       if ((parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
05346          ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
05347       }
05348       if ((parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
05349          ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
05350       }
05351       if ((parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
05352          ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
05353       }
05354       if ((parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
05355          ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
05356       }
05357       if ((parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
05358          ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
05359       }
05360       if ((parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
05361          ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
05362       }
05363 
05364       res = ast_bridge_call(chan, peer, &config);
05365 
05366       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
05367       ast_cdr_setdestchan(chan->cdr, peer->name);
05368 
05369       /* Simulate the PBX hanging up */
05370       ast_hangup(peer);
05371    } else {
05372       if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) {
05373          ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark",
05374             chan->name);
05375       }
05376       ast_verb(3, "Channel %s tried to retrieve nonexistent parked call %d\n",
05377          chan->name, park);
05378       res = -1;
05379    }
05380 
05381    parkinglot_unref(parkinglot);
05382    return res;
05383 }

static int parkinglot_activate ( struct ast_parkinglot parkinglot  )  [static]

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

05595 {
05596    int disabled = 0;
05597    char app_data[5 + AST_MAX_CONTEXT];
05598 
05599    /* Create Park option list.  Must match with struct park_app_args options. */
05600    if (parkinglot->cfg.parkext_exclusive) {
05601       /* Specify the parking lot this parking extension parks calls. */
05602       snprintf(app_data, sizeof(app_data), ",,,,,%s", parkinglot->name);
05603    } else {
05604       /* The dialplan must specify which parking lot to use. */
05605       app_data[0] = '\0';
05606    }
05607 
05608    /* Create context */
05609    if (!ast_context_find_or_create(NULL, NULL, parkinglot->cfg.parking_con, registrar)) {
05610       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n",
05611          parkinglot->cfg.parking_con);
05612       disabled = 1;
05613 
05614    /* Add a parking extension into the context */
05615    } else if (ast_add_extension(parkinglot->cfg.parking_con, 1, parkinglot->cfg.parkext,
05616       1, NULL, NULL, parkcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
05617       ast_log(LOG_ERROR, "Could not create parking lot %s access exten %s@%s\n",
05618          parkinglot->name, parkinglot->cfg.parkext, parkinglot->cfg.parking_con);
05619       disabled = 1;
05620    } else {
05621       /* Add parking hints */
05622       if (parkinglot->cfg.parkaddhints) {
05623          park_add_hints(parkinglot->cfg.parking_con, parkinglot->cfg.parking_start,
05624             parkinglot->cfg.parking_stop);
05625       }
05626 
05627       /*
05628        * XXX Not sure why we should need to notify the metermaids for
05629        * this exten.  It was originally done for the default parking
05630        * lot entry exten only but should be done for all entry extens
05631        * if we do it for one.
05632        */
05633       /* Notify metermaids about parking lot entry exten state. */
05634       notify_metermaids(parkinglot->cfg.parkext, parkinglot->cfg.parking_con,
05635          AST_DEVICE_INUSE);
05636    }
05637 
05638    parkinglot->disabled = disabled;
05639    return disabled ? -1 : 0;
05640 }

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

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

06644 {
06645    struct ast_parkinglot *parkinglot = obj;
06646 
06647    if (parkinglot->the_mark) {
06648       /*
06649        * Don't activate a parking lot that still bears the_mark since
06650        * it is effectively deleted.
06651        */
06652       return 0;
06653    }
06654 
06655    if (parkinglot_activate(parkinglot)) {
06656       /*
06657        * The parking lot failed to activate.  Allow reloading later to
06658        * see if that fixes it.
06659        */
06660       force_reload_load = 1;
06661       ast_log(LOG_WARNING, "Parking lot %s not open for business.\n", parkinglot->name);
06662    } else {
06663       ast_debug(1, "Parking lot %s now open for business. (parkpos %d-%d)\n",
06664          parkinglot->name, parkinglot->cfg.parking_start,
06665          parkinglot->cfg.parking_stop);
06666    }
06667 
06668    return 0;
06669 }

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

Definition at line 5395 of file features.c.

References ao2_ref, ast_debug, and ast_parkinglot::name.

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

05396 {
05397    int refcount;
05398 
05399    refcount = ao2_ref(parkinglot, +1);
05400    ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1);
05401    return parkinglot;
05402 }

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

Definition at line 865 of file features.c.

References CMP_MATCH, CMP_STOP, and ast_parkinglot::name.

Referenced by ast_features_init().

00866 {
00867    struct ast_parkinglot *parkinglot = obj;
00868    struct ast_parkinglot *parkinglot2 = arg;
00869 
00870    return !strcasecmp(parkinglot->name, parkinglot2->name) ? CMP_MATCH | CMP_STOP : 0;
00871 }

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

Definition at line 5506 of file features.c.

References ast_copy_string(), ast_log(), ast_strlen_zero(), ast_true(), 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().

05507 {
05508    int error = 0;
05509 
05510    while (var) {
05511       if (!strcasecmp(var->name, "context")) {
05512          ast_copy_string(cfg->parking_con, var->value, sizeof(cfg->parking_con));
05513       } else if (!strcasecmp(var->name, "parkext")) {
05514          ast_copy_string(cfg->parkext, var->value, sizeof(cfg->parkext));
05515       } else if (!strcasecmp(var->name, "parkext_exclusive")) {
05516          cfg->parkext_exclusive = ast_true(var->value);
05517       } else if (!strcasecmp(var->name, "parkinghints")) {
05518          cfg->parkaddhints = ast_true(var->value);
05519       } else if (!strcasecmp(var->name, "parkedmusicclass")) {
05520          ast_copy_string(cfg->mohclass, var->value, sizeof(cfg->mohclass));
05521       } else if (!strcasecmp(var->name, "parkingtime")) {
05522          int parkingtime = 0;
05523 
05524          if ((sscanf(var->value, "%30d", &parkingtime) != 1) || parkingtime < 1) {
05525             ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
05526             error = -1;
05527          } else {
05528             cfg->parkingtime = parkingtime * 1000;
05529          }
05530       } else if (!strcasecmp(var->name, "parkpos")) {
05531          int start = 0;
05532          int end = 0;
05533 
05534          if (sscanf(var->value, "%30d-%30d", &start, &end) != 2) {
05535             ast_log(LOG_WARNING,
05536                "Format for parking positions is a-b, where a and b are numbers at line %d of %s\n",
05537                var->lineno, var->file);
05538             error = -1;
05539          } else if (end < start || start <= 0 || end <= 0) {
05540             ast_log(LOG_WARNING, "Parking range is invalid. Must be a <= b, at line %d of %s\n",
05541                var->lineno, var->file);
05542             error = -1;
05543          } else {
05544             cfg->parking_start = start;
05545             cfg->parking_stop = end;
05546          }
05547       } else if (!strcasecmp(var->name, "findslot")) {
05548          cfg->parkfindnext = (!strcasecmp(var->value, "next"));
05549       } else if (!strcasecmp(var->name, "parkedcalltransfers")) {
05550          parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcalltransfers, var);
05551       } else if (!strcasecmp(var->name, "parkedcallreparking")) {
05552          parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallreparking, var);
05553       } else if (!strcasecmp(var->name, "parkedcallhangup")) {
05554          parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallhangup, var);
05555       } else if (!strcasecmp(var->name, "parkedcallrecording")) {
05556          parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallrecording, var);
05557       }
05558       var = var->next;
05559    }
05560 
05561    /* Check for configuration errors */
05562    if (ast_strlen_zero(cfg->parking_con)) {
05563       ast_log(LOG_WARNING, "Parking lot %s needs context\n", pl_name);
05564       error = -1;
05565    }
05566    if (ast_strlen_zero(cfg->parkext)) {
05567       ast_log(LOG_WARNING, "Parking lot %s needs parkext\n", pl_name);
05568       error = -1;
05569    }
05570    if (!cfg->parking_start) {
05571       ast_log(LOG_WARNING, "Parking lot %s needs parkpos\n", pl_name);
05572       error = -1;
05573    }
05574    if (error) {
05575       cfg->is_invalid = 1;
05576    }
05577 
05578    return error;
05579 }

static void parkinglot_destroy ( void *  obj  )  [static]

Destroy a parking lot.

Definition at line 5405 of file features.c.

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

Referenced by create_parkinglot().

05406 {
05407    struct ast_parkinglot *doomed = obj;
05408 
05409    /*
05410     * No need to destroy parked calls here because any parked call
05411     * holds a parking lot reference.  Therefore the parkings list
05412     * must be empty.
05413     */
05414    ast_assert(AST_LIST_EMPTY(&doomed->parkings));
05415    AST_LIST_HEAD_DESTROY(&doomed->parkings);
05416 }

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

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

05484 {
05485    ast_debug(1, "Setting parking lot %s %s to %s\n", pl_name, var->name, var->value);
05486    if (!strcasecmp(var->value, "both")) {
05487       *param = AST_FEATURE_FLAG_BYBOTH;
05488    } else if (!strcasecmp(var->value, "caller")) {
05489       *param = AST_FEATURE_FLAG_BYCALLER;
05490    } else if (!strcasecmp(var->value, "callee")) {
05491       *param = AST_FEATURE_FLAG_BYCALLEE;
05492    }
05493 }

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

Definition at line 858 of file features.c.

References ast_str_case_hash(), and ast_parkinglot::name.

Referenced by ast_features_init().

00859 {
00860    const struct ast_parkinglot *parkinglot = obj;
00861 
00862    return ast_str_case_hash(parkinglot->name);
00863 }

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

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

06624 {
06625    struct ast_parkinglot *parkinglot = obj;
06626 
06627    if (parkinglot->the_mark) {
06628       if (AST_LIST_EMPTY(&parkinglot->parkings)) {
06629          /* This parking lot can actually be deleted. */
06630          return CMP_MATCH;
06631       }
06632       /* Try reloading later when parking lot is empty. */
06633       ast_log(LOG_WARNING,
06634          "Parking lot %s has parked calls.  Could not remove.\n",
06635          parkinglot->name);
06636       parkinglot->disabled = 1;
06637       force_reload_load = 1;
06638    }
06639 
06640    return 0;
06641 }

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

Definition at line 6615 of file features.c.

References ast_parkinglot::the_mark.

Referenced by load_config().

06616 {
06617    struct ast_parkinglot *parkinglot = obj;
06618 
06619    parkinglot->the_mark = 1;
06620    return 0;
06621 }

static void parkinglot_unref ( struct ast_parkinglot parkinglot  )  [static]

Unreference parkinglot object.

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

05389 {
05390    ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name,
05391       ao2_ref(parkinglot, 0) - 1);
05392    ao2_ref(parkinglot, -1);
05393 }

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

References AST_CDR_FLAG_LOCKED, ast_test_flag, and ast_cdr::next.

Referenced by ast_bridge_call().

03833 {
03834    struct ast_cdr *cdr_orig = cdr;
03835    while (cdr) {
03836       if (!ast_test_flag(cdr,AST_CDR_FLAG_LOCKED))
03837          return cdr;
03838       cdr = cdr->next;
03839    }
03840    return cdr_orig; /* everybody LOCKED or some other weirdness, like a NULL */
03841 }

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

References play_message_to_chans().

Referenced by builtin_automonitor().

02072 {
02073    return play_message_to_chans(caller_chan, callee_chan, 0, "automon message",
02074       audiofile);
02075 }

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

02018 {
02019    /* Put other channel in autoservice. */
02020    if (ast_autoservice_start(other)) {
02021       return -1;
02022    }
02023    ast_autoservice_ignore(other, AST_FRAME_DTMF_BEGIN);
02024    ast_autoservice_ignore(other, AST_FRAME_DTMF_END);
02025    if (ast_stream_and_wait(play_to, audiofile, "")) {
02026       ast_log(LOG_WARNING, "Failed to play %s '%s'!\n", msg, audiofile);
02027       ast_autoservice_stop(other);
02028       return -1;
02029    }
02030    if (ast_autoservice_stop(other)) {
02031       return -1;
02032    }
02033    return 0;
02034 }

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

References play_message_on_chan().

Referenced by parked_call_exec(), and play_message_in_bridged_call().

02053 {
02054    /* First play the file to the left channel if requested. */
02055    if (which <= 0 && play_message_on_chan(left, right, msg, audiofile)) {
02056       return -1;
02057    }
02058 
02059    /* Then play the file to the right channel if requested. */
02060    if (which >= 0 && play_message_on_chan(right, left, msg, audiofile)) {
02061       return -1;
02062    }
02063 
02064    return 0;
02065 }

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

Output parking event to manager.

Definition at line 4607 of file features.c.

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

04608 {
04609    manager_event(EVENT_FLAG_CALL, s,
04610       "Exten: %s\r\n"
04611       "Channel: %s\r\n"
04612       "Parkinglot: %s\r\n"
04613       "CallerIDNum: %s\r\n"
04614       "CallerIDName: %s\r\n"
04615       "ConnectedLineNum: %s\r\n"
04616       "ConnectedLineName: %s\r\n"
04617       "UniqueID: %s\r\n",
04618       pu->parkingexten, 
04619       pu->chan->name,
04620       pu->parkinglot->name,
04621       S_COR(pu->chan->caller.id.number.valid, pu->chan->caller.id.number.str, "<unknown>"),
04622       S_COR(pu->chan->caller.id.name.valid, pu->chan->caller.id.name.str, "<unknown>"),
04623       S_COR(pu->chan->connected.id.number.valid, pu->chan->connected.id.number.str, "<unknown>"),
04624       S_COR(pu->chan->connected.id.name.valid, pu->chan->connected.id.name.str, "<unknown>"),
04625       pu->chan->uniqueid
04626       );
04627 }

static void process_applicationmap_line ( struct ast_variable var  )  [static]

Todo:
XXX var_name or app_args ?

Definition at line 5722 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_free, 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, and ast_variable::value.

Referenced by process_config().

05723 {
05724    char *tmp_val = ast_strdupa(var->value);
05725    char *activateon, *new_syn;
05726    struct ast_call_feature *feature;
05727    AST_DECLARE_APP_ARGS(args,
05728       AST_APP_ARG(exten);
05729       AST_APP_ARG(activatedby);
05730       AST_APP_ARG(app);
05731       AST_APP_ARG(app_args);
05732       AST_APP_ARG(moh_class);
05733    );
05734 
05735    AST_STANDARD_APP_ARGS(args, tmp_val);
05736    if ((new_syn = strchr(args.app, '('))) {
05737       /* New syntax */
05738       args.moh_class = args.app_args;
05739       args.app_args = new_syn;
05740       *args.app_args++ = '\0';
05741       if (args.app_args[strlen(args.app_args) - 1] == ')') {
05742          args.app_args[strlen(args.app_args) - 1] = '\0';
05743       }
05744    }
05745 
05746    activateon = strsep(&args.activatedby, "/");
05747 
05748    /*! \todo XXX var_name or app_args ? */
05749    if (ast_strlen_zero(args.app)
05750       || ast_strlen_zero(args.exten)
05751       || ast_strlen_zero(activateon)
05752       || ast_strlen_zero(var->name)) {
05753       ast_log(LOG_NOTICE,
05754          "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
05755          args.app, args.exten, activateon, var->name);
05756       return;
05757    }
05758 
05759    AST_RWLIST_RDLOCK(&feature_list);
05760    if (find_dynamic_feature(var->name)) {
05761       AST_RWLIST_UNLOCK(&feature_list);
05762       ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n",
05763          var->name);
05764       return;
05765    }
05766    AST_RWLIST_UNLOCK(&feature_list);
05767 
05768    if (!(feature = ast_calloc(1, sizeof(*feature)))) {
05769       return;
05770    }
05771 
05772    ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
05773    ast_copy_string(feature->app, args.app, FEATURE_APP_LEN);
05774    ast_copy_string(feature->exten, args.exten, FEATURE_EXTEN_LEN);
05775 
05776    if (args.app_args) {
05777       ast_copy_string(feature->app_args, args.app_args, FEATURE_APP_ARGS_LEN);
05778    }
05779 
05780    if (args.moh_class) {
05781       ast_copy_string(feature->moh_class, args.moh_class, FEATURE_MOH_LEN);
05782    }
05783 
05784    ast_copy_string(feature->exten, args.exten, sizeof(feature->exten));
05785    feature->operation = feature_exec_app;
05786    ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
05787 
05788    /* Allow caller and callee to be specified for backwards compatability */
05789    if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller")) {
05790       ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
05791    } else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee")) {
05792       ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
05793    } else {
05794       ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
05795          " must be 'self', or 'peer'\n", var->name);
05796       ast_free(feature);
05797       return;
05798    }
05799 
05800    if (ast_strlen_zero(args.activatedby)) {
05801       ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
05802    } else if (!strcasecmp(args.activatedby, "caller")) {
05803       ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
05804    } else if (!strcasecmp(args.activatedby, "callee")) {
05805       ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
05806    } else if (!strcasecmp(args.activatedby, "both")) {
05807       ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
05808    } else {
05809       ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
05810          " must be 'caller', or 'callee', or 'both'\n", var->name);
05811       ast_free(feature);
05812       return;
05813    }
05814 
05815    ast_register_feature(feature);
05816 
05817    ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n",
05818       var->name, args.app, args.app_args, args.exten);
05819 }

static int process_config ( struct ast_config cfg  )  [static]

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

05822 {
05823    int i;
05824    struct ast_variable *var = NULL;
05825    struct feature_group *fg = NULL;
05826    char *ctg; 
05827    static const char * const categories[] = { 
05828       /* Categories in features.conf that are not
05829        * to be parsed as group categories
05830        */
05831       "general",
05832       "featuremap",
05833       "applicationmap"
05834    };
05835 
05836    /* Set general features global defaults. */
05837    featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
05838 
05839    /* Set global call pickup defaults. */
05840    strcpy(pickup_ext, "*8");
05841    pickupsound[0] = '\0';
05842    pickupfailsound[0] = '\0';
05843 
05844    /* Set global call transfer defaults. */
05845    strcpy(xfersound, "beep");
05846    strcpy(xferfailsound, "beeperr");
05847    transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
05848    atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
05849    atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
05850    atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
05851    atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
05852 
05853    /* Set global call parking defaults. */
05854    comebacktoorigin = 1;
05855    courtesytone[0] = '\0';
05856    parkedplay = 0;
05857    adsipark = 0;
05858    parkeddynamic = 0;
05859 
05860    var = ast_variable_browse(cfg, "general");
05861    build_parkinglot(DEFAULT_PARKINGLOT, var);
05862    for (; var; var = var->next) {
05863       if (!strcasecmp(var->name, "parkeddynamic")) {
05864          parkeddynamic = ast_true(var->value);
05865       } else if (!strcasecmp(var->name, "adsipark")) {
05866          adsipark = ast_true(var->value);
05867       } else if (!strcasecmp(var->name, "transferdigittimeout")) {
05868          if ((sscanf(var->value, "%30d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
05869             ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
05870             transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
05871          } else {
05872             transferdigittimeout = transferdigittimeout * 1000;
05873          }
05874       } else if (!strcasecmp(var->name, "featuredigittimeout")) {
05875          if ((sscanf(var->value, "%30d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
05876             ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
05877             featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
05878          }
05879       } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
05880          if ((sscanf(var->value, "%30d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
05881             ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
05882             atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
05883          } else {
05884             atxfernoanswertimeout = atxfernoanswertimeout * 1000;
05885          }
05886       } else if (!strcasecmp(var->name, "atxferloopdelay")) {
05887          if ((sscanf(var->value, "%30u", &atxferloopdelay) != 1)) {
05888             ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
05889             atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
05890          } else {
05891             atxferloopdelay *= 1000;
05892          }
05893       } else if (!strcasecmp(var->name, "atxferdropcall")) {
05894          atxferdropcall = ast_true(var->value);
05895       } else if (!strcasecmp(var->name, "atxfercallbackretries")) {
05896          if ((sscanf(var->value, "%30u", &atxfercallbackretries) != 1)) {
05897             ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
05898             atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
05899          }
05900       } else if (!strcasecmp(var->name, "courtesytone")) {
05901          ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
05902       }  else if (!strcasecmp(var->name, "parkedplay")) {
05903          if (!strcasecmp(var->value, "both")) {
05904             parkedplay = 2;
05905          } else if (!strcasecmp(var->value, "parked")) {
05906             parkedplay = 1;
05907          } else {
05908             parkedplay = 0;
05909          }
05910       } else if (!strcasecmp(var->name, "xfersound")) {
05911          ast_copy_string(xfersound, var->value, sizeof(xfersound));
05912       } else if (!strcasecmp(var->name, "xferfailsound")) {
05913          ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
05914       } else if (!strcasecmp(var->name, "pickupexten")) {
05915          ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
05916       } else if (!strcasecmp(var->name, "pickupsound")) {
05917          ast_copy_string(pickupsound, var->value, sizeof(pickupsound));
05918       } else if (!strcasecmp(var->name, "pickupfailsound")) {
05919          ast_copy_string(pickupfailsound, var->value, sizeof(pickupfailsound));
05920       } else if (!strcasecmp(var->name, "comebacktoorigin")) {
05921          comebacktoorigin = ast_true(var->value);
05922       }
05923    }
05924 
05925    unmap_features();
05926    for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
05927       if (remap_feature(var->name, var->value)) {
05928          ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
05929       }
05930    }
05931 
05932    /* Map a key combination to an application */
05933    ast_unregister_features();
05934    for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
05935       process_applicationmap_line(var);
05936    }
05937 
05938    ast_unregister_groups();
05939    AST_RWLIST_WRLOCK(&feature_groups);
05940 
05941    ctg = NULL;
05942    while ((ctg = ast_category_browse(cfg, ctg))) {
05943       /* Is this a parkinglot definition ? */
05944       if (!strncasecmp(ctg, "parkinglot_", strlen("parkinglot_"))) {
05945          ast_debug(2, "Found configuration section %s, assume parking context\n", ctg);
05946          if (!build_parkinglot(ctg, ast_variable_browse(cfg, ctg))) {
05947             ast_log(LOG_ERROR, "Could not build parking lot %s. Configuration error.\n", ctg);
05948          } else {
05949             ast_debug(1, "Configured parking context %s\n", ctg);
05950          }
05951          continue;   
05952       }
05953 
05954       /* No, check if it's a group */
05955       for (i = 0; i < ARRAY_LEN(categories); i++) {
05956          if (!strcasecmp(categories[i], ctg)) {
05957             break;
05958          }
05959       }
05960       if (i < ARRAY_LEN(categories)) {
05961          continue;
05962       }
05963 
05964       if (!(fg = register_group(ctg))) {
05965          continue;
05966       }
05967 
05968       for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
05969          struct ast_call_feature *feature;
05970 
05971          AST_RWLIST_RDLOCK(&feature_list);
05972          if (!(feature = find_dynamic_feature(var->name)) && 
05973              !(feature = ast_find_call_feature(var->name))) {
05974             AST_RWLIST_UNLOCK(&feature_list);
05975             ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
05976             continue;
05977          }
05978          AST_RWLIST_UNLOCK(&feature_list);
05979 
05980          register_group_feature(fg, var->value, feature);
05981       }
05982    }
05983 
05984    AST_RWLIST_UNLOCK(&feature_groups);
05985 
05986    return 0;
05987 }

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

02306 {
02307    const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
02308    if (ast_strlen_zero(s)) {
02309       s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
02310    }
02311    if (ast_strlen_zero(s)) { /* Use the non-macro context to transfer the call XXX ? */
02312       s = transferer->macrocontext;
02313    }
02314    if (ast_strlen_zero(s)) {
02315       s = transferer->context;
02316    }
02317    return s;  
02318 }

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

03000 {
03001    struct feature_group *fg;
03002 
03003    if (!fgname) {
03004       ast_log(LOG_NOTICE, "You didn't pass a new group name!\n");
03005       return NULL;
03006    }
03007 
03008    if (!(fg = ast_calloc_with_stringfields(1, struct feature_group, 128))) {
03009       return NULL;
03010    }
03011 
03012    ast_string_field_set(fg, gname, fgname);
03013 
03014    AST_LIST_INSERT_HEAD(&feature_groups, fg, entry);
03015 
03016    ast_verb(2, "Registered group '%s'\n", fg->gname);
03017 
03018    return fg;
03019 }

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

03031 {
03032    struct feature_group_exten *fge;
03033 
03034    if (!fg) {
03035       ast_log(LOG_NOTICE, "You didn't pass a group!\n");
03036       return;
03037    }
03038 
03039    if (!feature) {
03040       ast_log(LOG_NOTICE, "You didn't pass a feature!\n");
03041       return;
03042    }
03043 
03044    if (!(fge = ast_calloc_with_stringfields(1, struct feature_group_exten, 128))) {
03045       return;
03046    }
03047 
03048    ast_string_field_set(fge, exten, S_OR(exten, feature->exten));
03049 
03050    fge->feature = feature;
03051 
03052    AST_LIST_INSERT_HEAD(&fg->features, fge, entry);
03053 
03054    ast_verb(2, "Registered feature '%s' for group '%s' at exten '%s'\n",
03055                feature->sname, fg->gname, fge->exten);
03056 }

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

Definition at line 3236 of file features.c.

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

Referenced by process_config().

03237 {
03238    int x, res = -1;
03239 
03240    ast_rwlock_wrlock(&features_lock);
03241    for (x = 0; x < FEATURES_COUNT; x++) {
03242       if (strcasecmp(builtin_features[x].sname, name))
03243          continue;
03244 
03245       ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
03246       res = 0;
03247       break;
03248    }
03249    ast_rwlock_unlock(&features_lock);
03250 
03251    return res;
03252 }

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

06549 {
06550    remove_dead_ramp_usage(context, &old_ctx->access_extens, &new_ctx->access_extens);
06551    remove_dead_spaces_usage(context, &old_ctx->spaces, &new_ctx->spaces, destroy_space);
06552 #if 0
06553    /* I don't think we should destroy hints if the parking space still exists. */
06554    remove_dead_spaces_usage(context, &old_ctx->hints, &new_ctx->hints, destroy_space_hint);
06555 #endif
06556 }

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

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

06572 {
06573    struct parking_dp_context *old_ctx;
06574    struct parking_dp_context *new_ctx;
06575    struct ast_context *con;
06576    int cmp;
06577 
06578    old_ctx = AST_LIST_FIRST(old_map);
06579    new_ctx = AST_LIST_FIRST(new_map);
06580 
06581    while (new_ctx) {
06582       if (!old_ctx) {
06583          /* No old contexts left, so no dead stuff can remain. */
06584          return;
06585       }
06586       cmp = strcmp(old_ctx->context, new_ctx->context);
06587       if (cmp < 0) {
06588          /* New map does not have old map context. */
06589          con = ast_context_find(old_ctx->context);
06590          if (con) {
06591             ast_context_destroy(con, registrar);
06592          }
06593          old_ctx = AST_LIST_NEXT(old_ctx, node);
06594          continue;
06595       }
06596       if (cmp == 0) {
06597          /* Old and new map have this context. */
06598          remove_dead_context_usage(old_ctx->context, old_ctx, new_ctx);
06599          old_ctx = AST_LIST_NEXT(old_ctx, node);
06600       } else {
06601          /* Old map does not have new map context. */
06602       }
06603       new_ctx = AST_LIST_NEXT(new_ctx, node);
06604    }
06605 
06606    /* Any old contexts left must be dead. */
06607    for (; old_ctx; old_ctx = AST_LIST_NEXT(old_ctx, node)) {
06608       con = ast_context_find(old_ctx->context);
06609       if (con) {
06610          ast_context_destroy(con, registrar);
06611       }
06612    }
06613 }

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

06408 {
06409    struct parking_dp_ramp *old_ramp;
06410    struct parking_dp_ramp *new_ramp;
06411    int cmp;
06412 
06413    old_ramp = AST_LIST_FIRST(old_ramps);
06414    new_ramp = AST_LIST_FIRST(new_ramps);
06415 
06416    while (new_ramp) {
06417       if (!old_ramp) {
06418          /* No old ramps left, so no dead ramps can remain. */
06419          return;
06420       }
06421       cmp = strcmp(old_ramp->exten, new_ramp->exten);
06422       if (cmp < 0) {
06423          /* New map does not have old ramp. */
06424          remove_exten_if_exist(context, old_ramp->exten, 1);
06425          old_ramp = AST_LIST_NEXT(old_ramp, node);
06426          continue;
06427       }
06428       if (cmp == 0) {
06429          /* Old and new map have this ramp. */
06430          old_ramp = AST_LIST_NEXT(old_ramp, node);
06431       } else {
06432          /* Old map does not have new ramp. */
06433       }
06434       new_ramp = AST_LIST_NEXT(new_ramp, node);
06435    }
06436 
06437    /* Any old ramps left must be dead. */
06438    for (; old_ramp; old_ramp = AST_LIST_NEXT(old_ramp, node)) {
06439       remove_exten_if_exist(context, old_ramp->exten, 1);
06440    }
06441 }

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

06480 {
06481    struct parking_dp_spaces *old_range;
06482    struct parking_dp_spaces *new_range;
06483    int space;/*!< Current position in the current old range. */
06484    int stop;
06485 
06486    old_range = AST_LIST_FIRST(old_spaces);
06487    new_range = AST_LIST_FIRST(new_spaces);
06488    space = -1;
06489 
06490    while (old_range) {
06491       if (space < old_range->start) {
06492          space = old_range->start;
06493       }
06494       if (new_range) {
06495          if (space < new_range->start) {
06496             /* Current position in old range starts before new range. */
06497             if (old_range->stop < new_range->start) {
06498                /* Old range ends before new range. */
06499                stop = old_range->stop;
06500                old_range = AST_LIST_NEXT(old_range, node);
06501             } else {
06502                /* Tail of old range overlaps new range. */
06503                stop = new_range->start - 1;
06504             }
06505          } else if (/* new_range->start <= space && */ space <= new_range->stop) {
06506             /* Current position in old range overlaps new range. */
06507             if (old_range->stop <= new_range->stop) {
06508                /* Old range ends at or before new range. */
06509                old_range = AST_LIST_NEXT(old_range, node);
06510             } else {
06511                /* Old range extends beyond end of new range. */
06512                space = new_range->stop + 1;
06513                new_range = AST_LIST_NEXT(new_range, node);
06514             }
06515             continue;
06516          } else /* if (new_range->stop < space) */ {
06517             /* Current position in old range starts after new range. */
06518             new_range = AST_LIST_NEXT(new_range, node);
06519             continue;
06520          }
06521       } else {
06522          /* No more new ranges.  All remaining old spaces are dead. */
06523          stop = old_range->stop;
06524          old_range = AST_LIST_NEXT(old_range, node);
06525       }
06526 
06527       /* Destroy dead parking spaces. */
06528       for (; space <= stop; ++space) {
06529          destroy_space(context, space);
06530       }
06531    }
06532 }

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

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

06382 {
06383    struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
06384 
06385    if (pbx_find_extension(NULL, NULL, &q, context, exten, priority, NULL, NULL,
06386       E_MATCH)) {
06387       ast_debug(1, "Removing unneeded parking lot exten: %s@%s priority:%d\n",
06388          context, exten, priority);
06389       ast_context_remove_extension(context, exten, priority, registrar);
06390    }
06391 }

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

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

03844 {
03845    const char *feature;
03846 
03847    if (ast_strlen_zero(features)) {
03848       return;
03849    }
03850 
03851    for (feature = features; *feature; feature++) {
03852       switch (*feature) {
03853       case 'T' :
03854       case 't' :
03855          ast_set_flag(&(config->features_caller), AST_FEATURE_REDIRECT);
03856          break;
03857       case 'K' :
03858       case 'k' :
03859          ast_set_flag(&(config->features_caller), AST_FEATURE_PARKCALL);
03860          break;
03861       case 'H' :
03862       case 'h' :
03863          ast_set_flag(&(config->features_caller), AST_FEATURE_DISCONNECT);
03864          break;
03865       case 'W' :
03866       case 'w' :
03867          ast_set_flag(&(config->features_caller), AST_FEATURE_AUTOMON);
03868          break;
03869       default :
03870          ast_log(LOG_WARNING, "Skipping unknown feature code '%c'\n", *feature);
03871       }
03872    }
03873 }

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

00878 {
00879    ast_copy_string(chan->context, context, sizeof(chan->context));
00880    ast_copy_string(chan->exten, ext, sizeof(chan->exten));
00881    chan->priority = pri;
00882 }

static int set_chan_app_data ( struct ast_channel chan,
const char *  src_app_data 
) [static]

Definition at line 953 of file features.c.

References ast_channel_datastore_add(), ast_datastore_alloc, ast_datastore_free(), ast_malloc, channel_app_data_datastore, ast_datastore::data, and ast_channel::data.

Referenced by bridge_call_thread().

00954 {
00955    struct ast_datastore *datastore;
00956    char *dst_app_data;
00957 
00958    datastore = ast_datastore_alloc(&channel_app_data_datastore, NULL);
00959    if (!datastore) {
00960       return -1;
00961    }
00962 
00963    dst_app_data = ast_malloc(strlen(src_app_data) + 1);
00964    if (!dst_app_data) {
00965       ast_datastore_free(datastore);
00966       return -1;
00967    }
00968 
00969    chan->data = strcpy(dst_app_data, src_app_data);
00970    datastore->data = dst_app_data;
00971    ast_channel_datastore_add(chan, datastore);
00972    return 0;
00973 }

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

Definition at line 3427 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(), and pbx_builtin_getvar_helper().

Referenced by ast_bridge_call().

03428 {
03429    int x;
03430 
03431    ast_clear_flag(config, AST_FLAGS_ALL);
03432 
03433    ast_rwlock_rdlock(&features_lock);
03434    for (x = 0; x < FEATURES_COUNT; x++) {
03435       if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
03436          continue;
03437 
03438       if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
03439          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
03440 
03441       if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
03442          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
03443    }
03444    ast_rwlock_unlock(&features_lock);
03445 
03446    if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
03447       const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
03448 
03449       if (dynamic_features) {
03450          char *tmp = ast_strdupa(dynamic_features);
03451          char *tok;
03452          struct ast_call_feature *feature;
03453 
03454          /* while we have a feature */
03455          while ((tok = strsep(&tmp, "#"))) {
03456             struct feature_group *fg;
03457 
03458             AST_RWLIST_RDLOCK(&feature_groups);
03459             AST_RWLIST_TRAVERSE(&feature_groups, fg, entry) {
03460                struct feature_group_exten *fge;
03461 
03462                AST_LIST_TRAVERSE(&fg->features, fge, entry) {
03463                   if (ast_test_flag(fge->feature, AST_FEATURE_FLAG_BYCALLER)) {
03464                      ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
03465                   }
03466                   if (ast_test_flag(fge->feature, AST_FEATURE_FLAG_BYCALLEE)) {
03467                      ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
03468                   }
03469                }
03470             }
03471             AST_RWLIST_UNLOCK(&feature_groups);
03472 
03473             AST_RWLIST_RDLOCK(&feature_list);
03474             if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
03475                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER)) {
03476                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
03477                }
03478                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE)) {
03479                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
03480                }
03481             }
03482             AST_RWLIST_UNLOCK(&feature_list);
03483          }
03484       }
03485    }
03486 }

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

References FEATURE_SENSE_PEER.

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

01946 {
01947    if (sense == FEATURE_SENSE_PEER) {
01948       *caller = peer;
01949       *callee = chan;
01950    } else {
01951       *callee = peer;
01952       *caller = chan;
01953    }
01954 }

static void unmap_features ( void   )  [static]

Definition at line 3226 of file features.c.

References ast_rwlock_unlock, ast_rwlock_wrlock, FEATURES_COUNT, and features_lock.

Referenced by process_config().

03227 {
03228    int x;
03229 
03230    ast_rwlock_wrlock(&features_lock);
03231    for (x = 0; x < FEATURES_COUNT; x++)
03232       strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
03233    ast_rwlock_unlock(&features_lock);
03234 }

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

06068 {
06069    struct parking_dp_ramp *cur_ramp;
06070    struct parking_dp_ramp *new_ramp;
06071    int cmp;
06072 
06073    /* Make sure that exclusive is only 0 or 1 */
06074    if (exclusive) {
06075       exclusive = 1;
06076    }
06077 
06078    AST_LIST_TRAVERSE_SAFE_BEGIN(ramp_map, cur_ramp, node) {
06079       cmp = strcmp(exten, cur_ramp->exten);
06080       if (cmp > 0) {
06081          /* The parking lot ramp goes after this node. */
06082          continue;
06083       }
06084       if (cmp == 0) {
06085          /* The ramp is already in the map. */
06086          if (complain && (cur_ramp->exclusive || exclusive)) {
06087             ast_log(LOG_WARNING,
06088                "Parking lot '%s' parkext %s@%s used by another parking lot.\n",
06089                lot->name, exten, lot->cfg.parking_con);
06090          }
06091          return 0;
06092       }
06093       /* The new parking lot ramp goes before this node. */
06094       new_ramp = build_dialplan_useage_ramp(exten, exclusive);
06095       if (!new_ramp) {
06096          return -1;
06097       }
06098       AST_LIST_INSERT_BEFORE_CURRENT(new_ramp, node);
06099       return 0;
06100    }
06101    AST_LIST_TRAVERSE_SAFE_END;
06102 
06103    /* New parking lot access ramp goes on the end. */
06104    new_ramp = build_dialplan_useage_ramp(exten, exclusive);
06105    if (!new_ramp) {
06106       return -1;
06107    }
06108    AST_LIST_INSERT_TAIL(ramp_map, new_ramp, node);
06109    return 0;
06110 }

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

06149 {
06150    struct parking_dp_spaces *cur_node;
06151    struct parking_dp_spaces *expand_node;
06152    struct parking_dp_spaces *new_node;
06153 
06154    expand_node = NULL;
06155    AST_LIST_TRAVERSE_SAFE_BEGIN(space_map, cur_node, node) {
06156       /* NOTE: stop + 1 to combine immediately adjacent nodes into one. */
06157       if (expand_node) {
06158          /* The previous node is expanding to possibly eat following nodes. */
06159          if (expand_node->stop + 1 < cur_node->start) {
06160             /* Current node is completely after expanding node. */
06161             return 0;
06162          }
06163 
06164          if (complain
06165             && ((cur_node->start <= start && start <= cur_node->stop)
06166                || (cur_node->start <= stop && stop <= cur_node->stop)
06167                || (start < cur_node->start && cur_node->stop < stop))) {
06168             /* Only complain once per range add. */
06169             complain = 0;
06170             ast_log(LOG_WARNING,
06171                "Parking lot '%s' parkpos %d-%d@%s overlaps another parking lot.\n",
06172                lot->name, start, stop, lot->cfg.parking_con);
06173          }
06174 
06175          /* Current node is eaten by the expanding node. */
06176          if (expand_node->stop < cur_node->stop) {
06177             expand_node->stop = cur_node->stop;
06178          }
06179          AST_LIST_REMOVE_CURRENT(node);
06180          ast_free(cur_node);
06181          continue;
06182       }
06183 
06184       if (cur_node->stop + 1 < start) {
06185          /* New range is completely after current node. */
06186          continue;
06187       }
06188       if (stop + 1 < cur_node->start) {
06189          /* New range is completely before current node. */
06190          new_node = build_dialplan_useage_spaces(start, stop);
06191          if (!new_node) {
06192             return -1;
06193          }
06194          AST_LIST_INSERT_BEFORE_CURRENT(new_node, node);
06195          return 0;
06196       }
06197 
06198       if (complain
06199          && ((cur_node->start <= start && start <= cur_node->stop)
06200             || (cur_node->start <= stop && stop <= cur_node->stop)
06201             || (start < cur_node->start && cur_node->stop < stop))) {
06202          /* Only complain once per range add. */
06203          complain = 0;
06204          ast_log(LOG_WARNING,
06205             "Parking lot '%s' parkpos %d-%d@%s overlaps another parking lot.\n",
06206             lot->name, start, stop, lot->cfg.parking_con);
06207       }
06208 
06209       /* Current node range overlaps or is immediately adjacent to new range. */
06210       if (start < cur_node->start) {
06211          /* Expand the current node in the front. */
06212          cur_node->start = start;
06213       }
06214       if (stop <= cur_node->stop) {
06215          /* Current node is not expanding in the rear. */
06216          return 0;
06217       }
06218       cur_node->stop = stop;
06219       expand_node = cur_node;
06220    }
06221    AST_LIST_TRAVERSE_SAFE_END;
06222 
06223    if (expand_node) {
06224       /*
06225        * The previous node expanded and either ate all following nodes
06226        * or it was the last node.
06227        */
06228       return 0;
06229    }
06230 
06231    /* New range goes on the end. */
06232    new_node = build_dialplan_useage_spaces(start, stop);
06233    if (!new_node) {
06234       return -1;
06235    }
06236    AST_LIST_INSERT_TAIL(space_map, new_node, node);
06237    return 0;
06238 }

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

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

01885 {
01886    char *parse;
01887    const char *app_data;
01888    const char *pl_name;
01889    struct ast_park_call_args args = { 0, };
01890    struct park_app_args app_args;
01891    int res;
01892 
01893    app_data = ast_get_extension_app_data(park_exten);
01894    if (!app_data) {
01895       app_data = "";
01896    }
01897    parse = ast_strdupa(app_data);
01898    AST_STANDARD_APP_ARGS(app_args, parse);
01899 
01900    /* Find the parking lot */
01901    if (!ast_strlen_zero(app_args.pl_name)) {
01902       pl_name = app_args.pl_name;
01903    } else {
01904       pl_name = findparkinglotname(parker);
01905    }
01906    if (ast_strlen_zero(pl_name)) {
01907       /* Parking lot is not specified, so use the default parking lot. */
01908       args.parkinglot = parkinglot_addref(default_parkinglot);
01909    } else {
01910       args.parkinglot = find_parkinglot(pl_name);
01911       if (!args.parkinglot && parkeddynamic) {
01912          args.parkinglot = create_dynamic_parkinglot(pl_name, park_me);
01913       }
01914    }
01915 
01916    if (args.parkinglot) {
01917       /* Park the call */
01918       res = finishup(park_me);
01919       if (res) {
01920          /* park_me hungup on us. */
01921          parkinglot_unref(args.parkinglot);
01922          return -1;
01923       }
01924       res = masq_park_call(park_me, parker, &args);
01925       parkinglot_unref(args.parkinglot);
01926    } else {
01927       /* Parking failed because parking lot does not exist. */
01928       if (!ast_test_flag(&args, AST_PARK_OPT_SILENCE)) {
01929          ast_stream_and_wait(parker, "pbx-parkingfailed", "");
01930       }
01931       finishup(park_me);
01932       res = -1;
01933    }
01934 
01935    return res ? AST_FEATURE_RETURN_SUCCESS : -1;
01936 }


Variable Documentation

int adsipark [static]

Definition at line 608 of file features.c.

Referenced by park_call_full(), and process_config().

char* app_bridge = "Bridge" [static]

Definition at line 7447 of file features.c.

unsigned int atxfercallbackretries [static]

Definition at line 617 of file features.c.

Referenced by builtin_atxfer(), and process_config().

unsigned int atxferdropcall [static]

Definition at line 615 of file features.c.

Referenced by builtin_atxfer(), and process_config().

unsigned int atxferloopdelay [static]

Definition at line 616 of file features.c.

Referenced by builtin_atxfer(), and process_config().

int atxfernoanswertimeout [static]

Definition at line 614 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 7484 of file features.c.

Referenced by bridge_exec().

Definition at line 2965 of file features.c.

Initial value:
 {
   .type = "Channel appdata datastore",
   .destroy = ast_free_ptr,
}

Definition at line 948 of file features.c.

Referenced by set_chan_app_data().

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

int comebacktoorigin = 1 [static]

Definition at line 612 of file features.c.

Referenced by manage_parked_call(), and process_config().

char courtesytone[256] [static]

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

Definition at line 589 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 582 of file features.c.

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

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

Definition at line 748 of file features.c.

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

int featuredigittimeout [static]

Definition at line 611 of file features.c.

Referenced by ast_bridge_call(), and process_config().

ast_rwlock_t features_lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, 1 } [static]
ast_mutex_t features_reload_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]

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

Definition at line 606 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 585 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 638 of file features.c.

Referenced by builtin_automixmonitor().

int mixmonitor_ok = 1 [static]

Definition at line 639 of file features.c.

Referenced by builtin_automixmonitor().

struct ast_app* monitor_app = NULL [static]

Definition at line 635 of file features.c.

Referenced by ast_bridge_call(), and builtin_automonitor().

int monitor_ok = 1 [static]

Definition at line 636 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 5020 of file features.c.

Referenced by park_call_exec().

const char* parkcall = "Park" [static]
const char* parkedcall = "ParkedCall" [static]

Definition at line 419 of file features.c.

int parkeddynamic = 0 [static]

Enable creation of parkinglots dynamically

Definition at line 588 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 587 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 603 of file features.c.

Referenced by ast_features_reload(), and manage_parked_call().

pthread_t parking_thread [static]

Definition at line 644 of file features.c.

Referenced by ast_features_init(), features_shutdown(), and park_call_full().

Initial value:
 {
   .parkext = DEFAULT_PARK_EXTENSION,
   .parkingtime = DEFAULT_PARK_TIME,
}

Default configuration for normal parking lots.

Definition at line 5468 of file features.c.

Default configuration for default parking lot.

Definition at line 5458 of file features.c.

struct ao2_container* parkinglots [static]
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 7288 of file features.c.

char pickup_ext[AST_MAX_EXTENSION] [static]

Call pickup extension

Definition at line 421 of file features.c.

Referenced by analog_canmatch_featurecode(), and canmatch_featurecode().

char pickupfailsound[256] [static]

Pickup failure sound

Definition at line 593 of file features.c.

Referenced by ast_pickup_call(), and process_config().

char pickupsound[256] [static]

Pickup sound

Definition at line 592 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 641 of file features.c.

Referenced by builtin_automixmonitor().

int stopmixmonitor_ok = 1 [static]

Definition at line 642 of file features.c.

Referenced by builtin_automixmonitor().

int transferdigittimeout [static]

Definition at line 610 of file features.c.

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

char xferfailsound[256] [static]

Call transfer failure sound

Definition at line 591 of file features.c.

Referenced by builtin_atxfer(), and process_config().

char xfersound[256] [static]

Call transfer sound

Definition at line 590 of file features.c.

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


Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1