Sat Apr 26 2014 22:02:48

Asterisk developer's documentation


features.c File Reference

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

#include "asterisk.h"
#include "asterisk/_private.h"
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/causes.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/app.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/utils.h"
#include "asterisk/adsi.h"
#include "asterisk/devicestate.h"
#include "asterisk/monitor.h"
#include "asterisk/audiohook.h"
#include "asterisk/global_datastores.h"
#include "asterisk/astobj2.h"
#include "asterisk/cel.h"
#include "asterisk/test.h"
Include dependency graph for features.c:

Go to the source code of this file.

Data Structures

struct  ast_bridge_thread_obj
struct  ast_dial_features
struct  ast_park_call_args
struct  ast_parkinglot
 Structure for parking lots which are put in a container. More...
struct  feature_ds
struct  feature_exten
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  ast_parkinglot::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_COMEBACK_CONTEXT   "parkedcallstimeout"
#define DEFAULT_COMEBACK_DIAL_TIME   30
#define DEFAULT_COMEBACK_TO_ORIGIN   1
#define DEFAULT_FEATURE_DIGIT_TIMEOUT   1000
#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER   15000
#define DEFAULT_PARK_EXTENSION   "700"
#define DEFAULT_PARK_TIME   45000
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT   3000
#define FEATURES_COUNT   ARRAY_LEN(builtin_features)
#define HFS_FORMAT   "%-25s %-7s %-7s\n"
#define MAX_DIAL_FEATURE_OPTIONS   30

Enumerations

enum  {
  BRIDGE_OPT_PLAYTONE = (1 << 0), OPT_CALLEE_HANGUP = (1 << 1), OPT_CALLER_HANGUP = (1 << 2), OPT_DURATION_LIMIT = (1 << 3),
  OPT_DURATION_STOP = (1 << 4), OPT_CALLEE_TRANSFER = (1 << 5), OPT_CALLER_TRANSFER = (1 << 6), OPT_CALLEE_MONITOR = (1 << 7),
  OPT_CALLER_MONITOR = (1 << 8), OPT_CALLEE_PARK = (1 << 9), OPT_CALLER_PARK = (1 << 10), OPT_CALLEE_KILL = (1 << 11),
  OPT_CALLEE_GO_ON = (1 << 12)
}
enum  { OPT_ARG_DURATION_LIMIT = 0, OPT_ARG_DURATION_STOP, OPT_ARG_CALLEE_GO_ON, 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.
struct ast_channelast_pickup_find_by_group (struct ast_channel *chan)
 Find a pickup channel target by group.
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_feature_get_exten (struct ast_channel *chan, const char *feature_name, char *buf, size_t len)
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 void feature_ds_destroy (void *data)
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_exten_cmp (void *obj, void *arg, int flags)
static int feature_exten_hash (const void *obj, int flags)
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 int feature_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static struct ast_channelfeature_request_and_dial (struct ast_channel *caller, const char *caller_name, struct ast_channel *requestor, struct ast_channel *transferee, const char *type, struct ast_format_cap *cap, const char *addr, int timeout, int *outstate, const char *language)
static int feature_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
static int featuremap_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int featuremap_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
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 feature_dsget_feature_ds (struct ast_channel *chan)
static struct ast_extenget_parking_exten (const char *exten_str, struct ast_channel *chan, const char *context)
static unsigned int get_parkingtime (struct ast_channel *chan, struct ast_parkinglot *parkinglot)
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 manager_parkinglot_list (struct mansession *s, const struct message *m)
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_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 }, [ 'F' ] = { .flag = OPT_CALLEE_GO_ON , .arg_index = OPT_ARG_CALLEE_GO_ON + 1 }, [ '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 char courtesytone [256]
static struct ast_parkinglotdefault_parkinglot
 Default parking lot.
static struct ast_datastore_info dial_features_info
static struct ast_datastore_info feature_ds_info
static struct ast_custom_function feature_function
static struct feature_groups feature_groups
static struct feature_list feature_list
static int featuredigittimeout
static struct ast_custom_function featuremap_function
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 528 of file features.c.

Definition at line 523 of file features.c.

Referenced by process_config().

#define DEFAULT_ATXFER_DROP_CALL   0

Do not drop call.

Definition at line 521 of file features.c.

Referenced by process_config().

#define DEFAULT_ATXFER_LOOP_DELAY   10000

ms

Definition at line 522 of file features.c.

Referenced by process_config().

#define DEFAULT_COMEBACK_CONTEXT   "parkedcallstimeout"

Definition at line 524 of file features.c.

#define DEFAULT_COMEBACK_DIAL_TIME   30

Definition at line 526 of file features.c.

Referenced by parkinglot_config_read().

Definition at line 525 of file features.c.

#define DEFAULT_FEATURE_DIGIT_TIMEOUT   1000

ms

Definition at line 519 of file features.c.

Referenced by process_config().

ms

Definition at line 520 of file features.c.

Referenced by process_config().

#define DEFAULT_PARK_EXTENSION   "700"

Definition at line 517 of file features.c.

#define DEFAULT_PARK_TIME   45000

ms

Definition at line 516 of file features.c.

#define DEFAULT_TRANSFER_DIGIT_TIMEOUT   3000

ms

Definition at line 518 of file features.c.

Referenced by process_config().

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

Referenced by handle_feature_show().

#define MAX_DIAL_FEATURE_OPTIONS   30

Definition at line 529 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 
OPT_CALLEE_GO_ON 

Definition at line 8004 of file features.c.

     {
   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),
   OPT_CALLEE_GO_ON = (1 << 12),
};
anonymous enum
Enumerator:
OPT_ARG_DURATION_LIMIT 
OPT_ARG_DURATION_STOP 
OPT_ARG_CALLEE_GO_ON 
OPT_ARG_ARRAY_SIZE 

Definition at line 8020 of file features.c.

     {
   OPT_ARG_DURATION_LIMIT = 0,
   OPT_ARG_DURATION_STOP,
   OPT_ARG_CALLEE_GO_ON,
   /* note: this entry _MUST_ be the last one in the enum */
   OPT_ARG_ARRAY_SIZE,
};

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

                           {
   /*! Provide ringing to the parked caller instead of music on hold */
   AST_PARK_OPT_RINGING =   (1 << 0),
   /*! Randomly choose a parking spot for the caller instead of choosing
    *  the first one that is available. */
   AST_PARK_OPT_RANDOMIZE = (1 << 1),
   /*! Do not announce the parking number */
   AST_PARK_OPT_SILENCE = (1 << 2),
};
Enumerator:
FEATURE_INTERPRET_DETECT 
FEATURE_INTERPRET_DO 
FEATURE_INTERPRET_CHECK 

Definition at line 549 of file features.c.

             {
   FEATURE_INTERPRET_DETECT, /* Used by ast_feature_detect */
   FEATURE_INTERPRET_DO,     /* Used by feature_interpret */
   FEATURE_INTERPRET_CHECK,  /* Used by feature_check */
} feature_interpret_op;

Function Documentation

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

Bridge channels together.

Parameters:
s
mMake 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 7336 of file features.c.

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

Referenced by ast_features_init().

{
   const char *channela = astman_get_header(m, "Channel1");
   const char *channelb = astman_get_header(m, "Channel2");
   const char *playtone = astman_get_header(m, "Tone");
   struct ast_channel *chana = NULL, *chanb = NULL, *chans[2];
   struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
   struct ast_bridge_thread_obj *tobj = NULL;
   char buf[256];

   /* make sure valid channels were specified */
   if (ast_strlen_zero(channela) || ast_strlen_zero(channelb)) {
      astman_send_error(s, m, "Missing channel parameter in request");
      return 0;
   }

   /* Start with chana */
   chana = ast_channel_get_by_name_prefix(channela, strlen(channela));
   if (!chana) {
      snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
      astman_send_error(s, m, buf);
      return 0;
   }

   /* Answer the channels if needed */
   if (ast_channel_state(chana) != AST_STATE_UP)
      ast_answer(chana);

   /* create the placeholder channels and grab the other channels */
   if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
      NULL, NULL, ast_channel_linkedid(chana), 0, "Bridge/%s", ast_channel_name(chana)))) {
      astman_send_error(s, m, "Unable to create temporary channel!");
      chana = ast_channel_unref(chana);
      return 0;
   }

   if (do_bridge_masquerade(chana, tmpchana)) {
      snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channela);
      astman_send_error(s, m, buf);
      ast_hangup(tmpchana);
      chana = ast_channel_unref(chana);
      return 0;
   }

   chana = ast_channel_unref(chana);

   /* now do chanb */
   chanb = ast_channel_get_by_name_prefix(channelb, strlen(channelb));
   if (!chanb) {
      snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
      astman_send_error(s, m, buf);
      ast_hangup(tmpchana);
      return 0;
   }

   /* Answer the channels if needed */
   if (ast_channel_state(chanb) != AST_STATE_UP)
      ast_answer(chanb);

   /* create the placeholder channels and grab the other channels */
   if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
      NULL, NULL, ast_channel_linkedid(chanb), 0, "Bridge/%s", ast_channel_name(chanb)))) {
      astman_send_error(s, m, "Unable to create temporary channels!");
      ast_hangup(tmpchana);
      chanb = ast_channel_unref(chanb);
      return 0;
   }

   if (do_bridge_masquerade(chanb, tmpchanb)) {
      snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channelb);
      astman_send_error(s, m, buf);
      ast_hangup(tmpchana);
      ast_hangup(tmpchanb);
      chanb = ast_channel_unref(chanb);
      return 0;
   }

   chanb = ast_channel_unref(chanb);

   /* make the channels compatible, send error if we fail doing so */
   if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
      ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb));
      astman_send_error(s, m, "Could not make channels compatible for manager bridge");
      ast_hangup(tmpchana);
      ast_hangup(tmpchanb);
      return 0;
   }

   /* setup the bridge thread object and start the bridge */
   if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
      ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb), strerror(errno));
      astman_send_error(s, m, "Unable to spawn a new bridge thread");
      ast_hangup(tmpchana);
      ast_hangup(tmpchanb);
      return 0;
   }

   tobj->chan = tmpchana;
   tobj->peer = tmpchanb;
   tobj->return_to_pbx = 1;

   if (ast_true(playtone)) {
      if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, ast_channel_language(tmpchanb))) {
         if (ast_waitstream(tmpchanb, "") < 0)
            ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", ast_channel_name(tmpchanb));
      }
   }

   chans[0] = tmpchana;
   chans[1] = tmpchanb;
   /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when a bridge is successfully created due to a manager action.</synopsis>
         <syntax>
            <parameter name="Response">
               <enumlist>
                  <enum name="Success"/>
                  <enum name="Failed"/>
               </enumlist>
            </parameter>
         </syntax>
         <see-also>
            <ref type="manager">Bridge</ref>
         </see-also>
      </managerEventInstance>
   ***/
   ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeAction", 2, chans,
            "Response: Success\r\n"
            "Channel1: %s\r\n"
            "Channel2: %s\r\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb));

   bridge_call_thread_launch(tobj);

   astman_send_ack(s, m, "Launched bridge thread with success");

   return 0;
}
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 905 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().

{
   struct ast_datastore *datastore;
   struct ast_dial_features *dialfeatures;

   ast_channel_lock(chan);
   datastore = ast_channel_datastore_find(chan, &dial_features_info, NULL);
   ast_channel_unlock(chan);
   if (datastore) {
      /* Already exists. */
      return 1;
   }

   /* Create a new datastore with specified feature flags. */
   datastore = ast_datastore_alloc(&dial_features_info, NULL);
   if (!datastore) {
      ast_log(LOG_WARNING, "Unable to create channel features datastore.\n");
      return 0;
   }
   dialfeatures = ast_calloc(1, sizeof(*dialfeatures));
   if (!dialfeatures) {
      ast_log(LOG_WARNING, "Unable to allocate memory for feature flags.\n");
      ast_datastore_free(datastore);
      return 0;
   }
   ast_copy_flags(&dialfeatures->my_features, my_features, AST_FLAGS_ALL);
   ast_copy_flags(&dialfeatures->peer_features, peer_features, AST_FLAGS_ALL);
   datastore->inheritance = DATASTORE_INHERIT_FOREVER;
   datastore->data = dialfeatures;
   ast_channel_lock(chan);
   ast_channel_datastore_add(chan, datastore);
   ast_channel_unlock(chan);
   return 0;
}
static void add_features_datastores ( struct ast_channel caller,
struct ast_channel callee,
struct ast_bridge_config config 
) [static]

Definition at line 4229 of file features.c.

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

Referenced by ast_bridge_call().

{
   if (add_features_datastore(caller, &config->features_caller, &config->features_callee)) {
      /*
       * If we don't return here, then when we do a builtin_atxfer we
       * will copy the disconnect flags over from the atxfer to the
       * callee (Party C).
       */
      return;
   }

   add_features_datastore(callee, &config->features_callee, &config->features_caller);
}
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:
0on success.
-1on failure.

Definition at line 1213 of file features.c.

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

Referenced by park_call_full().

{
   int res;
   int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
   char tmp[256];
   char *message[5] = {NULL, NULL, NULL, NULL, NULL};

   snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
   message[0] = tmp;
   res = ast_adsi_load_session(chan, NULL, 0, 1);
   if (res == -1)
      return res;
   return ast_adsi_print(chan, message, justify, 1);
}
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:
chanThe bridge considers this channel the caller.
peerThe bridge considers this channel the callee.
configConfiguration for this bridge.

Set start time, check for two channels,check if monitor on check for feature activation, create new CDR

Return values:
reson success.
-1on failure to bridge.

TRUE if h-exten or hangup handlers run.

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

References ast_cdr::accountcode, add_features_datastores(), ast_cdr::amaflags, ast_cdr::answer, ast_autoservice_start(), ast_autoservice_stop(), 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_accountcode(), ast_channel_amaflags(), ast_channel_appl(), ast_channel_bridge(), ast_channel_caller(), ast_channel_cdr(), ast_channel_cdr_set(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), ast_channel_context(), ast_channel_context_set(), ast_channel_data(), ast_channel_exten(), ast_channel_exten_set(), ast_channel_flags(), ast_channel_get_by_name(), ast_channel_lock, ast_channel_lock_both, ast_channel_log(), ast_channel_macrocontext(), AST_CHANNEL_NAME, ast_channel_name(), ast_channel_priority(), ast_channel_priority_set(), ast_channel_redirecting_macro(), ast_channel_redirecting_sub(), ast_channel_sending_dtmf_digit(), ast_channel_sending_dtmf_tv(), ast_channel_set_linkgroup(), ast_channel_setoption(), ast_channel_softhangup_internal_flag(), ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_unref, ast_channel_visible_indication(), ast_check_hangup(), ast_clear_flag, AST_CONTROL_AOC, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_MCID, AST_CONTROL_OPTION, AST_CONTROL_PVT_CAUSE_CODE, 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_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_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_pbx_h_exten_run(), ast_pbx_hangup_handler_run(), ast_raw_answer(), ast_set_flag, AST_SOFTHANGUP_ASYNCGOTO, AST_SOFTHANGUP_UNBRIDGE, AST_STATE_RINGING, AST_STATE_UP, ast_strlen_zero(), ast_test_flag, ast_tvcmp(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), ast_write(), ast_cdr::channel, clear_dialed_interfaces(), ast_frame::data, ast_option_header::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, 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_frame_subclass::integer, ast_cdr::lastapp, ast_cdr::lastdata, ast_frame::len, LOG_DEBUG, LOG_WARNING, monitor_app, monitor_ok, ast_cdr::next, option_debug, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pick_unlocked_cdr(), ast_frame::ptr, S_COR, S_OR, set_bridge_features_on_config(), set_config_flags(), ast_cdr::start, ast_frame::subclass, ast_bridge_config::timelimit, ast_cdr::uniqueid, ast_channel::userfield, and ast_cdr::userfield.

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

{
   /* Copy voice back and forth between the two channels.  Give the peer
      the ability to transfer calls with '#<extension' syntax. */
   struct ast_frame *f;
   struct ast_channel *who;
   char chan_featurecode[FEATURE_MAX_LEN + 1]="";
   char peer_featurecode[FEATURE_MAX_LEN + 1]="";
   char orig_channame[AST_CHANNEL_NAME];
   char orig_peername[AST_CHANNEL_NAME];
   int res;
   int diff;
   int hasfeatures=0;
   int hadfeatures=0;
   int sendingdtmfdigit = 0;
   int we_disabled_peer_cdr = 0;
   struct ast_option_header *aoh;
   struct ast_cdr *bridge_cdr = NULL;
   struct ast_cdr *chan_cdr = ast_channel_cdr(chan); /* the proper chan cdr, if there are forked cdrs */
   struct ast_cdr *peer_cdr = ast_channel_cdr(peer); /* the proper chan cdr, if there are forked cdrs */
   struct ast_cdr *new_chan_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
   struct ast_cdr *new_peer_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
   struct ast_silence_generator *silgen = NULL;
   /*! TRUE if h-exten or hangup handlers run. */
   int hangup_run = 0;

   pbx_builtin_setvar_helper(chan, "BRIDGEPEER", ast_channel_name(peer));
   pbx_builtin_setvar_helper(peer, "BRIDGEPEER", ast_channel_name(chan));

   /* Clear any BLINDTRANSFER since the transfer has completed. */
   pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
   pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", NULL);

   set_bridge_features_on_config(config, pbx_builtin_getvar_helper(chan, "BRIDGE_FEATURES"));
   add_features_datastores(chan, peer, config);

   /* This is an interesting case.  One example is if a ringing channel gets redirected to
    * an extension that picks up a parked call.  This will make sure that the call taken
    * out of parking gets told that the channel it just got bridged to is still ringing. */
   if (ast_channel_state(chan) == AST_STATE_RINGING && ast_channel_visible_indication(peer) != AST_CONTROL_RINGING) {
      ast_indicate(peer, AST_CONTROL_RINGING);
   }

   if (monitor_ok) {
      const char *monitor_exec;
      struct ast_channel *src = NULL;
      if (!monitor_app) {
         if (!(monitor_app = pbx_findapp("Monitor")))
            monitor_ok=0;
      }
      if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR")))
         src = chan;
      else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
         src = peer;
      if (monitor_app && src) {
         char *tmp = ast_strdupa(monitor_exec);
         pbx_exec(src, monitor_app, tmp);
      }
   }

   set_config_flags(chan, config);

   /* Answer if need be */
   if (ast_channel_state(chan) != AST_STATE_UP) {
      if (ast_raw_answer(chan, 1)) {
         return -1;
      }
   }

#ifdef FOR_DEBUG
   /* show the two channels and cdrs involved in the bridge for debug & devel purposes */
   ast_channel_log("Pre-bridge CHAN Channel info", chan);
   ast_channel_log("Pre-bridge PEER Channel info", peer);
#endif
   /* two channels are being marked as linked here */
   ast_channel_set_linkgroup(chan,peer);

   /* copy the userfield from the B-leg to A-leg if applicable */
   if (ast_channel_cdr(chan) && ast_channel_cdr(peer) && !ast_strlen_zero(ast_channel_cdr(peer)->userfield)) {
      char tmp[256];

      ast_channel_lock(chan);
      if (!ast_strlen_zero(ast_channel_cdr(chan)->userfield)) {
         snprintf(tmp, sizeof(tmp), "%s;%s", ast_channel_cdr(chan)->userfield, ast_channel_cdr(peer)->userfield);
         ast_cdr_appenduserfield(chan, tmp);
      } else {
         ast_cdr_setuserfield(chan, ast_channel_cdr(peer)->userfield);
      }
      ast_channel_unlock(chan);
      /* Don't delete the CDR; just disable it. */
      ast_set_flag(ast_channel_cdr(peer), AST_CDR_FLAG_POST_DISABLED);
      we_disabled_peer_cdr = 1;
   }
   ast_copy_string(orig_channame,ast_channel_name(chan),sizeof(orig_channame));
   ast_copy_string(orig_peername,ast_channel_name(peer),sizeof(orig_peername));

   if (!chan_cdr || (chan_cdr && !ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED))) {
      ast_channel_lock_both(chan, peer);
      if (chan_cdr) {
         ast_set_flag(chan_cdr, AST_CDR_FLAG_MAIN);
         ast_cdr_update(chan);
         bridge_cdr = ast_cdr_dup_unique_swap(chan_cdr);
         /* rip any forked CDR's off of the chan_cdr and attach
          * them to the bridge_cdr instead */
         bridge_cdr->next = chan_cdr->next;
         chan_cdr->next = NULL;
         ast_copy_string(bridge_cdr->lastapp, S_OR(ast_channel_appl(chan), ""), sizeof(bridge_cdr->lastapp));
         ast_copy_string(bridge_cdr->lastdata, S_OR(ast_channel_data(chan), ""), sizeof(bridge_cdr->lastdata));
         if (peer_cdr && !ast_strlen_zero(peer_cdr->userfield)) {
            ast_copy_string(bridge_cdr->userfield, peer_cdr->userfield, sizeof(bridge_cdr->userfield));
         }
         ast_cdr_setaccount(peer, ast_channel_accountcode(chan));
      } else {
         /* better yet, in a xfer situation, find out why the chan cdr got zapped (pun unintentional) */
         bridge_cdr = ast_cdr_alloc(); /* this should be really, really rare/impossible? */
         ast_copy_string(bridge_cdr->channel, ast_channel_name(chan), sizeof(bridge_cdr->channel));
         ast_copy_string(bridge_cdr->dstchannel, ast_channel_name(peer), sizeof(bridge_cdr->dstchannel));
         ast_copy_string(bridge_cdr->uniqueid, ast_channel_uniqueid(chan), sizeof(bridge_cdr->uniqueid));
         ast_copy_string(bridge_cdr->lastapp, S_OR(ast_channel_appl(chan), ""), sizeof(bridge_cdr->lastapp));
         ast_copy_string(bridge_cdr->lastdata, S_OR(ast_channel_data(chan), ""), sizeof(bridge_cdr->lastdata));
         ast_cdr_setcid(bridge_cdr, chan);
         bridge_cdr->disposition = (ast_channel_state(chan) == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NULL;
         bridge_cdr->amaflags = ast_channel_amaflags(chan) ? ast_channel_amaflags(chan) :  ast_default_amaflags;
         ast_copy_string(bridge_cdr->accountcode, ast_channel_accountcode(chan), sizeof(bridge_cdr->accountcode));
         /* Destination information */
         ast_copy_string(bridge_cdr->dst, ast_channel_exten(chan), sizeof(bridge_cdr->dst));
         ast_copy_string(bridge_cdr->dcontext, ast_channel_context(chan), sizeof(bridge_cdr->dcontext));
         if (peer_cdr) {
            bridge_cdr->start = peer_cdr->start;
            ast_copy_string(bridge_cdr->userfield, peer_cdr->userfield, sizeof(bridge_cdr->userfield));
         } else {
            ast_cdr_start(bridge_cdr);
         }
      }
      ast_channel_unlock(chan);
      ast_channel_unlock(peer);

      ast_debug(4, "bridge answer set, chan answer set\n");
      /* peer_cdr->answer will be set when a macro runs on the peer;
         in that case, the bridge answer will be delayed while the
         macro plays on the peer channel. The peer answered the call
         before the macro started playing. To the phone system,
         this is billable time for the call, even tho the caller
         hears nothing but ringing while the macro does its thing. */

      /* Another case where the peer cdr's time will be set, is when
         A self-parks by pickup up phone and dialing 700, then B
         picks up A by dialing its parking slot; there may be more
         practical paths that get the same result, tho... in which
         case you get the previous answer time from the Park... which
         is before the bridge's start time, so I added in the
         tvcmp check to the if below */

      if (peer_cdr && !ast_tvzero(peer_cdr->answer) && ast_tvcmp(peer_cdr->answer, bridge_cdr->start) >= 0) {
         ast_cdr_setanswer(bridge_cdr, peer_cdr->answer);
         ast_cdr_setdisposition(bridge_cdr, peer_cdr->disposition);
         if (chan_cdr) {
            ast_cdr_setanswer(chan_cdr, peer_cdr->answer);
            ast_cdr_setdisposition(chan_cdr, peer_cdr->disposition);
         }
      } else {
         ast_cdr_answer(bridge_cdr);
         if (chan_cdr) {
            ast_cdr_answer(chan_cdr); /* for the sake of cli status checks */
         }
      }
      if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT) && (chan_cdr || peer_cdr)) {
         if (chan_cdr) {
            ast_set_flag(chan_cdr, AST_CDR_FLAG_BRIDGED);
         }
         if (peer_cdr) {
            ast_set_flag(peer_cdr, AST_CDR_FLAG_BRIDGED);
         }
      }
      /* the DIALED flag may be set if a dialed channel is transferred
       * and then bridged to another channel.  In order for the
       * bridge CDR to be written, the DIALED flag must not be
       * present. */
      ast_clear_flag(bridge_cdr, AST_CDR_FLAG_DIALED);
   }
   ast_cel_report_event(chan, AST_CEL_BRIDGE_START, NULL, NULL, peer);

   /* If we are bridging a call, stop worrying about forwarding loops. We presume that if
    * a call is being bridged, that the humans in charge know what they're doing. If they
    * don't, well, what can we do about that? */
   clear_dialed_interfaces(chan);
   clear_dialed_interfaces(peer);

   for (;;) {
      struct ast_channel *other; /* used later */

      res = ast_channel_bridge(chan, peer, config, &f, &who);

      if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
         || ast_test_flag(ast_channel_flags(peer), AST_FLAG_ZOMBIE)) {
         /* Zombies are present time to leave! */
         res = -1;
         if (f) {
            ast_frfree(f);
         }
         goto before_you_go;
      }

      /* When frame is not set, we are probably involved in a situation
         where we've timed out.
         When frame is set, we'll come this code twice; once for DTMF_BEGIN
         and also for DTMF_END. If we flow into the following 'if' for both, then
         our wait times are cut in half, as both will subtract from the
         feature_timer. Not good!
      */
      if (config->feature_timer && (!f || f->frametype == AST_FRAME_DTMF_END)) {
         /* Update feature timer for next pass */
         diff = ast_tvdiff_ms(ast_tvnow(), config->feature_start_time);
         if (res == AST_BRIDGE_RETRY) {
            /* The feature fully timed out but has not been updated. Skip
             * the potential round error from the diff calculation and
             * explicitly set to expired. */
            config->feature_timer = -1;
         } else {
            config->feature_timer -= diff;
         }

         if (hasfeatures) {
            if (config->feature_timer <= 0) {
               /* Not *really* out of time, just out of time for
                  digits to come in for features. */
               ast_debug(1, "Timed out for feature!\n");
               if (!ast_strlen_zero(peer_featurecode)) {
                  ast_dtmf_stream(chan, peer, peer_featurecode, 0, f ? f->len : 0);
                  memset(peer_featurecode, 0, sizeof(peer_featurecode));
               }
               if (!ast_strlen_zero(chan_featurecode)) {
                  ast_dtmf_stream(peer, chan, chan_featurecode, 0, f ? f->len : 0);
                  memset(chan_featurecode, 0, sizeof(chan_featurecode));
               }
               if (f)
                  ast_frfree(f);
               hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
               if (!hasfeatures) {
                  /* No more digits expected - reset the timer */
                  config->feature_timer = 0;
               }
               hadfeatures = hasfeatures;
               /* Continue as we were */
               continue;
            } else if (!f) {
               /* The bridge returned without a frame and there is a feature in progress.
                * However, we don't think the feature has quite yet timed out, so just
                * go back into the bridge. */
               continue;
            }
         } else {
            if (config->feature_timer <=0) {
               /* We ran out of time */
               config->feature_timer = 0;
               who = chan;
               if (f)
                  ast_frfree(f);
               f = NULL;
               res = 0;
            }
         }
      }
      if (res < 0) {
         if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) && !ast_test_flag(ast_channel_flags(peer), AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer)) {
            ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", ast_channel_name(chan), ast_channel_name(peer));
         }
         goto before_you_go;
      }

      if (!f || (f->frametype == AST_FRAME_CONTROL &&
            (f->subclass.integer == AST_CONTROL_HANGUP || f->subclass.integer == AST_CONTROL_BUSY ||
               f->subclass.integer == AST_CONTROL_CONGESTION))) {
         res = -1;
         break;
      }
      /* many things should be sent to the 'other' channel */
      other = (who == chan) ? peer : chan;
      if (f->frametype == AST_FRAME_CONTROL) {
         switch (f->subclass.integer) {
         case AST_CONTROL_RINGING:
         case AST_CONTROL_FLASH:
         case AST_CONTROL_MCID:
         case -1:
            ast_indicate(other, f->subclass.integer);
            break;
         case AST_CONTROL_CONNECTED_LINE:
            if (ast_channel_connected_line_sub(who, other, f, 1) &&
               ast_channel_connected_line_macro(who, other, f, who != chan, 1)) {
               ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
            }
            break;
         case AST_CONTROL_REDIRECTING:
            if (ast_channel_redirecting_sub(who, other, f, 1) &&
               ast_channel_redirecting_macro(who, other, f, who != chan, 1)) {
               ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
            }
            break;
         case AST_CONTROL_PVT_CAUSE_CODE:
         case AST_CONTROL_AOC:
         case AST_CONTROL_HOLD:
         case AST_CONTROL_UNHOLD:
            ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
            break;
         case AST_CONTROL_OPTION:
            aoh = f->data.ptr;
            /* Forward option Requests, but only ones we know are safe
             * These are ONLY sent by chan_iax2 and I'm not convinced that
             * they are useful. I haven't deleted them entirely because I
             * just am not sure of the ramifications of removing them. */
            if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
               switch (ntohs(aoh->option)) {
               case AST_OPTION_TONE_VERIFY:
               case AST_OPTION_TDD:
               case AST_OPTION_RELAXDTMF:
               case AST_OPTION_AUDIO_MODE:
               case AST_OPTION_DIGIT_DETECT:
               case AST_OPTION_FAX_DETECT:
                  ast_channel_setoption(other, ntohs(aoh->option), aoh->data,
                     f->datalen - sizeof(struct ast_option_header), 0);
               }
            }
            break;
         }
      } else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
         struct ast_flags *cfg;
         char dtmfcode[2] = { f->subclass.integer, };
         size_t featurelen;

         if (who == chan) {
            featurelen = strlen(chan_featurecode);
            cfg = &(config->features_caller);
         } else {
            featurelen = strlen(peer_featurecode);
            cfg = &(config->features_callee);
         }
         /* Take a peek if this (possibly) matches a feature. If not, just pass this
          * DTMF along untouched. If this is not the first digit of a multi-digit code
          * then we need to fall through and stream the characters if it matches */
         if (featurelen == 0
            && feature_check(chan, cfg, &dtmfcode[0]) == AST_FEATURE_RETURN_PASSDIGITS) {
            if (option_debug > 3) {
               ast_log(LOG_DEBUG, "Passing DTMF through, since it is not a feature code\n");
            }
            ast_write(other, f);
            sendingdtmfdigit = 1;
         } else {
            /* If ast_opt_transmit_silence is set, then we need to make sure we are
             * transmitting something while we hold on to the DTMF waiting for a
             * feature. */
            if (!silgen && ast_opt_transmit_silence) {
               silgen = ast_channel_start_silence_generator(other);
            }
            if (option_debug > 3) {
               ast_log(LOG_DEBUG, "Not passing DTMF through, since it may be a feature code\n");
            }
         }
      } else if (f->frametype == AST_FRAME_DTMF_END) {
         char *featurecode;
         int sense;
         unsigned int dtmfduration = f->len;

         hadfeatures = hasfeatures;
         /* This cannot overrun because the longest feature is one shorter than our buffer */
         if (who == chan) {
            sense = FEATURE_SENSE_CHAN;
            featurecode = chan_featurecode;
         } else  {
            sense = FEATURE_SENSE_PEER;
            featurecode = peer_featurecode;
         }

         if (sendingdtmfdigit == 1) {
            /* We let the BEGIN go through happily, so let's not bother with the END,
             * since we already know it's not something we bother with */
            ast_write(other, f);
            sendingdtmfdigit = 0;
         } else {
            /*! 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 ?
             */
            featurecode[strlen(featurecode)] = f->subclass.integer;
            /* Get rid of the frame before we start doing "stuff" with the channels */
            ast_frfree(f);
            f = NULL;
            if (silgen) {
               ast_channel_stop_silence_generator(other, silgen);
               silgen = NULL;
            }
            config->feature_timer = 0;
            res = feature_interpret(chan, peer, config, featurecode, sense);
            switch(res) {
            case AST_FEATURE_RETURN_PASSDIGITS:
               ast_dtmf_stream(other, who, featurecode, 0, dtmfduration);
               /* Fall through */
            case AST_FEATURE_RETURN_SUCCESS:
               memset(featurecode, 0, sizeof(chan_featurecode));
               break;
            }
            if (res >= AST_FEATURE_RETURN_PASSDIGITS) {
               res = 0;
            } else {
               break;
            }
            hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
            if (hadfeatures && !hasfeatures) {
               /* Feature completed or timed out */
               config->feature_timer = 0;
            } else if (hasfeatures) {
               if (config->timelimit) {
                  /* No warning next time - we are waiting for feature code */
                  ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
               }
               config->feature_start_time = ast_tvnow();
               config->feature_timer = featuredigittimeout;
               ast_debug(1, "Set feature timer to %ld ms\n", config->feature_timer);
            }
         }
      }
      if (f)
         ast_frfree(f);
   }
   ast_cel_report_event(chan, AST_CEL_BRIDGE_END, NULL, NULL, peer);

