Fri Apr 29 2011 07:57:50

Asterisk developer's documentation


features.h File Reference

Call Parking and Pickup API Includes code and algorithms from the Zapata library. More...

#include "asterisk/pbx.h"
#include "asterisk/linkedlists.h"
Include dependency graph for features.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ast_call_feature

Defines

#define AST_FEATURE_RETURN_HANGUP   -1
#define AST_FEATURE_RETURN_KEEPTRYING   24
#define AST_FEATURE_RETURN_NO_HANGUP_PEER   AST_PBX_NO_HANGUP_PEER
#define AST_FEATURE_RETURN_PARKFAILED   25
#define AST_FEATURE_RETURN_PASSDIGITS   21
#define AST_FEATURE_RETURN_PBX_KEEPALIVE   AST_PBX_KEEPALIVE
#define AST_FEATURE_RETURN_STOREDIGITS   22
#define AST_FEATURE_RETURN_SUCCESS   23
#define AST_FEATURE_RETURN_SUCCESSBREAK   0
#define DEFAULT_PARKINGLOT   "default"
#define FEATURE_APP_ARGS_LEN   256
#define FEATURE_APP_LEN   64
#define FEATURE_EXTEN_LEN   32
#define FEATURE_MAX_LEN   11
#define FEATURE_MOH_LEN   80
#define FEATURE_SENSE_CHAN   (1 << 0)
#define FEATURE_SENSE_PEER   (1 << 1)
#define FEATURE_SNAME_LEN   32
#define PARK_APP_NAME   "Park"

Typedefs

typedef int(* ast_feature_operation )(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)

Enumerations

enum  {
  AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0), AST_FEATURE_FLAG_ONPEER = (1 << 1), AST_FEATURE_FLAG_ONSELF = (1 << 2), AST_FEATURE_FLAG_BYCALLEE = (1 << 3),
  AST_FEATURE_FLAG_BYCALLER = (1 << 4), AST_FEATURE_FLAG_BYBOTH = (3 << 3)
}
 main call feature structure More...

Functions

int ast_bridge_call (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
 Bridge a call, optionally allowing redirection.
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_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_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 *host, int timeout, int *extout)
 Park a call via a masqueraded channel.
int ast_park_call (struct ast_channel *chan, struct ast_channel *host, int timeout, const char *parkexten, int *extout)
 Park a call and read back parked location.
int ast_parking_ext_valid (const char *exten_str, struct ast_channel *chan, const char *context)
 Determine if parking extension exists in a given context.
int ast_pickup_call (struct ast_channel *chan)
 Pickup a call.
const char * ast_pickup_ext (void)
 Determine system call pickup extension.
void ast_rdlock_call_features (void)
void ast_register_feature (struct ast_call_feature *feature)
 register new feature into feature_set
void ast_unlock_call_features (void)
void ast_unregister_feature (struct ast_call_feature *feature)
 unregister feature from feature_set

Detailed Description

Call Parking and Pickup API Includes code and algorithms from the Zapata library.

Definition in file features.h.


Define Documentation

#define AST_FEATURE_RETURN_HANGUP   -1

Definition at line 40 of file features.h.

Referenced by builtin_disconnect().

#define AST_FEATURE_RETURN_KEEPTRYING   24

Definition at line 47 of file features.h.

Referenced by feature_exec_app(), and feature_interpret_helper().

#define AST_FEATURE_RETURN_NO_HANGUP_PEER   AST_PBX_NO_HANGUP_PEER

Definition at line 43 of file features.h.

#define AST_FEATURE_RETURN_PARKFAILED   25

Definition at line 48 of file features.h.

Referenced by builtin_blindtransfer(), and masq_park_call().

#define AST_FEATURE_RETURN_PASSDIGITS   21

Definition at line 44 of file features.h.

Referenced by ast_bridge_call(), and feature_interpret_helper().

#define AST_FEATURE_RETURN_PBX_KEEPALIVE   AST_PBX_KEEPALIVE

Definition at line 42 of file features.h.

#define AST_FEATURE_RETURN_STOREDIGITS   22