before_you_go:
   if (ast_channel_sending_dtmf_digit(chan)) {
      ast_bridge_end_dtmf(chan, ast_channel_sending_dtmf_digit(chan),
         ast_channel_sending_dtmf_tv(chan), "bridge end");
   }
   if (ast_channel_sending_dtmf_digit(peer)) {
      ast_bridge_end_dtmf(peer, ast_channel_sending_dtmf_digit(peer),
         ast_channel_sending_dtmf_tv(peer), "bridge end");
   }

   /* Just in case something weird happened and we didn't clean up the silence generator... */
   if (silgen) {
      ast_channel_stop_silence_generator(who == chan ? peer : chan, silgen);
      silgen = NULL;
   }

   /* Wait for any dual redirect to complete. */
   while (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) {
      sched_yield();
   }

   if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT)) {
      ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT); /* its job is done */
      if (bridge_cdr) {
         ast_cdr_discard(bridge_cdr);
         /* QUESTION: should we copy bridge_cdr fields to the peer before we throw it away? */
      }
      return res; /* if we shouldn't do the h-exten, we shouldn't do the bridge cdr, either! */
   }

   if (config->end_bridge_callback) {
      config->end_bridge_callback(config->end_bridge_callback_data);
   }

   /* run the hangup exten on the chan object IFF it was NOT involved in a parking situation
    * if it were, then chan belongs to a different thread now, and might have been hung up long
    * ago.
    */
   if (!(ast_channel_softhangup_internal_flag(chan) & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE))
         && !ast_test_flag(&config->features_caller, AST_FEATURE_NO_H_EXTEN)) {
      struct ast_cdr *swapper = NULL;
      char savelastapp[AST_MAX_EXTENSION];
      char savelastdata[AST_MAX_EXTENSION];
      char save_context[AST_MAX_CONTEXT];
      char save_exten[AST_MAX_EXTENSION];
      int  save_prio;

      ast_channel_lock(chan);
      if (bridge_cdr) {
         /*
          * Swap the bridge_cdr and the chan cdr for a moment, and let
          * the hangup dialplan code operate on it.
          */
         swapper = ast_channel_cdr(chan);
         ast_channel_cdr_set(chan, bridge_cdr);

         /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */
         ast_copy_string(savelastapp, bridge_cdr->lastapp, sizeof(bridge_cdr->lastapp));
         ast_copy_string(savelastdata, bridge_cdr->lastdata, sizeof(bridge_cdr->lastdata));
      }
      ast_copy_string(save_context, ast_channel_context(chan), sizeof(save_context));
      ast_copy_string(save_exten, ast_channel_exten(chan), sizeof(save_exten));
      save_prio = ast_channel_priority(chan);
      ast_channel_unlock(chan);

      ast_autoservice_start(peer);
      if (ast_exists_extension(chan, ast_channel_context(chan), "h", 1,
         S_COR(ast_channel_caller(chan)->id.number.valid,
            ast_channel_caller(chan)->id.number.str, NULL))) {
         ast_pbx_h_exten_run(chan, ast_channel_context(chan));
         hangup_run = 1;
      } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
         && ast_exists_extension(chan, ast_channel_macrocontext(chan), "h", 1,
            S_COR(ast_channel_caller(chan)->id.number.valid,
               ast_channel_caller(chan)->id.number.str, NULL))) {
         ast_pbx_h_exten_run(chan, ast_channel_macrocontext(chan));
         hangup_run = 1;
      }
      if (ast_pbx_hangup_handler_run(chan)) {
         /* Indicate hangup handlers were run. */
         hangup_run = 1;
      }
      ast_autoservice_stop(peer);

      ast_channel_lock(chan);

      /* swap it back */
      ast_channel_context_set(chan, save_context);
      ast_channel_exten_set(chan, save_exten);
      ast_channel_priority_set(chan, save_prio);
      if (bridge_cdr) {
         if (ast_channel_cdr(chan) == bridge_cdr) {
            ast_channel_cdr_set(chan, swapper);

            /* Restore the lastapp/lastdata */
            ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp));
            ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
         } else {
            bridge_cdr = NULL;
         }
      }
      ast_channel_unlock(chan);
   }

   /* obey the NoCDR() wishes. -- move the DISABLED flag to the bridge CDR if it was set on the channel during the bridge... */
   new_chan_cdr = pick_unlocked_cdr(ast_channel_cdr(chan)); /* the proper chan cdr, if there are forked cdrs */

   /*
    * If the channel CDR has been modified during the call, record
    * the changes in the bridge cdr, BUT, if hangup_run, the CDR
    * got swapped so don't overwrite what was done in the
    * h-extension or hangup handlers.  What a mess.  This is why
    * you never touch CDR code.
    */
   if (new_chan_cdr && bridge_cdr && !hangup_run) {
      ast_cdr_copy_vars(bridge_cdr, new_chan_cdr);
      ast_copy_string(bridge_cdr->userfield, new_chan_cdr->userfield, sizeof(bridge_cdr->userfield));
      bridge_cdr->amaflags = new_chan_cdr->amaflags;
      ast_copy_string(bridge_cdr->accountcode, new_chan_cdr->accountcode, sizeof(bridge_cdr->accountcode));
      if (ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED)) {
         ast_set_flag(bridge_cdr, AST_CDR_FLAG_POST_DISABLED);
      }
   }

   /* we can post the bridge CDR at this point */
   if (bridge_cdr) {
      ast_cdr_end(bridge_cdr);
      ast_cdr_detach(bridge_cdr);
   }

   /* do a specialized reset on the beginning channel
      CDR's, if they still exist, so as not to mess up
      issues in future bridges;

      Here are the rules of the game:
      1. The chan and peer channel pointers will not change
         during the life of the bridge.
      2. But, in transfers, the channel names will change.
         between the time the bridge is started, and the
         time the channel ends.
         Usually, when a channel changes names, it will
         also change CDR pointers.
      3. Usually, only one of the two channels (chan or peer)
         will change names.
      4. Usually, if a channel changes names during a bridge,
         it is because of a transfer. Usually, in these situations,
         it is normal to see 2 bridges running simultaneously, and
         it is not unusual to see the two channels that change
         swapped between bridges.
      5. After a bridge occurs, we have 2 or 3 channels' CDRs
         to attend to; if the chan or peer changed names,
         we have the before and after attached CDR's.
   */

   if (new_chan_cdr) {
      struct ast_channel *chan_ptr = NULL;

      if (strcasecmp(orig_channame, ast_channel_name(chan)) != 0) {
         /* old channel */
         if ((chan_ptr = ast_channel_get_by_name(orig_channame))) {
            ast_channel_lock(chan_ptr);
            if (!ast_bridged_channel(chan_ptr)) {
               struct ast_cdr *cur;
               for (cur = ast_channel_cdr(chan_ptr); cur; cur = cur->next) {
                  if (cur == chan_cdr) {
                     break;
                  }
               }
               if (cur) {
                  ast_cdr_specialized_reset(chan_cdr, 0);
               }
            }
            ast_channel_unlock(chan_ptr);
            chan_ptr = ast_channel_unref(chan_ptr);
         }
         /* new channel */
         ast_cdr_specialized_reset(new_chan_cdr, 0);
      } else {
         ast_cdr_specialized_reset(ast_channel_cdr(chan), 0); /* nothing changed, reset the chan cdr  */
      }
   }

   {
      struct ast_channel *chan_ptr = NULL;
      new_peer_cdr = pick_unlocked_cdr(ast_channel_cdr(peer)); /* the proper chan cdr, if there are forked cdrs */
      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))
         ast_set_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED); /* DISABLED is viral-- it will propagate across a bridge */
      if (strcasecmp(orig_peername, ast_channel_name(peer)) != 0) {
         /* old channel */
         if ((chan_ptr = ast_channel_get_by_name(orig_peername))) {
            ast_channel_lock(chan_ptr);
            if (!ast_bridged_channel(chan_ptr)) {
               struct ast_cdr *cur;
               for (cur = ast_channel_cdr(chan_ptr); cur; cur = cur->next) {
                  if (cur == peer_cdr) {
                     break;
                  }
               }
               if (cur) {
                  ast_cdr_specialized_reset(peer_cdr, 0);
               }
            }
            ast_channel_unlock(chan_ptr);
            chan_ptr = ast_channel_unref(chan_ptr);
         }
         /* new channel */
         if (new_peer_cdr) {
            ast_cdr_specialized_reset(new_peer_cdr, 0);
         }
      } else {
         if (we_disabled_peer_cdr) {
            ast_clear_flag(ast_channel_cdr(peer), AST_CDR_FLAG_POST_DISABLED);
         }
         ast_cdr_specialized_reset(ast_channel_cdr(peer), 0); /* nothing changed, reset the peer cdr  */
      }
   }

   return res;
}
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:
chanChannel sending DTMF that has not ended.
digitDTMF digit to stop.
startDTMF digit start time.
whyReason bridge broken.
Returns:
Nothing

Definition at line 4259 of file features.c.

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

{
   int dead;
   long duration;

   ast_channel_lock(chan);
   dead = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
      || (ast_channel_softhangup_internal_flag(chan)
         & ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE));
   ast_channel_unlock(chan);
   if (dead) {
      /* Channel is a zombie or a real hangup. */
      return;
   }

   duration = ast_tvdiff_ms(ast_tvnow(), start);
   ast_senddigit_end(chan, digit, duration);
   ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n",
      digit, ast_channel_name(chan), why, duration);
}
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 8044 of file features.c.

References ast_channel_lock, ast_channel_unlock, AST_FEATURE_PLAY_WARNING, ast_log(), ast_set_flag, ast_strdup, 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().

{
   char *stringp = ast_strdupa(parse);
   char *limit_str, *warning_str, *warnfreq_str;
   const char *var;
   int play_to_caller = 0, play_to_callee = 0;
   int delta;

   limit_str = strsep(&stringp, ":");
   warning_str = strsep(&stringp, ":");
   warnfreq_str = strsep(&stringp, ":");

   config->timelimit = atol(limit_str);
   if (warning_str)
      config->play_warning = atol(warning_str);
   if (warnfreq_str)
      config->warning_freq = atol(warnfreq_str);

   if (!config->timelimit) {
      ast_log(LOG_WARNING, "Bridge does not accept L(%s), hanging up.\n", limit_str);
      config->timelimit = config->play_warning = config->warning_freq = 0;
      config->warning_sound = NULL;
      return -1; /* error */
   } else if ( (delta = config->play_warning - config->timelimit) > 0) {
      int w = config->warning_freq;

      /*
       * If the first warning is requested _after_ the entire call
       * would end, and no warning frequency is requested, then turn
       * off the warning. If a warning frequency is requested, reduce
       * the 'first warning' time by that frequency until it falls
       * within the call's total time limit.
       *
       * Graphically:
       *                timelim->|    delta        |<-playwarning
       *      0__________________|_________________|
       *                       | w  |    |    |    |
       *
       * so the number of intervals to cut is 1+(delta-1)/w
       */
      if (w == 0) {
         config->play_warning = 0;
      } else {
         config->play_warning -= w * ( 1 + (delta-1)/w );
         if (config->play_warning < 1)
            config->play_warning = config->warning_freq = 0;
      }
   }

   ast_channel_lock(chan);

   var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLER");
   play_to_caller = var ? ast_true(var) : 1;

   var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLEE");
   play_to_callee = var ? ast_true(var) : 0;

   if (!play_to_caller && !play_to_callee)
      play_to_caller = 1;

   var = pbx_builtin_getvar_helper(chan, "LIMIT_WARNING_FILE");
   config->warning_sound = !ast_strlen_zero(var) ? ast_strdup(var) : ast_strdup("timeleft");

   /* The code looking at config wants a NULL, not just "", to decide
    * that the message should not be played, so we replace "" with NULL.
    * Note, pbx_builtin_getvar_helper _can_ return NULL if the variable is
    * not found.
    */

   var = pbx_builtin_getvar_helper(chan, "LIMIT_TIMEOUT_FILE");
   config->end_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;

   var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
   config->start_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;

   ast_channel_unlock(chan);

   /* undo effect of S(x) in case they are both used */
   calldurationlimit->tv_sec = 0;
   calldurationlimit->tv_usec = 0;

   /* more efficient to do it like S(x) does since no advanced opts */
   if (!config->play_warning && !config->start_sound && !config->end_sound && config->timelimit) {
      calldurationlimit->tv_sec = config->timelimit / 1000;
      calldurationlimit->tv_usec = (config->timelimit % 1000) * 1000;
      ast_verb(3, "Setting call duration limit to %.3lf seconds.\n",
         calldurationlimit->tv_sec + calldurationlimit->tv_usec / 1000000.0);
      config->timelimit = play_to_caller = play_to_callee =
      config->play_warning = config->warning_freq = 0;
   } else {
      ast_verb(4, "Limit Data for this call:\n");
      ast_verb(4, "timelimit      = %ld ms (%.3lf s)\n", config->timelimit, config->timelimit / 1000.0);
      ast_verb(4, "play_warning   = %ld ms (%.3lf s)\n", config->play_warning, config->play_warning / 1000.0);
      ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
      ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
      ast_verb(4, "warning_freq   = %ld ms (%.3lf s)\n", config->warning_freq, config->warning_freq / 1000.0);
      ast_verb(4, "start_sound    = %s\n", S_OR(config->start_sound, ""));
      ast_verb(4, "warning_sound  = %s\n", config->warning_sound);
      ast_verb(4, "end_sound      = %s\n", S_OR(config->end_sound, ""));
   }
   if (play_to_caller)
      ast_set_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
   if (play_to_callee)
      ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
   return 0;
}
int ast_can_pickup ( struct ast_channel chan)

Test if a channel can be picked up.

Parameters:
chanChannel 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 7752 of file features.c.

References ast_channel_datastore_find(), ast_channel_flags(), ast_channel_masq(), ast_channel_pbx(), AST_FLAG_ZOMBIE, AST_STATE_DOWN, AST_STATE_RING, AST_STATE_RINGING, and ast_test_flag.

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

{
   if (!ast_channel_pbx(chan) && !ast_channel_masq(chan) && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
      && (ast_channel_state(chan) == AST_STATE_RINGING
         || ast_channel_state(chan) == AST_STATE_RING
         /*
          * Check the down state as well because some SIP devices do not
          * give 180 ringing when they can just give 183 session progress
          * instead.  Issue 14005.  (Some ISDN switches as well for that
          * matter.)
          */
         || ast_channel_state(chan) == AST_STATE_DOWN)
      && !ast_channel_datastore_find(chan, &pickup_active, NULL)) {
      return 1;
   }
   return 0;
}
void ast_channel_log ( char *  title,
struct ast_channel chan 
)

Definition at line 4162 of file features.c.

References ast_channel_accountcode(), ast_channel_amaflags(), ast_channel_appl(), ast_channel_cdr(), ast_channel_context(), ast_channel_data(), ast_channel_dialcontext(), ast_channel_exten(), ast_channel_internal_bridged_channel(), ast_channel_linkedid(), ast_channel_macrocontext(), ast_channel_macroexten(), ast_channel_macropriority(), ast_channel_masq(), ast_channel_masqr(), ast_channel_name(), ast_channel_priority(), ast_channel_uniqueid(), ast_log(), and LOG_NOTICE.

Referenced by ast_bridge_call().

{
   ast_log(LOG_NOTICE, "______ %s (%lx)______\n", title, (unsigned long) chan);
   ast_log(LOG_NOTICE, "CHAN: name: %s;  appl: %s; data: %s; contxt: %s;  exten: %s; pri: %d;\n",
      ast_channel_name(chan), ast_channel_appl(chan), ast_channel_data(chan), ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
   ast_log(LOG_NOTICE, "CHAN: acctcode: %s;  dialcontext: %s; amaflags: %x; maccontxt: %s;  macexten: %s; macpri: %d;\n",
      ast_channel_accountcode(chan), ast_channel_dialcontext(chan), ast_channel_amaflags(chan), ast_channel_macrocontext(chan), ast_channel_macroexten(chan), ast_channel_macropriority(chan));
   ast_log(LOG_NOTICE, "CHAN: masq: %p;  masqr: %p; _bridge: %p; uniqueID: %s; linkedID:%s\n",
      ast_channel_masq(chan), ast_channel_masqr(chan),
      ast_channel_internal_bridged_channel(chan), ast_channel_uniqueid(chan), ast_channel_linkedid(chan));
   if (ast_channel_masqr(chan)) {
      ast_log(LOG_NOTICE, "CHAN: masquerading as: %s;  cdr: %p;\n",
         ast_channel_name(ast_channel_masqr(chan)), ast_channel_cdr(ast_channel_masqr(chan)));
   }
   if (ast_channel_internal_bridged_channel(chan)) {
      ast_log(LOG_NOTICE, "CHAN: Bridged to %s\n", ast_channel_name(ast_channel_internal_bridged_channel(chan)));
   }

   ast_log(LOG_NOTICE, "===== done ====\n");
}
int ast_do_pickup ( struct ast_channel chan,
struct ast_channel target 
)

Pickup a call target.

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

< A masquerade changes channel names.

< A masquerade changes channel names.

Definition at line 7910 of file features.c.

References ast_answer(), AST_CAUSE_ANSWERED_ELSEWHERE, AST_CEL_PICKUP, ast_cel_report_event(), ast_channel_caller(), ast_channel_connected(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), ast_channel_datastore_add(), ast_channel_datastore_remove(), ast_channel_hangupcause_set(), ast_channel_lock, ast_channel_masquerade(), ast_channel_name(), ast_channel_queue_connected_line_update(), ast_channel_unlock, ast_channel_update_connected_line(), ast_connected_line_copy_from_caller(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, AST_CONTROL_ANSWER, ast_datastore_alloc(), ast_datastore_free(), ast_debug, ast_do_masquerade(), ast_log(), ast_manager_event_multichan, ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_party_id_reset(), ast_queue_control(), EVENT_FLAG_CALL, LOG_WARNING, ast_party_connected_line::priv, 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().

{
   struct ast_party_connected_line connected_caller;
   struct ast_channel *chans[2] = { chan, target };
   struct ast_datastore *ds_pickup;
   const char *chan_name;/*!< A masquerade changes channel names. */
   const char *target_name;/*!< A masquerade changes channel names. */
   int res = -1;

   target_name = ast_strdupa(ast_channel_name(target));
   ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, ast_channel_name(chan));

   /* Mark the target to block any call pickup race. */
   ds_pickup = ast_datastore_alloc(&pickup_active, NULL);
   if (!ds_pickup) {
      ast_log(LOG_WARNING,
         "Unable to create channel datastore on '%s' for call pickup\n", target_name);
      return -1;
   }
   ast_channel_datastore_add(target, ds_pickup);

   ast_party_connected_line_init(&connected_caller);
   ast_party_connected_line_copy(&connected_caller, ast_channel_connected(target));
   ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */
   /* Reset any earlier private connected id representation */
   ast_party_id_reset(&connected_caller.priv);

   connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
   if (ast_channel_connected_line_sub(NULL, chan, &connected_caller, 0) &&
      ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
      ast_channel_update_connected_line(chan, &connected_caller, NULL);
   }
   ast_party_connected_line_free(&connected_caller);

   ast_channel_lock(chan);
   chan_name = ast_strdupa(ast_channel_name(chan));
   ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(chan));
   ast_channel_unlock(chan);
   connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;

   ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan);

   if (ast_answer(chan)) {
      ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
      goto pickup_failed;
   }

   if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
      ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name);
      goto pickup_failed;
   }

   ast_channel_queue_connected_line_update(chan, &connected_caller, NULL);

   /* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */
   ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE);

   if (ast_channel_masquerade(target, chan)) {
      ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name,
         target_name);
      goto pickup_failed;
   }

   /* If you want UniqueIDs, set channelvars in manager.conf to CHANNEL(uniqueid) */
   /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when a call pickup occurs.</synopsis>
         <syntax>
            <parameter name="Channel"><para>The name of the channel that initiated the pickup.</para></parameter>
            <parameter name="TargetChannel"><para>The name of the channel that is being picked up.</para></parameter>
         </syntax>
      </managerEventInstance>
   ***/
   ast_manager_event_multichan(EVENT_FLAG_CALL, "Pickup", 2, chans,
      "Channel: %s\r\n"
      "TargetChannel: %s\r\n",
      chan_name, target_name);

   /* Do the masquerade manually to make sure that it is completed. */
   ast_do_masquerade(target);
   res = 0;

pickup_failed:
   ast_channel_lock(target);
   if (!ast_channel_datastore_remove(target, ds_pickup)) {
      ast_datastore_free(ds_pickup);
   }
   ast_party_connected_line_free(&connected_caller);

   return res;
}
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
featuresan ast_flags ptr
codeptr of input code
feature
Return values:
ast_call_featureptr to be set if found

Definition at line 3746 of file features.c.

References FEATURE_INTERPRET_DETECT, and feature_interpret_helper().

Referenced by detect_disconnect().

                                                                                                                                 {

   return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_DETECT, feature);
}
int ast_features_init ( void  )

Provided by features.c

Definition at line 9070 of file features.c.

References __ast_custom_function_register(), action_bridge(), ao2_container_alloc, ARRAY_LEN, ast_cli_register_multiple(), ast_devstate_prov_add(), ast_manager_register_xml_core, 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(), manager_parkinglot_list(), metermaidstate(), park_call_exec(), parkcall, parked_call_exec(), parking_thread, parkinglot_cmp_cb(), parkinglot_hash_cb(), and parkinglots.

Referenced by main().

int ast_features_reload ( void  )

Reload call features from features.conf.

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

{
   struct ast_context *con;
   int res;

   ast_mutex_lock(&features_reload_lock);/* Searialize reloading features.conf */

   /*
    * Always destroy the parking_con_dial context to remove buildup
    * of recalled extensions in the context.  At worst, the parked
    * call gets hungup attempting to run an invalid extension when
    * we are trying to callback the parker or the preset return
    * extension.  This is a small window of opportunity on an
    * execution chain that is not expected to happen very often.
    */
   con = ast_context_find(parking_con_dial);
   if (con) {
      ast_context_destroy(con, registrar);
   }

   res = load_config(1);
   ast_mutex_unlock(&features_reload_lock);

   return res;
}
struct ast_call_feature* ast_find_call_feature ( const char *  name) [read]

look for a call feature entry by its sname

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

Definition at line 3322 of file features.c.

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

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

{
   int x;
   for (x = 0; x < FEATURES_COUNT; x++) {
      if (!strcasecmp(name, builtin_features[x].sname))
         return &builtin_features[x];
   }

   return find_dynamic_feature(name);
}
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_meChannel to be parked.
parkerChannel parking the call.
timeoutis a timeout in milliseconds
extoutis 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:
0on success.
-1on failure.

Definition at line 2028 of file features.c.

References ast_channel_name(), ast_park_call_args::extout, 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().

{
   struct ast_park_call_args args = {
      .timeout = timeout,
      .extout = extout,
   };

   if (peer) {
      args.orig_chan_name = ast_strdupa(ast_channel_name(peer));
   }
   return masq_park_call(rchan, peer, &args);
}
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_meChannel to be parked.
parkerChannel parking the call.
park_extenParking lot access extension
park_contextParking lot context
timeoutis a timeout in milliseconds
extoutis 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:
0on success.
-1on failure.

Definition at line 1974 of file features.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_channel_name(), ast_get_extension_app_data(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), create_dynamic_parkinglot(), feature_group_exten::exten, ast_park_call_args::extout, 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().

{
   int res;
   char *parse;
   const char *app_data;
   struct ast_exten *exten;
   struct park_app_args app_args;
   struct ast_park_call_args args = {
      .timeout = timeout,
      .extout = extout,
   };

   if (parker) {
      args.orig_chan_name = ast_strdupa(ast_channel_name(parker));
   }
   if (!park_exten || !park_context) {
      return masq_park_call(park_me, parker, &args);
   }

   /*
    * Determiine if the specified park extension has an exclusive
    * parking lot to use.
    */
   if (parker && parker != park_me) {
      ast_autoservice_start(park_me);
   }
   exten = get_parking_exten(park_exten, parker, park_context);
   if (exten) {
      app_data = ast_get_extension_app_data(exten);
      if (!app_data) {
         app_data = "";
      }
      parse = ast_strdupa(app_data);
      AST_STANDARD_APP_ARGS(app_args, parse);

      if (!ast_strlen_zero(app_args.pl_name)) {
         /* Find the specified exclusive parking lot */
         args.parkinglot = find_parkinglot(app_args.pl_name);
         if (!args.parkinglot && parkeddynamic) {
            args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
         }
      }
   }
   if (parker && parker != park_me) {
      ast_autoservice_stop(park_me);
   }

   res = masq_park_call(park_me, parker, &args);
   if (args.parkinglot) {
      parkinglot_unref(args.parkinglot);
   }
   return res;
}
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_meChannel to be parked.
parkerChannel parking the call.
timeoutis a timeout in milliseconds
park_extenParking lot access extension (Not used)
extoutis 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:
0on success.
-1on failure.

Definition at line 1877 of file features.c.

References ast_park_call_args::extout, park_call_full(), and ast_park_call_args::timeout.

{
   struct ast_park_call_args args = {
      .timeout = timeout,
      .extout = extout,
   };

   return park_call_full(park_me, parker, &args);
}
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_meChannel to be parked.
parkerChannel parking the call.
park_extenParking lot access extension
park_contextParking lot context
timeoutis a timeout in milliseconds
extoutis 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:
0on success.
-1on failure.

Definition at line 1826 of file features.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_get_extension_app_data(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), create_dynamic_parkinglot(), feature_group_exten::exten, ast_park_call_args::extout, 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().

{
   int res;
   char *parse;
   const char *app_data;
   struct ast_exten *exten;
   struct park_app_args app_args;
   struct ast_park_call_args args = {
      .timeout = timeout,
      .extout = extout,
   };

   if (!park_exten || !park_context) {
      return park_call_full(park_me, parker, &args);
   }

   /*
    * Determiine if the specified park extension has an exclusive
    * parking lot to use.
    */
   if (parker && parker != park_me) {
      ast_autoservice_start(park_me);
   }
   exten = get_parking_exten(park_exten, parker, park_context);
   if (exten) {
      app_data = ast_get_extension_app_data(exten);
      if (!app_data) {
         app_data = "";
      }
      parse = ast_strdupa(app_data);
      AST_STANDARD_APP_ARGS(app_args, parse);

      if (!ast_strlen_zero(app_args.pl_name)) {
         /* Find the specified exclusive parking lot */
         args.parkinglot = find_parkinglot(app_args.pl_name);
         if (!args.parkinglot && parkeddynamic) {
            args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
         }
      }
   }
   if (parker && parker != park_me) {
      ast_autoservice_stop(park_me);
   }

   res = park_call_full(park_me, parker, &args);
   if (args.parkinglot) {
      parkinglot_unref(args.parkinglot);
   }
   return res;
}
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:
0if extension does not exist
1if extension does exist

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

{
   return get_parking_exten(exten_str, chan, context) ? 1 : 0;
}
int ast_pickup_call ( struct ast_channel chan)

Pickup a call.

Parameters:
chanchannel 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 7875 of file features.c.

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

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

{
   struct ast_channel *target;/*!< Potential pickup target */
   int res = -1;

   ast_debug(1, "pickup attempt by %s\n", ast_channel_name(chan));

   /* The found channel is already locked. */
   target = ast_pickup_find_by_group(chan);
   if (target) {
      ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));

      res = ast_do_pickup(chan, target);
      ast_channel_unlock(target);
      if (!res) {
         if (!ast_strlen_zero(pickupsound)) {
            pbx_builtin_setvar_helper(target, "BRIDGE_PLAY_SOUND", pickupsound);
         }
      } else {
         ast_log(LOG_WARNING, "pickup %s failed by %s\n", ast_channel_name(target), ast_channel_name(chan));
      }
      target = ast_channel_unref(target);
   }

   if (res < 0) {
      ast_debug(1, "No call pickup possible... for %s\n", ast_channel_name(chan));
      if (!ast_strlen_zero(pickupfailsound)) {
         ast_answer(chan);
         ast_stream_and_wait(chan, pickupfailsound, "");
      }
   }

   return res;
}
struct ast_channel* ast_pickup_find_by_group ( struct ast_channel chan) [read]

Find a pickup channel target by group.

Parameters:
chanchannel that initiated pickup.
Return values:
targeton success. The returned channel is locked and reffed.
NULLon error.

< Candidate channels found to pickup.

< Potential pickup target

< Potential new older target

Definition at line 7808 of file features.c.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_container_alloc_options, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, ast_can_pickup(), ast_channel_callback(), ast_channel_creationtime(), ast_channel_lock, ast_channel_unlock, ast_channel_unref, ast_tvcmp(), and find_channel_by_group().

Referenced by ast_pickup_call(), and pickup_by_group().

{
   struct ao2_container *candidates;/*!< Candidate channels found to pickup. */
   struct ast_channel *target;/*!< Potential pickup target */

   candidates = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
   if (!candidates) {
      return NULL;
   }

   /* Find all candidate targets by group. */
   ast_channel_callback(find_channel_by_group, chan, candidates, 0);

   /* Find the oldest pickup target candidate */
   target = NULL;
   for (;;) {
      struct ast_channel *candidate;/*!< Potential new older target */
      struct ao2_iterator iter;

      iter = ao2_iterator_init(candidates, 0);
      while ((candidate = ao2_iterator_next(&iter))) {
         if (!target) {
            /* First target. */
            target = candidate;
            continue;
         }
         if (ast_tvcmp(ast_channel_creationtime(candidate), ast_channel_creationtime(target)) < 0) {
            /* We have a new target. */
            ast_channel_unref(target);
            target = candidate;
            continue;
         }
         ast_channel_unref(candidate);
      }
      ao2_iterator_destroy(&iter);
      if (!target) {
         /* No candidates found. */
         break;
      }

      /* The found channel must be locked and ref'd. */
      ast_channel_lock(target);

      /* Recheck pickup ability */
      if (ast_can_pickup(target)) {
         /* This is the channel to pickup. */
         break;
      }

      /* Someone else picked it up or the call went away. */
      ast_channel_unlock(target);
      ao2_unlink(candidates, target);
      target = ast_channel_unref(target);
   }
   ao2_ref(candidates, -1);

   return target;
}
void ast_register_feature ( struct ast_call_feature feature)

register new feature into feature_list

register new feature into feature_set

Definition at line 3152 of file features.c.

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

Referenced by process_applicationmap_line().

{
   if (!feature) {
      ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
      return;
   }

   AST_RWLIST_WRLOCK(&feature_list);
   AST_RWLIST_INSERT_HEAD(&feature_list,feature,feature_entry);
   AST_RWLIST_UNLOCK(&feature_list);

   ast_verb(2, "Registered Feature '%s'\n",feature->sname);
}
void ast_unregister_feature ( struct ast_call_feature feature)

unregister feature from feature_set

Parameters:
featurethe ast_call_feature object which was registered before

Definition at line 3232 of file features.c.

References ast_free, AST_RWLIST_REMOVE, AST_RWLIST_UNLOCK, and AST_RWLIST_WRLOCK.

{
   if (!feature) {
      return;
   }

   AST_RWLIST_WRLOCK(&feature_list);
   AST_RWLIST_REMOVE(&feature_list, feature, feature_entry);
   AST_RWLIST_UNLOCK(&feature_list);

   ast_free(feature);
}
static void ast_unregister_features ( void  ) [static]
static void ast_unregister_groups ( void  ) [static]
static void atxfer_fail_cleanup ( struct ast_channel transferee,
struct ast_channel transferer,
struct ast_party_connected_line connected_line 
) [static]

Definition at line 2645 of file features.c.

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

Referenced by builtin_atxfer().

{
   finishup(transferee);

   /*
    * Restore party B connected line info about party A.
    *
    * Party B was the caller to party C and is the last known mode
    * for party B.
    */
   if (ast_channel_connected_line_sub(transferee, transferer, connected_line, 0) &&
      ast_channel_connected_line_macro(transferee, transferer, connected_line, 1, 0)) {
      ast_channel_update_connected_line(transferer, connected_line, NULL);
   }
   ast_party_connected_line_free(connected_line);
}
static void* bridge_call_thread ( void *  data) [static]

bridge the call

Parameters:
datathread 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 1125 of file features.c.

References ast_autoservice_chan_hangup_peer(), ast_bridge_call(), ast_callid_threadassoc_add(), ast_callid_unref, ast_channel_appl_set(), ast_channel_data_set(), ast_channel_name(), ast_check_hangup(), ast_free, ast_hangup(), ast_log(), ast_pbx_start(), ast_verb, ast_bridge_thread_obj::bconfig, ast_bridge_thread_obj::callid, ast_bridge_thread_obj::chan, 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().

{
   struct ast_bridge_thread_obj *tobj = data;

   if (tobj->callid) {
      ast_callid_threadassoc_add(tobj->callid);
      /* Need to deref and set to null since ast_bridge_thread_obj has no common destructor */
      tobj->callid = ast_callid_unref(tobj->callid);
   }

   ast_channel_appl_set(tobj->chan, !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge");
   if (set_chan_app_data(tobj->chan, ast_channel_name(tobj->peer))) {
      ast_channel_data_set(tobj->chan, "(Empty)");
   }
   ast_channel_appl_set(tobj->peer, !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge");
   if (set_chan_app_data(tobj->peer, ast_channel_name(tobj->chan))) {
      ast_channel_data_set(tobj->peer, "(Empty)");
   }

   ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);

   if (tobj->return_to_pbx) {
      if (!ast_check_hangup(tobj->peer)) {
         ast_verb(0, "putting peer %s into PBX again\n", ast_channel_name(tobj->peer));
         if (ast_pbx_start(tobj->peer)) {
            ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", ast_channel_name(tobj->peer));
            ast_autoservice_chan_hangup_peer(tobj->chan, tobj->peer);
         }
      } else {
         ast_autoservice_chan_hangup_peer(tobj->chan, tobj->peer);
      }
      if (!ast_check_hangup(tobj->chan)) {
         ast_verb(0, "putting chan %s into PBX again\n", ast_channel_name(tobj->chan));
         if (ast_pbx_start(tobj->chan)) {
            ast_log(LOG_WARNING, "FAILED continuing PBX on chan %s\n", ast_channel_name(tobj->chan));
            ast_hangup(tobj->chan);
         }
      } else {
         ast_hangup(tobj->chan);
      }
   } else {
      ast_hangup(tobj->chan);
      ast_hangup(tobj->peer);
   }

   ast_free(tobj);

   return NULL;
}
static void bridge_call_thread_launch ( struct ast_bridge_thread_obj data) [static]

create thread for the parked call

Parameters:
dataCreate thread and attributes, call bridge_call_thread

Definition at line 1181 of file features.c.

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

Referenced by action_bridge(), and builtin_atxfer().

{
   pthread_t thread;
   pthread_attr_t attr;
   struct sched_param sched;

   /* This needs to be unreffed once it has been associated with the new thread. */
   data->callid = ast_read_threadstorage_callid();

   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   if (ast_pthread_create(&thread, &attr, bridge_call_thread, data)) {
      /* Failed to create thread. Ditch the reference to callid. */
      ast_callid_unref(data->callid);
      ast_hangup(data->chan);
      ast_hangup(data->peer);
      ast_log(LOG_ERROR, "Failed to create bridge_call_thread.\n");
      return;
   }
   pthread_attr_destroy(&attr);
   memset(&sched, 0, sizeof(sched));
   pthread_setschedparam(thread, SCHED_RR, &sched);
}
static int bridge_exec ( struct ast_channel chan,
const char *  data 
) [static]

Bridge channels.

Parameters:
chan
datachannel 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 transferred party hangs up return to PBX extension.

Definition at line 8162 of file features.c.

References args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_autoservice_chan_hangup_peer(), ast_bridge_call(), ast_bridge_timelimit(), ast_channel_alloc(), ast_channel_context(), ast_channel_exten(), ast_channel_flags(), ast_channel_get_by_name_prefix(), ast_channel_language(), ast_channel_linkedid(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_name(), ast_channel_priority(), ast_channel_unlock, 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_goto_if_exists(), ast_hangup(), ast_log(), ast_manager_event, ast_manager_event_multichan, ast_parseable_goto(), ast_pbx_start(), ast_replace_subargument_delimiter(), ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_DOWN, AST_STATE_UP, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), bridge_exec_options, BRIDGE_OPT_PLAYTONE, do_bridge_masquerade(), ast_bridge_config::end_sound, EVENT_FLAG_CALL, ast_bridge_config::features_callee, ast_bridge_config::features_caller, LOG_WARNING, OPT_ARG_ARRAY_SIZE, OPT_ARG_CALLEE_GO_ON, OPT_ARG_DURATION_LIMIT, OPT_CALLEE_GO_ON, 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_bridge_config::start_sound, ast_bridge_config::warning_sound, and xfersound.

Referenced by ast_features_init().

{
   struct ast_channel *current_dest_chan, *final_dest_chan, *chans[2];
   char *tmp_data  = NULL;
   struct ast_flags opts = { 0, };
   struct ast_bridge_config bconfig = { { 0, }, };
   char *opt_args[OPT_ARG_ARRAY_SIZE];
   struct timeval calldurationlimit = { 0, };

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(dest_chan);
      AST_APP_ARG(options);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "Bridge require at least 1 argument specifying the other end of the bridge\n");
      return -1;
   }

   tmp_data = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, tmp_data);
   if (!ast_strlen_zero(args.options))
      ast_app_parse_options(bridge_exec_options, &opts, opt_args, args.options);

   /* avoid bridge with ourselves */
   if (!strcmp(ast_channel_name(chan), args.dest_chan)) {
      ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", ast_channel_name(chan));
      /*** DOCUMENTATION
         <managerEventInstance>
            <synopsis>Raised when an error occurs during bridge creation.</synopsis>
            <see-also>
               <ref type="application">Bridge</ref>
            </see-also>
         </managerEventInstance>
      ***/
      ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
         "Response: Failed\r\n"
         "Reason: Unable to bridge channel to itself\r\n"
         "Channel1: %s\r\n"
         "Channel2: %s\r\n",
         ast_channel_name(chan), args.dest_chan);
      pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP");
      return 0;
   }

   /* make sure we have a valid end point */
   if (!(current_dest_chan = ast_channel_get_by_name_prefix(args.dest_chan,
         strlen(args.dest_chan)))) {
      ast_log(LOG_WARNING, "Bridge failed because channel %s does not exist\n",
         args.dest_chan);
      ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
         "Response: Failed\r\n"
         "Reason: Channel2 does not exist\r\n"
         "Channel1: %s\r\n"
         "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan);
      pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT");
      return 0;
   }

   /* try to allocate a place holder where current_dest_chan will be placed */
   if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
      NULL, NULL, ast_channel_linkedid(current_dest_chan), 0, "Bridge/%s", ast_channel_name(current_dest_chan)))) {
      ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan);
      ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
         "Response: Failed\r\n"
         "Reason: Cannot create placeholder channel\r\n"
         "Channel1: %s\r\n"
         "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan);
      pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE");
      ast_channel_unref(current_dest_chan);
      return 0;
   }

   if (ast_test_flag(&opts, OPT_DURATION_LIMIT)
      && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])
      && ast_bridge_timelimit(chan, &bconfig, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit)) {
      ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
         "Response: Failed\r\n"
         "Reason: Cannot setup bridge time limit\r\n"
         "Channel1: %s\r\n"
         "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan);
      ast_hangup(final_dest_chan);
      pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE");
      current_dest_chan = ast_channel_unref(current_dest_chan);
      goto done;
   }

   if (do_bridge_masquerade(current_dest_chan, final_dest_chan)) {
      ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
         "Response: Failed\r\n"
         "Reason: Cannot masquerade channels\r\n"
         "Channel1: %s\r\n"
         "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan);
      ast_hangup(final_dest_chan);
      pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE");
      current_dest_chan = ast_channel_unref(current_dest_chan);
      goto done;
   }

   /* answer the channel if needed */
   if (ast_channel_state(final_dest_chan) != AST_STATE_UP) {
      ast_answer(final_dest_chan);
   }

   chans[0] = current_dest_chan;
   chans[1] = final_dest_chan;

   /* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */
   /* try to make compatible, send error if we fail */
   if (ast_channel_make_compatible(chan, final_dest_chan) < 0) {
      ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", ast_channel_name(chan), ast_channel_name(final_dest_chan));
      ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans,
         "Response: Failed\r\n"
         "Reason: Could not make channels compatible for bridge\r\n"
         "Channel1: %s\r\n"
         "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan));

      /* Maybe we should return this channel to the PBX? */
      ast_autoservice_chan_hangup_peer(chan, final_dest_chan);

      pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE");
      current_dest_chan = ast_channel_unref(current_dest_chan);
      goto done;
   }

   /* Report that the bridge will be successfull */
   /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when the bridge is created successfully.</synopsis>
         <see-also>
            <ref type="application">Bridge</ref>
         </see-also>
      </managerEventInstance>
   ***/
   ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans,
      "Response: Success\r\n"
      "Channel1: %s\r\n"
      "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan));

   current_dest_chan = ast_channel_unref(current_dest_chan);

   /* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */
   if (ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE) && !ast_strlen_zero(xfersound)) {
      if (!ast_streamfile(final_dest_chan, xfersound, ast_channel_language(final_dest_chan))) {
         if (ast_waitstream(final_dest_chan, "") < 0)
            ast_log(LOG_WARNING, "Failed to play courtesy tone on %s\n", ast_channel_name(final_dest_chan));
      }
   }

   if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER))
      ast_set_flag(&(bconfig.features_callee), AST_FEATURE_REDIRECT);
   if (ast_test_flag(&opts, OPT_CALLER_TRANSFER))
      ast_set_flag(&(bconfig.features_caller), AST_FEATURE_REDIRECT);
   if (ast_test_flag(&opts, OPT_CALLEE_HANGUP))
      ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
   if (ast_test_flag(&opts, OPT_CALLER_HANGUP))
      ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
   if (ast_test_flag(&opts, OPT_CALLEE_MONITOR))
      ast_set_flag(&(bconfig.features_callee), AST_FEATURE_AUTOMON);
   if (ast_test_flag(&opts, OPT_CALLER_MONITOR))
      ast_set_flag(&(bconfig.features_caller), AST_FEATURE_AUTOMON);
   if (ast_test_flag(&opts, OPT_CALLEE_PARK))
      ast_set_flag(&(bconfig.features_callee), AST_FEATURE_PARKCALL);
   if (ast_test_flag(&opts, OPT_CALLER_PARK))
      ast_set_flag(&(bconfig.features_caller), AST_FEATURE_PARKCALL);

   /*
    * Don't let the after-bridge code run the h-exten.  We want to
    * continue in the dialplan.
    */
   ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT);
   ast_bridge_call(chan, final_dest_chan, &bconfig);

   /* The bridge has ended, set BRIDGERESULT to SUCCESS. */
   pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS");

   /* If the other channel has not been hung up, return it to the PBX */
   if (!ast_check_hangup(final_dest_chan)) {
      if (ast_test_flag(&opts, OPT_CALLEE_GO_ON)) {
         char *caller_context;
         char *caller_extension;
         int caller_priority;
         int goto_opt;

         ast_channel_lock(chan);
         caller_context = ast_strdupa(ast_channel_context(chan));
         caller_extension = ast_strdupa(ast_channel_exten(chan));
         caller_priority = ast_channel_priority(chan);
         ast_channel_unlock(chan);

         if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
            ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
            /* Set current dialplan position to bridger dialplan position */
            goto_opt = ast_goto_if_exists(final_dest_chan, caller_context, caller_extension, caller_priority)
               /* Then perform the goto */
               || ast_parseable_goto(final_dest_chan, opt_args[OPT_ARG_CALLEE_GO_ON]);
         } else { /* F() */
            goto_opt = ast_goto_if_exists(final_dest_chan, caller_context, caller_extension, caller_priority + 1);
         }
         if (goto_opt || ast_pbx_start(final_dest_chan)) {
            ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
         }
      } else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) {
         ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n",
            ast_channel_context(final_dest_chan), ast_channel_exten(final_dest_chan),
            ast_channel_priority(final_dest_chan), ast_channel_name(final_dest_chan));

         if (ast_pbx_start(final_dest_chan)) {
            ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", ast_channel_name(final_dest_chan));
            ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
         } else {
            ast_debug(1, "SUCCESS continuing PBX on chan %s\n", ast_channel_name(final_dest_chan));
         }
      } else {
         ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
      }
   } else {
      ast_debug(1, "chan %s was hungup\n", ast_channel_name(final_dest_chan));
      ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
   }
done:
   ast_free((char *) bconfig.warning_sound);
   ast_free((char *) bconfig.end_sound);
   ast_free((char *) bconfig.start_sound);

   return 0;
}
static struct parking_dp_context* build_dialplan_useage_context ( struct ast_parkinglot lot) [static, read]

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

{
   struct parking_dp_context *ctx_node;

   ctx_node = ast_calloc(1, sizeof(*ctx_node) + strlen(lot->cfg.parking_con));
   if (!ctx_node) {
      return NULL;
   }
   if (dialplan_usage_add_parkinglot_data(ctx_node, lot, 0)) {
      destroy_dialplan_usage_context(ctx_node);
      return NULL;
   }
   strcpy(ctx_node->context, lot->cfg.parking_con);
   return ctx_node;
}
static int build_dialplan_useage_map ( struct parking_dp_map usage_map,
int  complain 
) [static]

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

{
   int status = 0;
   struct ao2_iterator iter;
   struct ast_parkinglot *curlot;

   /* For all parking lots */
   iter = ao2_iterator_init(parkinglots, 0);
   for (; (curlot = ao2_iterator_next(&iter)); ao2_ref(curlot, -1)) {
      /* Add the parking lot to the map. */
      if (dialplan_usage_add_parkinglot(usage_map, curlot, complain)) {
         ao2_ref(curlot, -1);
         status = -1;
         break;
      }
   }
   ao2_iterator_destroy(&iter);

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

Definition at line 6441 of file features.c.

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

Referenced by usage_context_add_ramp().

{
   struct parking_dp_ramp *ramp_node;

   ramp_node = ast_calloc(1, sizeof(*ramp_node) + strlen(exten));
   if (!ramp_node) {
      return NULL;
   }
   ramp_node->exclusive = exclusive;
   strcpy(ramp_node->exten, exten);
   return ramp_node;
}
static struct parking_dp_spaces* build_dialplan_useage_spaces ( int  start,
int  stop 
) [static, read]

Definition at line 6522 of file features.c.

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

Referenced by usage_context_add_spaces().

{
   struct parking_dp_spaces *spaces_node;

   spaces_node = ast_calloc(1, sizeof(*spaces_node));
   if (!spaces_node) {
      return NULL;
   }
   spaces_node->start = start;
   spaces_node->stop = stop;
   return spaces_node;
}
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 6046 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, parkinglot_cfg_default, parkinglot_cfg_default_default, parkinglot_config_read(), parkinglot_unref(), parkinglots, ast_parkinglot::parkings, and ast_parkinglot::the_mark.

Referenced by load_config(), and process_config().

{
   struct ast_parkinglot *parkinglot;
   const struct parkinglot_cfg *cfg_defaults;
   struct parkinglot_cfg new_cfg;
   int cfg_error;
   int oldparkinglot = 0;

   parkinglot = find_parkinglot(pl_name);
   if (parkinglot) {
      oldparkinglot = 1;
   } else {
      parkinglot = create_parkinglot(pl_name);
      if (!parkinglot) {
         return NULL;
      }
   }
   if (!strcmp(parkinglot->name, DEFAULT_PARKINGLOT)) {
      cfg_defaults = &parkinglot_cfg_default_default;
   } else {
      cfg_defaults = &parkinglot_cfg_default;
   }
   new_cfg = *cfg_defaults;

   ast_debug(1, "Building parking lot %s\n", parkinglot->name);

   ao2_lock(parkinglot);

   /* Do some config stuff */
   cfg_error = parkinglot_config_read(parkinglot->name, &new_cfg, var);
   if (oldparkinglot) {
      if (cfg_error) {
         /* Bad configuration read.  Keep using the original config. */
         ast_log(LOG_WARNING, "Changes to parking lot %s are discarded.\n",
            parkinglot->name);
         cfg_error = 0;
      } else if (!AST_LIST_EMPTY(&parkinglot->parkings)
         && memcmp(&new_cfg, &parkinglot->cfg, sizeof(parkinglot->cfg))) {
         /* Try reloading later when parking lot is empty. */
         ast_log(LOG_WARNING,
            "Parking lot %s has parked calls.  Parking lot changes discarded.\n",
            parkinglot->name);
         force_reload_load = 1;
      } else {
         /* Accept the new config */
         parkinglot->cfg = new_cfg;
      }
   } else {
      /* Load the initial parking lot config. */
      parkinglot->cfg = new_cfg;
   }
   parkinglot->the_mark = 0;

   ao2_unlock(parkinglot);

   if (cfg_error) {
      /* Only new parking lots could have config errors here. */
      ast_log(LOG_WARNING, "New parking lot %s is discarded.\n", parkinglot->name);
      parkinglot_unref(parkinglot);
      return NULL;
   }

   /* Move it into the list, if it wasn't already there */
   if (!oldparkinglot) {
      ao2_link(parkinglots, parkinglot);
   }
   parkinglot_unref(parkinglot);

   return parkinglot;
}
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:
chantransferred user
peerperson transfering call
config
code
sensefeature options
dataGet 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 2677 of file features.c.

References add_features_datastore(), ast_app_dtget(), ast_autoservice_chan_hangup_peer(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, AST_CEL_ATTENDEDTRANSFER, ast_cel_report_event(), ast_channel_alloc(), ast_channel_caller(), ast_channel_connected(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), ast_channel_datastore_find(), ast_channel_flags(), ast_channel_language(), ast_channel_linkedid(), ast_channel_lock, ast_channel_masquerade(), ast_channel_name(), ast_channel_nativeformats(), ast_channel_readformat(), ast_channel_state_set(), ast_channel_unlock, ast_channel_update_connected_line(), ast_channel_visible_indication_set(), ast_channel_writeformat(), 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_format_copy(), 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_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_bridge_thread_obj::chan, check_compat(), config, 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, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_bridge_thread_obj::peer, ast_dial_features::peer_features, real_ctx(), set_peers(), ast_party_connected_line::source, transferdigittimeout, xfer_park_call_helper(), xferfailsound, and xfersound.

{
   struct ast_channel *transferer;/* Party B */
   struct ast_channel *transferee;/* Party A */
   struct ast_exten *park_exten;
   const char *chan1_attended_sound;
   const char *chan2_attended_sound;
   const char *transferer_real_context;
   char xferto[256] = "";
   int res;
   int outstate=0;
   struct ast_channel *newchan;
   struct ast_channel *xferchan;
   struct ast_bridge_thread_obj *tobj;
   struct ast_bridge_config bconfig;
   int l;
   struct ast_party_connected_line connected_line;
   struct ast_datastore *features_datastore;
   struct ast_dial_features *dialfeatures;
   char *transferer_tech;
   char *transferer_name;
   char *transferer_name_orig;
   char *dash;

   ast_debug(1, "Executing Attended Transfer %s, %s (sense=%d) \n", ast_channel_name(chan), ast_channel_name(peer), sense);
   set_peers(&transferer, &transferee, peer, chan, sense);
   transferer_real_context = real_ctx(transferer, transferee);

   /* Start autoservice on transferee while we talk to the transferer */
   ast_autoservice_start(transferee);
   ast_indicate(transferee, AST_CONTROL_HOLD);

   /* Transfer */
   res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
   if (res < 0) {
      finishup(transferee);
      return -1;
   }
   if (res > 0) { /* If they've typed a digit already, handle it */
      xferto[0] = (char) res;
   }

   /* this is specific of atxfer */
   res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
   if (res < 0) {  /* hangup or error, (would be 0 for invalid and 1 for valid) */
      finishup(transferee);
      return -1;
   }
   l = strlen(xferto);
   if (res == 0) {
      if (l) {
         ast_log(LOG_WARNING, "Extension '%s' does not exist in context '%s'\n",
            xferto, transferer_real_context);
      } else {
         /* Does anyone care about this case? */
         ast_log(LOG_WARNING, "No digits dialed for atxfer.\n");
      }
      ast_stream_and_wait(transferer, "pbx-invalid", "");
      finishup(transferee);
      return AST_FEATURE_RETURN_SUCCESS;
   }

   park_exten = get_parking_exten(xferto, transferer, transferer_real_context);
   if (park_exten) {
      /* We are transfering the transferee to a parking lot. */
      return xfer_park_call_helper(transferee, transferer, park_exten);
   }

   /*
    * Append context to dialed transfer number.
    *
    * NOTE: The local channel needs the /n flag so party C will use
    * the feature flags set by the dialplan when calling that
    * party.
    */
   snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context);

   /* If we are performing an attended transfer and we have two channels involved then
      copy sound file information to play upon attended transfer completion */
   chan1_attended_sound = pbx_builtin_getvar_helper(transferer, "ATTENDED_TRANSFER_COMPLETE_SOUND");
   chan2_attended_sound = pbx_builtin_getvar_helper(transferee, "ATTENDED_TRANSFER_COMPLETE_SOUND");
   if (!ast_strlen_zero(chan1_attended_sound)) {
      pbx_builtin_setvar_helper(transferer, "BRIDGE_PLAY_SOUND", chan1_attended_sound);
   }
   if (!ast_strlen_zero(chan2_attended_sound)) {
      pbx_builtin_setvar_helper(transferee, "BRIDGE_PLAY_SOUND", chan2_attended_sound);
   }

   /* Extract redial transferer information from the channel name. */
   transferer_name_orig = ast_strdupa(ast_channel_name(transferer));
   transferer_name = ast_strdupa(transferer_name_orig);
   transferer_tech = strsep(&transferer_name, "/");
   dash = strrchr(transferer_name, '-');
   if (dash) {
      /* Trim off channel name sequence/serial number. */
      *dash = '\0';
   }

   /* Stop autoservice so we can monitor all parties involved in the transfer. */
   if (ast_autoservice_stop(transferee) < 0) {
      ast_indicate(transferee, AST_CONTROL_UNHOLD);
      return -1;
   }

   /* Save connected line info for party B about party A in case transfer fails. */
   ast_party_connected_line_init(&connected_line);
   ast_channel_lock(transferer);
   ast_party_connected_line_copy(&connected_line, ast_channel_connected(transferer));
   ast_channel_unlock(transferer);
   connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;

   /* Dial party C */
   newchan = feature_request_and_dial(transferer, transferer_name_orig, transferer,
      transferee, "Local", ast_channel_nativeformats(transferer), xferto,
      atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
   ast_debug(2, "Dial party C result: newchan:%d, outstate:%d\n", !!newchan, outstate);

   if (!ast_check_hangup(transferer)) {
      int hangup_dont = 0;

      /* Transferer (party B) is up */
      ast_debug(1, "Actually doing an attended transfer.\n");

      /* Start autoservice on transferee while the transferer deals with party C. */
      ast_autoservice_start(transferee);

      ast_indicate(transferer, -1);
      if (!newchan) {
         /* any reason besides user requested cancel and busy triggers the failed sound */
         switch (outstate) {
         case AST_CONTROL_UNHOLD:/* Caller requested cancel or party C answer timeout. */
         case AST_CONTROL_BUSY:
         case AST_CONTROL_CONGESTION:
            if (ast_stream_and_wait(transferer, xfersound, "")) {
               ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
            }
            break;
         default:
            if (ast_stream_and_wait(transferer, xferfailsound, "")) {
               ast_log(LOG_WARNING, "Failed to play transfer failed sound!\n");
            }
            break;
         }
         atxfer_fail_cleanup(transferee, transferer, &connected_line);
         return AST_FEATURE_RETURN_SUCCESS;
      }

      if (check_compat(transferer, newchan)) {
         if (ast_stream_and_wait(transferer, xferfailsound, "")) {
            ast_log(LOG_WARNING, "Failed to play transfer failed sound!\n");
         }
         atxfer_fail_cleanup(transferee, transferer, &connected_line);
         return AST_FEATURE_RETURN_SUCCESS;
      }
      memset(&bconfig,0,sizeof(struct ast_bridge_config));
      ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
      ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);

      /*
       * ast_bridge_call clears AST_FLAG_BRIDGE_HANGUP_DONT, but we
       * don't want that to happen here because the transferer is in
       * another bridge already.
       */
      if (ast_test_flag(ast_channel_flags(transferer), AST_FLAG_BRIDGE_HANGUP_DONT)) {
         hangup_dont = 1;
      }

      /*
       * Don't let the after-bridge code run the h-exten.  It is the
       * wrong bridge to run the h-exten after.
       */
      ast_set_flag(ast_channel_flags(transferer), AST_FLAG_BRIDGE_HANGUP_DONT);

      /*
       * Let party B and C talk as long as they want while party A
       * languishes in autoservice listening to MOH.
       */
      ast_bridge_call(transferer, newchan, &bconfig);

      if (hangup_dont) {
         /* Restore the AST_FLAG_BRIDGE_HANGUP_DONT flag */
         ast_set_flag(ast_channel_flags(transferer), AST_FLAG_BRIDGE_HANGUP_DONT);
      }

      if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) {
         ast_autoservice_chan_hangup_peer(transferer, newchan);
         if (ast_stream_and_wait(transferer, xfersound, "")) {
            ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
         }
         atxfer_fail_cleanup(transferee, transferer, &connected_line);
         return AST_FEATURE_RETURN_SUCCESS;
      }

      /* Transferer (party B) is confirmed hung up at this point. */
      if (check_compat(transferee, newchan)) {
         finishup(transferee);
         ast_party_connected_line_free(&connected_line);
         return -1;
      }

      ast_indicate(transferee, AST_CONTROL_UNHOLD);
      if ((ast_autoservice_stop(transferee) < 0)
         || (ast_waitfordigit(transferee, 100) < 0)
         || (ast_waitfordigit(newchan, 100) < 0)
         || ast_check_hangup(transferee)
         || ast_check_hangup(newchan)) {
         ast_hangup(newchan);
         ast_party_connected_line_free(&connected_line);
         return -1;
      }
   } else if (!ast_check_hangup(transferee)) {
      /* Transferer (party B) has hung up at this point.  Doing blonde transfer. */
      ast_debug(1, "Actually doing a blonde transfer.\n");

      if (!newchan && !atxferdropcall) {
         /* Party C is not available, try to call party B back. */
         unsigned int tries = 0;

         if (ast_strlen_zero(transferer_name) || ast_strlen_zero(transferer_tech)) {
            ast_log(LOG_WARNING,
               "Transferer channel name: '%s' cannot be used for callback.\n",
               transferer_name_orig);
            ast_indicate(transferee, AST_CONTROL_UNHOLD);
            ast_party_connected_line_free(&connected_line);
            return -1;
         }

         tries = 0;
         for (;;) {
            /* Try to get party B back. */
            ast_debug(1, "We're trying to callback %s/%s\n",
               transferer_tech, transferer_name);
            newchan = feature_request_and_dial(transferer, transferer_name_orig,
               transferee, transferee, transferer_tech,
               ast_channel_nativeformats(transferee), transferer_name,
               atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
            ast_debug(2, "Dial party B result: newchan:%d, outstate:%d\n",
               !!newchan, outstate);
            if (newchan) {
               /*
                * We have recalled party B (newchan).  We need to give this
                * call leg the same feature flags as the original party B call
                * leg.
                */
               ast_channel_lock(transferer);
               features_datastore = ast_channel_datastore_find(transferer,
                  &dial_features_info, NULL);
               if (features_datastore && (dialfeatures = features_datastore->data)) {
                  struct ast_flags my_features = { 0 };
                  struct ast_flags peer_features = { 0 };

                  ast_copy_flags(&my_features, &dialfeatures->my_features,
                     AST_FLAGS_ALL);
                  ast_copy_flags(&peer_features, &dialfeatures->peer_features,
                     AST_FLAGS_ALL);
                  ast_channel_unlock(transferer);
                  add_features_datastore(newchan, &my_features, &peer_features);
               } else {
                  ast_channel_unlock(transferer);
               }
               break;
            }
            if (ast_check_hangup(transferee)) {
               break;
            }

            ++tries;
            if (atxfercallbackretries <= tries) {
               /* No more callback tries remaining. */
               break;
            }

            if (atxferloopdelay) {
               /* Transfer failed, sleeping */
               ast_debug(1, "Sleeping for %d ms before retrying atxfer.\n",
                  atxferloopdelay);
               ast_safe_sleep(transferee, atxferloopdelay);
               if (ast_check_hangup(transferee)) {
                  ast_party_connected_line_free(&connected_line);
                  return -1;
               }
            }

            /* Retry dialing party C. */
            ast_debug(1, "We're retrying to call %s/%s\n", "Local", xferto);
            newchan = feature_request_and_dial(transferer, transferer_name_orig,
               transferer, transferee, "Local",
               ast_channel_nativeformats(transferee), xferto,
               atxfernoanswertimeout, &outstate, ast_channel_language(transferer));
            ast_debug(2, "Redial party C result: newchan:%d, outstate:%d\n",
               !!newchan, outstate);
            if (newchan || ast_check_hangup(transferee)) {
               break;
            }
         }
      }
      ast_indicate(transferee, AST_CONTROL_UNHOLD);
      if (!newchan) {
         /* No party C or could not callback party B. */
         ast_party_connected_line_free(&connected_line);
         return -1;
      }

      /* newchan is up, we should prepare transferee and bridge them */
      if (ast_check_hangup(newchan)) {
         ast_autoservice_chan_hangup_peer(transferee, newchan);
         ast_party_connected_line_free(&connected_line);
         return -1;
      }
      if (check_compat(transferee, newchan)) {
         ast_party_connected_line_free(&connected_line);
         return -1;
      }
   } else {
      /*
       * Both the transferer and transferee have hungup.  If newchan
       * is up, hang it up as it has no one to talk to.
       */
      ast_debug(1, "Everyone is hungup.\n");
      if (newchan) {
         ast_hangup(newchan);
      }
      ast_party_connected_line_free(&connected_line);
      return -1;
   }

   /* Initiate the channel transfer of party A to party C (or recalled party B). */
   ast_cel_report_event(transferee, AST_CEL_ATTENDEDTRANSFER, NULL, NULL, newchan);

   xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(transferee), 0, "Transfered/%s", ast_channel_name(transferee));
   if (!xferchan) {
      ast_autoservice_chan_hangup_peer(transferee, newchan);
      ast_party_connected_line_free(&connected_line);
      return -1;
   }

   /* Give party A a momentary ringback tone during transfer. */
   ast_channel_visible_indication_set(xferchan, AST_CONTROL_RINGING);

   /* Make formats okay */
   ast_format_copy(ast_channel_readformat(xferchan), ast_channel_readformat(transferee));
   ast_format_copy(ast_channel_writeformat(xferchan), ast_channel_writeformat(transferee));

   if (ast_channel_masquerade(xferchan, transferee)) {
      ast_hangup(xferchan);
      ast_autoservice_chan_hangup_peer(transferee, newchan);
      ast_party_connected_line_free(&connected_line);
      return -1;
   }

   dash = strrchr(xferto, '@');
   if (dash) {
      /* Trim off the context. */
      *dash = '\0';
   }
   ast_explicit_goto(xferchan, transferer_real_context, xferto, 1);
   ast_channel_state_set(xferchan, AST_STATE_UP);
   ast_clear_flag(ast_channel_flags(xferchan), AST_FLAGS_ALL);

   /* Do the masquerade manually to make sure that is is completed. */
   ast_do_masquerade(xferchan);

   ast_channel_state_set(newchan, AST_STATE_UP);
   ast_clear_flag(ast_channel_flags(newchan), AST_FLAGS_ALL);
   tobj = ast_calloc(1, sizeof(*tobj));
   if (!tobj) {
      ast_hangup(xferchan);
      ast_hangup(newchan);
      ast_party_connected_line_free(&connected_line);
      return -1;
   }

   tobj->chan = newchan;
   tobj->peer = xferchan;
   tobj->bconfig = *config;

   ast_channel_lock(newchan);
   features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL);
   if (features_datastore && (dialfeatures = features_datastore->data)) {
      ast_copy_flags(&tobj->bconfig.features_callee, &dialfeatures->my_features,
         AST_FLAGS_ALL);
   }
   ast_channel_unlock(newchan);

   ast_channel_lock(xferchan);
   features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL);
   if (features_datastore && (dialfeatures = features_datastore->data)) {
      ast_copy_flags(&tobj->bconfig.features_caller, &dialfeatures->my_features,
         AST_FLAGS_ALL);
   }
   ast_channel_unlock(xferchan);

   if (tobj->bconfig.end_bridge_callback_data_fixup) {
      tobj->bconfig.end_bridge_callback_data_fixup(&tobj->bconfig, tobj->peer, tobj->chan);
   }

   /*
    * xferchan is transferee, and newchan is the transfer target
    * So...in a transfer, who is the caller and who is the callee?
    *
    * When the call is originally made, it is clear who is caller and callee.
    * When a transfer occurs, it is my humble opinion that the transferee becomes
    * the caller, and the transfer target is the callee.
    *
    * The problem is that these macros were set with the intention of the original
    * caller and callee taking those roles.  A transfer can totally mess things up,
    * to be technical.  What sucks even more is that you can't effectively change
    * the macros in the dialplan during the call from the transferer to the transfer
    * target because the transferee is stuck with whatever role he originally had.
    *
    * I think the answer here is just to make sure that it is well documented that
    * during a transfer, the transferee is the "caller" and the transfer target
    * is the "callee."
    *
    * This means that if party B calls party A, and party B transfers party A to
    * party C, then A has switched roles for the call.  Now party A will have the
    * caller macro called on his channel instead of the callee macro.
    *
    * Luckily, the method by which the party B to party C bridge is
    * launched above ensures that the transferee is the "chan" on
    * the bridge and the transfer target is the "peer," so my idea
    * for the roles post-transfer does not require extensive code
    * changes.
    */

   /* Transfer party C connected line to party A */
   ast_channel_lock(transferer);
   /*
    * Due to a limitation regarding when callerID is set on a Local channel,
    * we use the transferer's connected line information here.
    */
   ast_party_connected_line_copy(&connected_line, ast_channel_connected(transferer));
   ast_channel_unlock(transferer);
   connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
   if (ast_channel_connected_line_sub(newchan, xferchan, &connected_line, 0) &&
      ast_channel_connected_line_macro(newchan, xferchan, &connected_line, 1, 0)) {
      ast_channel_update_connected_line(xferchan, &connected_line, NULL);
   }

   /* Transfer party A connected line to party C */
   ast_channel_lock(xferchan);
   ast_connected_line_copy_from_caller(&connected_line, ast_channel_caller(xferchan));
   ast_channel_unlock(xferchan);
   connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
   if (ast_channel_connected_line_sub(xferchan, newchan, &connected_line, 0) &&
      ast_channel_connected_line_macro(xferchan, newchan, &connected_line, 0, 0)) {
      ast_channel_update_connected_line(newchan, &connected_line, NULL);
   }

   if (ast_stream_and_wait(newchan, xfersound, ""))
      ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
   bridge_call_thread_launch(tobj);

   ast_party_connected_line_free(&connected_line);
   return -1;/* The transferee is masqueraded and the original bridged channels can be hungup. */
}
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 2362 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_caller(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, AST_FEATURE_RETURN_SUCCESS, AST_FRAME_DTMF_END, ast_log(), ast_stream_and_wait(), ast_strlen_zero(), ast_verb, courtesytone, len(), LOG_ERROR, LOG_WARNING, mixmonitor_app, mixmonitor_ok, mixmonitor_spy_type, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), S_COR, S_OR, set_peers(), stopmixmonitor_app, and stopmixmonitor_ok.