Definition at line 45 of file features.h.

Referenced by detect_disconnect(), and feature_interpret_helper().

#define AST_FEATURE_RETURN_SUCCESS   23
#define AST_FEATURE_RETURN_SUCCESSBREAK   0

Definition at line 41 of file features.h.

Referenced by feature_exec_app().

#define DEFAULT_PARKINGLOT   "default"

Default parking lot

Definition at line 38 of file features.h.

Referenced by load_config(), and reload_config().

#define FEATURE_APP_ARGS_LEN   256

Definition at line 32 of file features.h.

Referenced by load_config().

#define FEATURE_APP_LEN   64

Definition at line 31 of file features.h.

Referenced by load_config().

#define FEATURE_EXTEN_LEN   32

Definition at line 34 of file features.h.

Referenced by load_config().

#define FEATURE_MAX_LEN   11

Definition at line 30 of file features.h.

Referenced by ast_bridge_call(), and wait_for_answer().

#define FEATURE_MOH_LEN   80

Definition at line 35 of file features.h.

Referenced by load_config().

#define FEATURE_SENSE_CHAN   (1 << 0)

Definition at line 50 of file features.h.

Referenced by ast_bridge_call(), feature_exec_app(), and feature_interpret().

#define FEATURE_SENSE_PEER   (1 << 1)

Definition at line 51 of file features.h.

Referenced by ast_bridge_call(), and set_peers().

#define FEATURE_SNAME_LEN   32

Definition at line 33 of file features.h.

Referenced by load_config().

#define PARK_APP_NAME   "Park"

Definition at line 37 of file features.h.

Referenced by ast_parking_ext_valid(), and handle_exec().


Typedef Documentation

typedef int(* ast_feature_operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data)

Definition at line 53 of file features.h.


Enumeration Type Documentation

anonymous enum

main call feature structure

Enumerator:
AST_FEATURE_FLAG_NEEDSDTMF 
AST_FEATURE_FLAG_ONPEER 
AST_FEATURE_FLAG_ONSELF 
AST_FEATURE_FLAG_BYCALLEE 
AST_FEATURE_FLAG_BYCALLER 
AST_FEATURE_FLAG_BYBOTH 

Definition at line 57 of file features.h.


Function Documentation

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

Bridge a call, optionally allowing redirection.

Bridge a call, optionally allowing redirection.

Parameters:
chan,peer,configSet 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.

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