{
   char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
   int x = 0;
   size_t len;
   struct ast_channel *caller_chan, *callee_chan;
   const char *mixmonitor_spy_type = "MixMonitor";
   const char *touch_format;
   const char *touch_monitor;
   int count = 0;

   if (!mixmonitor_ok) {
      ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
      return -1;
   }

   if (!(mixmonitor_app = pbx_findapp("MixMonitor"))) {
      mixmonitor_ok = 0;
      ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n");
      return -1;
   }

   set_peers(&caller_chan, &callee_chan, peer, chan, sense);

   if (!ast_strlen_zero(courtesytone)) {
      if (ast_autoservice_start(callee_chan))
         return -1;
      ast_autoservice_ignore(callee_chan, AST_FRAME_DTMF_END);
      if (ast_stream_and_wait(caller_chan, courtesytone, "")) {
         ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
         ast_autoservice_stop(callee_chan);
         return -1;
      }
      if (ast_autoservice_stop(callee_chan))
         return -1;
   }

   ast_channel_lock(callee_chan);
   count = ast_channel_audiohook_count_by_source(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
   ast_channel_unlock(callee_chan);

   /* This means a mixmonitor is attached to the channel, running or not is unknown. */
   if (count > 0) {
      ast_verb(3, "User hit '%s' to stop recording call.\n", code);

      /* Make sure they are running */
      ast_channel_lock(callee_chan);
      count = ast_channel_audiohook_count_by_source_running(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
      ast_channel_unlock(callee_chan);
      if (count > 0) {
         if (!stopmixmonitor_ok) {
            ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
            return -1;
         }
         if (!(stopmixmonitor_app = pbx_findapp("StopMixMonitor"))) {
            stopmixmonitor_ok = 0;
            ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n");
            return -1;
         } else {
            pbx_exec(callee_chan, stopmixmonitor_app, "");
            return AST_FEATURE_RETURN_SUCCESS;
         }
      }

      ast_log(LOG_WARNING,"Stopped MixMonitors are attached to the channel.\n");
   }

   touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR_FORMAT");
   touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR");

   if (!touch_format)
      touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR_FORMAT");

   if (!touch_monitor)
      touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR");

   if (touch_monitor) {
      len = strlen(touch_monitor) + 50;
      args = ast_alloca(len);
      touch_filename = ast_alloca(len);
      snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
      snprintf(args, len, "%s.%s,b", touch_filename, (touch_format) ? touch_format : "wav");
   } else {
      caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(caller_chan)->id.number.valid,
         ast_channel_caller(caller_chan)->id.number.str, ast_channel_name(caller_chan)));
      callee_chan_id = ast_strdupa(S_COR(ast_channel_caller(callee_chan)->id.number.valid,
         ast_channel_caller(callee_chan)->id.number.str, ast_channel_name(callee_chan)));
      len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
      args = ast_alloca(len);
      touch_filename = ast_alloca(len);
      snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
      snprintf(args, len, "%s.%s,b", touch_filename, S_OR(touch_format, "wav"));
   }

   for( x = 0; x < strlen(args); x++) {
      if (args[x] == '/')
         args[x] = '-';
   }

   ast_verb(3, "User hit '%s' to record call. filename: %s\n", code, touch_filename);

   pbx_exec(callee_chan, mixmonitor_app, args);
   pbx_builtin_setvar_helper(callee_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
   pbx_builtin_setvar_helper(caller_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
   return AST_FEATURE_RETURN_SUCCESS;
}
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:
chanchannel requesting monitor
peerchannel to be monitored
config
code
sensefeature options
dataCheck 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_SUCCESSon success.
-1on error.

Definition at line 2269 of file features.c.

References args, ast_alloca, ast_channel_caller(), ast_channel_monitor(), ast_channel_name(), AST_FEATURE_RETURN_SUCCESS, ast_log(), ast_strlen_zero(), ast_verb, courtesytone, len(), LOG_ERROR, monitor_app, monitor_ok, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), play_message_in_bridged_call(), S_COR, S_OR, set_peers(), and ast_channel_monitor::stop.

{
   char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
   int x = 0;
   size_t len;
   struct ast_channel *caller_chan, *callee_chan;
   const char *automon_message_start = NULL;
   const char *automon_message_stop = NULL;
   const char *touch_format = NULL;
   const char *touch_monitor = NULL;
   const char *touch_monitor_prefix = NULL;

   if (!monitor_ok) {
      ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
      return -1;
   }

   if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
      monitor_ok = 0;
      ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
      return -1;
   }

   set_peers(&caller_chan, &callee_chan, peer, chan, sense);

   /* Find extra messages */
   automon_message_start = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_MESSAGE_START");
   automon_message_stop = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_MESSAGE_STOP");

   if (!ast_strlen_zero(courtesytone)) {  /* Play courtesy tone if configured */
      if(play_message_in_bridged_call(caller_chan, callee_chan, courtesytone) == -1) {
         return -1;
      }
   }

   if (ast_channel_monitor(callee_chan)) {
      ast_verb(4, "User hit '%s' to stop recording call.\n", code);
      if (!ast_strlen_zero(automon_message_stop)) {
         play_message_in_bridged_call(caller_chan, callee_chan, automon_message_stop);
      }
      ast_channel_monitor(callee_chan)->stop(callee_chan, 1);
      return AST_FEATURE_RETURN_SUCCESS;
   }

   touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
   touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
   touch_monitor_prefix = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_PREFIX");

   if (!touch_format)
      touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");

   if (!touch_monitor)
      touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");

   if (!touch_monitor_prefix)
      touch_monitor_prefix = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_PREFIX");

   if (touch_monitor) {
      len = strlen(touch_monitor) + 50;
      args = ast_alloca(len);
      touch_filename = ast_alloca(len);
      snprintf(touch_filename, len, "%s-%ld-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor);
      snprintf(args, len, "%s,%s,m", S_OR(touch_format, "wav"), touch_filename);
   } else {
      caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(caller_chan)->id.number.valid,
         ast_channel_caller(caller_chan)->id.number.str, ast_channel_name(caller_chan)));
      callee_chan_id = ast_strdupa(S_COR(ast_channel_caller(callee_chan)->id.number.valid,
         ast_channel_caller(callee_chan)->id.number.str, ast_channel_name(callee_chan)));
      len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
      args = ast_alloca(len);
      touch_filename = ast_alloca(len);
      snprintf(touch_filename, len, "%s-%ld-%s-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), caller_chan_id, callee_chan_id);
      snprintf(args, len, "%s,%s,m", S_OR(touch_format, "wav"), touch_filename);
   }

   for(x = 0; x < strlen(args); x++) {
      if (args[x] == '/')
         args[x] = '-';
   }

   ast_verb(4, "User hit '%s' to record call. filename: %s\n", code, args);

   pbx_exec(callee_chan, monitor_app, args);
   pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
   pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);

   if (!ast_strlen_zero(automon_message_start)) {  /* Play start message for both channels */
      play_message_in_bridged_call(caller_chan, callee_chan, automon_message_start);
   }

   return AST_FEATURE_RETURN_SUCCESS;
}
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:
chanchannel to be transferred
peerchannel initiated blind transfer
config
code
data
sensefeature options

Place chan on hold, check if transferred to parkinglot extension, otherwise check extension exists and transfer caller.

Return values:
AST_FEATURE_RETURN_SUCCESS.
-1on failure.

Definition at line 2512 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_cdr(), ast_channel_cdr_set(), ast_channel_flags(), ast_channel_lock, ast_channel_name(), ast_channel_pbx(), 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_cdr::channel, check_goto_on_transfer(), ast_cdr::dstchannel, finishup(), get_parking_exten(), ast_cdr::lastapp, ast_cdr::lastdata, LOG_WARNING, pbx_builtin_setvar_helper(), real_ctx(), set_peers(), transferdigittimeout, and xfer_park_call_helper().

{
   struct ast_channel *transferer;
   struct ast_channel *transferee;
   struct ast_exten *park_exten;
   const char *transferer_real_context;
   char xferto[256] = "";
   int res;

   ast_debug(1, "Executing Blind Transfer %s, %s (sense=%d) \n", ast_channel_name(chan), ast_channel_name(peer), sense);
   set_peers(&transferer, &transferee, peer, chan, sense);
   transferer_real_context = ast_strdupa(real_ctx(transferer, transferee));

   /* Start autoservice on transferee while we talk to the transferer */
   ast_autoservice_start(transferee);
   ast_indicate(transferee, AST_CONTROL_HOLD);

   /* Transfer */
   res = ast_stream_and_wait(transferer, "pbx-transfer", AST_DIGIT_ANY);
   if (res < 0) {
      finishup(transferee);
      return -1; /* error ? */
   }
   if (res > 0) { /* If they've typed a digit already, handle it */
      xferto[0] = (char) res;
   }

   res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
   if (res < 0) {  /* hangup or error, (would be 0 for invalid and 1 for valid) */
      finishup(transferee);
      return -1;
   }
   if (res == 0) {
      if (xferto[0]) {
         ast_log(LOG_WARNING, "Extension '%s' does not exist in context '%s'\n",
            xferto, transferer_real_context);
      } else {
         /* Does anyone care about this case? */
         ast_log(LOG_WARNING, "No digits dialed.\n");
      }
      ast_stream_and_wait(transferer, "pbx-invalid", "");
      finishup(transferee);
      return AST_FEATURE_RETURN_SUCCESS;
   }

   park_exten = get_parking_exten(xferto, transferer, transferer_real_context);
   if (park_exten) {
      /* We are transfering the transferee to a parking lot. */
      return xfer_park_call_helper(transferee, transferer, park_exten);
   }

   /* Do blind transfer. */
   ast_verb(3, "Blind transferring %s to '%s' (context %s) priority 1\n",
      ast_channel_name(transferee), xferto, transferer_real_context);
   ast_cel_report_event(transferer, AST_CEL_BLINDTRANSFER, NULL, xferto, transferee);
   pbx_builtin_setvar_helper(transferer, "BLINDTRANSFER", ast_channel_name(transferee));
   pbx_builtin_setvar_helper(transferee, "BLINDTRANSFER", ast_channel_name(transferer));
   finishup(transferee);
   ast_channel_lock(transferer);
   if (!ast_channel_cdr(transferer)) {
      /* this code should never get called (in a perfect world) */
      ast_channel_cdr_set(transferer, ast_cdr_alloc());
      if (ast_channel_cdr(transferer)) {
         ast_cdr_init(ast_channel_cdr(transferer), transferer); /* initialize our channel's cdr */
         ast_cdr_start(ast_channel_cdr(transferer));
      }
   }
   ast_channel_unlock(transferer);
   if (ast_channel_cdr(transferer)) {
      struct ast_cdr *swap = ast_channel_cdr(transferer);

      ast_debug(1,
         "transferer=%s; transferee=%s; lastapp=%s; lastdata=%s; chan=%s; dstchan=%s\n",
         ast_channel_name(transferer), ast_channel_name(transferee), ast_channel_cdr(transferer)->lastapp,
         ast_channel_cdr(transferer)->lastdata, ast_channel_cdr(transferer)->channel,
         ast_channel_cdr(transferer)->dstchannel);
      ast_debug(1, "TRANSFEREE; lastapp=%s; lastdata=%s, chan=%s; dstchan=%s\n",
         ast_channel_cdr(transferee)->lastapp, ast_channel_cdr(transferee)->lastdata, ast_channel_cdr(transferee)->channel,
         ast_channel_cdr(transferee)->dstchannel);
      ast_debug(1, "transferer_real_context=%s; xferto=%s\n",
         transferer_real_context, xferto);
      /* swap cdrs-- it will save us some time & work */
      ast_channel_cdr_set(transferer, ast_channel_cdr(transferee));
      ast_channel_cdr_set(transferee, swap);
   }

   res = ast_channel_pbx(transferee) ? AST_FEATURE_RETURN_SUCCESSBREAK : -1;

   /* Doh!  Use our handy async_goto functions */
   if (ast_async_goto(transferee, transferer_real_context, xferto, 1)) {
      ast_log(LOG_WARNING, "Async goto failed :-(\n");
      res = -1;
   } else if (res == AST_FEATURE_RETURN_SUCCESSBREAK) {
      /* Don't let the after-bridge code run the h-exten */
      ast_channel_lock(transferee);
      ast_set_flag(ast_channel_flags(transferee), AST_FLAG_BRIDGE_HANGUP_DONT);
      ast_channel_unlock(transferee);
   }
   check_goto_on_transfer(transferer);
   return res;
}
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 2469 of file features.c.

References AST_FEATURE_RETURN_HANGUP, and ast_verb.

{
   ast_verb(4, "User hit '%s' to disconnect call.\n", code);
   return AST_FEATURE_RETURN_HANGUP;
}
static int builtin_feature_get_exten ( struct ast_channel chan,
const char *  feature_name,
char *  buf,
size_t  len 
) [static]
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:
chanchannel parking call
peerchannel to be parked
configunsed
codeunused
sensefeature options
dataunused
Return values:
-1on successful park.
-1on chan hangup.
AST_FEATURE_RETURN_SUCCESSon error to keep the bridge connected.

Definition at line 2147 of file features.c.

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

{
   struct ast_channel *parker;
   struct ast_channel *parkee;
   struct ast_park_call_args args = { 0, };

   /*
    * We used to set chan's exten and priority to "s" and 1 here,
    * but this generates (in some cases) an invalid extension, and
    * if "s" exists, could errantly cause execution of extensions
    * you don't expect.  It makes more sense to let nature take its
    * course when chan finishes, and let the pbx do its thing and
    * hang up when the park is over.
    */

   /* Answer if call is not up */
   if (ast_channel_state(chan) != AST_STATE_UP) {
      /*
       * XXX Why are we doing this?  Both of the channels should be up
       * since you cannot do DTMF features unless you are bridged.
       */
      if (ast_answer(chan)) {
         return -1;
      }

      /* Sleep to allow VoIP streams to settle down */
      if (ast_safe_sleep(chan, 1000)) {
         return -1;
      }
   }

   /* one direction used to call park_call.... */
   set_peers(&parker, &parkee, peer, chan, sense);
   return masq_park_call(parkee, parker, &args) ? AST_FEATURE_RETURN_SUCCESS : -1;
}
static char* callback_dialoptions ( struct ast_flags features_callee,
struct ast_flags features_caller,
char *  options,
size_t  len 
) [static]

Definition at line 4960 of file features.c.

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

Referenced by manage_parked_call().

{
   int i = 0;
   enum {
      OPT_CALLEE_REDIRECT   = 't',
      OPT_CALLER_REDIRECT   = 'T',
      OPT_CALLEE_AUTOMON    = 'w',
      OPT_CALLER_AUTOMON    = 'W',
      OPT_CALLEE_DISCONNECT = 'h',
      OPT_CALLER_DISCONNECT = 'H',
      OPT_CALLEE_PARKCALL   = 'k',
      OPT_CALLER_PARKCALL   = 'K',
   };

   memset(options, 0, len);
   if (ast_test_flag(features_caller, AST_FEATURE_REDIRECT) && i < len) {
      options[i++] = OPT_CALLER_REDIRECT;
   }
   if (ast_test_flag(features_caller, AST_FEATURE_AUTOMON) && i < len) {
      options[i++] = OPT_CALLER_AUTOMON;
   }
   if (ast_test_flag(features_caller, AST_FEATURE_DISCONNECT) && i < len) {
      options[i++] = OPT_CALLER_DISCONNECT;
   }
   if (ast_test_flag(features_caller, AST_FEATURE_PARKCALL) && i < len) {
      options[i++] = OPT_CALLER_PARKCALL;
   }

   if (ast_test_flag(features_callee, AST_FEATURE_REDIRECT) && i < len) {
      options[i++] = OPT_CALLEE_REDIRECT;
   }
   if (ast_test_flag(features_callee, AST_FEATURE_AUTOMON) && i < len) {
      options[i++] = OPT_CALLEE_AUTOMON;
   }
   if (ast_test_flag(features_callee, AST_FEATURE_DISCONNECT) && i < len) {
      options[i++] = OPT_CALLEE_DISCONNECT;
   }
   if (ast_test_flag(features_callee, AST_FEATURE_PARKCALL) && i < len) {
      options[i++] = OPT_CALLEE_PARKCALL;
   }

   return options;
}
static int check_compat ( struct ast_channel c,
struct ast_channel newchan 
) [static]

make channels compatible

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

Definition at line 2621 of file features.c.

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

Referenced by builtin_atxfer().

{
   if (ast_channel_make_compatible(c, newchan) < 0) {
      ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
         ast_channel_name(c), ast_channel_name(newchan));
      ast_autoservice_chan_hangup_peer(c, newchan);
      return -1;
   }
   return 0;
}
static void check_goto_on_transfer ( struct ast_channel chan) [static]

Check goto on transfer.

Parameters:
chanCheck 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 1034 of file features.c.

References ast_channel_alloc(), ast_channel_clear_softhangup(), ast_channel_flags(), ast_channel_linkedid(), ast_channel_lock, ast_channel_masquerade(), ast_channel_name(), ast_channel_readformat(), ast_channel_state_set(), ast_channel_unlock, ast_channel_writeformat(), ast_clear_flag, ast_debug, ast_do_masquerade(), AST_FLAGS_ALL, ast_format_copy(), ast_hangup(), ast_parseable_goto(), ast_pbx_start(), AST_SOFTHANGUP_ALL, AST_STATE_DOWN, AST_STATE_UP, ast_strlen_zero(), and pbx_builtin_getvar_helper().

Referenced by builtin_blindtransfer().

{
   struct ast_channel *xferchan;
   const char *val;
   char *goto_on_transfer;
   char *x;

   ast_channel_lock(chan);
   val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
   if (ast_strlen_zero(val)) {
      ast_channel_unlock(chan);
      return;
   }
   goto_on_transfer = ast_strdupa(val);
   ast_channel_unlock(chan);

   ast_debug(1, "Attempting GOTO_ON_BLINDXFR=%s for %s.\n", val, ast_channel_name(chan));

   xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(chan), 0,
      "%s", ast_channel_name(chan));
   if (!xferchan) {
      return;
   }

   /* Make formats okay */
   ast_format_copy(ast_channel_readformat(xferchan), ast_channel_readformat(chan));
   ast_format_copy(ast_channel_writeformat(xferchan), ast_channel_writeformat(chan));

   if (ast_channel_masquerade(xferchan, chan)) {
      /* Failed to setup masquerade. */
      ast_hangup(xferchan);
      return;
   }

   for (x = goto_on_transfer; *x; ++x) {
      if (*x == '^') {
         *x = ',';
      }
   }
   ast_parseable_goto(xferchan, goto_on_transfer);
   ast_channel_state_set(xferchan, AST_STATE_UP);
   ast_clear_flag(ast_channel_flags(xferchan), AST_FLAGS_ALL);
   ast_channel_clear_softhangup(xferchan, AST_SOFTHANGUP_ALL);

   ast_do_masquerade(xferchan);
   if (ast_pbx_start(xferchan)) {
      /* Failed to start PBX. */
      ast_hangup(xferchan);
   }
}
static void clear_dialed_interfaces ( struct ast_channel chan) [static]

Definition at line 4243 of file features.c.

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

Referenced by ast_bridge_call().

{
   struct ast_datastore *di_datastore;

   ast_channel_lock(chan);
   if ((di_datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL))) {
      if (option_debug) {
         ast_log(LOG_DEBUG, "Removing dialed interfaces datastore on %s since we're bridging\n", ast_channel_name(chan));
      }
      if (!ast_channel_datastore_remove(chan, di_datastore)) {
         ast_datastore_free(di_datastore);
      }
   }
   ast_channel_unlock(chan);
}
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 5349 of file features.c.

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

Referenced by create_dynamic_parkinglot().

{
   struct ast_parkinglot *copylot;

   if ((copylot = find_parkinglot(name))) { /* Parkinglot with that name already exists */
      ao2_ref(copylot, -1);
      return NULL;
   }

   copylot = create_parkinglot(name);
   if (!copylot) {
      return NULL;
   }

   ast_debug(1, "Building parking lot %s\n", name);

   /* Copy the source parking lot configuration. */
   copylot->cfg = parkinglot->cfg;

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

Definition at line 1315 of file features.c.

References ao2_link, ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, ast_log(), 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, 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().

{
   const char *dyn_context;
   const char *dyn_exten;
   const char *dyn_range;
   const char *template_name;
   struct ast_parkinglot *template_parkinglot = NULL;
   struct ast_parkinglot *parkinglot;
   int dyn_start;
   int dyn_end;

   ast_channel_lock(chan);
   template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
   dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
   dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
   dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
   ast_channel_unlock(chan);

   if (!ast_strlen_zero(template_name)) {
      template_parkinglot = find_parkinglot(template_name);
      if (!template_parkinglot) {
         ast_debug(1, "PARKINGDYNAMIC lot %s does not exist.\n",
            template_name);
      } else if (template_parkinglot->cfg.is_invalid) {
         ast_debug(1, "PARKINGDYNAMIC lot %s has invalid config.\n",
            template_name);
         parkinglot_unref(template_parkinglot);
         template_parkinglot = NULL;
      }
   }
   if (!template_parkinglot) {
      template_parkinglot = parkinglot_addref(default_parkinglot);
      ast_debug(1, "Using default parking lot for template\n");
   }

   parkinglot = copy_parkinglot(name, template_parkinglot);
   if (!parkinglot) {
      ast_log(LOG_ERROR, "Could not build dynamic parking lot!\n");
   } else {
      /* Configure the dynamic parking lot. */
      if (!ast_strlen_zero(dyn_context)) {
         ast_copy_string(parkinglot->cfg.parking_con, dyn_context,
            sizeof(parkinglot->cfg.parking_con));
      }
      if (!ast_strlen_zero(dyn_exten)) {
         ast_copy_string(parkinglot->cfg.parkext, dyn_exten,
            sizeof(parkinglot->cfg.parkext));
      }
      if (!ast_strlen_zero(dyn_range)) {
         if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
            ast_log(LOG_WARNING,
               "Format for parking positions is a-b, where a and b are numbers\n");
         } else if (dyn_end < dyn_start || dyn_start <= 0 || dyn_end <= 0) {
            ast_log(LOG_WARNING,
               "Format for parking positions is a-b, where a <= b\n");
         } else {
            parkinglot->cfg.parking_start = dyn_start;
            parkinglot->cfg.parking_stop = dyn_end;
         }
      }

      /*
       * Sanity check for dynamic parking lot configuration.
       *
       * XXX It may be desirable to instead check if the dynamic
       * parking lot overlaps any existing lots like what is done for
       * a reload.
       */
      if (!strcmp(parkinglot->cfg.parking_con, template_parkinglot->cfg.parking_con)) {
         if (!strcmp(parkinglot->cfg.parkext, template_parkinglot->cfg.parkext)
            && parkinglot->cfg.parkext_exclusive) {
            ast_log(LOG_WARNING,
               "Parking lot '%s' conflicts with template parking lot '%s'!\n"
               "Change either PARKINGDYNCONTEXT or PARKINGDYNEXTEN.\n",
               parkinglot->name, template_parkinglot->name);
         }
         if ((template_parkinglot->cfg.parking_start <= parkinglot->cfg.parking_start
               && parkinglot->cfg.parking_start <= template_parkinglot->cfg.parking_stop)
            || (template_parkinglot->cfg.parking_start <= parkinglot->cfg.parking_stop
               && parkinglot->cfg.parking_stop <= template_parkinglot->cfg.parking_stop)
            || (parkinglot->cfg.parking_start < template_parkinglot->cfg.parking_start
               && template_parkinglot->cfg.parking_stop < parkinglot->cfg.parking_stop)) {
            ast_log(LOG_WARNING,
               "Parking lot '%s' parking spaces overlap template parking lot '%s'!\n"
               "Change PARKINGDYNPOS.\n",
               parkinglot->name, template_parkinglot->name);
         }
      }

      parkinglot_activate(parkinglot);
      ao2_link(parkinglots, parkinglot);
   }
   parkinglot_unref(template_parkinglot);

   return parkinglot;
}
static struct ast_parkinglot * create_parkinglot ( const char *  name) [static, read]

Allocate parking lot structure.

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

{
   struct ast_parkinglot *newlot;

   if (ast_strlen_zero(name)) { /* No name specified */
      return NULL;
   }

   newlot = ao2_alloc(sizeof(*newlot), parkinglot_destroy);
   if (!newlot)
      return NULL;

   ast_copy_string(newlot->name, name, sizeof(newlot->name));
   newlot->cfg.is_invalid = 1;/* No config is set yet. */
   AST_LIST_HEAD_INIT(&newlot->parkings);

   return newlot;
}
static void destroy_dialplan_usage_context ( struct parking_dp_context doomed) [static]

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

{
   struct parking_dp_ramp *ramp;
   struct parking_dp_spaces *spaces;

   while ((ramp = AST_LIST_REMOVE_HEAD(&doomed->access_extens, node))) {
      ast_free(ramp);
   }
   while ((spaces = AST_LIST_REMOVE_HEAD(&doomed->spaces, node))) {
      ast_free(spaces);
   }
   while ((spaces = AST_LIST_REMOVE_HEAD(&doomed->hints, node))) {
      ast_free(spaces);
   }
   ast_free(doomed);
}
static void destroy_dialplan_usage_map ( struct parking_dp_map doomed) [static]

Definition at line 6422 of file features.c.

References AST_LIST_REMOVE_HEAD, and destroy_dialplan_usage_context().

Referenced by load_config().

{
   struct parking_dp_context *item;

   while ((item = AST_LIST_REMOVE_HEAD(doomed, node))) {
      destroy_dialplan_usage_context(item);
   }
}
static void destroy_space ( const char *  context,
int  space 
) [static]

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

{
   char exten[AST_MAX_EXTENSION];

   /* Destroy priorities of the parking space that we registered. */
   snprintf(exten, sizeof(exten), "%d", space);
   remove_exten_if_exist(context, exten, PRIORITY_HINT);
   remove_exten_if_exist(context, exten, 1);
}
static void dial_features_destroy ( void *  data) [static]

Definition at line 881 of file features.c.

References ast_free.

{
   struct ast_dial_features *df = data;
   if (df) {
      ast_free(df);
   }
}
static void* dial_features_duplicate ( void *  data) [static]

Definition at line 868 of file features.c.

References ast_calloc.

{
   struct ast_dial_features *df = data, *df_copy;

   if (!(df_copy = ast_calloc(1, sizeof(*df)))) {
      return NULL;
   }

   memcpy(df_copy, df, sizeof(*df));

   return df_copy;
}
static int dialplan_usage_add_parkinglot ( struct parking_dp_map usage_map,
struct ast_parkinglot lot,
int  complain 
) [static]

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

{
   struct parking_dp_context *cur_ctx;
   struct parking_dp_context *new_ctx;
   int cmp;

   AST_LIST_TRAVERSE_SAFE_BEGIN(usage_map, cur_ctx, node) {
      cmp = strcmp(lot->cfg.parking_con, cur_ctx->context);
      if (cmp > 0) {
         /* The parking lot context goes after this node. */
         continue;
      }
      if (cmp == 0) {
         /* This is the node we will add parking lot spaces to the map. */
         return dialplan_usage_add_parkinglot_data(cur_ctx, lot, complain);
      }
      /* The new parking lot context goes before this node. */
      new_ctx = build_dialplan_useage_context(lot);
      if (!new_ctx) {
         return -1;
      }
      AST_LIST_INSERT_BEFORE_CURRENT(new_ctx, node);
      return 0;
   }
   AST_LIST_TRAVERSE_SAFE_END;

   /* New parking lot context goes on the end. */
   new_ctx = build_dialplan_useage_context(lot);
   if (!new_ctx) {
      return -1;
   }
   AST_LIST_INSERT_TAIL(usage_map, new_ctx, node);
   return 0;
}
static int do_bridge_masquerade ( struct ast_channel chan,
struct ast_channel tmpchan 
) [static]

Actual bridge.

Parameters:
chan
tmpchanStop hold music, lock both channels, masq channels, after bridge return channel to next priority.
Return values:
0on success.
-1on error.

Definition at line 7294 of file features.c.

References ast_channel_context(), ast_channel_exten(), ast_channel_lock_both, ast_channel_masquerade(), ast_channel_priority(), ast_channel_readformat(), ast_channel_unlock, ast_channel_writeformat(), ast_do_masquerade(), ast_explicit_goto(), ast_format_copy(), ast_moh_stop(), ast_setstate(), context, and feature_group_exten::exten.

Referenced by action_bridge(), and bridge_exec().

{
   const char *context;
   const char *exten;
   int priority;

   ast_moh_stop(chan);
   ast_channel_lock_both(chan, tmpchan);
   context = ast_strdupa(ast_channel_context(chan));
   exten = ast_strdupa(ast_channel_exten(chan));
   priority = ast_channel_priority(chan);
   ast_setstate(tmpchan, ast_channel_state(chan));
   ast_format_copy(ast_channel_readformat(tmpchan), ast_channel_readformat(chan));
   ast_format_copy(ast_channel_writeformat(tmpchan), ast_channel_writeformat(chan));
   ast_channel_unlock(chan);
   ast_channel_unlock(tmpchan);

   /* Masquerade setup and execution must be done without any channel locks held */
   if (ast_channel_masquerade(tmpchan, chan)) {
      return -1;
   }
   ast_do_masquerade(tmpchan);

   /* when returning from bridge, the channel will continue at the next priority */
   ast_explicit_goto(tmpchan, context, exten, priority + 1);

   return 0;
}
static void* do_parking_thread ( void *  ignore) [static]

Take care of parked calls and unpark them if needed.

Parameters:
ignoreunused 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 5299 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().

{
   struct pollfd *pfds = NULL, *new_pfds = NULL;
   int nfds = 0, new_nfds = 0;

   for (;;) {
      struct ao2_iterator iter;
      struct ast_parkinglot *curlot;
      int ms = -1;   /* poll2 timeout, uninitialized */

      iter = ao2_iterator_init(parkinglots, 0);
      while ((curlot = ao2_iterator_next(&iter))) {
         manage_parkinglot(curlot, pfds, nfds, &new_pfds, &new_nfds, &ms);
         ao2_ref(curlot, -1);
      }
      ao2_iterator_destroy(&iter);

      /* Recycle */
      ast_free(pfds);
      pfds = new_pfds;
      nfds = new_nfds;
      new_pfds = NULL;
      new_nfds = 0;

      /* Wait for something to happen */
      ast_poll(pfds, nfds, ms);
      pthread_testcancel();
   }
   /* If this WERE reached, we'd need to free(pfds) */
   return NULL;   /* Never reached */
}
static int feature_check ( struct ast_channel chan,
struct ast_flags features,
char *  code 
) [static]

Check if a feature exists.

Definition at line 3752 of file features.c.

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

Referenced by ast_bridge_call().

                                                                                           {
   char *chan_dynamic_features;
   ast_channel_lock(chan);
   chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
   ast_channel_unlock(chan);

   return feature_interpret_helper(chan, NULL, NULL, code, 0, chan_dynamic_features, features, FEATURE_INTERPRET_CHECK, NULL);
}
static void feature_ds_destroy ( void *  data) [static]

Definition at line 3352 of file features.c.

References ao2_ref, ast_free, and feature_ds::feature_map.

Referenced by get_feature_ds().

{
   struct feature_ds *feature_ds = data;

   if (feature_ds->feature_map) {
      ao2_ref(feature_ds->feature_map, -1);
      feature_ds->feature_map = NULL;
   }

   ast_free(feature_ds);
}
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,dataFind a feature, determine which channel activated
Return values:
AST_FEATURE_RETURN_NO_HANGUP_PEER
-1error.
-2when an application cannot be found.
Todo:
XXX should probably return res

Definition at line 3479 of file features.c.

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

Referenced by process_applicationmap_line().

{
   struct ast_app *app;
   struct ast_call_feature *feature = data;
   struct ast_channel *work, *idle;
   int res;

   if (!feature) { /* shouldn't ever happen! */
      ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
      return -1;
   }

   if (sense == FEATURE_SENSE_CHAN) {
      if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
         return AST_FEATURE_RETURN_KEEPTRYING;
      if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
         work = chan;
         idle = peer;
      } else {
         work = peer;
         idle = chan;
      }
   } else {
      if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
         return AST_FEATURE_RETURN_KEEPTRYING;
      if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
         work = peer;
         idle = chan;
      } else {
         work = chan;
         idle = peer;
      }
   }

   if (!(app = pbx_findapp(feature->app))) {
      ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
      return -2;
   }

   ast_autoservice_start(idle);
   ast_autoservice_ignore(idle, AST_FRAME_DTMF_END);

   pbx_builtin_setvar_helper(work, "DYNAMIC_PEERNAME", ast_channel_name(idle));
   pbx_builtin_setvar_helper(idle, "DYNAMIC_PEERNAME", ast_channel_name(work));
   pbx_builtin_setvar_helper(work, "DYNAMIC_FEATURENAME", feature->sname);
   pbx_builtin_setvar_helper(idle, "DYNAMIC_FEATURENAME", feature->sname);

   if (!ast_strlen_zero(feature->moh_class))
      ast_moh_start(idle, feature->moh_class, NULL);

   res = pbx_exec(work, app, feature->app_args);

   if (!ast_strlen_zero(feature->moh_class))
      ast_moh_stop(idle);

   ast_autoservice_stop(idle);

   if (res) {
      return AST_FEATURE_RETURN_SUCCESSBREAK;
   }
   return AST_FEATURE_RETURN_SUCCESS;  /*! \todo XXX should probably return res */
}
static int feature_exten_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 3377 of file features.c.

References CMP_MATCH, CMP_STOP, OBJ_KEY, and feature_exten::sname.

Referenced by get_feature_ds().

{
   const struct feature_exten *fe = obj, *fe2 = arg;
   const char *sname = arg;

   return !strcmp(fe->sname, flags & OBJ_KEY ? sname : fe2->sname) ?
         CMP_MATCH | CMP_STOP : 0;
}
static int feature_exten_hash ( const void *  obj,
int  flags 
) [static]

Definition at line 3369 of file features.c.

References ast_str_hash(), OBJ_KEY, and feature_exten::sname.

Referenced by get_feature_ds().

{
   const struct feature_exten *fe = obj;
   const char *sname = obj;

   return ast_str_hash(flags & OBJ_KEY ? sname : fe->sname);
}
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:
reson success.
-1on failure.

Definition at line 3715 of file features.c.

References ast_channel_lock, ast_channel_name(), ast_channel_unlock, ast_copy_flags, ast_debug, AST_FLAGS_ALL, 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().

                                                                                                                                                {

   char dynamic_features_buf[128];
   const char *peer_dynamic_features, *chan_dynamic_features;
   struct ast_flags features;
   struct ast_call_feature feature;
   if (sense == FEATURE_SENSE_CHAN) {
      /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
      ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
   }
   else {
      /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
      ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
   }

   ast_channel_lock(peer);
   peer_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"),""));
   ast_channel_unlock(peer);

   ast_channel_lock(chan);
   chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
   ast_channel_unlock(chan);

   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,""));

   ast_debug(3, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d, dynamic=%s\n", ast_channel_name(chan), ast_channel_name(peer), code, sense, features.flags, dynamic_features_buf);

   return feature_interpret_helper(chan, peer, config, code, sense, dynamic_features_buf, &features, FEATURE_INTERPRET_DO, &feature);
}
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,featureLock 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:
reson success.
-1on failure.

Definition at line 3580 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_test_suite_event_notify, ast_verb, builtin_feature_get_exten(), ast_call_feature::exten, feature_group_exten::exten, feature_group_exten::feature, FEATURE_INTERPRET_CHECK, FEATURE_INTERPRET_DO, ast_call_feature::feature_mask, FEATURE_MAX_LEN, 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().

{
   int x;
   struct feature_group *fg = NULL;
   struct feature_group_exten *fge;
   struct ast_call_feature *tmpfeature;
   char *tmp, *tok;
   int res = AST_FEATURE_RETURN_PASSDIGITS;
   int feature_detected = 0;

   if (!(peer && chan && config) && operation == FEATURE_INTERPRET_DO) {
      return -1; /* can not run feature operation */
   }

   ast_rwlock_rdlock(&features_lock);
   for (x = 0; x < FEATURES_COUNT; x++) {
      char feature_exten[FEATURE_MAX_LEN] = "";

      if (!ast_test_flag(features, builtin_features[x].feature_mask)) {
         continue;
      }

      if (builtin_feature_get_exten(chan, builtin_features[x].sname, feature_exten, sizeof(feature_exten))) {
         continue;
      }

      /* Feature is up for consideration */

      if (!strcmp(feature_exten, code)) {
         ast_debug(3, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, feature_exten);
         if (operation == FEATURE_INTERPRET_CHECK) {
            res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
         } else if (operation == FEATURE_INTERPRET_DO) {
            res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
            ast_test_suite_event_notify("FEATURE_DETECTION",
                  "Result: success\r\n"
                  "Feature: %s",
                  builtin_features[x].sname);
         }
         if (feature) {
            memcpy(feature, &builtin_features[x], sizeof(*feature));
         }
         feature_detected = 1;
         break;
      } else if (!strncmp(feature_exten, code, strlen(code))) {
         if (res == AST_FEATURE_RETURN_PASSDIGITS) {
            res = AST_FEATURE_RETURN_STOREDIGITS;
         }
      }
   }

   if (operation == FEATURE_INTERPRET_CHECK && x == FEATURES_COUNT) {
      ast_test_suite_event_notify("FEATURE_DETECTION",
            "Result: fail");
   }

   ast_rwlock_unlock(&features_lock);

   if (ast_strlen_zero(dynamic_features_buf) || feature_detected) {
      return res;
   }

   tmp = dynamic_features_buf;

   while ((tok = strsep(&tmp, "#"))) {
      AST_RWLIST_RDLOCK(&feature_groups);

      fg = find_group(tok);

      if (fg) {
         AST_LIST_TRAVERSE(&fg->features, fge, entry) {
            if (!strcmp(fge->exten, code)) {
               if (operation) {
                  res = fge->feature->operation(chan, peer, config, code, sense, fge->feature);
               }
               if (feature) {
                  memcpy(feature, fge->feature, sizeof(*feature));
               }
               if (res != AST_FEATURE_RETURN_KEEPTRYING) {
                  AST_RWLIST_UNLOCK(&feature_groups);
                  break;
               }
               res = AST_FEATURE_RETURN_PASSDIGITS;
            } else if (!strncmp(fge->exten, code, strlen(code))) {
               res = AST_FEATURE_RETURN_STOREDIGITS;
            }
         }
         if (fge) {
            break;
         }
      }

      AST_RWLIST_UNLOCK(&feature_groups);

      AST_RWLIST_RDLOCK(&feature_list);

      if (!(tmpfeature = find_dynamic_feature(tok))) {
         AST_RWLIST_UNLOCK(&feature_list);
         continue;
      }

      /* Feature is up for consideration */
      if (!strcmp(tmpfeature->exten, code)) {
         ast_verb(3, " Feature Found: %s exten: %s\n",tmpfeature->sname, tok);
         if (operation == FEATURE_INTERPRET_CHECK) {
            res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
         } else if (operation == FEATURE_INTERPRET_DO) {
            res = tmpfeature->operation(chan, peer, config, code, sense, tmpfeature);
         }
         if (feature) {
            memcpy(feature, tmpfeature, sizeof(*feature));
         }
         if (res != AST_FEATURE_RETURN_KEEPTRYING) {
            AST_RWLIST_UNLOCK(&feature_list);
            break;
         }
         res = AST_FEATURE_RETURN_PASSDIGITS;
      } else if (!strncmp(tmpfeature->exten, code, strlen(code)))
         res = AST_FEATURE_RETURN_STOREDIGITS;

      AST_RWLIST_UNLOCK(&feature_list);
   }

   return res;
}
static int feature_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 8911 of file features.c.

References ast_log(), get_parkingtime(), and LOG_WARNING.

{
   int res = 0;

   if (!chan) {
      ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
      return -1;
   }

   if (!strcasecmp(data, "parkingtime")) {
      snprintf(buf, len, "%u", get_parkingtime(chan, NULL) / 1000);
   } else {
      ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
      res = -1;
   }

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

Definition at line 3856 of file features.c.

References ast_alloca, ast_autoservice_start(), ast_autoservice_stop(), ast_best_codec(), ast_call(), ast_call_forward(), AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, ast_channel_call_forward(), ast_channel_caller(), ast_channel_connected(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), ast_channel_exten(), ast_channel_hangupcause(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_name(), ast_channel_redirecting_macro(), ast_channel_redirecting_sub(), 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_PVT_CAUSE_CODE, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_format_cap_add(), ast_format_cap_alloc_nolock(), ast_format_cap_destroy(), AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frfree, ast_frisolate(), ast_hangup(), ast_indicate(), ast_indicate_data(), ast_is_deferrable_frame(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_HEAD_NOLOCK, AST_LIST_INSERT_HEAD, AST_LIST_REMOVE_HEAD, ast_log(), ast_party_connected_line_free(), ast_party_connected_line_set_init(), ast_poll_channel_add(), ast_poll_channel_del(), ast_queue_frame_head(), ast_read(), ast_request(), ast_rwlock_rdlock, ast_rwlock_unlock, AST_STATE_UP, ast_strlen_zero(), ast_tvdiff_ms(), ast_tvnow(), ast_verb, ast_waitfor_n(), ast_write(), ast_frame::data, ast_frame::datalen, ast_call_feature::exten, f, FEATURES_COUNT, features_lock, ast_frame::frametype, ast_frame_subclass::integer, LOG_NOTICE, pbx_builtin_setvar_helper(), ast_frame::ptr, state, and ast_frame::subclass.

Referenced by builtin_atxfer().

{
   int state = 0;
   int cause = 0;
   int to;
   int caller_hungup;
   int transferee_hungup;
   struct ast_channel *chan;
   struct ast_channel *monitor_chans[3];
   struct ast_channel *active_channel;
   int res;
   int ready = 0;
   struct timeval started;
   int x, len = 0;
   char *disconnect_code = NULL, *dialed_code = NULL;
   struct ast_format_cap *tmp_cap;
   struct ast_format best_audio_fmt;
   struct ast_frame *f;
   AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;

   tmp_cap = ast_format_cap_alloc_nolock();
   if (!tmp_cap) {
      if (outstate) {
         *outstate = 0;
      }
      return NULL;
   }
   ast_best_codec(cap, &best_audio_fmt);
   ast_format_cap_add(tmp_cap, &best_audio_fmt);

   caller_hungup = ast_check_hangup(caller);

   if (!(chan = ast_request(type, tmp_cap, requestor, addr, &cause))) {
      ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, addr);
      switch (cause) {
      case AST_CAUSE_BUSY:
         state = AST_CONTROL_BUSY;
         break;
      case AST_CAUSE_CONGESTION:
         state = AST_CONTROL_CONGESTION;
         break;
      default:
         state = 0;
         break;
      }
      goto done;
   }

   ast_channel_language_set(chan, language);
   ast_channel_inherit_variables(caller, chan);
   pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller_name);

   ast_channel_lock(chan);
   ast_connected_line_copy_from_caller(ast_channel_connected(chan), ast_channel_caller(requestor));
   ast_channel_unlock(chan);

   if (ast_call(chan, addr, timeout)) {
      ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, addr);
      switch (ast_channel_hangupcause(chan)) {
      case AST_CAUSE_BUSY:
         state = AST_CONTROL_BUSY;
         break;
      case AST_CAUSE_CONGESTION:
         state = AST_CONTROL_CONGESTION;
         break;
      default:
         state = 0;
         break;
      }
      goto done;
   }

   /* support dialing of the featuremap disconnect code while performing an attended tranfer */
   ast_rwlock_rdlock(&features_lock);
   for (x = 0; x < FEATURES_COUNT; x++) {
      if (strcasecmp(builtin_features[x].sname, "disconnect"))
         continue;

      disconnect_code = builtin_features[x].exten;
      len = strlen(disconnect_code) + 1;
      dialed_code = ast_alloca(len);
      memset(dialed_code, 0, len);
      break;
   }
   ast_rwlock_unlock(&features_lock);
   x = 0;
   started = ast_tvnow();
   to = timeout;
   AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);

   ast_poll_channel_add(caller, chan);

   transferee_hungup = 0;
   while (!ast_check_hangup(transferee) && (ast_channel_state(chan) != AST_STATE_UP)) {
      int num_chans = 0;

      monitor_chans[num_chans++] = transferee;
      monitor_chans[num_chans++] = chan;
      if (!caller_hungup) {
         if (ast_check_hangup(caller)) {
            caller_hungup = 1;

#if defined(ATXFER_NULL_TECH)
            /* Change caller's name to ensure that it will remain unique. */
            set_new_chan_name(caller);

            /*
             * Get rid of caller's physical technology so it is free for
             * other calls.
             */
            set_kill_chan_tech(caller);
#endif   /* defined(ATXFER_NULL_TECH) */
         } else {
            /* caller is not hungup so monitor it. */
            monitor_chans[num_chans++] = caller;
         }
      }

      /* see if the timeout has been violated */
      if (ast_tvdiff_ms(ast_tvnow(), started) > timeout) {
         state = AST_CONTROL_UNHOLD;
         ast_log(LOG_NOTICE, "We exceeded our AT-timeout for %s\n", ast_channel_name(chan));
         break; /*doh! timeout*/
      }

      active_channel = ast_waitfor_n(monitor_chans, num_chans, &to);
      if (!active_channel)
         continue;

      f = NULL;
      if (transferee == active_channel) {
         struct ast_frame *dup_f;

         f = ast_read(transferee);
         if (f == NULL) { /*doh! where'd he go?*/
            transferee_hungup = 1;
            state = 0;
            break;
         }
         if (ast_is_deferrable_frame(f)) {
            dup_f = ast_frisolate(f);
            if (dup_f) {
               if (dup_f == f) {
                  f = NULL;
               }
               AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list);
            }
         }
      } else if (chan == active_channel) {
         if (!ast_strlen_zero(ast_channel_call_forward(chan))) {
            state = 0;
            ast_autoservice_start(transferee);
            chan = ast_call_forward(caller, chan, NULL, tmp_cap, NULL, &state);
            ast_autoservice_stop(transferee);
            if (!chan) {
               break;
            }
            continue;
         }
         f = ast_read(chan);
         if (f == NULL) { /*doh! where'd he go?*/
            switch (ast_channel_hangupcause(chan)) {
            case AST_CAUSE_BUSY:
               state = AST_CONTROL_BUSY;
               break;
            case AST_CAUSE_CONGESTION:
               state = AST_CONTROL_CONGESTION;
               break;
            default:
               state = 0;
               break;
            }
            break;
         }

         if (f->frametype == AST_FRAME_CONTROL) {
            if (f->subclass.integer == AST_CONTROL_RINGING) {
               ast_verb(3, "%s is ringing\n", ast_channel_name(chan));
               ast_indicate(caller, AST_CONTROL_RINGING);
            } else if (f->subclass.integer == AST_CONTROL_BUSY) {
               state = f->subclass.integer;
               ast_verb(3, "%s is busy\n", ast_channel_name(chan));
               ast_indicate(caller, AST_CONTROL_BUSY);
               ast_frfree(f);
               break;
            } else if (f->subclass.integer == AST_CONTROL_INCOMPLETE) {
               ast_verb(3, "%s dialed incomplete extension %s; ignoring\n", ast_channel_name(chan), ast_channel_exten(chan));
            } else if (f->subclass.integer == AST_CONTROL_CONGESTION) {
               state = f->subclass.integer;
               ast_verb(3, "%s is congested\n", ast_channel_name(chan));
               ast_indicate(caller, AST_CONTROL_CONGESTION);
               ast_frfree(f);
               break;
            } else if (f->subclass.integer == AST_CONTROL_ANSWER) {
               /* This is what we are hoping for */
               state = f->subclass.integer;
               ast_frfree(f);
               ready=1;
               break;
            } else if (f->subclass.integer == AST_CONTROL_PVT_CAUSE_CODE) {
               ast_indicate_data(caller, AST_CONTROL_PVT_CAUSE_CODE, f->data.ptr, f->datalen);
            } else if (f->subclass.integer == AST_CONTROL_CONNECTED_LINE) {
               if (caller_hungup) {
                  struct ast_party_connected_line connected;

                  /* Just save it for the transfer. */
                  ast_party_connected_line_set_init(&connected, ast_channel_connected(caller));
                  res = ast_connected_line_parse_data(f->data.ptr, f->datalen,
                     &connected);
                  if (!res) {
                     ast_channel_set_connected_line(caller, &connected, NULL);
                  }
                  ast_party_connected_line_free(&connected);
               } else {
                  ast_autoservice_start(transferee);
                  if (ast_channel_connected_line_sub(chan, caller, f, 1) &&
                     ast_channel_connected_line_macro(chan, caller, f, 1, 1)) {
                     ast_indicate_data(caller, AST_CONTROL_CONNECTED_LINE,
                        f->data.ptr, f->datalen);
                  }
                  ast_autoservice_stop(transferee);
               }
            } else if (f->subclass.integer == AST_CONTROL_REDIRECTING) {
               if (!caller_hungup) {
                  ast_autoservice_start(transferee);
                  if (ast_channel_redirecting_sub(chan, caller, f, 1) &&
                     ast_channel_redirecting_macro(chan, caller, f, 1, 1)) {
                     ast_indicate_data(caller, AST_CONTROL_REDIRECTING,
                        f->data.ptr, f->datalen);
                  }
                  ast_autoservice_stop(transferee);
               }
            } else if (f->subclass.integer != -1
               && f->subclass.integer != AST_CONTROL_PROGRESS
               && f->subclass.integer != AST_CONTROL_PROCEEDING) {
               ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass.integer);
            }
            /* else who cares */
         } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
            ast_write(caller, f);
         }
      } else if (caller == active_channel) {
         f = ast_read(caller);
         if (f) {
            if (f->frametype == AST_FRAME_DTMF) {
               dialed_code[x++] = f->subclass.integer;
               dialed_code[x] = '\0';
               if (strlen(dialed_code) == len) {
                  x = 0;
               } else if (x && strncmp(dialed_code, disconnect_code, x)) {
                  x = 0;
                  dialed_code[x] = '\0';
               }
               if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
                  /* Caller Canceled the call */
                  state = AST_CONTROL_UNHOLD;
                  ast_frfree(f);
                  break;
               }
            } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
               ast_write(chan, f);
            }
         }
      }
      if (f)
         ast_frfree(f);
   } /* end while */

   ast_poll_channel_del(caller, chan);

   /*
    * We need to free all the deferred frames, but we only need to
    * queue the deferred frames if no hangup was received.
    */
   ast_channel_lock(transferee);
   transferee_hungup = (transferee_hungup || ast_check_hangup(transferee));
   while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) {
      if (!transferee_hungup) {
         ast_queue_frame_head(transferee, f);
      }
      ast_frfree(f);
   }
   ast_channel_unlock(transferee);

done:
   ast_indicate(caller, -1);
   if (chan && (ready || ast_channel_state(chan) == AST_STATE_UP)) {
      state = AST_CONTROL_ANSWER;
   } else if (chan) {
      ast_hangup(chan);
      chan = NULL;
   }

   tmp_cap = ast_format_cap_destroy(tmp_cap);

   if (outstate)
      *outstate = state;

   return chan;
}
static int feature_write ( struct ast_channel chan,
const char *  cmd,
char *  data,
const char *  value 
) [static]

Definition at line 8931 of file features.c.

References ast_channel_lock, ast_channel_unlock, ast_log(), get_feature_ds(), LOG_WARNING, feature_ds::parkingtime, and feature_ds::parkingtime_is_set.

{
   int res = 0;
   struct feature_ds *feature_ds;

   if (!chan) {
      ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
      return -1;
   }

   ast_channel_lock(chan);

   if (!(feature_ds = get_feature_ds(chan))) {
      res = -1;
      goto return_cleanup;
   }

   if (!strcasecmp(data, "parkingtime")) {
      feature_ds->parkingtime_is_set = 1;
      if (sscanf(value, "%30u", &feature_ds->parkingtime) == 1) {
         feature_ds->parkingtime *= 1000; /* stored in ms */
      } else {
         ast_log(LOG_WARNING, "'%s' is not a valid parkingtime\n", value);
         feature_ds->parkingtime_is_set = 0;
         res = -1;
      }
   } else {
      ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
      res = -1;
   }

return_cleanup:
   ast_channel_unlock(chan);

   return res;
}
static int featuremap_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 8969 of file features.c.

References ast_log(), ast_rdlock_call_features(), ast_unlock_call_features(), builtin_feature_get_exten(), and LOG_WARNING.

{
   int res;

   if (!chan) {
      ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
      return -1;
   }

   ast_rdlock_call_features();

   if ((res = builtin_feature_get_exten(chan, data, buf, len))) {
      ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
   }

   ast_unlock_call_features();

   return res;
}
static int featuremap_write ( struct ast_channel chan,
const char *  cmd,
char *  data,
const char *  value 
) [static]

Definition at line 8990 of file features.c.

References ao2_alloc, ao2_find, ao2_link, ao2_lock, ao2_ref, ao2_unlock, ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_find_call_feature(), ast_log(), feature_exten::exten, feature_ds::feature_map, get_feature_ds(), LOG_WARNING, OBJ_KEY, and feature_exten::sname.

{
   struct feature_ds *feature_ds;
   struct feature_exten *fe;

   if (!chan) {
      ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
      return -1;
   }

   if (!ast_find_call_feature(data)) {
      ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
      return -1;
   }

   ast_channel_lock(chan);

   if (!(feature_ds = get_feature_ds(chan))) {
      ast_channel_unlock(chan);
      return -1;
   }

   if (!(fe = ao2_find(feature_ds->feature_map, data, OBJ_KEY))) {
      if (!(fe = ao2_alloc(sizeof(*fe), NULL))) {
         ast_channel_unlock(chan);
         return -1;
      }
      ast_copy_string(fe->sname, data, sizeof(fe->sname));
      ao2_link(feature_ds->feature_map, fe);
   }

   ast_channel_unlock(chan);

   ao2_lock(fe);
   ast_copy_string(fe->exten, value, sizeof(fe->exten));
   ao2_unlock(fe);
   ao2_ref(fe, -1);
   fe = NULL;

   return 0;
}
static int find_channel_by_group ( void *  obj,
void *  arg,
void *  data,
int  flags 
) [static]

< Potential pickup target

< Channel wanting to pickup call

< Candidate channels found.

Definition at line 7770 of file features.c.

References ao2_link, ast_can_pickup(), ast_channel_callgroup(), ast_channel_lock, ast_channel_named_callgroups(), ast_channel_named_pickupgroups(), ast_channel_pickupgroup(), ast_channel_trylock, ast_channel_unlock, and ast_namedgroups_intersect().

Referenced by ast_pickup_find_by_group().

{
   struct ast_channel *target = obj;/*!< Potential pickup target */
   struct ast_channel *chan = arg;/*!< Channel wanting to pickup call */

   if (chan == target) {
      return 0;
   }

   ast_channel_lock(target);
   if (ast_can_pickup(target)) {
      /* Lock both channels. */
      while (ast_channel_trylock(chan)) {
         ast_channel_unlock(target);
         sched_yield();
         ast_channel_lock(target);
      }

      /*
       * Both callgroup and namedcallgroup pickup variants are
       * matched independently.  Checking for named group match is
       * done last since it's a more expensive operation.
       */
      if ((ast_channel_pickupgroup(chan) & ast_channel_callgroup(target))
         || (ast_namedgroups_intersect(ast_channel_named_pickupgroups(chan),
            ast_channel_named_callgroups(target)))) {
         struct ao2_container *candidates = data;/*!< Candidate channels found. */

         /* This is a candidate to pickup */
         ao2_link(candidates, target);
      }
      ast_channel_unlock(chan);
   }
   ast_channel_unlock(target);

   return 0;
}
static struct ast_call_feature* find_dynamic_feature ( const char *  name) [static, read]

find a call feature by name

Definition at line 3258 of file features.c.

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

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

{
   struct ast_call_feature *tmp;

   AST_RWLIST_TRAVERSE(&feature_list, tmp, feature_entry) {
      if (!strcasecmp(tmp->sname, name)) {
         break;
      }
   }

   return tmp;
}
static struct feature_group* find_group ( const char *  name) [static, read]

Find a group by name.

Parameters:
namefeature name
Return values:
featuregroup on success.
NULLon failure.

Definition at line 3296 of file features.c.

References AST_LIST_TRAVERSE, and feature_group::gname.

Referenced by feature_interpret_helper().

{
   struct feature_group *fg = NULL;

   AST_LIST_TRAVERSE(&feature_groups, fg, entry) {
      if (!strcasecmp(fg->gname, name))
         break;
   }

   return fg;
}
static struct ast_parkinglot * find_parkinglot ( const char *  name) [static, read]

Find parkinglot by name.

Definition at line 5332 of file features.c.

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

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

{
   struct ast_parkinglot *parkinglot;

   if (ast_strlen_zero(name)) {
      return NULL;
   }

   parkinglot = ao2_find(parkinglots, (void *) name, 0);
   if (parkinglot) {
      ast_debug(1, "Found Parking lot: %s\n", parkinglot->name);
   }

   return parkinglot;
}
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 1232 of file features.c.

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

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

{
   const char *name;

   /* The channel variable overrides everything */
   name = pbx_builtin_getvar_helper(chan, "PARKINGLOT");
   if (!name && !ast_strlen_zero(ast_channel_parkinglot(chan))) {
      /* Use the channel's parking lot. */
      name = ast_channel_parkinglot(chan);
   }
   return name;
}
static int finishup ( struct ast_channel chan) [static]
static struct feature_ds* get_feature_ds ( struct ast_channel chan) [static, read]

Definition at line 3394 of file features.c.

References ao2_container_alloc, ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_datastore_alloc(), ast_datastore::data, feature_ds_destroy(), feature_exten_cmp(), feature_exten_hash(), and feature_ds::feature_map.

Referenced by builtin_feature_get_exten(), feature_write(), featuremap_write(), and get_parkingtime().

{
   struct feature_ds *feature_ds;
   struct ast_datastore *ds;

   if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
      feature_ds = ds->data;
      return feature_ds;
   }

   if (!(feature_ds = ast_calloc(1, sizeof(*feature_ds)))) {
      return NULL;
   }

   if (!(feature_ds->feature_map = ao2_container_alloc(7, feature_exten_hash, feature_exten_cmp))) {
      feature_ds_destroy(feature_ds);
      return NULL;
   }

   if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
      feature_ds_destroy(feature_ds);
      return NULL;
   }

   ds->data = feature_ds;

   ast_channel_datastore_add(chan, ds);

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

Definition at line 960 of file features.c.

References ast_debug, ast_get_extension_app(), E_MATCH, feature_group_exten::exten, 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().

{
   struct ast_exten *exten;
   struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
   const char *app_at_exten;

   ast_debug(4, "Checking if %s@%s is a parking exten\n", exten_str, context);
   exten = pbx_find_extension(chan, NULL, &q, context, exten_str, 1, NULL, NULL,
      E_MATCH);
   if (!exten) {
      return NULL;
   }

   app_at_exten = ast_get_extension_app(exten);
   if (!app_at_exten || strcasecmp(parkcall, app_at_exten)) {
      return NULL;
   }

   return exten;
}
static unsigned int get_parkingtime ( struct ast_channel chan,
struct ast_parkinglot parkinglot 
) [static]

Definition at line 8872 of file features.c.

References ast_channel_lock, ast_channel_parkinglot(), ast_channel_unlock, ast_strlen_zero(), ast_parkinglot::cfg, default_parkinglot, find_parkinglot(), get_feature_ds(), parkinglot_addref(), parkinglot_unref(), parkinglot_cfg::parkingtime, feature_ds::parkingtime, feature_ds::parkingtime_is_set, and S_OR.

Referenced by feature_read(), and park_call_full().

{
   const char *parkinglot_name;
   struct feature_ds *feature_ds;
   unsigned int parkingtime;

   ast_channel_lock(chan);

   feature_ds = get_feature_ds(chan);
   if (feature_ds && feature_ds->parkingtime_is_set) {
      parkingtime = feature_ds->parkingtime;
      ast_channel_unlock(chan);
      return parkingtime;
   }

   parkinglot_name = ast_strdupa(S_OR(ast_channel_parkinglot(chan), ""));

   ast_channel_unlock(chan);

   if (!parkinglot) {
      if (!ast_strlen_zero(parkinglot_name)) {
         parkinglot = find_parkinglot(parkinglot_name);
      }

      if (!parkinglot) {
         parkinglot = parkinglot_addref(default_parkinglot);
      }
   } else {
      /* Just to balance the unref below */
      parkinglot_addref(parkinglot);
   }

   parkingtime = parkinglot->cfg.parkingtime;

   parkinglot_unref(parkinglot);

   return parkingtime;
}
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_SUCCESSon success.
NULLwhen tab completion is used.

Definition at line 7153 of file features.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_cli(), AST_CLI_YESNO, AST_LIST_TRAVERSE, ast_pickup_ext(), AST_RWLIST_EMPTY, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_rwlock_rdlock, ast_rwlock_unlock, ast_parkinglot::cfg, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, parkinglot_cfg::comebackcontext, parkinglot_cfg::comebackdialtime, parkinglot_cfg::comebacktoorigin, ast_cli_entry::command, ast_call_feature::default_exten, ast_parkinglot::disabled, ast_call_feature::exten, feature_group_exten::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.