References ast_channel::_state, ast_cdr::accountcode, ast_channel::accountcode, add_features_datastores(), ast_channel::amaflags, ast_cdr::amaflags, ast_cdr::answer, ast_channel::appl, AST_BRIDGE_RETRY, ast_bridged_channel(), ast_cdr_alloc(), ast_cdr_answer(), AST_CDR_ANSWERED, ast_cdr_appenduserfield(), ast_cdr_detach(), ast_cdr_discard(), ast_cdr_dup_unique_swap(), ast_cdr_end(), AST_CDR_FLAG_BRIDGED, AST_CDR_FLAG_DIALED, AST_CDR_FLAG_MAIN, AST_CDR_FLAG_POST_DISABLED, AST_CDR_NULL, ast_cdr_setaccount(), ast_cdr_setanswer(), ast_cdr_setcid(), ast_cdr_setdisposition(), ast_cdr_setuserfield(), ast_cdr_specialized_reset(), ast_cdr_start(), ast_cdr_update(), AST_CEL_BRIDGE_END, AST_CEL_BRIDGE_START, ast_cel_report_event(), ast_channel_bridge(), ast_channel_connected_line_macro(), ast_channel_get_by_name(), ast_channel_lock, ast_channel_log(), ast_channel_redirecting_macro(), ast_channel_set_linkgroup(), ast_channel_setoption(), ast_channel_unlock, ast_channel_unref, ast_check_hangup(), ast_clear_flag, AST_CONTROL_AOC, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OPTION, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_copy_string(), ast_debug, ast_default_amaflags, ast_dtmf_stream(), ast_exists_extension(), AST_FEATURE_NO_H_EXTEN, AST_FEATURE_RETURN_PASSDIGITS, AST_FEATURE_RETURN_SUCCESS, AST_FEATURE_WARNING_ACTIVE, AST_FLAG_BRIDGE_HANGUP_DONT, AST_FLAG_BRIDGE_HANGUP_RUN, AST_FLAG_IN_AUTOLOOP, AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_DTMF_BEGIN, AST_FRAME_DTMF_END, ast_frfree, ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_EXTENSION, ast_opt_end_cdr_before_h_exten, AST_OPTION_FLAG_REQUEST, ast_raw_answer(), ast_set2_flag, ast_set_flag, ast_spawn_extension(), AST_STATE_RINGING, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_tvcmp(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), ast_verb, ast_channel::caller, ast_channel::cdr, ast_cdr::channel, ast_channel::context, ast_option_header::data, ast_frame::data, ast_channel::data, ast_frame::datalen, ast_cdr::dcontext, ast_cdr::disposition, ast_cdr::dst, ast_cdr::dstchannel, ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, ast_channel::exten, f, feature_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_caller, ast_frame::frametype, ast_party_caller::id, ast_frame_subclass::integer, ast_cdr::lastapp, ast_cdr::lastdata, LOG_WARNING, monitor_app, monitor_ok, ast_channel::name, ast_cdr::next, ast_party_id::number, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pick_unlocked_cdr(), ast_channel::priority, ast_frame::ptr, S_COR, S_OR, set_bridge_features_on_config(), set_config_flags(), ast_cdr::start, ast_party_number::str, ast_frame::subclass, ast_bridge_config::timelimit, ast_channel::uniqueid, ast_cdr::uniqueid, ast_cdr::userfield, ast_party_number::valid, and ast_channel::visible_indication.