{
   int i;
   struct ast_call_feature *feature;
   struct ao2_iterator iter;
   struct ast_parkinglot *curlot;
#define HFS_FORMAT "%-25s %-7s %-7s\n"

   switch (cmd) {

   case CLI_INIT:
      e->command = "features show";
      e->usage =
         "Usage: features show\n"
         "       Lists configured features\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
   ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");

   ast_cli(a->fd, HFS_FORMAT, "Pickup", "*8", ast_pickup_ext());          /* default hardcoded above, so we'll hardcode it here */

   ast_rwlock_rdlock(&features_lock);
   for (i = 0; i < FEATURES_COUNT; i++)
      ast_cli(a->fd, HFS_FORMAT, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
   ast_rwlock_unlock(&features_lock);

   ast_cli(a->fd, "\n");
   ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
   ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
   if (AST_RWLIST_EMPTY(&feature_list)) {
      ast_cli(a->fd, "(none)\n");
   } else {
      AST_RWLIST_RDLOCK(&feature_list);
      AST_RWLIST_TRAVERSE(&feature_list, feature, feature_entry) {
         ast_cli(a->fd, HFS_FORMAT, feature->sname, "no def", feature->exten);
      }
      AST_RWLIST_UNLOCK(&feature_list);
   }

   ast_cli(a->fd, "\nFeature Groups:\n");
   ast_cli(a->fd, "---------------\n");
   if (AST_RWLIST_EMPTY(&feature_groups)) {
      ast_cli(a->fd, "(none)\n");
   } else {
      struct feature_group *fg;
      struct feature_group_exten *fge;

      AST_RWLIST_RDLOCK(&feature_groups);
      AST_RWLIST_TRAVERSE(&feature_groups, fg, entry) {
         ast_cli(a->fd, "===> Group: %s\n", fg->gname);
         AST_LIST_TRAVERSE(&fg->features, fge, entry) {
            ast_cli(a->fd, "===> --> %s (%s)\n", fge->feature->sname, fge->exten);
         }
      }
      AST_RWLIST_UNLOCK(&feature_groups);
   }

   iter = ao2_iterator_init(parkinglots, 0);
   while ((curlot = ao2_iterator_next(&iter))) {
      ast_cli(a->fd, "\nCall parking (Parking lot: %s)\n", curlot->name);
      ast_cli(a->fd, "------------\n");
      ast_cli(a->fd,"%-22s:      %s\n", "Parking extension", curlot->cfg.parkext);
      ast_cli(a->fd,"%-22s:      %s\n", "Parking context", curlot->cfg.parking_con);
      ast_cli(a->fd,"%-22s:      %d-%d\n", "Parked call extensions",
         curlot->cfg.parking_start, curlot->cfg.parking_stop);
      ast_cli(a->fd,"%-22s:      %u ms\n", "Parkingtime", curlot->cfg.parkingtime);
      ast_cli(a->fd,"%-22s:      %s\n", "Comeback to origin",
            (curlot->cfg.comebacktoorigin ? "yes" : "no"));
      ast_cli(a->fd,"%-22s:      %s%s\n", "Comeback context",
            curlot->cfg.comebackcontext, (curlot->cfg.comebacktoorigin ?
               " (comebacktoorigin=yes, not used)" : ""));
      ast_cli(a->fd,"%-22s:      %d\n", "Comeback dial time",
            curlot->cfg.comebackdialtime);
      ast_cli(a->fd,"%-22s:      %s\n", "MusicOnHold class", curlot->cfg.mohclass);
      ast_cli(a->fd,"%-22s:      %s\n", "Enabled", AST_CLI_YESNO(!curlot->disabled));
      ast_cli(a->fd,"\n");
      ao2_ref(curlot, -1);
   }
   ao2_iterator_destroy(&iter);

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

Definition at line 7266 of file features.c.

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

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "features reload";
      e->usage =
         "Usage: features reload\n"
         "       Reloads configured call features from features.conf\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }
   ast_features_reload();

   return CLI_SUCCESS;
}
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
aCheck right usage, lock parking lot, display parked calls, unlock parking lot list.
Return values:
CLI_SUCCESSon success.
CLI_SHOWUSAGEon incorrect number of arguments.
NULLwhen tab completion is used.

Definition at line 7485 of file features.c.

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

{
   struct parkeduser *cur;
   int numparked = 0;
   struct ao2_iterator iter;
   struct ast_parkinglot *curlot;

   switch (cmd) {
   case CLI_INIT:
      e->command = "parkedcalls show";
      e->usage =
         "Usage: parkedcalls show\n"
         "       List currently parked calls\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc > e->args)
      return CLI_SHOWUSAGE;

   ast_cli(a->fd, "%-10s %-25s (%-15s %-12s %4s) %s\n", "Num", "Channel",
      "Context", "Extension", "Pri", "Timeout");

   iter = ao2_iterator_init(parkinglots, 0);
   while ((curlot = ao2_iterator_next(&iter))) {
      int lotparked = 0;

      /* subtract ref for iterator and for configured parking lot */
      ast_cli(a->fd, "*** Parking lot: %s (%d)\n", curlot->name,
         ao2_ref(curlot, 0) - 2 - (curlot == default_parkinglot));

      AST_LIST_LOCK(&curlot->parkings);
      AST_LIST_TRAVERSE(&curlot->parkings, cur, list) {
         ast_cli(a->fd, "%-10.10s %-25s (%-15s %-12s %4d) %6lds\n",
            cur->parkingexten, ast_channel_name(cur->chan), cur->context, cur->exten,
            cur->priority,
            (long) (cur->start.tv_sec + (cur->parkingtime / 1000) - time(NULL)));
         ++lotparked;
      }
      AST_LIST_UNLOCK(&curlot->parkings);
      if (lotparked) {
         numparked += lotparked;
         ast_cli(a->fd, "   %d parked call%s in parking lot %s\n", lotparked,
            ESS(lotparked), curlot->name);
      }

      ao2_ref(curlot, -1);
   }
   ao2_iterator_destroy(&iter);

   ast_cli(a->fd, "---\n%d parked call%s in total.\n", numparked, ESS(numparked));

   return CLI_SUCCESS;
}
static int load_config ( int  reload) [static]

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

{
   struct ast_flags config_flags = {
      reload && !force_reload_load ? CONFIG_FLAG_FILEUNCHANGED : 0 };
   struct ast_config *cfg;
   struct parking_dp_map old_usage_map = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
   struct parking_dp_map new_usage_map = AST_LIST_HEAD_NOLOCK_INIT_VALUE;

   /* We are reloading now and have already determined if we will force the reload. */
   force_reload_load = 0;

   if (!default_parkinglot) {
      /* Must create the default default parking lot */
      default_parkinglot = build_parkinglot(DEFAULT_PARKINGLOT, NULL);
      if (!default_parkinglot) {
         ast_log(LOG_ERROR, "Configuration of default default parking lot failed.\n");
         return -1;
      }
      ast_debug(1, "Configuration of default default parking lot done.\n");
   }

   cfg = ast_config_load2("features.conf", "features", config_flags);
   if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
      /* No sense in asking for reload trouble if nothing changed. */
      ast_debug(1, "features.conf did not change.\n");
      return 0;
   }
   if (cfg == CONFIG_STATUS_FILEMISSING
      || cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_WARNING, "Could not load features.conf\n");
      return 0;
   }

   /* Save current parking lot dialplan needs. */
   if (build_dialplan_useage_map(&old_usage_map, 0)) {
      destroy_dialplan_usage_map(&old_usage_map);

      /* Allow reloading later to see if conditions have improved. */
      force_reload_load = 1;
      return -1;
   }

   ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_markall_cb, NULL,
      "callback to mark all parking lots");
   process_config(cfg);
   ast_config_destroy(cfg);
   ao2_t_callback(parkinglots, OBJ_NODATA | OBJ_UNLINK, parkinglot_is_marked_cb, NULL,
      "callback to remove marked parking lots");

   /* Save updated parking lot dialplan needs. */
   if (build_dialplan_useage_map(&new_usage_map, 1)) {
      /*
       * Yuck, if this failure caused any parking lot dialplan items
       * to be lost, they will likely remain lost until Asterisk is
       * restarted.
       */
      destroy_dialplan_usage_map(&old_usage_map);
      destroy_dialplan_usage_map(&new_usage_map);
      return -1;
   }

   /* Remove no longer needed parking lot dialplan usage. */
   remove_dead_dialplan_useage(&old_usage_map, &new_usage_map);

   destroy_dialplan_usage_map(&old_usage_map);
   destroy_dialplan_usage_map(&new_usage_map);

   ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_activate_cb, NULL,
      "callback to activate all parking lots");

   return 0;
}
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 5012 of file features.c.

References ast_add_extension(), AST_CEL_PARK_END, ast_cel_report_event(), ast_channel_context(), ast_channel_datastore_find(), ast_channel_exten(), ast_channel_fd(), ast_channel_fd_isset(), ast_channel_fdno_set(), ast_channel_flags(), ast_channel_generatordata(), ast_channel_lock, ast_channel_name(), ast_channel_priority(), 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_exists_extension(), AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, ast_free_ptr(), ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_EXTENSION, AST_MAX_FDS, ast_pbx_start(), ast_read(), ast_realloc, ast_set_flag, ast_strdup, ast_strlen_zero(), ast_tvdiff_ms(), ast_tvnow(), ast_verb, callback_dialoptions(), ast_parkinglot::cfg, parkeduser::chan, parkinglot_cfg::comebackcontext, parkinglot_cfg::comebackdialtime, parkinglot_cfg::comebacktoorigin, parkeduser::context, ast_datastore::data, dial_features_info, parkeduser::exten, f, ast_frame::frametype, 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(), parkeduser::priority, registrar, S_OR, set_c_e_p(), parkeduser::start, and ast_frame::subclass.

Referenced by manage_parkinglot().

{
   struct ast_channel *chan = pu->chan;   /* shorthand */
   int tms;        /* timeout for this item */
   int x;          /* fd index in channel */

   tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
   if (tms > pu->parkingtime) {
      /*
       * Call has been parked too long.
       * Stop entertaining the caller.
       */
      switch (pu->hold_method) {
      case AST_CONTROL_HOLD:
         ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
         break;
      case AST_CONTROL_RINGING:
         ast_indicate(pu->chan, -1);
         break;
      default:
         break;
      }
      pu->hold_method = 0;

      /* Get chan, exten from derived kludge */
      if (pu->peername[0]) {
         char *peername;
         char *dash;
         char *peername_flat; /* using something like DAHDI/52 for an extension name is NOT a good idea */
         int i;

         peername = ast_strdupa(pu->peername);
         dash = strrchr(peername, '-');
         if (dash) {
            *dash = '\0';
         }

         peername_flat = ast_strdupa(peername);
         for (i = 0; peername_flat[i]; i++) {
            if (peername_flat[i] == '/') {
               peername_flat[i] = '_';
            }
         }

         if (!ast_context_find_or_create(NULL, NULL, parking_con_dial, registrar)) {
            ast_log(LOG_ERROR,
               "Parking dial context '%s' does not exist and unable to create\n",
               parking_con_dial);
         } else {
            char returnexten[AST_MAX_EXTENSION];
            char comebackdialtime[AST_MAX_EXTENSION];
            struct ast_datastore *features_datastore;
            struct ast_dial_features *dialfeatures;

            if (!strncmp(peername, "Parked/", 7)) {
               peername += 7;
            }

            ast_channel_lock(chan);
            features_datastore = ast_channel_datastore_find(chan, &dial_features_info,
               NULL);
            if (features_datastore && (dialfeatures = features_datastore->data)) {
               char buf[MAX_DIAL_FEATURE_OPTIONS] = {0,};

               snprintf(returnexten, sizeof(returnexten), "%s,%u,%s", peername,
                  pu->parkinglot->cfg.comebackdialtime,
                  callback_dialoptions(&dialfeatures->peer_features,
                     &dialfeatures->my_features, buf, sizeof(buf)));
            } else { /* Existing default */
               ast_log(LOG_NOTICE, "Dial features not found on %s, using default!\n",
                  ast_channel_name(chan));
               snprintf(returnexten, sizeof(returnexten), "%s,%u,t", peername,
                  pu->parkinglot->cfg.comebackdialtime);
            }
            ast_channel_unlock(chan);

            snprintf(comebackdialtime, sizeof(comebackdialtime), "%u",
                  pu->parkinglot->cfg.comebackdialtime);
            pbx_builtin_setvar_helper(chan, "COMEBACKDIALTIME", comebackdialtime);

            pbx_builtin_setvar_helper(chan, "PARKER", peername);

            if (ast_add_extension(parking_con_dial, 1, peername_flat, 1, NULL, NULL,
               "Dial", ast_strdup(returnexten), ast_free_ptr, registrar)) {
               ast_log(LOG_ERROR,
                  "Could not create parking return dial exten: %s@%s\n",
                  peername_flat, parking_con_dial);
            }
         }
         if (pu->options_specified) {
            /*
             * Park() was called with overriding return arguments, respect
             * those arguments.
             */
            set_c_e_p(chan, pu->context, pu->exten, pu->priority);
         } else if (pu->parkinglot->cfg.comebacktoorigin) {
            set_c_e_p(chan, parking_con_dial, peername_flat, 1);
         } else {
            char parkingslot[AST_MAX_EXTENSION];

            snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
            pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parkingslot);
            pbx_builtin_setvar_helper(chan, "PARKEDLOT", pu->parkinglot->name);

            /* Handle fallback when extensions don't exist here since that logic was removed from pbx */
            if (ast_exists_extension(chan, pu->parkinglot->cfg.comebackcontext, peername_flat, 1, NULL)) {
               set_c_e_p(chan, pu->parkinglot->cfg.comebackcontext, peername_flat, 1);
            } else if (ast_exists_extension(chan, pu->parkinglot->cfg.comebackcontext, "s", 1, NULL)) {
               ast_verb(2, "Can not start %s at %s,%s,1. Using 's@%s' instead.\n", ast_channel_name(chan),
                  pu->parkinglot->cfg.comebackcontext, peername_flat, pu->parkinglot->cfg.comebackcontext);
               set_c_e_p(chan, pu->parkinglot->cfg.comebackcontext, "s", 1);
            } else {
               ast_verb(2, "Can not start %s at %s,%s,1 and exten 's@%s' does not exist. Using 's@default'\n",
                  ast_channel_name(chan),
                  pu->parkinglot->cfg.comebackcontext, peername_flat,
                  pu->parkinglot->cfg.comebackcontext);
               set_c_e_p(chan, "default", "s", 1);
            }
         }
      } else {
         /*
          * They've been waiting too long, send them back to where they
          * came.  Theoretically they should have their original
          * extensions and such, but we copy to be on the safe side.
          */
         set_c_e_p(chan, pu->context, pu->exten, pu->priority);
      }
      post_manager_event("ParkedCallTimeOut", pu);
      ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallTimeOut", NULL);

      ast_verb(2, "Timeout for %s parked on %d (%s). Returning to %s,%s,%d\n",
         ast_channel_name(pu->chan), pu->parkingnum, pu->parkinglot->name, ast_channel_context(pu->chan),
         ast_channel_exten(pu->chan), ast_channel_priority(pu->chan));

      /* Start up the PBX, or hang them up */
      if (ast_pbx_start(chan))  {
         ast_log(LOG_WARNING,
            "Unable to restart the PBX for user on '%s', hanging them up...\n",
            ast_channel_name(pu->chan));
         ast_hangup(chan);
      }

      /* And take them out of the parking lot */
      return 1;
   }

   /* still within parking time, process descriptors */
   if (pfds) {
      for (x = 0; x < AST_MAX_FDS; x++) {
         struct ast_frame *f;
         int y;

         if (!ast_channel_fd_isset(chan, x)) {
            continue;   /* nothing on this descriptor */
         }

         for (y = 0; y < nfds; y++) {
            if (pfds[y].fd == ast_channel_fd(chan, x)) {
               /* Found poll record! */
               break;
            }
         }
         if (y == nfds) {
            /* Not found */
            continue;
         }

         if (!(pfds[y].revents & (POLLIN | POLLERR | POLLPRI))) {
            /* Next x */
            continue;
         }

         if (pfds[y].revents & POLLPRI) {
            ast_set_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION);
         } else {
            ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION);
         }
         ast_channel_fdno_set(chan, x);

         /* See if they need servicing */
         f = ast_read(pu->chan);
         /* Hangup? */
         if (!f || (f->frametype == AST_FRAME_CONTROL
            && f->subclass.integer == AST_CONTROL_HANGUP)) {
            if (f) {
               ast_frfree(f);
            }
            post_manager_event("ParkedCallGiveUp", pu);
            ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallGiveUp",
               NULL);

            /* There's a problem, hang them up */
            ast_verb(2, "%s got tired of being parked\n", ast_channel_name(chan));
            ast_hangup(chan);

            /* And take them out of the parking lot */
            return 1;
         } else {
            /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
            ast_frfree(f);
            if (pu->hold_method == AST_CONTROL_HOLD
               && pu->moh_trys < 3
               && !ast_channel_generatordata(chan)) {
               ast_debug(1,
                  "MOH on parked call stopped by outside source.  Restarting on channel %s.\n",
                  ast_channel_name(chan));
               ast_indicate_data(chan, AST_CONTROL_HOLD,
                  S_OR(pu->parkinglot->cfg.mohclass, NULL),
                  (!ast_strlen_zero(pu->parkinglot->cfg.mohclass)
                     ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0));
               pu->moh_trys++;
            }
            break;
         }
      } /* End for */
   }

   /* mark fds for next round */
   for (x = 0; x < AST_MAX_FDS; x++) {
      if (ast_channel_fd_isset(chan, x)) {
         void *tmp = ast_realloc(*new_pfds,
            (*new_nfds + 1) * sizeof(struct pollfd));

         if (!tmp) {
            continue;
         }
         *new_pfds = tmp;
         (*new_pfds)[*new_nfds].fd = ast_channel_fd(chan, x);
         (*new_pfds)[*new_nfds].events = POLLIN | POLLERR | POLLPRI;
         (*new_pfds)[*new_nfds].revents = 0;
         (*new_nfds)++;
      }
   }
   /* Keep track of our shortest wait */
   if (tms < *ms || *ms < 0) {
      *ms = tms;
   }

   /* Stay in the parking lot. */
   return 0;
}
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 5255 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().

{
   struct parkeduser *pu;
   struct ast_context *con;

   /* Lock parkings list */
   AST_LIST_LOCK(&curlot->parkings);
   AST_LIST_TRAVERSE_SAFE_BEGIN(&curlot->parkings, pu, list) {
      if (pu->notquiteyet) { /* Pretend this one isn't here yet */
         continue;
      }
      if (manage_parked_call(pu, pfds, nfds, new_pfds, new_nfds, ms)) {
         /* Parking is complete for this call so remove it from the parking lot. */
         con = ast_context_find(pu->parkinglot->cfg.parking_con);
         if (con) {
            if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL, 0)) {
               ast_log(LOG_WARNING,
                  "Whoa, failed to remove the parking extension %s@%s!\n",
                  pu->parkingexten, pu->parkinglot->cfg.parking_con);
            }
            notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con,
               AST_DEVICE_NOT_INUSE);
         } else {
            ast_log(LOG_WARNING,
               "Whoa, parking lot '%s' context '%s' does not exist.\n",
               pu->parkinglot->name, pu->parkinglot->cfg.parking_con);
         }
         AST_LIST_REMOVE_CURRENT(list);
         parkinglot_unref(pu->parkinglot);
         ast_free(pu);
      }
   }
   AST_LIST_TRAVERSE_SAFE_END;
   AST_LIST_UNLOCK(&curlot->parkings);
}
static int manager_park ( struct mansession s,
const struct message m 
) [static]

Create manager event for parked calls.

Parameters:
s
mGet 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 7659 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().

{
   const char *channel = astman_get_header(m, "Channel");
   const char *channel2 = astman_get_header(m, "Channel2");
   const char *timeout = astman_get_header(m, "Timeout");
   const char *parkinglotname = astman_get_header(m, "Parkinglot");
   char buf[BUFSIZ];
   int res = 0;
   struct ast_channel *ch1, *ch2;
   struct ast_park_call_args args = {
         /*
          * Don't say anything to ch2 since AMI is a third party parking
          * a call and we will likely crash if we do.
          *
          * XXX When the AMI action was originally implemented, the
          * parking space was announced to ch2.  Unfortunately, grabbing
          * the ch2 lock and holding it while the announcement is played
          * was not really a good thing to do to begin with since it
          * could hold up the system.  Also holding the lock is no longer
          * possible with a masquerade.
          *
          * Restoring the announcement to ch2 is not easily doable for
          * the following reasons:
          *
          * 1) The AMI manager is not the thread processing ch2.
          *
          * 2) ch2 could be the same as ch1, bridged to ch1, or some
          * random uninvolved channel.
          */
         .flags = AST_PARK_OPT_SILENCE,
      };

   if (ast_strlen_zero(channel)) {
      astman_send_error(s, m, "Channel not specified");
      return 0;
   }

   if (ast_strlen_zero(channel2)) {
      astman_send_error(s, m, "Channel2 not specified");
      return 0;
   }

   if (!ast_strlen_zero(timeout)) {
      if (sscanf(timeout, "%30d", &args.timeout) != 1) {
         astman_send_error(s, m, "Invalid timeout value.");
         return 0;
      }
   }

   if (!(ch1 = ast_channel_get_by_name(channel))) {
      snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
      astman_send_error(s, m, buf);
      return 0;
   }

   if (!(ch2 = ast_channel_get_by_name(channel2))) {
      snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
      astman_send_error(s, m, buf);
      ast_channel_unref(ch1);
      return 0;
   }

   if (!ast_strlen_zero(parkinglotname)) {
      args.parkinglot = find_parkinglot(parkinglotname);
   }

   res = masq_park_call(ch1, ch2, &args);
   if (!res) {
      ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
      astman_send_ack(s, m, "Park successful");
   } else {
      astman_send_error(s, m, "Park failure");
   }

   if (args.parkinglot) {
      parkinglot_unref(args.parkinglot);
   }
   ch1 = ast_channel_unref(ch1);
   ch2 = ast_channel_unref(ch2);

   return 0;
}
static int manager_parking_status ( struct mansession s,
const struct message m 
) [static]

Dump parking lot status.

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

Definition at line 7590 of file features.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_caller(), ast_channel_connected(), ast_channel_name(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), parkeduser::chan, ast_party_caller::id, ast_party_connected_line::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().

{
   struct parkeduser *cur;
   const char *id = astman_get_header(m, "ActionID");
   char idText[256] = "";
   struct ao2_iterator iter;
   struct ast_parkinglot *curlot;
   int numparked = 0;
   long now = time(NULL);

   if (!ast_strlen_zero(id))
      snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);

   astman_send_ack(s, m, "Parked calls will follow");

   iter = ao2_iterator_init(parkinglots, 0);
   while ((curlot = ao2_iterator_next(&iter))) {
      AST_LIST_LOCK(&curlot->parkings);
      AST_LIST_TRAVERSE(&curlot->parkings, cur, list) {
         astman_append(s, "Event: ParkedCall\r\n"
            "Parkinglot: %s\r\n"
            "Exten: %d\r\n"
            "Channel: %s\r\n"
            "From: %s\r\n"
            "Timeout: %ld\r\n"
            "Duration: %ld\r\n"
            "CallerIDNum: %s\r\n"
            "CallerIDName: %s\r\n"
            "ConnectedLineNum: %s\r\n"
            "ConnectedLineName: %s\r\n"
            "%s"
            "\r\n",
            curlot->name,
            cur->parkingnum, ast_channel_name(cur->chan), cur->peername,
            (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - now,
            now - (long) cur->start.tv_sec,
            S_COR(ast_channel_caller(cur->chan)->id.number.valid, ast_channel_caller(cur->chan)->id.number.str, ""), /* XXX in other places it is <unknown> */
            S_COR(ast_channel_caller(cur->chan)->id.name.valid, ast_channel_caller(cur->chan)->id.name.str, ""),
            S_COR(ast_channel_connected(cur->chan)->id.number.valid, ast_channel_connected(cur->chan)->id.number.str, ""), /* XXX in other places it is <unknown> */
            S_COR(ast_channel_connected(cur->chan)->id.name.valid, ast_channel_connected(cur->chan)->id.name.str, ""),
            idText);
         ++numparked;
      }
      AST_LIST_UNLOCK(&curlot->parkings);
      ao2_ref(curlot, -1);
   }
   ao2_iterator_destroy(&iter);

   astman_append(s,
      "Event: ParkedCallsComplete\r\n"
      "Total: %d\r\n"
      "%s"
      "\r\n",
      numparked, idText);

   return RESULT_SUCCESS;
}
static int manager_parkinglot_list ( struct mansession s,
const struct message m 
) [static]

Definition at line 7547 of file features.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), ast_parkinglot::cfg, ast_parkinglot::name, parkinglot_cfg::parking_start, parkinglot_cfg::parking_stop, parkinglots, parkinglot_cfg::parkingtime, and RESULT_SUCCESS.

Referenced by ast_features_init().

{
   const char *id = astman_get_header(m, "ActionID");
   char idText[256] = "";
   struct ao2_iterator iter;
   struct ast_parkinglot *curlot;

   if (!ast_strlen_zero(id))
      snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);

   astman_send_ack(s, m, "Parking lots will follow");

   iter = ao2_iterator_init(parkinglots, 0);
   while ((curlot = ao2_iterator_next(&iter))) {
      astman_append(s, "Event: Parkinglot\r\n"
         "Name: %s\r\n"
         "StartExten: %d\r\n"
         "StopExten: %d\r\n"
         "Timeout: %d\r\n"
         "\r\n",
         curlot->name,
         curlot->cfg.parking_start,
         curlot->cfg.parking_stop,
         curlot->cfg.parkingtime ? curlot->cfg.parkingtime / 1000 : curlot->cfg.parkingtime);
      ao2_ref(curlot, -1);
   }

   astman_append(s,
      "Event: ParkinglotsComplete\r\n"
      "%s"
      "\r\n",idText);

   return RESULT_SUCCESS;
}
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:
rchanthe real channel to be parked
peerthe channel to have the parking read to.
argsAdditional parking options when parking a call.
Return values:
0on success.
-1on failure.

Definition at line 1897 of file features.c.

References ast_channel_accountcode(), ast_channel_alloc(), ast_channel_amaflags(), ast_channel_context(), ast_channel_exten(), ast_channel_linkedid(), ast_channel_macrocontext(), ast_channel_macrocontext_set(), ast_channel_macroexten(), ast_channel_macroexten_set(), ast_channel_macropriority(), ast_channel_macropriority_set(), ast_channel_masquerade(), ast_channel_name(), ast_channel_priority(), ast_channel_readformat(), ast_channel_writeformat(), ast_do_masquerade(), ast_format_copy(), ast_hangup(), ast_log(), AST_PARK_OPT_SILENCE, AST_STATE_DOWN, ast_stream_and_wait(), ast_test_flag, LOG_WARNING, park_call_full(), park_space_abort(), park_space_reserve(), play_message_on_chan(), ast_park_call_args::pu, and set_c_e_p().

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

{
   struct ast_channel *chan;

   /* Make a new, channel that we'll use to masquerade in the real one */
   chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(rchan), ast_channel_exten(rchan),
      ast_channel_context(rchan), ast_channel_linkedid(rchan), ast_channel_amaflags(rchan), "Parked/%s", ast_channel_name(rchan));
   if (!chan) {
      ast_log(LOG_WARNING, "Unable to create parked channel\n");
      if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
         if (peer == rchan) {
            /* Only have one channel to worry about. */
            ast_stream_and_wait(peer, "pbx-parkingfailed", "");
         } else if (peer) {
            /* Have two different channels to worry about. */
            play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
         }
      }
      return -1;
   }

   args->pu = park_space_reserve(rchan, peer, args);
   if (!args->pu) {
      ast_hangup(chan);
      if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
         if (peer == rchan) {
            /* Only have one channel to worry about. */
            ast_stream_and_wait(peer, "pbx-parkingfailed", "");
         } else if (peer) {
            /* Have two different channels to worry about. */
            play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
         }
      }
      return -1;
   }

   /* Make formats okay */
   ast_format_copy(ast_channel_readformat(chan), ast_channel_readformat(rchan));
   ast_format_copy(ast_channel_writeformat(chan), ast_channel_writeformat(rchan));

   if (ast_channel_masquerade(chan, rchan)) {
      park_space_abort(args->pu);
      args->pu = NULL;
      ast_hangup(chan);
      if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
         if (peer == rchan) {
            /* Only have one channel to worry about. */
            ast_stream_and_wait(peer, "pbx-parkingfailed", "");
         } else if (peer) {
            /* Have two different channels to worry about. */
            play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
         }
      }
      return -1;
   }

   /* Setup the extensions and such */
   set_c_e_p(chan, ast_channel_context(rchan), ast_channel_exten(rchan), ast_channel_priority(rchan));

   /* Setup the macro extension and such */
   ast_channel_macrocontext_set(chan, ast_channel_macrocontext(rchan));
   ast_channel_macroexten_set(chan, ast_channel_macroexten(rchan));
   ast_channel_macropriority_set(chan, ast_channel_macropriority(rchan));

   /* Manually do the masquerade to make sure it is complete. */
   ast_do_masquerade(chan);

   if (peer == rchan) {
      peer = chan;
   }

   /* parking space reserved, return code check unnecessary */
   park_call_full(chan, peer, args);

   return 0;
}
static enum ast_device_state metermaidstate ( const char *  data) [static]

metermaids callback from devicestate.c

Definition at line 1255 of file features.c.

References ast_debug, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, ast_exists_extension(), context, and feature_group_exten::exten.

Referenced by ast_features_init().

{
   char *context;
   char *exten;

   context = ast_strdupa(data);

   exten = strsep(&context, "@");
   if (!context)
      return AST_DEVICE_INVALID;

   ast_debug(4, "Checking state of exten %s in context %s\n", exten, context);

   if (!ast_exists_extension(NULL, context, exten, 1, NULL))
      return AST_DEVICE_NOT_INUSE;

   return AST_DEVICE_INUSE;
}
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 1246 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().

{
   ast_debug(4, "Notification of state change to metermaids %s@%s\n to state '%s'",
      exten, context, ast_devstate2str(state));

   ast_devstate_changed(state, AST_DEVSTATE_CACHABLE, "park:%s@%s", exten, context);
}
static void park_add_hints ( const char *  context,
int  start,
int  stop 
) [static]

Add parking hints for all defined parking spaces.

Parameters:
contextDialplan context to add the hints.
startStarting space in parkinglot.
stopEnding space in parkinglot.

Definition at line 5825 of file features.c.

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

Referenced by parkinglot_activate().

{
   int numext;
   char device[AST_MAX_EXTENSION];
   char exten[10];

   for (numext = start; numext <= stop; numext++) {
      snprintf(exten, sizeof(exten), "%d", numext);
      snprintf(device, sizeof(device), "park:%s@%s", exten, context);
      ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
   }
}
static int park_call_exec ( struct ast_channel chan,
const char *  data 
) [static]

Park a call.

Definition at line 5378 of file features.c.

References ast_answer(), ast_app_parse_options(), ast_channel_exten(), ast_channel_exten_set(), ast_channel_name(), ast_channel_priority(), ast_channel_priority_set(), ast_copy_string(), ast_log(), AST_MAX_EXTENSION, AST_PARK_OPT_SILENCE, ast_safe_sleep(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stream_and_wait(), ast_strlen_zero(), ast_test_flag, create_dynamic_parkinglot(), default_parkinglot, 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, park_app_args::return_con, ast_park_call_args::return_con, park_app_args::return_ext, ast_park_call_args::return_ext, park_app_args::return_pri, ast_park_call_args::return_pri, S_OR, park_app_args::timeout, and ast_park_call_args::timeout.

Referenced by ast_features_init().

{
   struct ast_park_call_args args = { 0, };
   struct ast_flags flags = { 0 };
   char orig_exten[AST_MAX_EXTENSION];
   int orig_priority;
   int res;
   const char *pl_name;
   char *parse;
   struct park_app_args app_args;

   /*
    * Cache the original channel name because we are going to
    * masquerade the channel.  Prefer the BLINDTRANSFER channel
    * name over this channel name.  BLINDTRANSFER could be set if
    * the parking access extension did not get detected and we are
    * executing the Park application from the dialplan.
    *
    * The orig_chan_name is used to return the call to the
    * originator on parking timeout.
    */
   args.orig_chan_name = ast_strdupa(S_OR(
      pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"), ast_channel_name(chan)));

   /* Answer if call is not up */
   if (ast_channel_state(chan) != AST_STATE_UP) {
      if (ast_answer(chan)) {
         return -1;
      }

      /* Sleep to allow VoIP streams to settle down */
      if (ast_safe_sleep(chan, 1000)) {
         return -1;
      }
   }

   /* Process the dialplan application options. */
   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(app_args, parse);

   if (!ast_strlen_zero(app_args.timeout)) {
      if (sscanf(app_args.timeout, "%30d", &args.timeout) != 1) {
         ast_log(LOG_WARNING, "Invalid timeout '%s' provided\n", app_args.timeout);
         args.timeout = 0;
      }
   }
   if (!ast_strlen_zero(app_args.return_con)) {
      args.return_con = app_args.return_con;
   }
   if (!ast_strlen_zero(app_args.return_ext)) {
      args.return_ext = app_args.return_ext;
   }
   if (!ast_strlen_zero(app_args.return_pri)) {
      if (sscanf(app_args.return_pri, "%30d", &args.return_pri) != 1) {
         ast_log(LOG_WARNING, "Invalid priority '%s' specified\n", app_args.return_pri);
         args.return_pri = 0;
      }
   }

   ast_app_parse_options(park_call_options, &flags, NULL, app_args.options);
   args.flags = flags.flags;

   /*
    * Setup the exten/priority to be s/1 since we don't know where
    * this call should return.
    */
   ast_copy_string(orig_exten, ast_channel_exten(chan), sizeof(orig_exten));
   orig_priority = ast_channel_priority(chan);
   ast_channel_exten_set(chan, "s");
   ast_channel_priority_set(chan, 1);

   /* Park the call */
   if (!ast_strlen_zero(app_args.pl_name)) {
      pl_name = app_args.pl_name;
   } else {
      pl_name = findparkinglotname(chan);
   }
   if (ast_strlen_zero(pl_name)) {
      /* Parking lot is not specified, so use the default parking lot. */
      args.parkinglot = parkinglot_addref(default_parkinglot);
   } else {
      args.parkinglot = find_parkinglot(pl_name);
      if (!args.parkinglot && parkeddynamic) {
         args.parkinglot = create_dynamic_parkinglot(pl_name, chan);
      }
   }
   if (args.parkinglot) {
      res = masq_park_call(chan, chan, &args);
      parkinglot_unref(args.parkinglot);
   } else {
      /* Parking failed because the parking lot does not exist. */
      if (!ast_test_flag(&args, AST_PARK_OPT_SILENCE)) {
         ast_stream_and_wait(chan, "pbx-parkingfailed", "");
      }
      res = -1;
   }
   if (res) {
      /* Park failed, try to continue in the dialplan. */
      ast_channel_exten_set(chan, orig_exten);
      ast_channel_priority_set(chan, orig_priority);
      res = 0;
   } else {
      /* Park succeeded. */
      res = -1;
   }

   return res;
}
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 1622 of file features.c.

References adsi_announce_park(), adsipark, ast_add_extension(), ast_adsi_available(), ast_adsi_unload_session(), ast_bridged_channel(), AST_CEL_PARK_START, ast_cel_report_event(), ast_channel_appl_set(), ast_channel_caller(), ast_channel_connected(), ast_channel_context(), ast_channel_data_set(), ast_channel_exten(), ast_channel_flags(), ast_channel_get_by_name(), ast_channel_language(), ast_channel_lock, ast_channel_macrocontext(), ast_channel_macroexten(), ast_channel_macropriority(), AST_CHANNEL_NAME, ast_channel_name(), ast_channel_priority(), ast_channel_tech(), ast_channel_uniqueid(), 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_parkinglot::cfg, parkeduser::chan, parkeduser::context, EVENT_FLAG_CALL, parkeduser::exten, ast_park_call_args::extout, get_parkingtime(), parkeduser::hold_method, LOG_ERROR, parkinglot_cfg::mohclass, name, ast_parkinglot::name, notify_metermaids(), parkeduser::notquiteyet, 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, parkeduser::parkingtime, pbx_builtin_getvar_helper(), parkeduser::peername, 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_park_call_args::timeout, and type.

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

{
   struct parkeduser *pu = args->pu;
   const char *event_from;    /*!< Channel name that is parking the call. */
   char app_data[AST_MAX_EXTENSION + AST_MAX_CONTEXT];

   if (pu == NULL) {
      args->pu = pu = park_space_reserve(chan, peer, args);
      if (pu == NULL) {
         return -1;
      }
   }

   ast_channel_appl_set(chan, "Parked Call");
   ast_channel_data_set(chan, NULL);

   pu->chan = chan;

   /* Put the parked channel on hold if we have two different channels */
   if (chan != peer) {
      if (ast_test_flag(args, AST_PARK_OPT_RINGING)) {
         pu->hold_method = AST_CONTROL_RINGING;
         ast_indicate(chan, AST_CONTROL_RINGING);
      } else {
         pu->hold_method = AST_CONTROL_HOLD;
         ast_indicate_data(chan, AST_CONTROL_HOLD,
            S_OR(pu->parkinglot->cfg.mohclass, NULL),
            !ast_strlen_zero(pu->parkinglot->cfg.mohclass) ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0);
      }
   }

   pu->start = ast_tvnow();
   pu->parkingtime = (args->timeout > 0) ? args->timeout : get_parkingtime(chan, pu->parkinglot);
   if (args->extout)
      *(args->extout) = pu->parkingnum;

   if (peer) {
      event_from = S_OR(args->orig_chan_name, ast_channel_name(peer));

      /*
       * This is so ugly that it hurts, but implementing
       * get_base_channel() on local channels could have ugly side
       * effects.  We could have
       * transferer<->local;1<->local;2<->parking and we need the
       * callback name to be that of transferer.  Since local;1/2 have
       * the same name we can be tricky and just grab the bridged
       * channel from the other side of the local.
       */
      if (!strcasecmp(ast_channel_tech(peer)->type, "Local")) {
         struct ast_channel *tmpchan, *base_peer;
         char other_side[AST_CHANNEL_NAME];
         char *c;

         ast_copy_string(other_side, event_from, sizeof(other_side));
         if ((c = strrchr(other_side, ';'))) {
            *++c = '1';
         }
         if ((tmpchan = ast_channel_get_by_name(other_side))) {
            ast_channel_lock(tmpchan);
            if ((base_peer = ast_bridged_channel(tmpchan))) {
               ast_copy_string(pu->peername, ast_channel_name(base_peer), sizeof(pu->peername));
            }
            ast_channel_unlock(tmpchan);
            tmpchan = ast_channel_unref(tmpchan);
         }
      } else {
         ast_copy_string(pu->peername, event_from, sizeof(pu->peername));
      }
   } else {
      event_from = S_OR(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"),
         ast_channel_name(chan));
   }

   /*
    * Remember what had been dialed, so that if the parking
    * expires, we try to come back to the same place
    */
   pu->options_specified = (!ast_strlen_zero(args->return_con) || !ast_strlen_zero(args->return_ext) || args->return_pri);

   /*
    * If extension has options specified, they override all other
    * possibilities such as the returntoorigin flag and transferred
    * context.  Information on extension options is lost here, so
    * we set a flag
    */
   ast_copy_string(pu->context,
      S_OR(args->return_con, S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan))),
      sizeof(pu->context));
   ast_copy_string(pu->exten,
      S_OR(args->return_ext, S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan))),
      sizeof(pu->exten));
   pu->priority = args->return_pri ? args->return_pri :
      (ast_channel_macropriority(chan) ? ast_channel_macropriority(chan) : ast_channel_priority(chan));

   /*
    * If parking a channel directly, don't quite yet get parking
    * running on it.  All parking lot entries are put into the
    * parking lot with notquiteyet on.
    */
   if (peer != chan) {
      pu->notquiteyet = 0;
   }

   /* Wake up the (presumably select()ing) thread */
   pthread_kill(parking_thread, SIGURG);
   ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %u seconds\n",
      ast_channel_name(chan), pu->parkingnum, pu->parkinglot->name,
      pu->context, pu->exten, pu->priority, (pu->parkingtime / 1000));

   ast_cel_report_event(chan, AST_CEL_PARK_START, NULL, pu->parkinglot->name, peer);
   /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when a call has been parked.</synopsis>
         <syntax>
            <parameter name="Exten">
               <para>The parking lot extension.</para>
            </parameter>
            <parameter name="Parkinglot">
               <para>The name of the parking lot.</para>
            </parameter>
            <parameter name="From">
               <para>The name of the channel that parked the call.</para>
            </parameter>
         </syntax>
         <see-also>
            <ref type="application">Park</ref>
            <ref type="manager">Park</ref>
            <ref type="managerEvent">ParkedCallTimeOut</ref>
            <ref type="managerEvent">ParkedCallGiveUp</ref>
         </see-also>
      </managerEventInstance>
   ***/
   ast_manager_event(chan, EVENT_FLAG_CALL, "ParkedCall",
      "Exten: %s\r\n"
      "Channel: %s\r\n"
      "Parkinglot: %s\r\n"
      "From: %s\r\n"
      "Timeout: %ld\r\n"
      "CallerIDNum: %s\r\n"
      "CallerIDName: %s\r\n"
      "ConnectedLineNum: %s\r\n"
      "ConnectedLineName: %s\r\n"
      "Uniqueid: %s\r\n",
      pu->parkingexten, ast_channel_name(chan), pu->parkinglot->name, event_from,
      (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
      S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
      S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>"),
      S_COR(ast_channel_connected(chan)->id.number.valid, ast_channel_connected(chan)->id.number.str, "<unknown>"),
      S_COR(ast_channel_connected(chan)->id.name.valid, ast_channel_connected(chan)->id.name.str, "<unknown>"),
      ast_channel_uniqueid(chan)
      );
   ast_debug(4, "peer: %s\n", peer ? ast_channel_name(peer) : "-No peer-");
   ast_debug(4, "args->orig_chan_name: %s\n", args->orig_chan_name ? args->orig_chan_name : "-none-");
   ast_debug(4, "pu->peername: %s\n", pu->peername);
   ast_debug(4, "AMI ParkedCall Channel: %s\n", ast_channel_name(chan));
   ast_debug(4, "AMI ParkedCall From: %s\n", event_from);

   if (peer && adsipark && ast_adsi_available(peer)) {
      adsi_announce_park(peer, pu->parkingexten);  /* Only supports parking numbers */
      ast_adsi_unload_session(peer);
   }

   snprintf(app_data, sizeof(app_data), "%s,%s", pu->parkingexten,
      pu->parkinglot->name);
   if (ast_add_extension(pu->parkinglot->cfg.parking_con, 1, pu->parkingexten, 1,
      NULL, NULL, parkedcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
      ast_log(LOG_ERROR, "Could not create parked call exten: %s@%s\n",
         pu->parkingexten, pu->parkinglot->cfg.parking_con);
   } else {
      notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con, AST_DEVICE_INUSE);
   }

   AST_LIST_UNLOCK(&pu->parkinglot->parkings);

   /* Only say number if it's a number and the channel hasn't been masqueraded away */
   if (peer && !ast_test_flag(args, AST_PARK_OPT_SILENCE)
      && (ast_strlen_zero(args->orig_chan_name) || !strcasecmp(ast_channel_name(peer), args->orig_chan_name))) {
      /*
       * If a channel is masqueraded into peer while playing back the
       * parking space number do not continue playing it back.  This
       * is the case if an attended transfer occurs.
       */
      ast_set_flag(ast_channel_flags(peer), AST_FLAG_MASQ_NOSTREAM);
      /* Tell the peer channel the number of the parking space */
      ast_say_digits(peer, pu->parkingnum, "", ast_channel_language(peer));
      ast_clear_flag(ast_channel_flags(peer), AST_FLAG_MASQ_NOSTREAM);
   }
   if (peer == chan) { /* pu->notquiteyet = 1 */
      /* Wake up parking thread if we're really done */
      if (ast_test_flag(args, AST_PARK_OPT_RINGING)) {
         pu->hold_method = AST_CONTROL_RINGING;
         ast_indicate(chan, AST_CONTROL_RINGING);
      } else {
         pu->hold_method = AST_CONTROL_HOLD;
         ast_indicate_data(chan, AST_CONTROL_HOLD,
            S_OR(pu->parkinglot->cfg.mohclass, NULL),
            !ast_strlen_zero(pu->parkinglot->cfg.mohclass) ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0);
      }
      pu->notquiteyet = 0;
      pthread_kill(parking_thread, SIGURG);
   }
   return 0;
}
static void park_space_abort ( struct parkeduser pu) [static]

Definition at line 1422 of file features.c.

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

Referenced by masq_park_call().

{
   struct ast_parkinglot *parkinglot;

   parkinglot = pu->parkinglot;

   /* Put back the parking space just allocated. */
   --parkinglot->next_parking_space;

   AST_LIST_REMOVE(&parkinglot->parkings, pu, list);

   AST_LIST_UNLOCK(&parkinglot->parkings);
   parkinglot_unref(parkinglot);
   ast_free(pu);
}
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 1449 of file features.c.

References ast_calloc, ast_channel_name(), ast_debug, ast_free, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_PARK_OPT_RANDOMIZE, ast_random(), ast_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, parkinglot, 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().

{
   struct parkeduser *pu;
   int i;
   int parking_space = -1;
   const char *parkinglotname;
   const char *parkingexten;
   struct parkeduser *cur;
   struct ast_parkinglot *parkinglot = NULL;

   if (args->parkinglot) {
      parkinglot = parkinglot_addref(args->parkinglot);
      parkinglotname = parkinglot->name;
   } else {
      if (parker) {
         parkinglotname = findparkinglotname(parker);
      } else { /* parker was NULL, check park_me (ParkAndAnnounce / res_agi) */
         parkinglotname = findparkinglotname(park_me);
      }
      if (!ast_strlen_zero(parkinglotname)) {
         parkinglot = find_parkinglot(parkinglotname);
      } else {
         /* Parking lot is not specified, so use the default parking lot. */
         ast_debug(4, "This could be an indication channel driver needs updating, using default lot.\n");
         parkinglot = parkinglot_addref(default_parkinglot);
      }
   }

   /* Dynamically create parkinglot */
   if (!parkinglot && parkeddynamic && !ast_strlen_zero(parkinglotname)) {
      parkinglot = create_dynamic_parkinglot(parkinglotname, park_me);
   }

   if (!parkinglot) {
      ast_log(LOG_WARNING, "Parking lot not available to park %s.\n", ast_channel_name(park_me));
      return NULL;
   }

   ast_debug(1, "Parking lot: %s\n", parkinglot->name);
   if (parkinglot->disabled || parkinglot->cfg.is_invalid) {
      ast_log(LOG_WARNING, "Parking lot %s is not in a useable state.\n",
         parkinglot->name);
      parkinglot_unref(parkinglot);
      return NULL;
   }

   /* Allocate memory for parking data */
   if (!(pu = ast_calloc(1, sizeof(*pu)))) {
      parkinglot_unref(parkinglot);
      return NULL;
   }

   /* Lock parking list */
   AST_LIST_LOCK(&parkinglot->parkings);

   /* Check for channel variable PARKINGEXTEN */
   parkingexten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(park_me, "PARKINGEXTEN"), ""));
   if (!ast_strlen_zero(parkingexten)) {
      /*!
       * \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.
       */
      if (sscanf(parkingexten, "%30d", &parking_space) != 1 || parking_space <= 0) {
         ast_log(LOG_WARNING, "PARKINGEXTEN='%s' is not a valid parking space.\n",
            parkingexten);
         AST_LIST_UNLOCK(&parkinglot->parkings);
         parkinglot_unref(parkinglot);
         ast_free(pu);
         return NULL;
      }

      if (parking_space < parkinglot->cfg.parking_start
         || parkinglot->cfg.parking_stop < parking_space) {
         /*
          * Cannot allow park because parking lots are not setup for
          * spaces outside of the lot.  (Things like dialplan hints don't
          * exist for outside lot space.)
          */
         ast_log(LOG_WARNING, "PARKINGEXTEN=%d is not in %s (%d-%d).\n",
            parking_space, parkinglot->name, parkinglot->cfg.parking_start,
            parkinglot->cfg.parking_stop);
         AST_LIST_UNLOCK(&parkinglot->parkings);
         parkinglot_unref(parkinglot);
         ast_free(pu);
         return NULL;
      }

      /* Check if requested parking space is in use. */
      AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
         if (cur->parkingnum == parking_space) {
            ast_log(LOG_WARNING, "PARKINGEXTEN=%d is already in use in %s\n",
               parking_space, parkinglot->name);
            AST_LIST_UNLOCK(&parkinglot->parkings);
            parkinglot_unref(parkinglot);
            ast_free(pu);
            return NULL;
         }
      }
   } else {
      /* PARKINGEXTEN is empty, so find a usable extension in the lot to park the call */
      int start; /* The first slot we look in the parkinglot. It can be randomized. */
      int start_checked = 0; /* flag raised once the first slot is checked */

      /* If using randomize mode, set start to random position on parking range */
      if (ast_test_flag(args, AST_PARK_OPT_RANDOMIZE)) {
         start = ast_random() % (parkinglot->cfg.parking_stop - parkinglot->cfg.parking_start + 1);
         start += parkinglot->cfg.parking_start;
      } else if (parkinglot->cfg.parkfindnext
         && parkinglot->cfg.parking_start <= parkinglot->next_parking_space
         && parkinglot->next_parking_space <= parkinglot->cfg.parking_stop) {
         /* Start looking with the next parking space in the lot. */
         start = parkinglot->next_parking_space;
      } else {
         /* Otherwise, just set it to the start position. */
         start = parkinglot->cfg.parking_start;
      }

      /* free parking extension linear search: O(n^2) */
      for (i = start; ; i++) {
         /* If we are past the end, wrap around to the first parking slot*/
         if (i == parkinglot->cfg.parking_stop + 1) {
            i = parkinglot->cfg.parking_start;
         }

         if (i == start) {
            /* At this point, if start_checked, we've exhausted all the possible slots. */
            if (start_checked) {
               break;
            } else {
               start_checked = 1;
            }
         }

         /* Search the list of parked calls already in use for i. If we find it, it's in use. */
         AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
            if (cur->parkingnum == i) {
               break;
            }
         }
         if (!cur) {
            /* We found a parking space. */
            parking_space = i;
            break;
         }
      }
      if (parking_space == -1) {
         /* We did not find a parking space.  Lot is full. */
         ast_log(LOG_WARNING, "No more parking spaces in %s\n", parkinglot->name);
         AST_LIST_UNLOCK(&parkinglot->parkings);
         parkinglot_unref(parkinglot);
         ast_free(pu);
         return NULL;
      }
   }

   /* Prepare for next parking space search. */
   parkinglot->next_parking_space = parking_space + 1;

   snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space);
   pu->notquiteyet = 1;
   pu->parkingnum = parking_space;
   pu->parkinglot = parkinglot;
   AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list);

   return pu;
}
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 5488 of file features.c.

References ast_answer(), AST_APP_ARG, ast_autoservice_chan_hangup_peer(), ast_bridge_call(), ast_callid_unref, ast_cdr_setdestchan(), AST_CEL_PARK_END, ast_cel_report_event(), ast_channel_caller(), ast_channel_callid_set(), ast_channel_cdr(), ast_channel_connected(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_name(), ast_channel_pbx(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_update_connected_line(), ast_connected_line_copy_from_caller(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, ast_context_find(), ast_context_remove_extension2(), AST_CONTROL_HOLD, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_copy_flags, AST_DECLARE_APP_ARGS, AST_DEVICE_NOT_INUSE, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_FLAG_BYBOTH, AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, AST_FLAGS_ALL, ast_free, ast_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_read_threadstorage_callid(), ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stream_and_wait(), ast_strlen_zero(), ast_verb, ast_parkinglot::cfg, parkeduser::chan, 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_caller::id, ast_party_connected_line::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, parkinglot, parkeduser::parkinglot, parkinglot_addref(), parkinglot_unref(), parkeduser::parkingnum, ast_parkinglot::parkings, parse(), 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().

{
   int res;
   struct ast_channel *peer = NULL;
   struct parkeduser *pu;
   struct ast_context *con;
   char *parse;
   const char *pl_name;
   int park = 0;
   struct ast_bridge_config config;
   struct ast_parkinglot *parkinglot;
   AST_DECLARE_APP_ARGS(app_args,
      AST_APP_ARG(pl_space);  /*!< Parking lot space to retrieve if present. */
      AST_APP_ARG(pl_name);   /*!< Parking lot name to use if present. */
      AST_APP_ARG(dummy);     /*!< Place to put any remaining args string. */
   );

   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(app_args, parse);

   if (!ast_strlen_zero(app_args.pl_space)) {
      if (sscanf(app_args.pl_space, "%30u", &park) != 1) {
         ast_log(LOG_WARNING, "Specified parking extension not a number: %s\n",
            app_args.pl_space);
         park = -1;
      }
   }

   if (!ast_strlen_zero(app_args.pl_name)) {
      pl_name = app_args.pl_name;
   } else {
      pl_name = findparkinglotname(chan);
   }
   if (ast_strlen_zero(pl_name)) {
      /* Parking lot is not specified, so use the default parking lot. */
      parkinglot = parkinglot_addref(default_parkinglot);
   } else {
      parkinglot = find_parkinglot(pl_name);
      if (!parkinglot) {
         /* It helps to answer the channel if not already up. :) */
         if (ast_channel_state(chan) != AST_STATE_UP) {
            ast_answer(chan);
         }
         if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) {
            ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n",
               "pbx-invalidpark", ast_channel_name(chan));
         }
         ast_log(LOG_WARNING,
            "Channel %s tried to retrieve parked call from unknown parking lot '%s'\n",
            ast_channel_name(chan), pl_name);
         return -1;
      }
   }

   AST_LIST_LOCK(&parkinglot->parkings);
   AST_LIST_TRAVERSE_SAFE_BEGIN(&parkinglot->parkings, pu, list) {
      if ((ast_strlen_zero(app_args.pl_space) || pu->parkingnum == park)
         && !pu->notquiteyet && !ast_channel_pbx(pu->chan)) {
         /* The parking space has a call and can be picked up now. */
         AST_LIST_REMOVE_CURRENT(list);
         break;
      }
   }
   AST_LIST_TRAVERSE_SAFE_END;
   if (pu) {
      struct ast_callid *callid = ast_read_threadstorage_callid();

      /* Found a parked call to pickup. */
      peer = pu->chan;

      /* We need to map the call id we have from this thread to the channel we found. */
      if (callid) {
         ast_channel_callid_set(peer, callid);
         callid = ast_callid_unref(callid);
      }

      con = ast_context_find(parkinglot->cfg.parking_con);
      if (con) {
         if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL, 0)) {
            ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
         } else {
            notify_metermaids(pu->parkingexten, parkinglot->cfg.parking_con, AST_DEVICE_NOT_INUSE);
         }
      } else {
         ast_log(LOG_WARNING, "Whoa, no parking context?\n");
      }

      ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "UnParkedCall", chan);
      /*** DOCUMENTATION
         <managerEventInstance>
            <synopsis>Raised when a call has been unparked.</synopsis>
            <syntax>
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter[@name='Exten'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter[@name='Parkinglot'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter[@name='From'])" />
            </syntax>
            <see-also>
               <ref type="application">ParkedCall</ref>
               <ref type="managerEvent">ParkedCall</ref>
            </see-also>
         </managerEventInstance>
      ***/
      ast_manager_event(pu->chan, EVENT_FLAG_CALL, "UnParkedCall",
         "Exten: %s\r\n"
         "Channel: %s\r\n"
         "Parkinglot: %s\r\n"
         "From: %s\r\n"
         "CallerIDNum: %s\r\n"
         "CallerIDName: %s\r\n"
         "ConnectedLineNum: %s\r\n"
         "ConnectedLineName: %s\r\n"
         "Uniqueid: %s\r\n",
         pu->parkingexten, ast_channel_name(pu->chan), pu->parkinglot->name,
         ast_channel_name(chan),
         S_COR(ast_channel_caller(pu->chan)->id.number.valid, ast_channel_caller(pu->chan)->id.number.str, "<unknown>"),
         S_COR(ast_channel_caller(pu->chan)->id.name.valid, ast_channel_caller(pu->chan)->id.name.str, "<unknown>"),
         S_COR(ast_channel_connected(pu->chan)->id.number.valid, ast_channel_connected(pu->chan)->id.number.str, "<unknown>"),
         S_COR(ast_channel_connected(pu->chan)->id.name.valid, ast_channel_connected(pu->chan)->id.name.str, "<unknown>"),
         ast_channel_uniqueid(pu->chan)
         );

      /* Stop entertaining the caller. */
      switch (pu->hold_method) {
      case AST_CONTROL_HOLD:
         ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
         break;
      case AST_CONTROL_RINGING:
         ast_indicate(pu->chan, -1);
         break;
      default:
         break;
      }
      pu->hold_method = 0;

      parkinglot_unref(pu->parkinglot);
      ast_free(pu);
   }
   AST_LIST_UNLOCK(&parkinglot->parkings);

   if (peer) {
      /* Update connected line between retrieving call and parked call. */
      struct ast_party_connected_line connected;

      ast_party_connected_line_init(&connected);

      /* Send our caller-id to peer. */
      ast_channel_lock(chan);
      ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
      ast_channel_unlock(chan);
      connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
      if (ast_channel_connected_line_sub(chan, peer, &connected, 0) &&
         ast_channel_connected_line_macro(chan, peer, &connected, 0, 0)) {
         ast_channel_update_connected_line(peer, &connected, NULL);
      }

      /*
       * Get caller-id from peer.
       *
       * Update the retrieving call before it is answered if possible
       * for best results.  Some phones do not support updating the
       * connected line information after connection.
       */
      ast_channel_lock(peer);
      ast_connected_line_copy_from_caller(&connected, ast_channel_caller(peer));
      ast_channel_unlock(peer);
      connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
      if (ast_channel_connected_line_sub(peer, chan, &connected, 0) &&
         ast_channel_connected_line_macro(peer, chan, &connected, 1, 0)) {
         ast_channel_update_connected_line(chan, &connected, NULL);
      }

      ast_party_connected_line_free(&connected);
   }

   /* JK02: it helps to answer the channel if not already up */
   if (ast_channel_state(chan) != AST_STATE_UP) {
      ast_answer(chan);
   }

   if (peer) {
      struct ast_datastore *features_datastore;
      struct ast_dial_features *dialfeatures;

      /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
      if (!ast_strlen_zero(courtesytone)) {
         static const char msg[] = "courtesy tone";

         switch (parkedplay) {
         case 0:/* Courtesy tone to pickup chan */
            res = play_message_to_chans(chan, peer, -1, msg, courtesytone);
            break;
         case 1:/* Courtesy tone to parked chan */
            res = play_message_to_chans(chan, peer, 1, msg, courtesytone);
            break;
         case 2:/* Courtesy tone to both chans */
            res = play_message_to_chans(chan, peer, 0, msg, courtesytone);
            break;
         default:
            res = 0;
            break;
         }
         if (res) {
            ast_autoservice_chan_hangup_peer(chan, peer);
            parkinglot_unref(parkinglot);
            return -1;
         }
      }

      res = ast_channel_make_compatible(chan, peer);
      if (res < 0) {
         ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", ast_channel_name(chan), ast_channel_name(peer));
         ast_autoservice_chan_hangup_peer(chan, peer);
         parkinglot_unref(parkinglot);
         return -1;
      }
      /* This runs sorta backwards, since we give the incoming channel control, as if it
         were the person called. */
      ast_verb(3, "Channel %s connected to parked call %d\n", ast_channel_name(chan), park);

      pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", ast_channel_name(peer));
      ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer));
      memset(&config, 0, sizeof(struct ast_bridge_config));

      /* Get datastore for peer and apply it's features to the callee side of the bridge config */
      ast_channel_lock(peer);
      features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL);
      if (features_datastore && (dialfeatures = features_datastore->data)) {
         ast_copy_flags(&config.features_callee, &dialfeatures->my_features,
            AST_FLAGS_ALL);
      }
      ast_channel_unlock(peer);

      if ((parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
         ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
      }
      if ((parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) {
         ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
      }
      if ((parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
         ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
      }
      if ((parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallreparking == AST_FEATURE_FLAG_BYBOTH)) {
         ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
      }
      if ((parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
         ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
      }
      if ((parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) {
         ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
      }
      if ((parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
         ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
      }
      if ((parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->cfg.parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) {
         ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
      }

      res = ast_bridge_call(chan, peer, &config);

      pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", ast_channel_name(peer));
      ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer));

      /* Simulate the PBX hanging up */
      ast_autoservice_chan_hangup_peer(chan, peer);
   } else {
      if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) {
         ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark",
            ast_channel_name(chan));
      }
      ast_verb(3, "Channel %s tried to retrieve nonexistent parked call %d\n",
         ast_channel_name(chan), park);
      res = -1;
   }

   parkinglot_unref(parkinglot);
   return res;
}
static int parkinglot_activate ( struct ast_parkinglot parkinglot) [static]

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

{
   int disabled = 0;
   char app_data[5 + AST_MAX_CONTEXT];

   /* Create Park option list.  Must match with struct park_app_args options. */
   if (parkinglot->cfg.parkext_exclusive) {
      /* Specify the parking lot this parking extension parks calls. */
      snprintf(app_data, sizeof(app_data), ",,,,,%s", parkinglot->name);
   } else {
      /* The dialplan must specify which parking lot to use. */
      app_data[0] = '\0';
   }

   /* Create context */
   if (!ast_context_find_or_create(NULL, NULL, parkinglot->cfg.parking_con, registrar)) {
      ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n",
         parkinglot->cfg.parking_con);
      disabled = 1;

   /* Add a parking extension into the context */
   } else if (ast_add_extension(parkinglot->cfg.parking_con, 1, parkinglot->cfg.parkext,
      1, NULL, NULL, parkcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
      ast_log(LOG_ERROR, "Could not create parking lot %s access exten %s@%s\n",
         parkinglot->name, parkinglot->cfg.parkext, parkinglot->cfg.parking_con);
      disabled = 1;
   } else {
      /* Add parking hints */
      if (parkinglot->cfg.parkaddhints) {
         park_add_hints(parkinglot->cfg.parking_con, parkinglot->cfg.parking_start,
            parkinglot->cfg.parking_stop);
      }

      /*
       * XXX Not sure why we should need to notify the metermaids for
       * this exten.  It was originally done for the default parking
       * lot entry exten only but should be done for all entry extens
       * if we do it for one.
       */
      /* Notify metermaids about parking lot entry exten state. */
      notify_metermaids(parkinglot->cfg.parkext, parkinglot->cfg.parking_con,
         AST_DEVICE_INUSE);
   }

   parkinglot->disabled = disabled;
   return disabled ? -1 : 0;
}
static int parkinglot_activate_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

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

{
   struct ast_parkinglot *parkinglot = obj;

   if (parkinglot->the_mark) {
      /*
       * Don't activate a parking lot that still bears the_mark since
       * it is effectively deleted.
       */
      return 0;
   }

   if (parkinglot_activate(parkinglot)) {
      /*
       * The parking lot failed to activate.  Allow reloading later to
       * see if that fixes it.
       */
      force_reload_load = 1;
      ast_log(LOG_WARNING, "Parking lot %s not open for business.\n", parkinglot->name);
   } else {
      ast_debug(1, "Parking lot %s now open for business. (parkpos %d-%d)\n",
         parkinglot->name, parkinglot->cfg.parking_start,
         parkinglot->cfg.parking_stop);
   }

   return 0;
}
static struct ast_parkinglot * parkinglot_addref ( struct ast_parkinglot parkinglot) [static, read]

Definition at line 5776 of file features.c.

References ao2_ref, ast_debug, ast_parkinglot::name, and parkinglot.

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

{
   int refcount;

   refcount = ao2_ref(parkinglot, +1);
   ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1);
   return parkinglot;
}
static int parkinglot_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1007 of file features.c.

References CMP_MATCH, CMP_STOP, and ast_parkinglot::name.

Referenced by ast_features_init().

{
   struct ast_parkinglot *parkinglot = obj;
   struct ast_parkinglot *parkinglot2 = arg;

   return !strcasecmp(parkinglot->name, parkinglot2->name) ? CMP_MATCH | CMP_STOP : 0;
}
static int parkinglot_config_read ( const char *  pl_name,
struct parkinglot_cfg cfg,
struct ast_variable var 
) [static]

Definition at line 5893 of file features.c.

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

Referenced by build_parkinglot().

{
   int error = 0;

   while (var) {
      if (!strcasecmp(var->name, "context")) {
         ast_copy_string(cfg->parking_con, var->value, sizeof(cfg->parking_con));
      } else if (!strcasecmp(var->name, "parkext")) {
         ast_copy_string(cfg->parkext, var->value, sizeof(cfg->parkext));
      } else if (!strcasecmp(var->name, "parkext_exclusive")) {
         cfg->parkext_exclusive = ast_true(var->value);
      } else if (!strcasecmp(var->name, "parkinghints")) {
         cfg->parkaddhints = ast_true(var->value);
      } else if (!strcasecmp(var->name, "parkedmusicclass")) {
         ast_copy_string(cfg->mohclass, var->value, sizeof(cfg->mohclass));
      } else if (!strcasecmp(var->name, "parkingtime")) {
         unsigned int parkingtime = 0;

         if ((sscanf(var->value, "%30u", &parkingtime) != 1) || parkingtime < 1) {
            ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
            error = -1;
         } else {
            cfg->parkingtime = parkingtime * 1000;
         }
      } else if (!strcasecmp(var->name, "parkpos")) {
         int start = 0;
         int end = 0;

         if (sscanf(var->value, "%30d-%30d", &start, &end) != 2) {
            ast_log(LOG_WARNING,
               "Format for parking positions is a-b, where a and b are numbers at line %d of %s\n",
               var->lineno, var->file);
            error = -1;
         } else if (end < start || start <= 0 || end <= 0) {
            ast_log(LOG_WARNING, "Parking range is invalid. Must be a <= b, at line %d of %s\n",
               var->lineno, var->file);
            error = -1;
         } else {
            cfg->parking_start = start;
            cfg->parking_stop = end;
         }
      } else if (!strcasecmp(var->name, "findslot")) {
         cfg->parkfindnext = (!strcasecmp(var->value, "next"));
      } else if (!strcasecmp(var->name, "parkedcalltransfers")) {
         parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcalltransfers, var);
      } else if (!strcasecmp(var->name, "parkedcallreparking")) {
         parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallreparking, var);
      } else if (!strcasecmp(var->name, "parkedcallhangup")) {
         parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallhangup, var);
      } else if (!strcasecmp(var->name, "parkedcallrecording")) {
         parkinglot_feature_flag_cfg(pl_name, &cfg->parkedcallrecording, var);
      } else if (!strcasecmp(var->name, "comebackcontext")) {
         ast_copy_string(cfg->comebackcontext, var->value, sizeof(cfg->comebackcontext));
      } else if (!strcasecmp(var->name, "comebacktoorigin")) {
         cfg->comebacktoorigin = ast_true(var->value);
      } else if (!strcasecmp(var->name, "comebackdialtime")) {
         if ((sscanf(var->value, "%30u", &cfg->comebackdialtime) != 1)
               || (cfg->comebackdialtime < 1)) {
            ast_log(LOG_WARNING, "%s is not a valid comebackdialtime\n", var->value);
            cfg->parkingtime = DEFAULT_COMEBACK_DIAL_TIME;
         }
      }
      var = var->next;
   }

   /* Check for configuration errors */
   if (ast_strlen_zero(cfg->parking_con)) {
      ast_log(LOG_WARNING, "Parking lot %s needs context\n", pl_name);
      error = -1;
   }
   if (ast_strlen_zero(cfg->parkext)) {
      ast_log(LOG_WARNING, "Parking lot %s needs parkext\n", pl_name);
      error = -1;
   }
   if (!cfg->parking_start) {
      ast_log(LOG_WARNING, "Parking lot %s needs parkpos\n", pl_name);
      error = -1;
   }
   if (!cfg->comebacktoorigin && ast_strlen_zero(cfg->comebackcontext)) {
      ast_log(LOG_WARNING, "Parking lot %s has comebacktoorigin set false"
            "but has no comebackcontext.\n",
            pl_name);
      error = -1;
   }
   if (error) {
      cfg->is_invalid = 1;
   }

   return error;
}
static void parkinglot_destroy ( void *  obj) [static]

Destroy a parking lot.

Definition at line 5786 of file features.c.

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

Referenced by create_parkinglot().

{
   struct ast_parkinglot *doomed = obj;

   /*
    * No need to destroy parked calls here because any parked call
    * holds a parking lot reference.  Therefore the parkings list
    * must be empty.
    */
   ast_assert(AST_LIST_EMPTY(&doomed->parkings));
   AST_LIST_HEAD_DESTROY(&doomed->parkings);
}
static void parkinglot_feature_flag_cfg ( const char *  pl_name,
int *  param,
struct ast_variable var 
) [static]

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

{
   ast_debug(1, "Setting parking lot %s %s to %s\n", pl_name, var->name, var->value);
   if (!strcasecmp(var->value, "both")) {
      *param = AST_FEATURE_FLAG_BYBOTH;
   } else if (!strcasecmp(var->value, "caller")) {
      *param = AST_FEATURE_FLAG_BYCALLER;
   } else if (!strcasecmp(var->value, "callee")) {
      *param = AST_FEATURE_FLAG_BYCALLEE;
   }
}
static int parkinglot_hash_cb ( const void *  obj,
const int  flags 
) [static]

Definition at line 1000 of file features.c.

References ast_str_case_hash(), and ast_parkinglot::name.

Referenced by ast_features_init().

{
   const struct ast_parkinglot *parkinglot = obj;

   return ast_str_case_hash(parkinglot->name);
}
static int parkinglot_is_marked_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

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

{
   struct ast_parkinglot *parkinglot = obj;

   if (parkinglot->the_mark) {
      if (AST_LIST_EMPTY(&parkinglot->parkings)) {
         /* This parking lot can actually be deleted. */
         return CMP_MATCH;
      }
      /* Try reloading later when parking lot is empty. */
      ast_log(LOG_WARNING,
         "Parking lot %s has parked calls.  Could not remove.\n",
         parkinglot->name);
      parkinglot->disabled = 1;
      force_reload_load = 1;
   }

   return 0;
}
static int parkinglot_markall_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7015 of file features.c.