Referenced by app_exec(), bridge_call_thread(), bridge_exec(), builtin_atxfer(), dial_exec_full(), park_exec_full(), 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_MAX_EXTENSION];
   char orig_peername[AST_MAX_EXTENSION];
   int res;
   int diff;
   int hasfeatures=0;
   int hadfeatures=0;
   int autoloopflag;
   int we_disabled_peer_cdr = 0;
   struct ast_option_header *aoh;
   struct ast_cdr *bridge_cdr = NULL;
   struct ast_cdr *orig_peer_cdr = NULL;
   struct ast_cdr *chan_cdr = chan->cdr; /* the proper chan cdr, if there are forked cdrs */
   struct ast_cdr *peer_cdr = peer->cdr; /* 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 */

   if (chan && peer) {
      pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
      pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
   } else if (chan) {
      pbx_builtin_setvar_helper(chan, "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 (chan->_state == AST_STATE_RINGING && peer->visible_indication != 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, peer, config);

   /* Answer if need be */
   if (chan->_state != 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 (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
      char tmp[256];
      if (!ast_strlen_zero(chan->cdr->userfield)) {
         snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield);
         ast_cdr_appenduserfield(chan, tmp);
      } else
         ast_cdr_setuserfield(chan, peer->cdr->userfield);
      /* Don't delete the CDR; just disable it. */
      ast_set_flag(peer->cdr, AST_CDR_FLAG_POST_DISABLED);
      we_disabled_peer_cdr = 1;
   }
   ast_copy_string(orig_channame,chan->name,sizeof(orig_channame));
   ast_copy_string(orig_peername,peer->name,sizeof(orig_peername));
   orig_peer_cdr = peer_cdr;
   
   if (!chan_cdr || (chan_cdr && !ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED))) {
      
      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(chan->appl, ""), sizeof(bridge_cdr->lastapp));
         ast_copy_string(bridge_cdr->lastdata, S_OR(chan->data, ""), 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, chan->accountcode);

      } 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, chan->name, sizeof(bridge_cdr->channel));
         ast_copy_string(bridge_cdr->dstchannel, peer->name, sizeof(bridge_cdr->dstchannel));
         ast_copy_string(bridge_cdr->uniqueid, chan->uniqueid, sizeof(bridge_cdr->uniqueid));
         ast_copy_string(bridge_cdr->lastapp, S_OR(chan->appl, ""), sizeof(bridge_cdr->lastapp));
         ast_copy_string(bridge_cdr->lastdata, S_OR(chan->data, ""), sizeof(bridge_cdr->lastdata));
         ast_cdr_setcid(bridge_cdr, chan);
         bridge_cdr->disposition = (chan->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NULL;
         bridge_cdr->amaflags = chan->amaflags ? chan->amaflags :  ast_default_amaflags;
         ast_copy_string(bridge_cdr->accountcode, chan->accountcode, sizeof(bridge_cdr->accountcode));
         /* Destination information */
         ast_copy_string(bridge_cdr->dst, chan->exten, sizeof(bridge_cdr->dst));
         ast_copy_string(bridge_cdr->dcontext, chan->context, 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_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(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 transfered
       * 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, NULL);
   for (;;) {
      struct ast_channel *other; /* used later */
   
      res = ast_channel_bridge(chan, peer, config, &f, &who);
      
      /* 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, 0);
                  memset(peer_featurecode, 0, sizeof(peer_featurecode));
               }
               if (!ast_strlen_zero(chan_featurecode)) {
                  ast_dtmf_stream(peer, chan, chan_featurecode, 0, 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(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer)) {
            ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
         }
         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 -1:
            ast_indicate(other, f->subclass.integer);
            break;
         case AST_CONTROL_CONNECTED_LINE:
            if (!ast_channel_connected_line_macro(who, other, f, who != chan, 1)) {
               break;
            }
            ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
            break;
         case AST_CONTROL_REDIRECTING:
            if (!ast_channel_redirecting_macro(who, other, f, who != chan, 1)) {
               break;
            }
            ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
            break;
         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 */
            if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
               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) {
         /* eat it */
      } else if (f->frametype == AST_FRAME_DTMF) {
         char *featurecode;
         int sense;

         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;
         }
         /*! 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;
         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, 0);
            /* 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 future */
               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, NULL);
   before_you_go:

   if (ast_test_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT)) {
      ast_clear_flag(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_test_flag(&(config->features_caller),AST_FEATURE_NO_H_EXTEN)
      && ast_exists_extension(chan, chan->context, "h", 1,
         S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
      struct ast_cdr *swapper = NULL;
      char savelastapp[AST_MAX_EXTENSION];
      char savelastdata[AST_MAX_EXTENSION];
      char save_exten[AST_MAX_EXTENSION];
      int  save_prio;
      int  found = 0;   /* set if we find at least one match */
      int  spawn_error = 0;
      
      autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
      ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
      if (bridge_cdr && ast_opt_end_cdr_before_h_exten) {
         ast_cdr_end(bridge_cdr);
      }
      /* swap the bridge cdr and the chan cdr for a moment, and let the endbridge
         dialplan code operate on it */
      ast_channel_lock(chan);
      if (bridge_cdr) {
         swapper = chan->cdr;
         ast_copy_string(savelastapp, bridge_cdr->lastapp, sizeof(bridge_cdr->lastapp));
         ast_copy_string(savelastdata, bridge_cdr->lastdata, sizeof(bridge_cdr->lastdata));
         chan->cdr = bridge_cdr;
      }
      ast_copy_string(save_exten, chan->exten, sizeof(save_exten));
      save_prio = chan->priority;
      ast_copy_string(chan->exten, "h", sizeof(chan->exten));
      chan->priority = 1;
      ast_channel_unlock(chan);
      while ((spawn_error = ast_spawn_extension(chan, chan->context, chan->exten,
         chan->priority,
         S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
         &found, 1)) == 0) {
         chan->priority++;
      }
      if (spawn_error
         && (!ast_exists_extension(chan, chan->context, chan->exten, chan->priority,
            S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))
            || ast_check_hangup(chan))) {
         /* if the extension doesn't exist or a hangup occurred, this isn't really a spawn error */
         spawn_error = 0;
      }
      if (found && spawn_error) {
         /* Something bad happened, or a hangup has been requested. */
         ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", chan->context, chan->exten, chan->priority, chan->name);
         ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", chan->context, chan->exten, chan->priority, chan->name);
      }
      /* swap it back */
      ast_channel_lock(chan);
      ast_copy_string(chan->exten, save_exten, sizeof(chan->exten));
      chan->priority = save_prio;
      if (bridge_cdr) {
         if (chan->cdr == bridge_cdr) {
            chan->cdr = swapper;
         } else {
            bridge_cdr = NULL;
         }
      }
      if (!spawn_error) {
         ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_RUN);
      }
      ast_channel_unlock(chan);
      /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */
      if (bridge_cdr) {
         ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp));
         ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
      }
      ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
   }
   
   /* 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(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
   if (bridge_cdr && new_chan_cdr && 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, chan->name) != 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 = chan_ptr->cdr; 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(chan->cdr, 0); /* nothing changed, reset the chan cdr  */
      }
   }

   {
      struct ast_channel *chan_ptr = NULL;
      new_peer_cdr = pick_unlocked_cdr(peer->cdr); /* 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, peer->name) != 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 = chan_ptr->cdr; 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(peer->cdr, AST_CDR_FLAG_POST_DISABLED);
         }
         ast_cdr_specialized_reset(peer->cdr, 0); /* nothing changed, reset the peer cdr  */
      }
   }
   
   return res;
}
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 5680 of file features.c.

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