References ast_parkinglot::the_mark.

Referenced by load_config().

{
   struct ast_parkinglot *parkinglot = obj;

   parkinglot->the_mark = 1;
   return 0;
}
static void parkinglot_unref ( struct ast_parkinglot parkinglot) [static]

Unreference parkinglot object.

Definition at line 5769 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(), get_parkingtime(), manage_parkinglot(), manager_park(), park_call_exec(), park_space_abort(), park_space_reserve(), parked_call_exec(), and xfer_park_call_helper().

{
   ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name,
      ao2_ref(parkinglot, 0) - 1);
   ao2_ref(parkinglot, -1);
}
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 4186 of file features.c.

References AST_CDR_FLAG_LOCKED, ast_test_flag, and ast_cdr::next.

Referenced by ast_bridge_call().

{
   struct ast_cdr *cdr_orig = cdr;
   while (cdr) {
      if (!ast_test_flag(cdr,AST_CDR_FLAG_LOCKED))
         return cdr;
      cdr = cdr->next;
   }
   return cdr_orig; /* everybody LOCKED or some other weirdness, like a NULL */
}
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 2249 of file features.c.

References play_message_to_chans().

Referenced by builtin_automonitor().

{
   return play_message_to_chans(caller_chan, callee_chan, 0, "automon message",
      audiofile);
}
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 2195 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().

{
   /* Put other channel in autoservice. */
   if (ast_autoservice_start(other)) {
      return -1;
   }
   ast_autoservice_ignore(other, AST_FRAME_DTMF_BEGIN);
   ast_autoservice_ignore(other, AST_FRAME_DTMF_END);
   if (ast_stream_and_wait(play_to, audiofile, "")) {
      ast_log(LOG_WARNING, "Failed to play %s '%s'!\n", msg, audiofile);
      ast_autoservice_stop(other);
      return -1;
   }
   if (ast_autoservice_stop(other)) {
      return -1;
   }
   return 0;
}
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 2230 of file features.c.

References play_message_on_chan().

Referenced by parked_call_exec(), and play_message_in_bridged_call().

{
   /* First play the file to the left channel if requested. */
   if (which <= 0 && play_message_on_chan(left, right, msg, audiofile)) {
      return -1;
   }

   /* Then play the file to the right channel if requested. */
   if (which >= 0 && play_message_on_chan(right, left, msg, audiofile)) {
      return -1;
   }

   return 0;
}
static void post_manager_event ( const char *  s,
struct parkeduser pu 
) [static]
static void process_applicationmap_line ( struct ast_variable var) [static]

Definition at line 6125 of file features.c.

References app, ast_call_feature::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_strlen_zero(), ast_verb, ast_call_feature::exten, feature_group_exten::feature, 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().

{
   char *tmp_val = ast_strdupa(var->value);
   char *activateon, *new_syn;
   struct ast_call_feature *feature;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(exten);
      AST_APP_ARG(activatedby);
      AST_APP_ARG(app);
      AST_APP_ARG(app_args);
      AST_APP_ARG(moh_class);
   );

   AST_STANDARD_APP_ARGS(args, tmp_val);

   activateon = strsep(&args.activatedby, "/");

   if (ast_strlen_zero(args.app)
      || ast_strlen_zero(args.exten)
      || ast_strlen_zero(activateon)
      || ast_strlen_zero(var->name)) {
      ast_log(LOG_NOTICE,
         "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
         args.app, args.exten, activateon, var->name);
      return;
   }

   if ((new_syn = strchr(args.app, '('))) {
      /* New syntax */
      args.moh_class = args.app_args;
      args.app_args = new_syn;
      *args.app_args++ = '\0';
      if (args.app_args[strlen(args.app_args) - 1] == ')') {
         args.app_args[strlen(args.app_args) - 1] = '\0';
      }
   }
   
   AST_RWLIST_RDLOCK(&feature_list);
   if (find_dynamic_feature(var->name)) {
      AST_RWLIST_UNLOCK(&feature_list);
      ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n",
         var->name);
      return;
   }
   AST_RWLIST_UNLOCK(&feature_list);

   if (!(feature = ast_calloc(1, sizeof(*feature)))) {
      return;
   }

   ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
   ast_copy_string(feature->app, args.app, FEATURE_APP_LEN);
   ast_copy_string(feature->exten, args.exten, FEATURE_EXTEN_LEN);

   if (args.app_args) {
      ast_copy_string(feature->app_args, args.app_args, FEATURE_APP_ARGS_LEN);
   }

   if (args.moh_class) {
      ast_copy_string(feature->moh_class, args.moh_class, FEATURE_MOH_LEN);
   }

   ast_copy_string(feature->exten, args.exten, sizeof(feature->exten));
   feature->operation = feature_exec_app;
   ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);

   /* Allow caller and callee to be specified for backwards compatability */
   if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller")) {
      ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
   } else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee")) {
      ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
   } else {
      ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
         " must be 'self', or 'peer'\n", var->name);
      ast_free(feature);
      return;
   }

   if (ast_strlen_zero(args.activatedby)) {
      ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
   } else if (!strcasecmp(args.activatedby, "caller")) {
      ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
   } else if (!strcasecmp(args.activatedby, "callee")) {
      ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
   } else if (!strcasecmp(args.activatedby, "both")) {
      ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
   } else {
      ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
         " must be 'caller', or 'callee', or 'both'\n", var->name);
      ast_free(feature);
      return;
   }

   ast_register_feature(feature);

   ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n",
      var->name, args.app, args.app_args, args.exten);
}
static int process_config ( struct ast_config cfg) [static]

Definition at line 6224 of file features.c.

References adsipark, ARRAY_LEN, ast_category_browse(), ast_copy_string(), ast_debug, ast_find_call_feature(), ast_log(), AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_true(), ast_unregister_features(), ast_unregister_groups(), ast_variable_browse(), atxfercallbackretries, atxferdropcall, atxferloopdelay, atxfernoanswertimeout, build_parkinglot(), courtesytone, DEFAULT_ATXFER_CALLBACK_RETRIES, DEFAULT_ATXFER_DROP_CALL, DEFAULT_ATXFER_LOOP_DELAY, DEFAULT_FEATURE_DIGIT_TIMEOUT, DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER, DEFAULT_PARKINGLOT, DEFAULT_TRANSFER_DIGIT_TIMEOUT, feature_group_exten::feature, 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().

{
   int i;
   struct ast_variable *var = NULL;
   struct feature_group *fg = NULL;
   char *ctg;
   static const char * const categories[] = {
      /* Categories in features.conf that are not
       * to be parsed as group categories
       */
      "general",
      "featuremap",
      "applicationmap"
   };

   /* Set general features global defaults. */
   featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;

   /* Set global call pickup defaults. */
   strcpy(pickup_ext, "*8");
   pickupsound[0] = '\0';
   pickupfailsound[0] = '\0';

   /* Set global call transfer defaults. */
   strcpy(xfersound, "beep");
   strcpy(xferfailsound, "beeperr");
   transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
   atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
   atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
   atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
   atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;

   /* Set global call parking defaults. */
   courtesytone[0] = '\0';
   parkedplay = 0;
   adsipark = 0;
   parkeddynamic = 0;

   var = ast_variable_browse(cfg, "general");
   build_parkinglot(DEFAULT_PARKINGLOT, var);
   for (; var; var = var->next) {
      if (!strcasecmp(var->name, "parkeddynamic")) {
         parkeddynamic = ast_true(var->value);
      } else if (!strcasecmp(var->name, "adsipark")) {
         adsipark = ast_true(var->value);
      } else if (!strcasecmp(var->name, "transferdigittimeout")) {
         if ((sscanf(var->value, "%30d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
            ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
            transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
         } else {
            transferdigittimeout = transferdigittimeout * 1000;
         }
      } else if (!strcasecmp(var->name, "featuredigittimeout")) {
         if ((sscanf(var->value, "%30d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
            ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
            featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
         }
      } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
         if ((sscanf(var->value, "%30d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
            ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
            atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
         } else {
            atxfernoanswertimeout = atxfernoanswertimeout * 1000;
         }
      } else if (!strcasecmp(var->name, "atxferloopdelay")) {
         if ((sscanf(var->value, "%30u", &atxferloopdelay) != 1)) {
            ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
            atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
         } else {
            atxferloopdelay *= 1000;
         }
      } else if (!strcasecmp(var->name, "atxferdropcall")) {
         atxferdropcall = ast_true(var->value);
      } else if (!strcasecmp(var->name, "atxfercallbackretries")) {
         if ((sscanf(var->value, "%30u", &atxfercallbackretries) != 1)) {
            ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
            atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
         }
      } else if (!strcasecmp(var->name, "courtesytone")) {
         ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
      }  else if (!strcasecmp(var->name, "parkedplay")) {
         if (!strcasecmp(var->value, "both")) {
            parkedplay = 2;
         } else if (!strcasecmp(var->value, "parked")) {
            parkedplay = 1;
         } else {
            parkedplay = 0;
         }
      } else if (!strcasecmp(var->name, "xfersound")) {
         ast_copy_string(xfersound, var->value, sizeof(xfersound));
      } else if (!strcasecmp(var->name, "xferfailsound")) {
         ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
      } else if (!strcasecmp(var->name, "pickupexten")) {
         ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
      } else if (!strcasecmp(var->name, "pickupsound")) {
         ast_copy_string(pickupsound, var->value, sizeof(pickupsound));
      } else if (!strcasecmp(var->name, "pickupfailsound")) {
         ast_copy_string(pickupfailsound, var->value, sizeof(pickupfailsound));
      }
   }

   unmap_features();
   for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
      if (remap_feature(var->name, var->value)) {
         ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
      }
   }

   /* Map a key combination to an application */
   ast_unregister_features();
   for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
      process_applicationmap_line(var);
   }

   ast_unregister_groups();
   AST_RWLIST_WRLOCK(&feature_groups);

   ctg = NULL;
   while ((ctg = ast_category_browse(cfg, ctg))) {
      /* Is this a parkinglot definition ? */
      if (!strncasecmp(ctg, "parkinglot_", strlen("parkinglot_"))) {
         ast_debug(2, "Found configuration section %s, assume parking context\n", ctg);
         if (!build_parkinglot(ctg, ast_variable_browse(cfg, ctg))) {
            ast_log(LOG_ERROR, "Could not build parking lot %s. Configuration error.\n", ctg);
         } else {
            ast_debug(1, "Configured parking context %s\n", ctg);
         }
         continue;
      }

      /* No, check if it's a group */
      for (i = 0; i < ARRAY_LEN(categories); i++) {
         if (!strcasecmp(categories[i], ctg)) {
            break;
         }
      }
      if (i < ARRAY_LEN(categories)) {
         continue;
      }

      if (!(fg = register_group(ctg))) {
         continue;
      }

      for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
         struct ast_call_feature *feature;

         AST_RWLIST_RDLOCK(&feature_list);
         if (!(feature = find_dynamic_feature(var->name)) &&
             !(feature = ast_find_call_feature(var->name))) {
            AST_RWLIST_UNLOCK(&feature_list);
            ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
            continue;
         }
         AST_RWLIST_UNLOCK(&feature_list);

         register_group_feature(fg, var->value, feature);
      }
   }

   AST_RWLIST_UNLOCK(&feature_groups);

   return 0;
}
static const char* real_ctx ( struct ast_channel transferer,
struct ast_channel transferee 
) [static]

Find the context for the transfer.

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

Definition at line 2483 of file features.c.

References ast_channel_context(), ast_channel_macrocontext(), ast_strlen_zero(), and pbx_builtin_getvar_helper().

Referenced by builtin_atxfer(), and builtin_blindtransfer().

{
   const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
   if (ast_strlen_zero(s)) {
      s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
   }
   if (ast_strlen_zero(s)) { /* Use the non-macro context to transfer the call XXX ? */
      s = ast_channel_macrocontext(transferer);
   }
   if (ast_strlen_zero(s)) {
      s = ast_channel_context(transferer);
   }
   return s;
}
static struct feature_group* register_group ( const char *  fgname) [static, read]

Add new feature group.

Parameters:
fgnamefeature 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 3173 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().

{
   struct feature_group *fg;

   if (!fgname) {
      ast_log(LOG_NOTICE, "You didn't pass a new group name!\n");
      return NULL;
   }

   if (!(fg = ast_calloc_with_stringfields(1, struct feature_group, 128))) {
      return NULL;
   }

   ast_string_field_set(fg, gname, fgname);

   AST_LIST_INSERT_HEAD(&feature_groups, fg, entry);

   ast_verb(2, "Registered group '%s'\n", fg->gname);

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

Add feature to group.

Parameters:
fgfeature group
exten
featurefeature 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 3204 of file features.c.

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

Referenced by process_config().

{
   struct feature_group_exten *fge;

   if (!fg) {
      ast_log(LOG_NOTICE, "You didn't pass a group!\n");
      return;
   }

   if (!feature) {
      ast_log(LOG_NOTICE, "You didn't pass a feature!\n");
      return;
   }

   if (!(fge = ast_calloc_with_stringfields(1, struct feature_group_exten, 128))) {
      return;
   }

   ast_string_field_set(fge, exten, S_OR(exten, feature->exten));

   fge->feature = feature;

   AST_LIST_INSERT_HEAD(&fg->features, fge, entry);

   ast_verb(2, "Registered feature '%s' for group '%s' at exten '%s'\n",
               feature->sname, fg->gname, fge->exten);
}
static int remap_feature ( const char *  name,
const char *  value 
) [static]

Definition at line 3552 of file features.c.

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

Referenced by process_config().

{
   int x, res = -1;

   ast_rwlock_wrlock(&features_lock);
   for (x = 0; x < FEATURES_COUNT; x++) {
      if (strcasecmp(builtin_features[x].sname, name))
         continue;

      ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
      res = 0;
      break;
   }
   ast_rwlock_unlock(&features_lock);

   return res;
}
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 6948 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().

{
   remove_dead_ramp_usage(context, &old_ctx->access_extens, &new_ctx->access_extens);
   remove_dead_spaces_usage(context, &old_ctx->spaces, &new_ctx->spaces, destroy_space);
#if 0
   /* I don't think we should destroy hints if the parking space still exists. */
   remove_dead_spaces_usage(context, &old_ctx->hints, &new_ctx->hints, destroy_space_hint);
#endif
}
static void remove_dead_dialplan_useage ( struct parking_dp_map old_map,
struct parking_dp_map new_map 
) [static]

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

{
   struct parking_dp_context *old_ctx;
   struct parking_dp_context *new_ctx;
   struct ast_context *con;
   int cmp;

   old_ctx = AST_LIST_FIRST(old_map);
   new_ctx = AST_LIST_FIRST(new_map);

   while (new_ctx) {
      if (!old_ctx) {
         /* No old contexts left, so no dead stuff can remain. */
         return;
      }
      cmp = strcmp(old_ctx->context, new_ctx->context);
      if (cmp < 0) {
         /* New map does not have old map context. */
         con = ast_context_find(old_ctx->context);
         if (con) {
            ast_context_destroy(con, registrar);
         }
         old_ctx = AST_LIST_NEXT(old_ctx, node);
         continue;
      }
      if (cmp == 0) {
         /* Old and new map have this context. */
         remove_dead_context_usage(old_ctx->context, old_ctx, new_ctx);
         old_ctx = AST_LIST_NEXT(old_ctx, node);
      } else {
         /* Old map does not have new map context. */
      }
      new_ctx = AST_LIST_NEXT(new_ctx, node);
   }

   /* Any old contexts left must be dead. */
   for (; old_ctx; old_ctx = AST_LIST_NEXT(old_ctx, node)) {
      con = ast_context_find(old_ctx->context);
      if (con) {
         ast_context_destroy(con, registrar);
      }
   }
}
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 6807 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().

{
   struct parking_dp_ramp *old_ramp;
   struct parking_dp_ramp *new_ramp;
   int cmp;

   old_ramp = AST_LIST_FIRST(old_ramps);
   new_ramp = AST_LIST_FIRST(new_ramps);

   while (new_ramp) {
      if (!old_ramp) {
         /* No old ramps left, so no dead ramps can remain. */
         return;
      }
      cmp = strcmp(old_ramp->exten, new_ramp->exten);
      if (cmp < 0) {
         /* New map does not have old ramp. */
         remove_exten_if_exist(context, old_ramp->exten, 1);
         old_ramp = AST_LIST_NEXT(old_ramp, node);
         continue;
      }
      if (cmp == 0) {
         /* Old and new map have this ramp. */
         old_ramp = AST_LIST_NEXT(old_ramp, node);
      } else {
         /* Old map does not have new ramp. */
      }
      new_ramp = AST_LIST_NEXT(new_ramp, node);
   }

   /* Any old ramps left must be dead. */
   for (; old_ramp; old_ramp = AST_LIST_NEXT(old_ramp, node)) {
      remove_exten_if_exist(context, old_ramp->exten, 1);
   }
}
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 6877 of file features.c.

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

Referenced by remove_dead_context_usage().

{
   struct parking_dp_spaces *old_range;
   struct parking_dp_spaces *new_range;
   int space;/*!< Current position in the current old range. */
   int stop;

   old_range = AST_LIST_FIRST(old_spaces);
   new_range = AST_LIST_FIRST(new_spaces);
   space = -1;

   while (old_range) {
      if (space < old_range->start) {
         space = old_range->start;
      }
      if (new_range) {
         if (space < new_range->start) {
            /* Current position in old range starts before new range. */
            if (old_range->stop < new_range->start) {
               /* Old range ends before new range. */
               stop = old_range->stop;
               old_range = AST_LIST_NEXT(old_range, node);
            } else {
               /* Tail of old range overlaps new range. */
               stop = new_range->start - 1;
            }
         } else if (/* new_range->start <= space && */ space <= new_range->stop) {
            /* Current position in old range overlaps new range. */
            if (old_range->stop <= new_range->stop) {
               /* Old range ends at or before new range. */
               old_range = AST_LIST_NEXT(old_range, node);
            } else {
               /* Old range extends beyond end of new range. */
               space = new_range->stop + 1;
               new_range = AST_LIST_NEXT(new_range, node);
            }
            continue;
         } else /* if (new_range->stop < space) */ {
            /* Current position in old range starts after new range. */
            new_range = AST_LIST_NEXT(new_range, node);
            continue;
         }
      } else {
         /* No more new ranges.  All remaining old spaces are dead. */
         stop = old_range->stop;
         old_range = AST_LIST_NEXT(old_range, node);
      }

      /* Destroy dead parking spaces. */
      for (; space <= stop; ++space) {
         destroy_space(context, space);
      }
   }
}
static void remove_exten_if_exist ( const char *  context,
const char *  exten,
int  priority 
) [static]

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

{
   struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */

   if (pbx_find_extension(NULL, NULL, &q, context, exten, priority, NULL, NULL,
      E_MATCH)) {
      ast_debug(1, "Removing unneeded parking lot exten: %s@%s priority:%d\n",
         context, exten, priority);
      ast_context_remove_extension(context, exten, priority, registrar);
   }
}
static void set_bridge_features_on_config ( struct ast_bridge_config config,
const char *  features 
) [static]

Definition at line 4197 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(), feature_group_exten::feature, ast_bridge_config::features_caller, and LOG_WARNING.

Referenced by ast_bridge_call().

{
   const char *feature;

   if (ast_strlen_zero(features)) {
      return;
   }

   for (feature = features; *feature; feature++) {
      switch (*feature) {
      case 'T' :
      case 't' :
         ast_set_flag(&(config->features_caller), AST_FEATURE_REDIRECT);
         break;
      case 'K' :
      case 'k' :
         ast_set_flag(&(config->features_caller), AST_FEATURE_PARKCALL);
         break;
      case 'H' :
      case 'h' :
         ast_set_flag(&(config->features_caller), AST_FEATURE_DISCONNECT);
         break;
      case 'W' :
      case 'w' :
         ast_set_flag(&(config->features_caller), AST_FEATURE_AUTOMON);
         break;
      default :
         ast_log(LOG_WARNING, "Skipping unknown feature code '%c'\n", *feature);
      }
   }
}
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 1019 of file features.c.

References ast_channel_context_set(), ast_channel_exten_set(), and ast_channel_priority_set().

Referenced by manage_parked_call(), and masq_park_call().

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

Definition at line 1095 of file features.c.

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

Referenced by bridge_call_thread().

{
   struct ast_datastore *datastore;
   char *dst_app_data;

   datastore = ast_datastore_alloc(&channel_app_data_datastore, NULL);
   if (!datastore) {
      return -1;
   }

   dst_app_data = ast_malloc(strlen(src_app_data) + 1);
   if (!dst_app_data) {
      ast_datastore_free(datastore);
      return -1;
   }

   ast_channel_data_set(chan, strcpy(dst_app_data, src_app_data));
   datastore->data = dst_app_data;
   ast_channel_datastore_add(chan, datastore);
   return 0;
}
static void set_config_flags ( struct ast_channel chan,
struct ast_bridge_config config 
) [static]

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

{
   int x;

   ast_clear_flag(config, AST_FLAGS_ALL);

   ast_rwlock_rdlock(&features_lock);
   for (x = 0; x < FEATURES_COUNT; x++) {
      if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
         continue;

      if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
         ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);

      if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
         ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
   }
   ast_rwlock_unlock(&features_lock);

   if (!(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
      const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");

      if (dynamic_features) {
         char *tmp = ast_strdupa(dynamic_features);
         char *tok;
         struct ast_call_feature *feature;

         /* while we have a feature */
         while ((tok = strsep(&tmp, "#"))) {
            struct feature_group *fg;

            AST_RWLIST_RDLOCK(&feature_groups);
            AST_RWLIST_TRAVERSE(&feature_groups, fg, entry) {
               struct feature_group_exten *fge;

               AST_LIST_TRAVERSE(&fg->features, fge, entry) {
                  if (ast_test_flag(fge->feature, AST_FEATURE_FLAG_BYCALLER)) {
                     ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
                  }
                  if (ast_test_flag(fge->feature, AST_FEATURE_FLAG_BYCALLEE)) {
                     ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
                  }
               }
            }
            AST_RWLIST_UNLOCK(&feature_groups);

            AST_RWLIST_RDLOCK(&feature_list);
            if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
               if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER)) {
                  ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
               }
               if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE)) {
                  ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
               }
            }
            AST_RWLIST_UNLOCK(&feature_list);
         }
      }
   }
}
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,senseDetect who triggered feature and set callee/caller variables accordingly

Definition at line 2122 of file features.c.

References FEATURE_SENSE_PEER.

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

{
   if (sense == FEATURE_SENSE_PEER) {
      *caller = peer;
      *callee = chan;
   } else {
      *callee = peer;
      *caller = chan;
   }
}
static void unmap_features ( void  ) [static]

Definition at line 3542 of file features.c.

References ast_rwlock_unlock, ast_rwlock_wrlock, FEATURES_COUNT, and features_lock.

Referenced by process_config().

{
   int x;

   ast_rwlock_wrlock(&features_lock);
   for (x = 0; x < FEATURES_COUNT; x++)
      strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
   ast_rwlock_unlock(&features_lock);
}
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 6467 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().

{
   struct parking_dp_ramp *cur_ramp;
   struct parking_dp_ramp *new_ramp;
   int cmp;

   /* Make sure that exclusive is only 0 or 1 */
   if (exclusive) {
      exclusive = 1;
   }

   AST_LIST_TRAVERSE_SAFE_BEGIN(ramp_map, cur_ramp, node) {
      cmp = strcmp(exten, cur_ramp->exten);
      if (cmp > 0) {
         /* The parking lot ramp goes after this node. */
         continue;
      }
      if (cmp == 0) {
         /* The ramp is already in the map. */
         if (complain && (cur_ramp->exclusive || exclusive)) {
            ast_log(LOG_WARNING,
               "Parking lot '%s' parkext %s@%s used by another parking lot.\n",
               lot->name, exten, lot->cfg.parking_con);
         }
         return 0;
      }
      /* The new parking lot ramp goes before this node. */
      new_ramp = build_dialplan_useage_ramp(exten, exclusive);
      if (!new_ramp) {
         return -1;
      }
      AST_LIST_INSERT_BEFORE_CURRENT(new_ramp, node);
      return 0;
   }
   AST_LIST_TRAVERSE_SAFE_END;

   /* New parking lot access ramp goes on the end. */
   new_ramp = build_dialplan_useage_ramp(exten, exclusive);
   if (!new_ramp) {
      return -1;
   }
   AST_LIST_INSERT_TAIL(ramp_map, new_ramp, node);
   return 0;
}
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 6548 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, parking_dp_spaces::stop, and stop.

Referenced by dialplan_usage_add_parkinglot_data().

{
   struct parking_dp_spaces *cur_node;
   struct parking_dp_spaces *expand_node;
   struct parking_dp_spaces *new_node;

   expand_node = NULL;
   AST_LIST_TRAVERSE_SAFE_BEGIN(space_map, cur_node, node) {
      /* NOTE: stop + 1 to combine immediately adjacent nodes into one. */
      if (expand_node) {
         /* The previous node is expanding to possibly eat following nodes. */
         if (expand_node->stop + 1 < cur_node->start) {
            /* Current node is completely after expanding node. */
            return 0;
         }

         if (complain
            && ((cur_node->start <= start && start <= cur_node->stop)
               || (cur_node->start <= stop && stop <= cur_node->stop)
               || (start < cur_node->start && cur_node->stop < stop))) {
            /* Only complain once per range add. */
            complain = 0;
            ast_log(LOG_WARNING,
               "Parking lot '%s' parkpos %d-%d@%s overlaps another parking lot.\n",
               lot->name, start, stop, lot->cfg.parking_con);
         }

         /* Current node is eaten by the expanding node. */
         if (expand_node->stop < cur_node->stop) {
            expand_node->stop = cur_node->stop;
         }
         AST_LIST_REMOVE_CURRENT(node);
         ast_free(cur_node);
         continue;
      }

      if (cur_node->stop + 1 < start) {
         /* New range is completely after current node. */
         continue;
      }
      if (stop + 1 < cur_node->start) {
         /* New range is completely before current node. */
         new_node = build_dialplan_useage_spaces(start, stop);
         if (!new_node) {
            return -1;
         }
         AST_LIST_INSERT_BEFORE_CURRENT(new_node, node);
         return 0;
      }

      if (complain
         && ((cur_node->start <= start && start <= cur_node->stop)
            || (cur_node->start <= stop && stop <= cur_node->stop)
            || (start < cur_node->start && cur_node->stop < stop))) {
         /* Only complain once per range add. */
         complain = 0;
         ast_log(LOG_WARNING,
            "Parking lot '%s' parkpos %d-%d@%s overlaps another parking lot.\n",
            lot->name, start, stop, lot->cfg.parking_con);
      }

      /* Current node range overlaps or is immediately adjacent to new range. */
      if (start < cur_node->start) {
         /* Expand the current node in the front. */
         cur_node->start = start;
      }
      if (stop <= cur_node->stop) {
         /* Current node is not expanding in the rear. */
         return 0;
      }
      cur_node->stop = stop;
      expand_node = cur_node;
   }
   AST_LIST_TRAVERSE_SAFE_END;

   if (expand_node) {
      /*
       * The previous node expanded and either ate all following nodes
       * or it was the last node.
       */
      return 0;
   }

   /* New range goes on the end. */
   new_node = build_dialplan_useage_spaces(start, stop);
   if (!new_node) {
      return -1;
   }
   AST_LIST_INSERT_TAIL(space_map, new_node, node);
   return 0;
}
static int xfer_park_call_helper ( struct ast_channel park_me,
struct ast_channel parker,
struct ast_exten park_exten 
) [static]

Definition at line 2062 of file features.c.

References AST_FEATURE_RETURN_SUCCESS, ast_get_extension_app_data(), AST_PARK_OPT_SILENCE, AST_STANDARD_APP_ARGS, 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().

{
   char *parse;
   const char *app_data;
   const char *pl_name;
   struct ast_park_call_args args = { 0, };
   struct park_app_args app_args;
   int res;

   app_data = ast_get_extension_app_data(park_exten);
   if (!app_data) {
      app_data = "";
   }
   parse = ast_strdupa(app_data);
   AST_STANDARD_APP_ARGS(app_args, parse);

   /* Find the parking lot */
   if (!ast_strlen_zero(app_args.pl_name)) {
      pl_name = app_args.pl_name;
   } else {
      pl_name = findparkinglotname(parker);
   }
   if (ast_strlen_zero(pl_name)) {
      /* Parking lot is not specified, so use the default parking lot. */
      args.parkinglot = parkinglot_addref(default_parkinglot);
   } else {
      args.parkinglot = find_parkinglot(pl_name);
      if (!args.parkinglot && parkeddynamic) {
         args.parkinglot = create_dynamic_parkinglot(pl_name, park_me);
      }
   }

   if (args.parkinglot) {
      /* Park the call */
      res = finishup(park_me);
      if (res) {
         /* park_me hungup on us. */
         parkinglot_unref(args.parkinglot);
         return -1;
      }
      res = masq_park_call(park_me, parker, &args);
      parkinglot_unref(args.parkinglot);
   } else {
      /* Parking failed because parking lot does not exist. */
      if (!ast_test_flag(&args, AST_PARK_OPT_SILENCE)) {
         ast_stream_and_wait(parker, "pbx-parkingfailed", "");
      }
      finishup(park_me);
      res = -1;
   }

   return res ? AST_FEATURE_RETURN_SUCCESS : -1;
}

Variable Documentation

int adsipark [static]

Definition at line 750 of file features.c.

Referenced by park_call_full(), and process_config().

char* app_bridge = "Bridge" [static]

Definition at line 8002 of file features.c.

unsigned int atxfercallbackretries [static]

Definition at line 758 of file features.c.

Referenced by builtin_atxfer(), and process_config().

unsigned int atxferdropcall [static]

Definition at line 756 of file features.c.

Referenced by builtin_atxfer(), and process_config().

unsigned int atxferloopdelay [static]

Definition at line 757 of file features.c.

Referenced by builtin_atxfer(), and process_config().

int atxfernoanswertimeout [static]

Definition at line 755 of file features.c.

Referenced by builtin_atxfer(), and process_config().

struct ast_app_option bridge_exec_options[128] = { [ 'p' ] = { .flag = BRIDGE_OPT_PLAYTONE }, [ 'F' ] = { .flag = OPT_CALLEE_GO_ON , .arg_index = OPT_ARG_CALLEE_GO_ON + 1 }, [ '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 8042 of file features.c.

Referenced by bridge_exec().

Definition at line 3139 of file features.c.

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

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

char courtesytone[256] [static]

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

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

Referenced by create_dynamic_parkinglot(), get_parkingtime(), 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 889 of file features.c.

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

Initial value:
 {
   .type = "FEATURE",
   .destroy = feature_ds_destroy,
}

Definition at line 3364 of file features.c.

Initial value:
 {
   .name = "FEATURE",
   .read = feature_read,
   .write = feature_write
}

Definition at line 9033 of file features.c.

struct feature_groups feature_groups [static]
struct feature_list feature_list [static]
int featuredigittimeout [static]

Definition at line 753 of file features.c.

Referenced by ast_bridge_call(), and process_config().

Initial value:
 {
   .name = "FEATUREMAP",
   .read = featuremap_read,
   .write = featuremap_write
}

Definition at line 9039 of file features.c.

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 748 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 727 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 779 of file features.c.

Referenced by builtin_automixmonitor().

int mixmonitor_ok = 1 [static]

Definition at line 780 of file features.c.

Referenced by builtin_automixmonitor().

struct ast_app* monitor_app = NULL [static]

Definition at line 776 of file features.c.

Referenced by ast_bridge_call(), and builtin_automonitor().

int monitor_ok = 1 [static]

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

Referenced by park_call_exec().

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

Definition at line 555 of file features.c.

int parkeddynamic = 0 [static]

Enable creation of parkinglots dynamically

Definition at line 730 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 729 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 745 of file features.c.

Referenced by ast_features_reload(), and manage_parked_call().

pthread_t parking_thread [static]

Definition at line 785 of file features.c.

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

Default configuration for normal parking lots.

Definition at line 5852 of file features.c.

Referenced by build_parkinglot().

Default configuration for default parking lot.

Definition at line 5839 of file features.c.

Referenced by build_parkinglot().

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

char pickup_ext[AST_MAX_EXTENSION] [static]

Call pickup extension

Definition at line 557 of file features.c.

Referenced by analog_canmatch_featurecode(), ast_pickup_ext(), and canmatch_featurecode().

char pickupfailsound[256] [static]

Pickup failure sound

Definition at line 735 of file features.c.

Referenced by ast_pickup_call(), and process_config().

char pickupsound[256] [static]

Pickup sound

Definition at line 734 of file features.c.

Referenced by ast_pickup_call(), and process_config().

struct ast_app* stopmixmonitor_app = NULL [static]

Definition at line 782 of file features.c.

Referenced by builtin_automixmonitor().

int stopmixmonitor_ok = 1 [static]

Definition at line 783 of file features.c.

Referenced by builtin_automixmonitor().

int transferdigittimeout [static]

Definition at line 752 of file features.c.

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

char xferfailsound[256] [static]

Call transfer failure sound

Definition at line 733 of file features.c.

Referenced by builtin_atxfer(), and process_config().

char xfersound[256] [static]

Call transfer sound

Definition at line 732 of file features.c.

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