Referenced by bridge_exec(), and dial_exec_full().

{
   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_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 2872 of file features.c.

References feature_interpret_helper().

Referenced by detect_disconnect().

                                                                                                                                 {

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

Reload call features from features.conf.

Definition at line 5179 of file features.c.

References ao2_t_callback, load_config(), OBJ_NODATA, OBJ_UNLINK, parkinglot_is_marked_cb(), parkinglot_markall_cb(), and parkinglots.

Referenced by handle_features_reload().

{
   int res;

   ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_markall_cb, NULL, "callback to mark all parkinglots");
   res = load_config(); /* Reload configuration */
   ao2_t_callback(parkinglots, OBJ_NODATA | OBJ_UNLINK, parkinglot_is_marked_cb, NULL, "callback to remove all marked parkinglots");
   
   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 2614 of file features.c.

References FEATURES_COUNT, and ast_call_feature::sname.

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

{
   int x;
   for (x = 0; x < FEATURES_COUNT; x++) {
      if (!strcasecmp(name, builtin_features[x].sname))
         return &builtin_features[x];
   }
   return NULL;
}
int ast_masq_park_call ( struct ast_channel rchan,
struct ast_channel host,
int  timeout,
int *  extout 
)

Park a call via a masqueraded channel.

Parameters:
rchanthe real channel to be parked
hostthe channel to have the parking read to.
timeoutis a timeout in milliseconds
extoutis a parameter to an int that will hold the parked location, or NULL if you want.

Masquerade the channel rchan into a new, empty channel which is then parked with ast_park_call

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

Definition at line 1299 of file features.c.

References masq_park_call().

Referenced by __analog_ss_thread(), analog_ss_thread(), handle_exec(), handle_soft_key_event_message(), handle_stimulus_message(), mgcp_ss(), parkandannounce_exec(), and rpt_exec().

{
   return masq_park_call(rchan, peer, timeout, extout, 0, NULL);
}
int ast_park_call ( struct ast_channel chan,
struct ast_channel peer,
int  timeout,
const char *  parkexten,
int *  extout 
)

Park a call and read back parked location.

Parameters:
chanthe channel to actually be parked
hostthe channel which will have the parked location read to.
timeoutis a timeout in milliseconds
extoutis a parameter to an int that will hold the parked location, or NULL if you want.

Park the channel chan, and read back the parked location to the host. 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 1225 of file features.c.

References ao2_callback, ast_park_call_args::extout, find_parkinglot_by_exten_cb(), park_call_full(), parkinglots, and ast_park_call_args::timeout.

Referenced by iax_park_thread(), and sip_park_thread().

{
   struct ast_parkinglot *found_lot = ao2_callback(parkinglots, 0, find_parkinglot_by_exten_cb, (void *) parkexten);

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

   return park_call_full(chan, peer, &args);
}
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 643 of file features.c.

References ast_get_extension_app(), E_MATCH, feature_group_exten::exten, PARK_APP_NAME, pbx_find_extension(), and pbx_find_info::stacklen.

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

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

   exten = pbx_find_extension(chan, NULL, &q, context, exten_str, 1, NULL, NULL, E_MATCH);
   if (!exten) {
      return 0;
   }

   app_at_exten = ast_get_extension_app(exten);
   if (!app_at_exten || strcmp(PARK_APP_NAME, app_at_exten)) {
      return 0;
   }

   return 1;
}
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.

Definition at line 5578 of file features.c.

References ast_answer(), ast_channel_callback(), ast_channel_connected_line_macro(), ast_channel_lock_both, ast_channel_masquerade(), ast_channel_queue_connected_line_update(), ast_channel_unlock, ast_channel_unref, ast_channel_update_connected_line(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, AST_CONTROL_ANSWER, ast_debug, ast_log(), ast_manager_event_multichan, ast_party_connected_line_collect_caller(), ast_queue_control(), ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_channel::caller, ast_channel::connected, EVENT_FLAG_CALL, find_channel_by_group(), LOG_WARNING, ast_channel::name, pickupfailsound, pickupsound, and ast_party_connected_line::source.

Referenced by __analog_ss_thread(), analog_ss_thread(), cb_events(), handle_request_invite(), mgcp_ss(), and pickup_exec().

{
   struct ast_channel *cur, *chans[2] = { chan, };
   struct ast_party_connected_line connected_caller;
   int res;
   const char *chan_name;
   const char *cur_name;

   if (!(cur = ast_channel_callback(find_channel_by_group, NULL, chan, 0))) {
      ast_debug(1, "No call pickup possible...\n");
      if (!ast_strlen_zero(pickupfailsound)) {
         ast_stream_and_wait(chan, pickupfailsound, "");
      }
      return -1;
   }

   chans[1] = cur;

   ast_channel_lock_both(cur, chan);

   cur_name = ast_strdupa(cur->name);
   chan_name = ast_strdupa(chan->name);

   ast_debug(1, "Call pickup on chan '%s' by '%s'\n", cur_name, chan_name);

   connected_caller = cur->connected;
   connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
   if (ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
      ast_channel_update_connected_line(chan, &connected_caller, NULL);
   }

   ast_party_connected_line_collect_caller(&connected_caller, &chan->caller);
   connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
   ast_channel_queue_connected_line_update(chan, &connected_caller, NULL);

   ast_channel_unlock(cur);
   ast_channel_unlock(chan);

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

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

   if ((res = ast_channel_masquerade(cur, chan))) {
      ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name, cur_name);
   }

   if (!ast_strlen_zero(pickupsound)) {
      ast_stream_and_wait(cur, pickupsound, "");
   }

   /* If you want UniqueIDs, set channelvars in manager.conf to CHANNEL(uniqueid) */
   ast_manager_event_multichan(EVENT_FLAG_CALL, "Pickup", 2, chans,
      "Channel: %s\r\nTargetChannel: %s\r\n", chan->name, cur->name);

   cur = ast_channel_unref(cur);

   return res;
}
const char* ast_pickup_ext ( void  )

Determine system call pickup extension.

Definition at line 662 of file features.c.

References pickup_ext.

Referenced by __analog_ss_thread(), analog_ss_thread(), cb_events(), get_destination(), handle_feature_show(), handle_request_invite(), and mgcp_ss().

{
   return pickup_ext;
}
void ast_rdlock_call_features ( void  )

Definition at line 2604 of file features.c.

References ast_rwlock_rdlock, and features_lock.

Referenced by handle_request_info().

void ast_register_feature ( struct ast_call_feature feature)

register new feature into feature_set

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

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

{
   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_unlock_call_features ( void  )

Definition at line 2609 of file features.c.

References ast_rwlock_unlock, and features_lock.

Referenced by handle_request_info().

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 2528 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);
}