#include "asterisk.h"
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/causes.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/app.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/utils.h"
#include "asterisk/adsi.h"
#include "asterisk/devicestate.h"
#include "asterisk/monitor.h"
#include "asterisk/indications.h"
Include dependency graph for res_features.c:
Go to the source code of this file.
Data Structures | |
struct | aauser |
struct | ast_bridge_thread_obj |
struct | holdeduser |
struct | parkeduser |
Defines | |
#define | AST_MAX_WATCHERS 256 |
#define | DEFAULT_FEATURE_DIGIT_TIMEOUT 500 |
#define | DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000 |
#define | DEFAULT_PARK_TIME 45000 |
#define | DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 |
#define | FEATURE_RETURN_HANGUP -1 |
#define | FEATURE_RETURN_KEEPTRYING 24 |
#define | FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER |
#define | FEATURE_RETURN_PASSDIGITS 21 |
#define | FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE |
#define | FEATURE_RETURN_STOREDIGITS 22 |
#define | FEATURE_RETURN_SUCCESS 23 |
#define | FEATURE_RETURN_SUCCESSBREAK 0 |
#define | FEATURE_SENSE_CHAN (1 << 0) |
#define | FEATURE_SENSE_PEER (1 << 1) |
#define | FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0])) |
Enumerations | |
enum | { AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0), AST_FEATURE_FLAG_ONPEER = (1 << 1), AST_FEATURE_FLAG_ONSELF = (1 << 2), AST_FEATURE_FLAG_BYCALLEE = (1 << 3), AST_FEATURE_FLAG_BYCALLER = (1 << 4), AST_FEATURE_FLAG_BYBOTH = (3 << 3) } |
enum | { BRIDGE_OPT_PLAYTONE = (1 << 0) } |
Functions | |
static int | action_bridge (struct mansession *s, const struct message *m) |
static int | adsi_announce_park (struct ast_channel *chan, char *parkingexten) |
AST_APP_OPTIONS (bridge_exec_options, BEGIN_OPTIONS END_OPTIONS) | |
int | ast_autoanswer_login (struct ast_channel *chan, void *data) |
int | ast_bridge_call (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config) |
Bridge a call, optionally allowing redirection. | |
static void * | ast_bridge_call_thread (void *data) |
static void | ast_bridge_call_thread_launch (void *data) |
static int | ast_feature_interpret (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense) |
static struct ast_channel * | ast_feature_request_and_dial (struct ast_channel *caller, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, const char *language) |
ast_channel * | ast_get_holded_call (char *uniqueid) |
int | ast_hold_call (struct ast_channel *chan, struct ast_channel *peer) |
static | AST_LIST_HEAD_STATIC (feature_list, ast_call_feature) |
int | ast_masq_autoanswer_login (struct ast_channel *rchan, void *data) |
int | ast_masq_hold_call (struct ast_channel *rchan, struct ast_channel *peer) |
int | ast_masq_park_call (struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout) |
Park a call via a masqueraded channel. | |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS,"Call Features Resource",.load=load_module,.unload=unload_module,.reload=reload,) | |
AST_MUTEX_DEFINE_STATIC (holding_lock) | |
AST_MUTEX_DEFINE_STATIC (parking_lock) | |
AST_MUTEX_DEFINE_STATIC (autoanswer_lock) | |
int | ast_park_call (struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout) |
Park a call and read back parked location. | |
char * | ast_parking_ext (void) |
Determine system parking extension Returns the call parking extension for drivers that provide special call parking help. | |
int | ast_pickup_call (struct ast_channel *chan) |
Pickup a call. | |
char * | ast_pickup_ext (void) |
Determine system call pickup extension. | |
void | ast_register_feature (struct ast_call_feature *feature) |
register new feature into feature_set | |
int | ast_retrieve_call (struct ast_channel *chan, char *uniqueid) |
int | ast_retrieve_call_to_death (char *uniqueid) |
AST_RWLOCK_DEFINE_STATIC (features_lock) | |
void | ast_unregister_feature (struct ast_call_feature *feature) |
unregister feature from feature_set | |
static void | ast_unregister_features (void) |
Remove all features in the list. | |
static int | autoanswer_exec (struct ast_channel *chan, void *data) |
static int | autoanswer_login_exec (struct ast_channel *chan, void *data) |
static void | autoanswer_reregister_extensions (void) |
static int | bridge_exec (struct ast_channel *chan, void *data) |
static int | builtin_atxfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) |
static int | builtin_automonitor (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) |
static int | builtin_blindtransfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) |
static int | builtin_disconnect (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) |
static int | builtin_parkcall (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) |
support routing for one touch call parking | |
static int | check_compat (struct ast_channel *c, struct ast_channel *newchan) |
static void | check_goto_on_transfer (struct ast_channel *chan) |
static void * | do_autoanswer_thread (void *ignore) |
static void | do_bridge_masquerade (struct ast_channel *chan, struct ast_channel *tmpchan) |
static void * | do_holding_thread (void *ignore) |
static void * | do_parking_thread (void *ignore) |
Take care of parked calls and unpark them if needed. | |
static int | feature_exec_app (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) |
exec an app by feature | |
static struct ast_call_feature * | find_dynamic_feature (const char *name) |
find a feature by name | |
static int | finishup (struct ast_channel *chan) |
static int | handle_autoanswer (int fd, int argc, char *argv[]) |
static int | handle_parkedcalls (int fd, int argc, char *argv[]) |
static int | handle_showfeatures (int fd, int argc, char *argv[]) |
static int | load_config (void) |
static int | load_module (void) |
static int | manager_park (struct mansession *s, const struct message *m) |
static int | manager_parking_status (struct mansession *s, const struct message *m) |
Dump lot status. | |
static int | metermaidstate (const char *data) |
metermaids callback from devicestate.c | |
static void | notify_metermaids (char *exten, char *context) |
Notify metermaids that we've changed an extension. | |
static void | park_add_hints (char *context, int start, int stop) |
Add parking hints for all defined parking lots. | |
static int | park_call_exec (struct ast_channel *chan, void *data) |
Park a call. | |
static int | park_call_full (struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, char *orig_chan_name) |
static int | park_exec (struct ast_channel *chan, void *data) |
Pickup parked call. | |
static void | post_manager_event (const char *s, char *parkingexten, struct ast_channel *chan) |
static const char * | real_ctx (struct ast_channel *transferer, struct ast_channel *transferee) |
Find the context for the transfer. | |
static int | reload (void) |
static int | remap_feature (const char *name, const char *value) |
static int | retrieve_call_exec (struct ast_channel *chan, void *data) |
static void | set_c_e_p (struct ast_channel *chan, const char *context, const char *ext, int pri) |
store context, priority and extension | |
static void | set_config_flags (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config) |
static void | set_peers (struct ast_channel **caller, struct ast_channel **callee, struct ast_channel *peer, struct ast_channel *chan, int sense) |
set caller and callee according to the direction | |
static int | unload_module (void) |
static void | unmap_features (void) |
Variables | |
static struct aauser * | aalot |
static int | adsipark |
static char * | app_bridge = "Bridge" |
static int | atxfernoanswertimeout |
static char * | autoanswer = "Autoanswer" |
static pthread_t | autoanswer_thread |
static char * | autoanswerlogin = "AutoanswerLogin" |
static char * | bridge_descrip |
static char * | bridge_synopsis = "Bridge two channels" |
static struct ast_call_feature | builtin_features [] |
static struct ast_cli_entry | cli_features [] |
static struct ast_cli_entry | cli_show_features_deprecated |
static char | courtesytone [256] |
static char * | descrip |
static char * | descrip2 |
static char * | descrip3 |
static char * | descrip4 |
static int | featuredigittimeout |
static char * | holdedcall = "HoldedCall" |
static pthread_t | holding_thread |
static struct holdeduser * | holdlist |
static char | mandescr_bridge [] |
static char | mandescr_park [] |
static struct ast_app * | monitor_app = NULL |
static int | monitor_ok = 1 |
static int | parkaddhints = 0 |
static char * | parkcall = "Park" |
static char * | parkedcall = "ParkedCall" |
static int | parkedplay = 0 |
static int | parkfindnext |
static char | parking_con [AST_MAX_EXTENSION] |
static char | parking_con_dial [AST_MAX_EXTENSION] |
static char | parking_ext [AST_MAX_EXTENSION] |
static int | parking_offset |
static int | parking_start |
static int | parking_stop |
static pthread_t | parking_thread |
static struct parkeduser * | parkinglot |
static int | parkingtime = DEFAULT_PARK_TIME |
static char | parkmohclass [MAX_MUSICCLASS] |
static char | pickup_ext [AST_MAX_EXTENSION] |
static char * | registrar = "res_features" |
static char | showautoanswer_help [] |
static char | showfeatures_help [] |
static char | showparked_help [] |
static char * | synopsis = "Answer a parked call" |
static char * | synopsis2 = "Park yourself" |
static char * | synopsis3 = "Log in for autoanswer" |
static char * | synopsis4 = "Autoanswer a call" |
static int | transferdigittimeout |
static char | xferfailsound [256] |
static char | xfersound [256] |
Definition in file res_features.c.
#define AST_MAX_WATCHERS 256 |
Definition at line 76 of file res_features.c.
#define DEFAULT_FEATURE_DIGIT_TIMEOUT 500 |
#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000 |
#define DEFAULT_PARK_TIME 45000 |
Definition at line 71 of file res_features.c.
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 |
#define FEATURE_RETURN_HANGUP -1 |
#define FEATURE_RETURN_KEEPTRYING 24 |
Definition at line 578 of file res_features.c.
Referenced by ast_feature_interpret(), and feature_exec_app().
#define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER |
#define FEATURE_RETURN_PASSDIGITS 21 |
Definition at line 575 of file res_features.c.
Referenced by ast_bridge_call(), and ast_feature_interpret().
#define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE |
#define FEATURE_RETURN_STOREDIGITS 22 |
#define FEATURE_RETURN_SUCCESS 23 |
Definition at line 577 of file res_features.c.
Referenced by ast_bridge_call(), builtin_atxfer(), builtin_automonitor(), builtin_blindtransfer(), and feature_exec_app().
#define FEATURE_RETURN_SUCCESSBREAK 0 |
#define FEATURE_SENSE_CHAN (1 << 0) |
Definition at line 580 of file res_features.c.
Referenced by ast_bridge_call(), ast_feature_interpret(), builtin_parkcall(), and feature_exec_app().
#define FEATURE_SENSE_PEER (1 << 1) |
#define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0])) |
Definition at line 989 of file res_features.c.
Referenced by ast_feature_interpret(), ast_feature_request_and_dial(), handle_showfeatures(), remap_feature(), set_config_flags(), and unmap_features().
anonymous enum |
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 78 of file res_features.c.
00078 { 00079 AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0), 00080 AST_FEATURE_FLAG_ONPEER = (1 << 1), 00081 AST_FEATURE_FLAG_ONSELF = (1 << 2), 00082 AST_FEATURE_FLAG_BYCALLEE = (1 << 3), 00083 AST_FEATURE_FLAG_BYCALLER = (1 << 4), 00084 AST_FEATURE_FLAG_BYBOTH = (3 << 3), 00085 };
anonymous enum |
Definition at line 3370 of file res_features.c.
03370 { 03371 BRIDGE_OPT_PLAYTONE = (1 << 0), 03372 };
static int action_bridge | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 2438 of file res_features.c.
References ast_channel::_state, ast_answer(), ast_bridge_call_thread_launch(), ast_calloc, ast_channel_alloc(), ast_channel_free(), ast_channel_make_compatible(), ast_get_channel_by_name_prefix_locked(), ast_hangup(), ast_log(), ast_mutex_unlock(), AST_STATE_DOWN, AST_STATE_UP, ast_streamfile(), ast_strlen_zero(), ast_true(), ast_waitstream(), astman_get_header(), astman_send_ack(), astman_send_error(), do_bridge_masquerade(), errno, ast_channel::lock, LOG_WARNING, playtone(), s, and xfersound.
Referenced by load_module().
02439 { 02440 const char *channela = astman_get_header(m, "Channel1"); 02441 const char *channelb = astman_get_header(m, "Channel2"); 02442 const char *playtone = astman_get_header(m, "Tone"); 02443 struct ast_channel *chana = NULL, *chanb = NULL; 02444 struct ast_channel *tmpchana = NULL, *tmpchanb = NULL; 02445 struct ast_bridge_thread_obj *tobj = NULL; 02446 02447 /* make sure valid channels were specified */ 02448 if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) { 02449 chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela)); 02450 chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb)); 02451 if (chana) 02452 ast_mutex_unlock(&chana->lock); 02453 if (chanb) 02454 ast_mutex_unlock(&chanb->lock); 02455 02456 /* send errors if any of the channels could not be found/locked */ 02457 if (!chana) { 02458 char buf[256]; 02459 snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela); 02460 astman_send_error(s, m, buf); 02461 return 0; 02462 } 02463 if (!chanb) { 02464 char buf[256]; 02465 snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb); 02466 astman_send_error(s, m, buf); 02467 return 0; 02468 } 02469 } else { 02470 astman_send_error(s, m, "Missing channel parameter in request"); 02471 return 0; 02472 } 02473 02474 /* Answer the channels if needed */ 02475 if (chana->_state != AST_STATE_UP) 02476 ast_answer(chana); 02477 if (chanb->_state != AST_STATE_UP) 02478 ast_answer(chanb); 02479 02480 /* create the placeholder channels and grab the other channels */ 02481 if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 02482 NULL, NULL, 0, "Bridge/%s", chana->name))) { 02483 astman_send_error(s, m, "Unable to create temporary channel!"); 02484 return 1; 02485 } 02486 02487 if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 02488 NULL, NULL, 0, "Bridge/%s", chanb->name))) { 02489 astman_send_error(s, m, "Unable to create temporary channels!"); 02490 ast_channel_free(tmpchana); 02491 return 1; 02492 } 02493 02494 do_bridge_masquerade(chana, tmpchana); 02495 do_bridge_masquerade(chanb, tmpchanb); 02496 02497 /* make the channels compatible, send error if we fail doing so */ 02498 if (ast_channel_make_compatible(tmpchana, tmpchanb)) { 02499 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name); 02500 astman_send_error(s, m, "Could not make channels compatible for manager bridge"); 02501 ast_hangup(tmpchana); 02502 ast_hangup(tmpchanb); 02503 return 1; 02504 } 02505 02506 /* setup the bridge thread object and start the bridge */ 02507 if (!(tobj = ast_calloc(1, sizeof(*tobj)))) { 02508 ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno)); 02509 astman_send_error(s, m, "Unable to spawn a new bridge thread"); 02510 ast_hangup(tmpchana); 02511 ast_hangup(tmpchanb); 02512 return 1; 02513 } 02514 02515 tobj->chan = tmpchana; 02516 tobj->peer = tmpchanb; 02517 tobj->return_to_pbx = 1; 02518 02519 if (ast_true(playtone)) { 02520 if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) { 02521 if (ast_waitstream(tmpchanb, "") < 0) 02522 ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", tmpchanb->name); 02523 } 02524 } 02525 02526 ast_bridge_call_thread_launch(tobj); 02527 02528 astman_send_ack(s, m, "Launched bridge thread with success"); 02529 02530 return 0; 02531 }
static int adsi_announce_park | ( | struct ast_channel * | chan, | |
char * | parkingexten | |||
) | [static] |
Definition at line 345 of file res_features.c.
References ADSI_JUST_CENT, ast_adsi_load_session(), ast_adsi_print(), and justify.
Referenced by park_call_full().
00346 { 00347 int res; 00348 int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT}; 00349 char tmp[256]; 00350 char *message[5] = {NULL, NULL, NULL, NULL, NULL}; 00351 00352 snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten); 00353 message[0] = tmp; 00354 res = ast_adsi_load_session(chan, NULL, 0, 1); 00355 if (res == -1) 00356 return res; 00357 return ast_adsi_print(chan, message, justify, 1); 00358 }
AST_APP_OPTIONS | ( | bridge_exec_options | , | |
BEGIN_OPTIONS | END_OPTIONS | |||
) |
int ast_autoanswer_login | ( | struct ast_channel * | chan, | |
void * | data | |||
) |
Definition at line 2758 of file res_features.c.
References ast_channel::_state, aalot, ast_channel::appl, ast_add_extension2(), ast_answer(), ast_context_create(), ast_context_find(), ast_hangup(), ast_log(), AST_MAX_EXTENSION, ast_moh_start(), ast_mutex_lock(), ast_mutex_unlock(), AST_STATE_UP, ast_strdupa, ast_verbose(), autoanswer, autoanswer_thread, aauser::chan, aauser::context, ast_channel::data, EVENT_FLAG_CALL, aauser::exten, exten, free, LOG_ERROR, LOG_NOTICE, LOG_WARNING, malloc, manager_event(), aauser::next, option_verbose, registrar, s, strdup, strsep(), and VERBOSE_PREFIX_2.
Referenced by ast_masq_autoanswer_login(), and autoanswer_exec().
02759 { 02760 /* We put the user in the parking list, then wake up the parking thread to be sure it looks 02761 after these channels too */ 02762 struct ast_context *con; 02763 char exten[AST_MAX_EXTENSION]; 02764 struct aauser *pu,*pl = NULL; 02765 char *s, *stringp, *aacontext, *aaexten = NULL; 02766 02767 s = ast_strdupa((void *) data); 02768 stringp=s; 02769 aacontext = strsep(&stringp, "|"); 02770 aaexten = strsep(&stringp, "|"); 02771 if (!aaexten) { 02772 aaexten = aacontext; 02773 aacontext = NULL; 02774 } 02775 if (!aaexten) { 02776 ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n"); 02777 return -1; 02778 } else { 02779 if (!aacontext) { 02780 aacontext = "default"; 02781 } 02782 } 02783 02784 ast_mutex_lock(&autoanswer_lock); 02785 pu = aalot; 02786 while(pu) { 02787 if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){ 02788 if (pl) 02789 pl->next = pu->next; 02790 else 02791 aalot = pu->next; 02792 break; 02793 } 02794 pl = pu; 02795 pu = pu->next; 02796 } 02797 ast_mutex_unlock(&autoanswer_lock); 02798 if (pu) { 02799 ast_log(LOG_NOTICE, "Logout old Channel %s for %s@%s.\n",pu->chan->name, pu->exten, pu->context); 02800 manager_event(EVENT_FLAG_CALL, "AutoanswerLogout", 02801 "Channel: %s\r\n" 02802 "Uniqueid: %s\r\n" 02803 "Context: %s\r\n" 02804 "Exten: %s\r\n" 02805 ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); 02806 ast_hangup(pu->chan); 02807 free(pu); 02808 } 02809 pu = malloc(sizeof(struct aauser)); 02810 if (pu) { 02811 memset(pu, 0, sizeof(pu)); 02812 ast_mutex_lock(&autoanswer_lock); 02813 chan->appl = "Autoanswer"; 02814 chan->data = NULL; 02815 02816 pu->chan = chan; 02817 if (chan->_state != AST_STATE_UP) { 02818 ast_answer(chan); 02819 } 02820 02821 /* Start music on hold */ 02822 ast_moh_start(pu->chan, NULL, NULL); 02823 gettimeofday(&pu->start, NULL); 02824 strncpy(pu->exten, aaexten, sizeof(pu->exten)-1); 02825 strncpy(pu->context, aacontext, sizeof(pu->exten)-1); 02826 pu->next = aalot; 02827 aalot = pu; 02828 con = ast_context_find(aacontext); 02829 if (!con) { 02830 con = ast_context_create(NULL,aacontext, registrar); 02831 if (!con) { 02832 ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", aacontext); 02833 } 02834 } 02835 if (con) { 02836 snprintf(exten, sizeof(exten), "%s", aaexten); 02837 ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)data), free, registrar); 02838 } 02839 02840 ast_mutex_unlock(&autoanswer_lock); 02841 /* Wake up the (presumably select()ing) thread */ 02842 pthread_kill(autoanswer_thread, SIGURG); 02843 if (option_verbose > 1) 02844 ast_verbose(VERBOSE_PREFIX_2 "Autoanswer login from %s for %s@%s.\n", pu->chan->name, pu->exten, pu->context); 02845 manager_event(EVENT_FLAG_CALL, "AutoanswerLogin", 02846 "Channel: %s\r\n" 02847 "Uniqueid: %s\r\n" 02848 "Context: %s\r\n" 02849 "Exten: %s\r\n" 02850 ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); 02851 02852 return 0; 02853 } else { 02854 ast_log(LOG_WARNING, "Out of memory\n"); 02855 return -1; 02856 } 02857 return 0; 02858 }
int ast_bridge_call | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config | |||
) |
Bridge a call, optionally allowing redirection.
append the event to featurecode. we rely on the string being zero-filled, and not overflowing it.
Definition at line 1437 of file res_features.c.
References ast_channel::appl, ast_answer(), ast_cdr_alloc(), ast_cdr_appenduserfield(), ast_cdr_discard(), AST_CDR_FLAG_LOCKED, ast_cdr_init(), ast_cdr_merge(), ast_cdr_setdestchan(), ast_cdr_setuserfield(), ast_cdr_start(), ast_channel_bridge(), ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OPTION, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_dtmf_stream(), ast_feature_interpret(), AST_FEATURE_PLAY_WARNING, AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_DTMF_BEGIN, ast_frfree, ast_indicate(), ast_indicate_data(), ast_log(), AST_OPTION_FLAG_REQUEST, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_channel::cdr, ast_cdr::channel, config, ast_option_header::data, ast_channel::data, ast_cdr::dstchannel, f, FEATURE_MAX_LEN, FEATURE_RETURN_PASSDIGITS, FEATURE_RETURN_SUCCESS, FEATURE_SENSE_CHAN, FEATURE_SENSE_PEER, ast_option_header::flag, free, LOG_DEBUG, LOG_WARNING, monitor_app, ast_option_header::option, option_debug, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), set_config_flags(), and ast_cdr::userfield.
Referenced by app_exec(), ast_bridge_call_thread(), ast_retrieve_call(), autoanswer_exec(), bridge_exec(), builtin_atxfer(), park_exec(), and try_calling().
01438 { 01439 /* Copy voice back and forth between the two channels. Give the peer 01440 the ability to transfer calls with '#<extension' syntax. */ 01441 struct ast_frame *f; 01442 struct ast_channel *who; 01443 char chan_featurecode[FEATURE_MAX_LEN + 1]=""; 01444 char peer_featurecode[FEATURE_MAX_LEN + 1]=""; 01445 int res; 01446 int diff; 01447 int hasfeatures=0; 01448 int hadfeatures=0; 01449 struct ast_option_header *aoh; 01450 struct ast_bridge_config backup_config; 01451 struct ast_cdr *bridge_cdr; 01452 01453 memset(&backup_config, 0, sizeof(backup_config)); 01454 01455 config->start_time = ast_tvnow(); 01456 01457 if (chan && peer) { 01458 pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name); 01459 pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name); 01460 } else if (chan) 01461 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL); 01462 01463 if (monitor_ok) { 01464 const char *monitor_exec; 01465 struct ast_channel *src = NULL; 01466 if (!monitor_app) { 01467 if (!(monitor_app = pbx_findapp("Monitor"))) 01468 monitor_ok=0; 01469 } 01470 if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 01471 src = chan; 01472 else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR"))) 01473 src = peer; 01474 if (monitor_app && src) { 01475 char *tmp = ast_strdupa(monitor_exec); 01476 pbx_exec(src, monitor_app, tmp); 01477 } 01478 } 01479 01480 set_config_flags(chan, peer, config); 01481 config->firstpass = 1; 01482 01483 /* Answer if need be */ 01484 if (ast_answer(chan)) 01485 return -1; 01486 peer->appl = "Bridged Call"; 01487 peer->data = chan->name; 01488 01489 /* copy the userfield from the B-leg to A-leg if applicable */ 01490 if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) { 01491 char tmp[256]; 01492 if (!ast_strlen_zero(chan->cdr->userfield)) { 01493 snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield); 01494 ast_cdr_appenduserfield(chan, tmp); 01495 } else 01496 ast_cdr_setuserfield(chan, peer->cdr->userfield); 01497 /* free the peer's cdr without ast_cdr_free complaining */ 01498 free(peer->cdr); 01499 peer->cdr = NULL; 01500 } 01501 01502 for (;;) { 01503 struct ast_channel *other; /* used later */ 01504 01505 res = ast_channel_bridge(chan, peer, config, &f, &who); 01506 01507 if (config->feature_timer) { 01508 /* Update time limit for next pass */ 01509 diff = ast_tvdiff_ms(ast_tvnow(), config->start_time); 01510 config->feature_timer -= diff; 01511 if (hasfeatures) { 01512 /* Running on backup config, meaning a feature might be being 01513 activated, but that's no excuse to keep things going 01514 indefinitely! */ 01515 if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) { 01516 if (option_debug) 01517 ast_log(LOG_DEBUG, "Timed out, realtime this time!\n"); 01518 config->feature_timer = 0; 01519 who = chan; 01520 if (f) 01521 ast_frfree(f); 01522 f = NULL; 01523 res = 0; 01524 } else if (config->feature_timer <= 0) { 01525 /* Not *really* out of time, just out of time for 01526 digits to come in for features. */ 01527 if (option_debug) 01528 ast_log(LOG_DEBUG, "Timed out for feature!\n"); 01529 if (!ast_strlen_zero(peer_featurecode)) { 01530 ast_dtmf_stream(chan, peer, peer_featurecode, 0); 01531 memset(peer_featurecode, 0, sizeof(peer_featurecode)); 01532 } 01533 if (!ast_strlen_zero(chan_featurecode)) { 01534 ast_dtmf_stream(peer, chan, chan_featurecode, 0); 01535 memset(chan_featurecode, 0, sizeof(chan_featurecode)); 01536 } 01537 if (f) 01538 ast_frfree(f); 01539 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode); 01540 if (!hasfeatures) { 01541 /* Restore original (possibly time modified) bridge config */ 01542 memcpy(config, &backup_config, sizeof(struct ast_bridge_config)); 01543 memset(&backup_config, 0, sizeof(backup_config)); 01544 } 01545 hadfeatures = hasfeatures; 01546 /* Continue as we were */ 01547 continue; 01548 } else if (!f) { 01549 /* The bridge returned without a frame and there is a feature in progress. 01550 * However, we don't think the feature has quite yet timed out, so just 01551 * go back into the bridge. */ 01552 continue; 01553 } 01554 } else { 01555 if (config->feature_timer <=0) { 01556 /* We ran out of time */ 01557 config->feature_timer = 0; 01558 who = chan; 01559 if (f) 01560 ast_frfree(f); 01561 f = NULL; 01562 res = 0; 01563 } 01564 } 01565 } 01566 if (res < 0) { 01567 if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer)) 01568 ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name); 01569 return -1; 01570 } 01571 01572 if (!f || (f->frametype == AST_FRAME_CONTROL && 01573 (f->subclass == AST_CONTROL_HANGUP || f->subclass == AST_CONTROL_BUSY || 01574 f->subclass == AST_CONTROL_CONGESTION ) ) ) { 01575 res = -1; 01576 break; 01577 } 01578 /* many things should be sent to the 'other' channel */ 01579 other = (who == chan) ? peer : chan; 01580 if (f->frametype == AST_FRAME_CONTROL) { 01581 switch (f->subclass) { 01582 case AST_CONTROL_RINGING: 01583 case AST_CONTROL_FLASH: 01584 case -1: 01585 ast_indicate(other, f->subclass); 01586 break; 01587 case AST_CONTROL_HOLD: 01588 case AST_CONTROL_UNHOLD: 01589 ast_indicate_data(other, f->subclass, f->data, f->datalen); 01590 break; 01591 case AST_CONTROL_OPTION: 01592 aoh = f->data; 01593 /* Forward option Requests */ 01594 if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) { 01595 ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 01596 f->datalen - sizeof(struct ast_option_header), 0); 01597 } 01598 break; 01599 } 01600 } else if (f->frametype == AST_FRAME_DTMF_BEGIN) { 01601 /* eat it */ 01602 } else if (f->frametype == AST_FRAME_DTMF) { 01603 char *featurecode; 01604 int sense; 01605 01606 hadfeatures = hasfeatures; 01607 /* This cannot overrun because the longest feature is one shorter than our buffer */ 01608 if (who == chan) { 01609 sense = FEATURE_SENSE_CHAN; 01610 featurecode = chan_featurecode; 01611 } else { 01612 sense = FEATURE_SENSE_PEER; 01613 featurecode = peer_featurecode; 01614 } 01615 /*! append the event to featurecode. we rely on the string being zero-filled, and 01616 * not overflowing it. 01617 * \todo XXX how do we guarantee the latter ? 01618 */ 01619 featurecode[strlen(featurecode)] = f->subclass; 01620 /* Get rid of the frame before we start doing "stuff" with the channels */ 01621 ast_frfree(f); 01622 f = NULL; 01623 config->feature_timer = backup_config.feature_timer; 01624 res = ast_feature_interpret(chan, peer, config, featurecode, sense); 01625 switch(res) { 01626 case FEATURE_RETURN_PASSDIGITS: 01627 ast_dtmf_stream(other, who, featurecode, 0); 01628 /* Fall through */ 01629 case FEATURE_RETURN_SUCCESS: 01630 memset(featurecode, 0, sizeof(chan_featurecode)); 01631 break; 01632 } 01633 if (res >= FEATURE_RETURN_PASSDIGITS) { 01634 res = 0; 01635 } else 01636 break; 01637 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode); 01638 if (hadfeatures && !hasfeatures) { 01639 /* Restore backup */ 01640 memcpy(config, &backup_config, sizeof(struct ast_bridge_config)); 01641 memset(&backup_config, 0, sizeof(struct ast_bridge_config)); 01642 } else if (hasfeatures) { 01643 if (!hadfeatures) { 01644 /* Backup configuration */ 01645 memcpy(&backup_config, config, sizeof(struct ast_bridge_config)); 01646 /* Setup temporary config options */ 01647 config->play_warning = 0; 01648 ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING); 01649 ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING); 01650 config->warning_freq = 0; 01651 config->warning_sound = NULL; 01652 config->end_sound = NULL; 01653 config->start_sound = NULL; 01654 config->firstpass = 0; 01655 } 01656 config->start_time = ast_tvnow(); 01657 config->feature_timer = featuredigittimeout; 01658 if (option_debug) 01659 ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->feature_timer); 01660 } 01661 } 01662 if (f) 01663 ast_frfree(f); 01664 01665 } 01666 01667 /* arrange the cdrs */ 01668 bridge_cdr = ast_cdr_alloc(); 01669 if (bridge_cdr) { 01670 if (chan->cdr && peer->cdr) { /* both of them? merge */ 01671 ast_channel_lock(chan); /* lock the channel before modifing cdrs */ 01672 ast_cdr_init(bridge_cdr,chan); /* seems more logicaller to use the destination as a base, but, really, it's random */ 01673 ast_cdr_start(bridge_cdr); /* now is the time to start */ 01674 01675 /* absorb the channel cdr */ 01676 ast_cdr_merge(bridge_cdr, chan->cdr); 01677 if (!ast_test_flag(chan->cdr, AST_CDR_FLAG_LOCKED)) 01678 ast_cdr_discard(chan->cdr); /* if locked cdrs are in chan, they are taken over in the merge */ 01679 01680 chan->cdr = NULL; /* remove pointer to freed memory before releasing the lock */ 01681 01682 ast_channel_unlock(chan); 01683 01684 /* absorb the peer cdr */ 01685 ast_channel_lock(peer); 01686 ast_cdr_merge(bridge_cdr, peer->cdr); 01687 if (!ast_test_flag(peer->cdr, AST_CDR_FLAG_LOCKED)) 01688 ast_cdr_discard(peer->cdr); /* if locked cdrs are in peer, they are taken over in the merge */ 01689 01690 peer->cdr = NULL; 01691 ast_channel_unlock(peer); 01692 01693 ast_channel_lock(chan); 01694 chan->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */ 01695 ast_channel_unlock(chan); 01696 01697 } else if (chan->cdr) { 01698 01699 ast_channel_lock(chan); /* Lock before modifying CDR */ 01700 /* take the cdr from the channel - literally */ 01701 ast_cdr_init(bridge_cdr,chan); 01702 /* absorb this data */ 01703 ast_cdr_merge(bridge_cdr, chan->cdr); 01704 if (!ast_test_flag(chan->cdr, AST_CDR_FLAG_LOCKED)) 01705 ast_cdr_discard(chan->cdr); /* if locked cdrs are in chan, they are taken over in the merge */ 01706 chan->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */ 01707 ast_channel_unlock(chan); 01708 } else if (peer->cdr) { 01709 ast_channel_lock(peer); /* Lock before modifying CDR */ 01710 /* take the cdr from the peer - literally */ 01711 ast_cdr_init(bridge_cdr,peer); 01712 /* absorb this data */ 01713 ast_cdr_merge(bridge_cdr, peer->cdr); 01714 if (!ast_test_flag(peer->cdr, AST_CDR_FLAG_LOCKED)) 01715 ast_cdr_discard(peer->cdr); /* if locked cdrs are in chan, they are taken over in the merge */ 01716 peer->cdr = NULL; 01717 peer->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */ 01718 ast_channel_unlock(peer); 01719 } else { 01720 ast_channel_lock(chan); /* Lock before modifying CDR */ 01721 /* make up a new cdr */ 01722 ast_cdr_init(bridge_cdr,chan); /* eh, just pick one of them */ 01723 chan->cdr = bridge_cdr; /* */ 01724 ast_channel_unlock(chan); 01725 } 01726 if (ast_strlen_zero(bridge_cdr->dstchannel)) { 01727 if (strcmp(bridge_cdr->channel, peer->name) != 0) 01728 ast_cdr_setdestchan(bridge_cdr, peer->name); 01729 else 01730 ast_cdr_setdestchan(bridge_cdr, chan->name); 01731 } 01732 } 01733 return res; 01734 }
static void* ast_bridge_call_thread | ( | void * | data | ) | [static] |
Definition at line 285 of file res_features.c.
References ast_channel::appl, ast_bridge_call(), ast_cdr_reset(), ast_cdr_setdestchan(), ast_check_hangup(), ast_hangup(), ast_log(), ast_pbx_start(), AST_PBX_SUCCESS, ast_bridge_thread_obj::bconfig, ast_channel::cdr, ast_bridge_thread_obj::chan, ast_channel::data, free, LOG_VERBOSE, LOG_WARNING, ast_bridge_thread_obj::peer, and ast_bridge_thread_obj::return_to_pbx.
Referenced by ast_bridge_call_thread_launch().
00286 { 00287 struct ast_bridge_thread_obj *tobj = data; 00288 int res; 00289 00290 tobj->chan->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge"; 00291 tobj->chan->data = tobj->peer->name; 00292 tobj->peer->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge"; 00293 tobj->peer->data = tobj->chan->name; 00294 00295 if (tobj->chan->cdr) { 00296 ast_cdr_reset(tobj->chan->cdr, NULL); 00297 ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name); 00298 } 00299 if (tobj->peer->cdr) { 00300 ast_cdr_reset(tobj->peer->cdr, NULL); 00301 ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name); 00302 } 00303 00304 ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig); 00305 00306 if (tobj->return_to_pbx) { 00307 if (!ast_check_hangup(tobj->peer)) { 00308 ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", tobj->peer->name); 00309 res = ast_pbx_start(tobj->peer); 00310 if (res != AST_PBX_SUCCESS) 00311 ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", tobj->peer->name); 00312 } else 00313 ast_hangup(tobj->peer); 00314 if (!ast_check_hangup(tobj->chan)) { 00315 ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", tobj->chan->name); 00316 res = ast_pbx_start(tobj->chan); 00317 if (res != AST_PBX_SUCCESS) 00318 ast_log(LOG_WARNING, "FAILED continuing PBX on chan %s\n", tobj->chan->name); 00319 } else 00320 ast_hangup(tobj->chan); 00321 } else { 00322 ast_hangup(tobj->chan); 00323 ast_hangup(tobj->peer); 00324 } 00325 00326 free(tobj); 00327 00328 return NULL; 00329 }
static void ast_bridge_call_thread_launch | ( | void * | data | ) | [static] |
Definition at line 331 of file res_features.c.
References ast_bridge_call_thread(), ast_pthread_create, and thread.
Referenced by action_bridge(), and builtin_atxfer().
00332 { 00333 pthread_t thread; 00334 pthread_attr_t attr; 00335 struct sched_param sched; 00336 00337 pthread_attr_init(&attr); 00338 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00339 ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data); 00340 pthread_attr_destroy(&attr); 00341 memset(&sched, 0, sizeof(sched)); 00342 pthread_setschedparam(thread, SCHED_RR, &sched); 00343 }
static int ast_feature_interpret | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
char * | code, | |||
int | sense | |||
) | [static] |
Definition at line 1147 of file res_features.c.
References ast_copy_flags, AST_FLAGS_ALL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_rwlock_rdlock(), ast_rwlock_unlock(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verbose(), builtin_features, config, ast_call_feature::exten, exten, ast_call_feature::feature_mask, FEATURE_RETURN_KEEPTRYING, FEATURE_RETURN_PASSDIGITS, FEATURE_RETURN_STOREDIGITS, FEATURE_SENSE_CHAN, FEATURES_COUNT, find_dynamic_feature(), ast_flags::flags, LOG_DEBUG, ast_call_feature::operation, option_debug, option_verbose, pbx_builtin_getvar_helper(), ast_call_feature::sname, strsep(), and VERBOSE_PREFIX_3.
Referenced by ast_bridge_call().
01148 { 01149 int x; 01150 struct ast_flags features; 01151 int res = FEATURE_RETURN_PASSDIGITS; 01152 struct ast_call_feature *feature; 01153 const char *dynamic_features; 01154 char *tmp, *tok; 01155 01156 if (sense == FEATURE_SENSE_CHAN) { 01157 ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL); 01158 dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"); 01159 } else { 01160 ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL); 01161 dynamic_features = pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"); 01162 } 01163 if (option_debug > 2) 01164 ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d dynamic=%s\n", chan->name, peer->name, code, sense, features.flags, dynamic_features); 01165 01166 ast_rwlock_rdlock(&features_lock); 01167 for (x = 0; x < FEATURES_COUNT; x++) { 01168 if ((ast_test_flag(&features, builtin_features[x].feature_mask)) && 01169 !ast_strlen_zero(builtin_features[x].exten)) { 01170 /* Feature is up for consideration */ 01171 if (!strcmp(builtin_features[x].exten, code)) { 01172 res = builtin_features[x].operation(chan, peer, config, code, sense, NULL); 01173 break; 01174 } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) { 01175 if (res == FEATURE_RETURN_PASSDIGITS) 01176 res = FEATURE_RETURN_STOREDIGITS; 01177 } 01178 } 01179 } 01180 ast_rwlock_unlock(&features_lock); 01181 01182 if (ast_strlen_zero(dynamic_features)) 01183 return res; 01184 01185 tmp = ast_strdupa(dynamic_features); 01186 01187 while ((tok = strsep(&tmp, "#"))) { 01188 AST_LIST_LOCK(&feature_list); 01189 if (!(feature = find_dynamic_feature(tok))) { 01190 AST_LIST_UNLOCK(&feature_list); 01191 continue; 01192 } 01193 01194 /* Feature is up for consideration */ 01195 if (!strcmp(feature->exten, code)) { 01196 if (option_verbose > 2) 01197 ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok); 01198 res = feature->operation(chan, peer, config, code, sense, feature); 01199 if (res != FEATURE_RETURN_KEEPTRYING) { 01200 AST_LIST_UNLOCK(&feature_list); 01201 break; 01202 } 01203 res = FEATURE_RETURN_PASSDIGITS; 01204 } else if (!strncmp(feature->exten, code, strlen(code))) 01205 res = FEATURE_RETURN_STOREDIGITS; 01206 01207 AST_LIST_UNLOCK(&feature_list); 01208 } 01209 01210 return res; 01211 }
static struct ast_channel * ast_feature_request_and_dial | ( | struct ast_channel * | caller, | |
const char * | type, | |||
int | format, | |||
void * | data, | |||
int | timeout, | |||
int * | outstate, | |||
const char * | cid_num, | |||
const char * | cid_name, | |||
const char * | language | |||
) | [static] |
Definition at line 1256 of file res_features.c.
References ast_channel::_softhangup, ast_channel::_state, ast_call(), AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_inherit_variables(), ast_check_hangup(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_TEXT, ast_frfree, ast_hangup(), ast_indicate(), ast_log(), ast_read(), ast_request(), ast_rwlock_rdlock(), ast_rwlock_unlock(), ast_set_callerid(), AST_STATE_UP, ast_string_field_set, ast_verbose(), ast_waitfor_n(), builtin_features, ast_channel::cdr, ast_call_feature::exten, f, FEATURES_COUNT, ast_channel::hangupcause, len, LOG_NOTICE, LOG_WARNING, option_verbose, pbx_builtin_setvar_helper(), and VERBOSE_PREFIX_3.
Referenced by builtin_atxfer().
01257 { 01258 int state = 0; 01259 int cause = 0; 01260 int to; 01261 struct ast_channel *chan; 01262 struct ast_channel *monitor_chans[2]; 01263 struct ast_channel *active_channel; 01264 int res = 0, ready = 0; 01265 01266 if ((chan = ast_request(type, format, data, &cause))) { 01267 ast_set_callerid(chan, cid_num, cid_name, cid_num); 01268 ast_string_field_set(chan, language, language); 01269 ast_channel_inherit_variables(caller, chan); 01270 pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller->name); 01271 if (!chan->cdr) { 01272 chan->cdr=ast_cdr_alloc(); 01273 if (chan->cdr) { 01274 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */ 01275 ast_cdr_start(chan->cdr); 01276 } 01277 } 01278 01279 if (!ast_call(chan, data, timeout)) { 01280 struct timeval started; 01281 int x, len = 0; 01282 char *disconnect_code = NULL, *dialed_code = NULL; 01283 01284 ast_indicate(caller, AST_CONTROL_RINGING); 01285 /* support dialing of the featuremap disconnect code while performing an attended tranfer */ 01286 ast_rwlock_rdlock(&features_lock); 01287 for (x = 0; x < FEATURES_COUNT; x++) { 01288 if (strcasecmp(builtin_features[x].sname, "disconnect")) 01289 continue; 01290 01291 disconnect_code = builtin_features[x].exten; 01292 len = strlen(disconnect_code) + 1; 01293 dialed_code = alloca(len); 01294 memset(dialed_code, 0, len); 01295 break; 01296 } 01297 ast_rwlock_unlock(&features_lock); 01298 x = 0; 01299 started = ast_tvnow(); 01300 to = timeout; 01301 while (!ast_check_hangup(caller) && timeout && (chan->_state != AST_STATE_UP)) { 01302 struct ast_frame *f = NULL; 01303 01304 monitor_chans[0] = caller; 01305 monitor_chans[1] = chan; 01306 active_channel = ast_waitfor_n(monitor_chans, 2, &to); 01307 01308 /* see if the timeout has been violated */ 01309 if(ast_tvdiff_ms(ast_tvnow(), started) > timeout) { 01310 state = AST_CONTROL_UNHOLD; 01311 ast_log(LOG_NOTICE, "We exceeded our AT-timeout\n"); 01312 break; /*doh! timeout*/ 01313 } 01314 01315 if (!active_channel) 01316 continue; 01317 01318 if (chan && (chan == active_channel)){ 01319 f = ast_read(chan); 01320 if (f == NULL) { /*doh! where'd he go?*/ 01321 state = AST_CONTROL_HANGUP; 01322 res = 0; 01323 break; 01324 } 01325 01326 if (f->frametype == AST_FRAME_CONTROL || f->frametype == AST_FRAME_DTMF || f->frametype == AST_FRAME_TEXT) { 01327 if (f->subclass == AST_CONTROL_RINGING) { 01328 state = f->subclass; 01329 if (option_verbose > 2) 01330 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", chan->name); 01331 ast_indicate(caller, AST_CONTROL_RINGING); 01332 } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) { 01333 state = f->subclass; 01334 if (option_verbose > 2) 01335 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", chan->name); 01336 ast_indicate(caller, AST_CONTROL_BUSY); 01337 ast_frfree(f); 01338 f = NULL; 01339 break; 01340 } else if (f->subclass == AST_CONTROL_ANSWER) { 01341 /* This is what we are hoping for */ 01342 state = f->subclass; 01343 ast_frfree(f); 01344 f = NULL; 01345 ready=1; 01346 break; 01347 } else if (f->subclass != -1) { 01348 ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass); 01349 } 01350 /* else who cares */ 01351 } 01352 01353 } else if (caller && (active_channel == caller)) { 01354 f = ast_read(caller); 01355 if (f == NULL) { /*doh! where'd he go?*/ 01356 if (caller->_softhangup && !chan->_softhangup) { 01357 /* make this a blind transfer */ 01358 ready = 1; 01359 break; 01360 } 01361 state = AST_CONTROL_HANGUP; 01362 res = 0; 01363 break; 01364 } 01365 01366 if (f->frametype == AST_FRAME_DTMF) { 01367 dialed_code[x++] = f->subclass; 01368 dialed_code[x] = '\0'; 01369 if (strlen(dialed_code) == len) { 01370 x = 0; 01371 } else if (x && strncmp(dialed_code, disconnect_code, x)) { 01372 x = 0; 01373 dialed_code[x] = '\0'; 01374 } 01375 if (*dialed_code && !strcmp(dialed_code, disconnect_code)) { 01376 /* Caller Canceled the call */ 01377 state = AST_CONTROL_UNHOLD; 01378 ast_frfree(f); 01379 f = NULL; 01380 break; 01381 } 01382 } 01383 } 01384 if (f) 01385 ast_frfree(f); 01386 } /* end while */ 01387 } else 01388 ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data); 01389 } else { 01390 ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); 01391 switch(cause) { 01392 case AST_CAUSE_BUSY: 01393 state = AST_CONTROL_BUSY; 01394 break; 01395 case AST_CAUSE_CONGESTION: 01396 state = AST_CONTROL_CONGESTION; 01397 break; 01398 } 01399 } 01400 01401 ast_indicate(caller, -1); 01402 if (chan && ready) { 01403 if (chan->_state == AST_STATE_UP) 01404 state = AST_CONTROL_ANSWER; 01405 res = 0; 01406 } else if(chan) { 01407 res = -1; 01408 ast_hangup(chan); 01409 chan = NULL; 01410 } else { 01411 res = -1; 01412 } 01413 01414 if (outstate) 01415 *outstate = state; 01416 01417 if (chan && res <= 0) { 01418 if (chan->cdr || (chan->cdr = ast_cdr_alloc())) { 01419 char tmp[256]; 01420 ast_cdr_init(chan->cdr, chan); 01421 snprintf(tmp, 256, "%s/%s", type, (char *)data); 01422 ast_cdr_setapp(chan->cdr,"Dial",tmp); 01423 ast_cdr_update(chan); 01424 ast_cdr_start(chan->cdr); 01425 ast_cdr_end(chan->cdr); 01426 /* If the cause wasn't handled properly */ 01427 if (ast_cdr_disposition(chan->cdr,chan->hangupcause)) 01428 ast_cdr_failed(chan->cdr); 01429 } else { 01430 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n"); 01431 } 01432 } 01433 01434 return chan; 01435 }
struct ast_channel* ast_get_holded_call | ( | char * | uniqueid | ) |
Definition at line 2243 of file res_features.c.
References ast_get_channel_by_uniqueid_locked(), ast_log(), ast_moh_stop(), ast_mutex_lock(), ast_mutex_unlock(), ast_verbose(), free, holdlist, LOG_WARNING, holdeduser::next, option_verbose, holdeduser::uniqueid, and VERBOSE_PREFIX_3.
Referenced by ast_retrieve_call(), and ast_retrieve_call_to_death().
02244 { 02245 int res=-1; 02246 struct ast_channel *peer=NULL; 02247 struct holdeduser *pu, *pl=NULL; 02248 02249 ast_mutex_lock(&holding_lock); 02250 pu = holdlist; 02251 while(pu) { 02252 if (!strncmp(uniqueid,pu->uniqueid,sizeof(pu->uniqueid))) { 02253 if (pl) 02254 pl->next = pu->next; 02255 else 02256 holdlist = pu->next; 02257 break; 02258 } 02259 pl = pu; 02260 pu = pu->next; 02261 } 02262 ast_mutex_unlock(&holding_lock); 02263 if (pu) { 02264 peer = ast_get_channel_by_uniqueid_locked(pu->uniqueid); 02265 free(pu); 02266 if (peer) { 02267 res=0; 02268 if (option_verbose > 2) 02269 ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name); 02270 ast_moh_stop(peer); 02271 return peer; 02272 } else { 02273 if (option_verbose > 2) 02274 ast_verbose(VERBOSE_PREFIX_3 "Could not find channel with uniqueid %s.\n", uniqueid); 02275 return NULL; 02276 } 02277 } else { 02278 ast_log(LOG_WARNING, "Could not find held channel with uniqueid %s to retrieve.\n", uniqueid); 02279 } 02280 return NULL; 02281 }
int ast_hold_call | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer | |||
) |
Definition at line 2101 of file res_features.c.
References ast_channel::appl, ast_log(), ast_moh_start(), ast_mutex_lock(), ast_mutex_unlock(), holdeduser::chan, ast_channel::data, EVENT_FLAG_CALL, holding_thread, holdlist, LOG_WARNING, malloc, and manager_event().
Referenced by ast_masq_hold_call().
02102 { 02103 /* We put the user in the parking list, then wake up the parking thread to be sure it looks 02104 after these channels too */ 02105 struct holdeduser *pu; 02106 pu = malloc(sizeof(struct holdeduser)); 02107 if (pu) { 02108 memset(pu, 0, sizeof(pu)); 02109 ast_mutex_lock(&holding_lock); 02110 chan->appl = "Holded Call"; 02111 chan->data = NULL; 02112 02113 pu->chan = chan; 02114 strncpy(pu->uniqueid, chan->uniqueid, sizeof(pu->uniqueid)); 02115 strncpy(pu->uniqueidpeer, peer->uniqueid, sizeof(pu->uniqueidpeer)); 02116 /* Start music on hold */ 02117 ast_moh_start(pu->chan, NULL, NULL); 02118 gettimeofday(&pu->start, NULL); 02119 pu->next = holdlist; 02120 holdlist = pu; 02121 ast_mutex_unlock(&holding_lock); 02122 /* Wake up the (presumably select()ing) thread */ 02123 pthread_kill(holding_thread, SIGURG); 02124 02125 manager_event(EVENT_FLAG_CALL, "HoldedCall", 02126 "Channel1: %s\r\n" 02127 "Channel2: %s\r\n" 02128 "Uniqueid1: %s\r\n" 02129 "Uniqueid2: %s\r\n" 02130 ,pu->chan->name, peer->name, pu->chan->uniqueid, peer->uniqueid); 02131 02132 } else { 02133 ast_log(LOG_WARNING, "Out of memory\n"); 02134 return -1; 02135 } 02136 return 0; 02137 }
static AST_LIST_HEAD_STATIC | ( | feature_list | , | |
ast_call_feature | ||||
) | [static] |
int ast_masq_autoanswer_login | ( | struct ast_channel * | rchan, | |
void * | data | |||
) |
Definition at line 2712 of file res_features.c.
References ast_autoanswer_login(), ast_channel_alloc(), ast_channel_masquerade(), ast_frfree, ast_log(), ast_read(), AST_STATE_DOWN, ast_string_field_build, ast_channel::context, ast_channel::exten, f, LOG_WARNING, name, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat.
Referenced by autoanswer_login_exec().
02713 { 02714 struct ast_channel *chan; 02715 struct ast_frame *f; 02716 /* Make a new, fake channel that we'll use to masquerade in the real one */ 02717 chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Autoanswer/%s", rchan->name); 02718 if (chan) { 02719 /* Let us keep track of the channel name */ 02720 ast_string_field_build(chan, name, "Autoanswer/%s",rchan->name); 02721 /* Make formats okay */ 02722 chan->readformat = rchan->readformat; 02723 chan->writeformat = rchan->writeformat; 02724 ast_channel_masquerade(chan, rchan); 02725 /* Setup the extensions and such */ 02726 strncpy(chan->context, rchan->context, sizeof(chan->context) - 1); 02727 strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1); 02728 chan->priority = rchan->priority; 02729 /* might be dirty but we want trackable channels */ 02730 ast_string_field_build(chan, uniqueid, "%s",rchan->uniqueid); 02731 /* Make the masq execute */ 02732 f = ast_read(chan); 02733 if (f) 02734 ast_frfree(f); 02735 ast_autoanswer_login(chan, data); 02736 } else { 02737 ast_log(LOG_WARNING, "Unable to create aa channel\n"); 02738 return -1; 02739 } 02740 return 0; 02741 }
int ast_masq_hold_call | ( | struct ast_channel * | rchan, | |
struct ast_channel * | peer | |||
) |
Definition at line 2139 of file res_features.c.
References ast_channel_alloc(), ast_channel_masquerade(), ast_frfree, ast_hold_call(), ast_log(), ast_read(), AST_STATE_DOWN, ast_string_field_build, ast_channel::context, ast_channel::exten, f, LOG_WARNING, name, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat.
02140 { 02141 struct ast_channel *chan; 02142 struct ast_frame *f; 02143 /* Make a new, fake channel that we'll use to masquerade in the real one */ 02144 chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Onhold/%s",rchan->name); 02145 if (chan) { 02146 /* Let us keep track of the channel name */ 02147 ast_string_field_build(chan, name, "Onhold/%s",rchan->name); 02148 /* Make formats okay */ 02149 chan->readformat = rchan->readformat; 02150 chan->writeformat = rchan->writeformat; 02151 ast_channel_masquerade(chan, rchan); 02152 /* Setup the extensions and such */ 02153 strncpy(chan->context, rchan->context, sizeof(chan->context) - 1); 02154 strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1); 02155 chan->priority = rchan->priority; 02156 /* this might be dirty, but we need to preserve the uniqueid */ 02157 ast_string_field_build(chan, uniqueid, "%s",rchan->uniqueid); 02158 /* Make the masq execute */ 02159 f = ast_read(chan); 02160 if (f) 02161 ast_frfree(f); 02162 ast_hold_call(chan, peer); 02163 return -1; 02164 } else { 02165 ast_log(LOG_WARNING, "Unable to create holded channel\n"); 02166 return -1; 02167 } 02168 return 0; 02169 }
int ast_masq_park_call | ( | struct ast_channel * | rchan, | |
struct ast_channel * | host, | |||
int | timeout, | |||
int * | extout | |||
) |
Park a call via a masqueraded channel.
rchan | the real channel to be parked | |
host | the channel to have the parking read to Masquerade the channel rchan into a new, empty channel which is then parked with ast_park_call | |
timeout | is a timeout in milliseconds | |
extout | is a parameter to an int that will hold the parked location, or NULL if you want |
Definition at line 538 of file res_features.c.
References ast_channel::amaflags, ast_channel_alloc(), ast_channel_masquerade(), ast_frfree, ast_log(), ast_read(), AST_STATE_DOWN, ast_strdupa, ast_channel::context, ast_channel::exten, f, LOG_WARNING, park_call_full(), ast_channel::priority, ast_channel::readformat, set_c_e_p(), and ast_channel::writeformat.
Referenced by manager_park(), mgcp_ss(), parkandannounce_exec(), and ss_thread().
00539 { 00540 struct ast_channel *chan; 00541 struct ast_frame *f; 00542 char *orig_chan_name = NULL; 00543 00544 /* Make a new, fake channel that we'll use to masquerade in the real one */ 00545 if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) { 00546 ast_log(LOG_WARNING, "Unable to create parked channel\n"); 00547 return -1; 00548 } 00549 00550 /* Make formats okay */ 00551 chan->readformat = rchan->readformat; 00552 chan->writeformat = rchan->writeformat; 00553 ast_channel_masquerade(chan, rchan); 00554 00555 /* Setup the extensions and such */ 00556 set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority); 00557 00558 /* Make the masq execute */ 00559 f = ast_read(chan); 00560 if (f) 00561 ast_frfree(f); 00562 00563 orig_chan_name = ast_strdupa(chan->name); 00564 00565 park_call_full(chan, peer, timeout, extout, orig_chan_name); 00566 00567 return 0; 00568 }
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_GLOBAL_SYMBOLS | , | |||
"Call Features Resource" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | reload = reload | |||
) |
AST_MUTEX_DEFINE_STATIC | ( | holding_lock | ) |
AST_MUTEX_DEFINE_STATIC | ( | parking_lock | ) |
protects all static variables above
AST_MUTEX_DEFINE_STATIC | ( | autoanswer_lock | ) |
int ast_park_call | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
int | timeout, | |||
int * | extout | |||
) |
Park a call and read back parked location.
Definition at line 533 of file res_features.c.
References park_call_full().
Referenced by builtin_blindtransfer(), builtin_parkcall(), iax_park_thread(), and sip_park_thread().
00534 { 00535 return park_call_full(chan, peer, timeout, extout, NULL); 00536 }
char* ast_parking_ext | ( | void | ) |
Determine system parking extension Returns the call parking extension for drivers that provide special call parking help.
Definition at line 218 of file res_features.c.
Referenced by builtin_blindtransfer(), dp_lookup(), handle_request_refer(), mgcp_ss(), socket_process(), and ss_thread().
00219 { 00220 return parking_ext; 00221 }
int ast_pickup_call | ( | struct ast_channel * | chan | ) |
Pickup a call.
Definition at line 3100 of file res_features.c.
References ast_channel::_state, ast_answer(), ast_channel_masquerade(), ast_channel_unlock, ast_channel_walk_locked(), AST_CONTROL_ANSWER, ast_log(), ast_queue_control(), AST_STATE_RING, AST_STATE_RINGING, ast_channel::callgroup, LOG_DEBUG, LOG_WARNING, option_debug, ast_channel::pbx, and ast_channel::pickupgroup.
Referenced by cb_events(), handle_request_invite(), mgcp_ss(), and ss_thread().
03101 { 03102 struct ast_channel *cur = NULL; 03103 int res = -1; 03104 03105 while ( (cur = ast_channel_walk_locked(cur)) != NULL) { 03106 if (!cur->pbx && 03107 (cur != chan) && 03108 (chan->pickupgroup & cur->callgroup) && 03109 ((cur->_state == AST_STATE_RINGING) || 03110 (cur->_state == AST_STATE_RING))) { 03111 break; 03112 } 03113 ast_channel_unlock(cur); 03114 } 03115 if (cur) { 03116 if (option_debug) 03117 ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name); 03118 res = ast_answer(chan); 03119 if (res) 03120 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name); 03121 res = ast_queue_control(chan, AST_CONTROL_ANSWER); 03122 if (res) 03123 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name); 03124 res = ast_channel_masquerade(cur, chan); 03125 if (res) 03126 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */ 03127 ast_channel_unlock(cur); 03128 } else { 03129 if (option_debug) 03130 ast_log(LOG_DEBUG, "No call pickup possible...\n"); 03131 } 03132 return res; 03133 }
char* ast_pickup_ext | ( | void | ) |
Determine system call pickup extension.
Definition at line 223 of file res_features.c.
Referenced by cb_events(), get_destination(), handle_request_invite(), handle_showfeatures(), mgcp_ss(), and ss_thread().
00224 { 00225 return pickup_ext; 00226 }
void ast_register_feature | ( | struct ast_call_feature * | feature | ) |
register new feature into feature_set
feature | an ast_call_feature object which contains a keysequence and a callback function which is called when this keysequence is pressed during a call. |
Definition at line 1006 of file res_features.c.
References AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_verbose(), LOG_NOTICE, option_verbose, ast_call_feature::sname, and VERBOSE_PREFIX_2.
01007 { 01008 if (!feature) { 01009 ast_log(LOG_NOTICE,"You didn't pass a feature!\n"); 01010 return; 01011 } 01012 01013 AST_LIST_LOCK(&feature_list); 01014 AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry); 01015 AST_LIST_UNLOCK(&feature_list); 01016 01017 if (option_verbose >= 2) 01018 ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname); 01019 }
int ast_retrieve_call | ( | struct ast_channel * | chan, | |
char * | uniqueid | |||
) |
Definition at line 2171 of file res_features.c.
References ast_channel::_state, ast_answer(), ast_bridge_call(), ast_channel_make_compatible(), AST_FEATURE_REDIRECT, ast_get_holded_call(), ast_hangup(), ast_log(), ast_moh_stop(), ast_mutex_unlock(), AST_PBX_NO_HANGUP_PEER, ast_set_flag, AST_STATE_UP, ast_streamfile(), ast_verbose(), ast_waitstream(), config, LOG_WARNING, option_verbose, and VERBOSE_PREFIX_3.
Referenced by retrieve_call_exec().
02172 { 02173 int res=-1, dres=-1; 02174 struct ast_channel *peer=NULL; 02175 struct ast_bridge_config config; 02176 02177 peer = ast_get_holded_call(uniqueid); 02178 02179 /* JK02: it helps to answer the channel if not already up */ 02180 if (chan->_state != AST_STATE_UP) { 02181 ast_answer(chan); 02182 } 02183 02184 if (peer) { 02185 ast_mutex_unlock(&peer->lock); 02186 ast_moh_stop(peer); 02187 res = ast_channel_make_compatible(chan, peer); 02188 if (res < 0) { 02189 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); 02190 ast_hangup(peer); 02191 return -1; 02192 } 02193 /* This runs sorta backwards, since we give the incoming channel control, as if it 02194 were the person called. */ 02195 if (option_verbose > 2) 02196 ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to holded call %s\n", chan->name, peer->name); 02197 02198 memset(&config,0,sizeof(struct ast_bridge_config)); 02199 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); 02200 ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); 02201 config.timelimit = 0; 02202 config.play_warning = 0; 02203 config.warning_freq = 0; 02204 config.warning_sound=NULL; 02205 res = ast_bridge_call(chan,peer,&config); 02206 02207 /* Simulate the PBX hanging up */ 02208 if (res != AST_PBX_NO_HANGUP_PEER) 02209 ast_hangup(peer); 02210 return res; 02211 } else { 02212 /* XXX Play a message XXX */ 02213 dres = ast_streamfile(chan, "pbx-invalidpark", chan->language); 02214 if (!dres) 02215 dres = ast_waitstream(chan, ""); 02216 else { 02217 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name); 02218 dres = 0; 02219 } 02220 } 02221 return res; 02222 }
int ast_retrieve_call_to_death | ( | char * | uniqueid | ) |
Definition at line 2224 of file res_features.c.
References ast_get_holded_call(), ast_hangup(), ast_log(), ast_mutex_unlock(), ast_verbose(), ast_channel::lock, LOG_WARNING, option_verbose, and VERBOSE_PREFIX_3.
02225 { 02226 int res=-1; 02227 struct ast_channel *peer=NULL; 02228 02229 peer = ast_get_holded_call(uniqueid); 02230 02231 if (peer) { 02232 res=0; 02233 if (option_verbose > 2) 02234 ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name); 02235 ast_mutex_unlock(&peer->lock); 02236 ast_hangup(peer); 02237 } else { 02238 ast_log(LOG_WARNING, "Could not find channel with uniqueid %s to retrieve.\n", uniqueid); 02239 } 02240 return res; 02241 }
AST_RWLOCK_DEFINE_STATIC | ( | features_lock | ) |
void ast_unregister_feature | ( | struct ast_call_feature * | feature | ) |
unregister feature from feature_set
feature | the ast_call_feature object which was registered before |
Definition at line 1022 of file res_features.c.
References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, and free.
01023 { 01024 if (!feature) 01025 return; 01026 01027 AST_LIST_LOCK(&feature_list); 01028 AST_LIST_REMOVE(&feature_list,feature,feature_entry); 01029 AST_LIST_UNLOCK(&feature_list); 01030 free(feature); 01031 }
static void ast_unregister_features | ( | void | ) | [static] |
Remove all features in the list.
Definition at line 1034 of file res_features.c.
References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.
01035 { 01036 struct ast_call_feature *feature; 01037 01038 AST_LIST_LOCK(&feature_list); 01039 while ((feature = AST_LIST_REMOVE_HEAD(&feature_list,feature_entry))) 01040 free(feature); 01041 AST_LIST_UNLOCK(&feature_list); 01042 }
static int autoanswer_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 2985 of file res_features.c.
References ast_channel::_state, aalot, ast_answer(), ast_autoanswer_login(), ast_bridge_call(), ast_channel_make_compatible(), AST_FEATURE_REDIRECT, ast_hangup(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_moh_stop(), ast_mutex_lock(), ast_mutex_unlock(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_verbose(), ast_waitstream(), aauser::chan, ast_module_user::chan, config, aauser::context, courtesytone, EVENT_FLAG_CALL, aauser::exten, free, LOG_WARNING, manager_event(), aauser::next, option_verbose, s, strsep(), and VERBOSE_PREFIX_3.
Referenced by load_module().
02986 { 02987 int res=0; 02988 struct ast_channel *peer=NULL; 02989 struct aauser *pu, *pl=NULL; 02990 struct ast_bridge_config config; 02991 char *s, *stringp, *aacontext, *aaexten = NULL; 02992 char datastring[80]; 02993 struct ast_module_user *u; 02994 02995 02996 if (!data) { 02997 ast_log(LOG_WARNING, "Autoanswer requires an argument (extension number)\n"); 02998 return -1; 02999 } 03000 s = ast_strdupa((void *) data); 03001 stringp=s; 03002 aacontext = strsep(&stringp, "|"); 03003 aaexten = strsep(&stringp, "|"); 03004 if (!aaexten) { 03005 aaexten = aacontext; 03006 aacontext = NULL; 03007 } 03008 if (!aaexten) { 03009 ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n"); 03010 return -1; 03011 } else { 03012 if (!aacontext) { 03013 aacontext = "default"; 03014 } 03015 } 03016 03017 u = ast_module_user_add(chan); 03018 ast_mutex_lock(&autoanswer_lock); 03019 pu = aalot; 03020 while(pu) { 03021 if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){ 03022 if (pl) 03023 pl->next = pu->next; 03024 else 03025 aalot = pu->next; 03026 break; 03027 } 03028 pl = pu; 03029 pu = pu->next; 03030 } 03031 ast_mutex_unlock(&autoanswer_lock); 03032 if (pu) { 03033 peer = pu->chan; 03034 free(pu); 03035 pu = NULL; 03036 } 03037 /* JK02: it helps to answer the channel if not already up */ 03038 if (chan->_state != AST_STATE_UP) { 03039 ast_answer(chan); 03040 } 03041 03042 if (peer) { 03043 ast_moh_stop(peer); 03044 /* Play a courtesy beep in the callED channel to prefix the bridge connecting */ 03045 if (!ast_strlen_zero(courtesytone)) { 03046 if (!ast_streamfile(peer, courtesytone, peer->language)) { 03047 if (ast_waitstream(peer, "") < 0) { 03048 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); 03049 ast_hangup(peer); 03050 return -1; 03051 } 03052 } 03053 } 03054 03055 res = ast_channel_make_compatible(chan, peer); 03056 if (res < 0) { 03057 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); 03058 ast_hangup(peer); 03059 return -1; 03060 } 03061 /* This runs sorta backwards, since we give the incoming channel control, as if it 03062 were the person called. */ 03063 if (option_verbose > 2) 03064 ast_verbose(VERBOSE_PREFIX_3 "Channel %s autoanswered %s\n", peer->name, chan->name); 03065 manager_event(EVENT_FLAG_CALL, "Autoanswer", 03066 "Channel: %s\r\n" 03067 "Uniqueid: %s\r\n" 03068 "Channel2: %s\r\n" 03069 "Uniqueid2: %s\r\n" 03070 "Context: %s\r\n" 03071 "Exten: %s\r\n" 03072 ,chan->name, chan->uniqueid, peer->name, peer->uniqueid, aacontext, aaexten); 03073 03074 03075 memset(&config,0,sizeof(struct ast_bridge_config)); 03076 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); 03077 ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); 03078 config.timelimit = 0; 03079 config.play_warning = 0; 03080 config.warning_freq = 0; 03081 config.warning_sound=NULL; 03082 res = ast_bridge_call(chan,peer,&config); 03083 03084 if (option_verbose > 2) 03085 ast_verbose(VERBOSE_PREFIX_3 "returning from bridge %s\n", peer->name); 03086 /* relogin */ 03087 snprintf(datastring, sizeof(datastring) - 1, "%s|%s", aacontext, aaexten); 03088 ast_autoanswer_login(peer, datastring); 03089 return res; 03090 } else { 03091 if (option_verbose > 2) 03092 ast_verbose(VERBOSE_PREFIX_3 "Nobody logged in for autoanswer %s@%s\n", aaexten, aacontext); 03093 res = -1; 03094 } 03095 ast_module_user_remove(u); 03096 return res; 03097 }
static int autoanswer_login_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 2743 of file res_features.c.
References ast_log(), ast_masq_autoanswer_login(), ast_module_user_add, ast_module_user_remove, ast_module_user::chan, and LOG_WARNING.
Referenced by load_module().
02744 { 02745 int res=0; 02746 struct ast_module_user *u; 02747 02748 u = ast_module_user_add(chan); 02749 if (!data) { 02750 ast_log(LOG_WARNING, "AutoanswerLogin requires an argument (extension number)\n"); 02751 return -1; 02752 } 02753 res = ast_masq_autoanswer_login(chan, data); 02754 ast_module_user_remove(u); 02755 return res; 02756 }
static void autoanswer_reregister_extensions | ( | void | ) | [static] |
Definition at line 2860 of file res_features.c.
References aalot, ast_add_extension2(), ast_context_create(), ast_context_find(), ast_log(), AST_MAX_EXTENSION, ast_mutex_lock(), ast_mutex_unlock(), autoanswer, aauser::context, aauser::exten, exten, free, LOG_ERROR, aauser::next, registrar, and strdup.
Referenced by reload().
02861 { 02862 struct aauser *cur; 02863 struct ast_context *con; 02864 char exten[AST_MAX_EXTENSION]; 02865 char args[AST_MAX_EXTENSION]; 02866 02867 ast_mutex_lock(&autoanswer_lock); 02868 02869 cur=aalot; 02870 while(cur) { 02871 con = ast_context_find(cur->context); 02872 if (!con) { 02873 con = ast_context_create(NULL,cur->context, registrar); 02874 if (!con) { 02875 ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", cur->context); 02876 } 02877 } 02878 if (con) { 02879 snprintf(exten, sizeof(exten), "%s", cur->exten); 02880 snprintf(args, sizeof(args), "%s|%s", cur->context, cur->exten); 02881 ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)args), free, registrar); 02882 } 02883 cur = cur->next; 02884 } 02885 02886 ast_mutex_unlock(&autoanswer_lock); 02887 }
static int bridge_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3378 of file res_features.c.
References ast_channel::_state, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_bridge_call(), ast_channel_alloc(), ast_channel_make_compatible(), ast_check_hangup(), AST_DECLARE_APP_ARGS, ast_get_channel_by_name_prefix_locked(), ast_hangup(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_unlock(), ast_pbx_start(), AST_PBX_SUCCESS, AST_STANDARD_APP_ARGS, AST_STATE_DOWN, AST_STATE_UP, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), BRIDGE_OPT_PLAYTONE, ast_channel::context, do_bridge_masquerade(), EVENT_FLAG_CALL, ast_channel::exten, ast_channel::lock, LOG_DEBUG, LOG_WARNING, manager_event(), option_debug, pbx_builtin_setvar_helper(), ast_channel::priority, and xfersound.
Referenced by load_module().
03379 { 03380 struct ast_module_user *u; 03381 struct ast_channel *current_dest_chan, *final_dest_chan; 03382 char *tmp_data = NULL; 03383 struct ast_flags opts = { 0, }; 03384 struct ast_bridge_config bconfig = { { 0, }, }; 03385 03386 AST_DECLARE_APP_ARGS(args, 03387 AST_APP_ARG(dest_chan); 03388 AST_APP_ARG(options); 03389 ); 03390 03391 if (ast_strlen_zero(data)) { 03392 ast_log(LOG_WARNING, "Bridge require at least 1 argument specifying the other end of the bridge\n"); 03393 return -1; 03394 } 03395 03396 u = ast_module_user_add(chan); 03397 03398 tmp_data = ast_strdupa(data); 03399 AST_STANDARD_APP_ARGS(args, tmp_data); 03400 if (!ast_strlen_zero(args.options)) 03401 ast_app_parse_options(bridge_exec_options, &opts, NULL, args.options); 03402 03403 /* avoid bridge with ourselves */ 03404 if (!strncmp(chan->name, args.dest_chan, 03405 strlen(chan->name) < strlen(args.dest_chan) ? 03406 strlen(chan->name) : strlen(args.dest_chan))) { 03407 ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", chan->name); 03408 manager_event(EVENT_FLAG_CALL, "BridgeExec", 03409 "Response: Failed\r\n" 03410 "Reason: Unable to bridge channel to itself\r\n" 03411 "Channel1: %s\r\n" 03412 "Channel2: %s\r\n", 03413 chan->name, args.dest_chan); 03414 pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP"); 03415 ast_module_user_remove(u); 03416 return 0; 03417 } 03418 03419 /* make sure we have a valid end point */ 03420 if (!(current_dest_chan = ast_get_channel_by_name_prefix_locked(args.dest_chan, 03421 strlen(args.dest_chan)))) { 03422 ast_log(LOG_WARNING, "Bridge failed because channel %s does not exists or we " 03423 "cannot get its lock\n", args.dest_chan); 03424 manager_event(EVENT_FLAG_CALL, "BridgeExec", 03425 "Response: Failed\r\n" 03426 "Reason: Cannot grab end point\r\n" 03427 "Channel1: %s\r\n" 03428 "Channel2: %s\r\n", chan->name, args.dest_chan); 03429 pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT"); 03430 ast_module_user_remove(u); 03431 return 0; 03432 } 03433 ast_mutex_unlock(¤t_dest_chan->lock); 03434 03435 /* answer the channel if needed */ 03436 if (current_dest_chan->_state != AST_STATE_UP) 03437 ast_answer(current_dest_chan); 03438 03439 /* try to allocate a place holder where current_dest_chan will be placed */ 03440 if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 03441 NULL, NULL, 0, "Bridge/%s", current_dest_chan->name))) { 03442 ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan); 03443 manager_event(EVENT_FLAG_CALL, "BridgeExec", 03444 "Response: Failed\r\n" 03445 "Reason: cannot create placeholder\r\n" 03446 "Channel1: %s\r\n" 03447 "Channel2: %s\r\n", chan->name, args.dest_chan); 03448 } 03449 do_bridge_masquerade(current_dest_chan, final_dest_chan); 03450 03451 /* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */ 03452 /* try to make compatible, send error if we fail */ 03453 if (ast_channel_make_compatible(chan, final_dest_chan) < 0) { 03454 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, final_dest_chan->name); 03455 manager_event(EVENT_FLAG_CALL, "BridgeExec", 03456 "Response: Failed\r\n" 03457 "Reason: Could not make channels compatible for bridge\r\n" 03458 "Channel1: %s\r\n" 03459 "Channel2: %s\r\n", chan->name, final_dest_chan->name); 03460 ast_hangup(final_dest_chan); /* may be we should return this channel to the PBX? */ 03461 pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE"); 03462 ast_module_user_remove(u); 03463 return 0; 03464 } 03465 03466 /* Report that the bridge will be successfull */ 03467 manager_event(EVENT_FLAG_CALL, "BridgeExec", 03468 "Response: Success\r\n" 03469 "Channel1: %s\r\n" 03470 "Channel2: %s\r\n", chan->name, final_dest_chan->name); 03471 03472 /* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */ 03473 if (ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE) && !ast_strlen_zero(xfersound)) { 03474 if (!ast_streamfile(final_dest_chan, xfersound, final_dest_chan->language)) { 03475 if (ast_waitstream(final_dest_chan, "") < 0) 03476 ast_log(LOG_WARNING, "Failed to play courtesy tone on %s\n", final_dest_chan->name); 03477 } 03478 } 03479 03480 /* do the bridge */ 03481 ast_bridge_call(chan, final_dest_chan, &bconfig); 03482 03483 /* the bridge has ended, set BRIDGERESULT to SUCCESS. If the other channel has not been hung up, return it to the PBX */ 03484 pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS"); 03485 if (!ast_check_hangup(final_dest_chan)) { 03486 if (option_debug) { 03487 ast_log(LOG_DEBUG, "starting new PBX in %s,%s,%d for chan %s\n", 03488 final_dest_chan->context, final_dest_chan->exten, 03489 final_dest_chan->priority, final_dest_chan->name); 03490 } 03491 03492 if (ast_pbx_start(final_dest_chan) != AST_PBX_SUCCESS) { 03493 ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", final_dest_chan->name); 03494 ast_hangup(final_dest_chan); 03495 } else if (option_debug) 03496 ast_log(LOG_DEBUG, "SUCCESS continuing PBX on chan %s\n", final_dest_chan->name); 03497 } else { 03498 if (option_debug) 03499 ast_log(LOG_DEBUG, "hangup chan %s since the other endpoint has hung up\n", final_dest_chan->name); 03500 ast_hangup(final_dest_chan); 03501 } 03502 03503 ast_module_user_remove(u); 03504 03505 return 0; 03506 }
static int builtin_atxfer | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
char * | code, | |||
int | sense, | |||
void * | data | |||
) | [static] |
Definition at line 846 of file res_features.c.
References ast_channel::_softhangup, ast_channel::_state, ast_app_dtget(), ast_autoservice_start(), ast_autoservice_stop(), ast_best_codec(), ast_bridge_call(), ast_bridge_call_thread_launch(), ast_calloc, ast_channel_alloc(), ast_channel_masquerade(), ast_check_hangup(), ast_clear_flag, AST_CONTROL_BUSY, AST_CONTROL_HOLD, AST_CONTROL_UNHOLD, AST_DIGIT_ANY, ast_exists_extension(), ast_explicit_goto(), AST_FEATURE_DISCONNECT, ast_feature_request_and_dial(), AST_FLAGS_ALL, ast_frfree, ast_hangup(), ast_indicate(), ast_log(), ast_read(), ast_set_flag, AST_STATE_DOWN, AST_STATE_UP, ast_stream_and_wait(), ast_waitfordigit(), ast_bridge_thread_obj::bconfig, ast_bridge_thread_obj::chan, check_compat(), ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, config, ast_channel::context, ast_channel::exten, f, FEATURE_RETURN_SUCCESS, ast_bridge_config::features_callee, ast_bridge_config::features_caller, finishup(), LOG_DEBUG, LOG_WARNING, ast_channel::nativeformats, option_debug, ast_bridge_thread_obj::peer, ast_channel::priority, ast_channel::readformat, real_ctx(), set_peers(), ast_channel::visible_indication, and ast_channel::writeformat.
00847 { 00848 struct ast_channel *transferer; 00849 struct ast_channel *transferee; 00850 const char *transferer_real_context; 00851 char xferto[256] = ""; 00852 int res; 00853 int outstate=0; 00854 struct ast_channel *newchan; 00855 struct ast_channel *xferchan; 00856 struct ast_bridge_thread_obj *tobj; 00857 struct ast_bridge_config bconfig; 00858 struct ast_frame *f; 00859 int l; 00860 00861 if (option_debug) 00862 ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense); 00863 set_peers(&transferer, &transferee, peer, chan, sense); 00864 transferer_real_context = real_ctx(transferer, transferee); 00865 /* Start autoservice on chan while we talk to the originator */ 00866 ast_autoservice_start(transferee); 00867 ast_indicate(transferee, AST_CONTROL_HOLD); 00868 00869 /* Transfer */ 00870 res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY); 00871 if (res < 0) { 00872 finishup(transferee); 00873 return res; 00874 } 00875 if (res > 0) /* If they've typed a digit already, handle it */ 00876 xferto[0] = (char) res; 00877 00878 /* this is specific of atxfer */ 00879 res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout); 00880 if (res < 0) { /* hangup, would be 0 for invalid and 1 for valid */ 00881 finishup(transferee); 00882 return res; 00883 } 00884 if (res == 0) { 00885 ast_log(LOG_WARNING, "Did not read data.\n"); 00886 finishup(transferee); 00887 if (ast_stream_and_wait(transferer, "beeperr", transferer->language, "")) 00888 return -1; 00889 return FEATURE_RETURN_SUCCESS; 00890 } 00891 00892 /* valid extension, res == 1 */ 00893 if (!ast_exists_extension(transferer, transferer_real_context, xferto, 1, transferer->cid.cid_num)) { 00894 ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context); 00895 finishup(transferee); 00896 if (ast_stream_and_wait(transferer, "beeperr", transferer->language, "")) 00897 return -1; 00898 return FEATURE_RETURN_SUCCESS; 00899 } 00900 00901 l = strlen(xferto); 00902 snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context); /* append context */ 00903 newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats), 00904 xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, transferer->language); 00905 ast_indicate(transferer, -1); 00906 if (!newchan) { 00907 finishup(transferee); 00908 /* any reason besides user requested cancel and busy triggers the failed sound */ 00909 if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY && 00910 ast_stream_and_wait(transferer, xferfailsound, transferer->language, "")) 00911 return -1; 00912 return FEATURE_RETURN_SUCCESS; 00913 } 00914 00915 if (check_compat(transferer, newchan)) { 00916 /* we do mean transferee here, NOT transferer */ 00917 finishup(transferee); 00918 return -1; 00919 } 00920 memset(&bconfig,0,sizeof(struct ast_bridge_config)); 00921 ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT); 00922 ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT); 00923 res = ast_bridge_call(transferer, newchan, &bconfig); 00924 if (newchan->_softhangup || !transferer->_softhangup) { 00925 ast_hangup(newchan); 00926 if (ast_stream_and_wait(transferer, xfersound, transferer->language, "")) 00927 ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); 00928 finishup(transferee); 00929 transferer->_softhangup = 0; 00930 return FEATURE_RETURN_SUCCESS; 00931 } 00932 00933 if (check_compat(transferee, newchan)) { 00934 finishup(transferee); 00935 return -1; 00936 } 00937 00938 ast_indicate(transferee, AST_CONTROL_UNHOLD); 00939 00940 if ((ast_autoservice_stop(transferee) < 0) 00941 || (ast_waitfordigit(transferee, 100) < 0) 00942 || (ast_waitfordigit(newchan, 100) < 0) 00943 || ast_check_hangup(transferee) 00944 || ast_check_hangup(newchan)) { 00945 ast_hangup(newchan); 00946 return -1; 00947 } 00948 00949 xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name); 00950 if (!xferchan) { 00951 ast_hangup(newchan); 00952 return -1; 00953 } 00954 /* Make formats okay */ 00955 xferchan->visible_indication = transferer->visible_indication; 00956 xferchan->readformat = transferee->readformat; 00957 xferchan->writeformat = transferee->writeformat; 00958 ast_channel_masquerade(xferchan, transferee); 00959 ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority); 00960 xferchan->_state = AST_STATE_UP; 00961 ast_clear_flag(xferchan, AST_FLAGS_ALL); 00962 xferchan->_softhangup = 0; 00963 00964 if ((f = ast_read(xferchan))) 00965 ast_frfree(f); 00966 00967 newchan->_state = AST_STATE_UP; 00968 ast_clear_flag(newchan, AST_FLAGS_ALL); 00969 newchan->_softhangup = 0; 00970 00971 tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj)); 00972 if (!tobj) { 00973 ast_hangup(xferchan); 00974 ast_hangup(newchan); 00975 return -1; 00976 } 00977 tobj->chan = newchan; 00978 tobj->peer = xferchan; 00979 tobj->bconfig = *config; 00980 00981 if (ast_stream_and_wait(newchan, xfersound, newchan->language, "")) 00982 ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); 00983 ast_bridge_call_thread_launch(tobj); 00984 return -1; /* XXX meaning the channel is bridged ? */ 00985 }
static int builtin_automonitor | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
char * | code, | |||
int | sense, | |||
void * | data | |||
) | [static] |
Definition at line 632 of file res_features.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_log(), ast_monitor_stop(), ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_verbose(), ast_channel::cid, ast_callerid::cid_num, FEATURE_RETURN_SUCCESS, len, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::monitor, monitor_app, option_verbose, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), S_OR, set_peers(), and VERBOSE_PREFIX_3.
00633 { 00634 char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL; 00635 int x = 0; 00636 size_t len; 00637 struct ast_channel *caller_chan, *callee_chan; 00638 00639 if (!monitor_ok) { 00640 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n"); 00641 return -1; 00642 } 00643 00644 if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) { 00645 monitor_ok = 0; 00646 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n"); 00647 return -1; 00648 } 00649 00650 set_peers(&caller_chan, &callee_chan, peer, chan, sense); 00651 00652 if (!ast_strlen_zero(courtesytone)) { 00653 if (ast_autoservice_start(callee_chan)) 00654 return -1; 00655 if (ast_stream_and_wait(caller_chan, courtesytone, caller_chan->language, "")) { 00656 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); 00657 ast_autoservice_stop(callee_chan); 00658 return -1; 00659 } 00660 if (ast_autoservice_stop(callee_chan)) 00661 return -1; 00662 } 00663 00664 if (callee_chan->monitor) { 00665 if (option_verbose > 3) 00666 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code); 00667 ast_monitor_stop(callee_chan, 1); 00668 return FEATURE_RETURN_SUCCESS; 00669 } 00670 00671 if (caller_chan && callee_chan) { 00672 const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT"); 00673 const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR"); 00674 00675 if (!touch_format) 00676 touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT"); 00677 00678 if (!touch_monitor) 00679 touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR"); 00680 00681 if (touch_monitor) { 00682 len = strlen(touch_monitor) + 50; 00683 args = alloca(len); 00684 touch_filename = alloca(len); 00685 snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor); 00686 snprintf(args, len, "%s|%s|m", (touch_format) ? touch_format : "wav", touch_filename); 00687 } else { 00688 caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name)); 00689 callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name)); 00690 len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50; 00691 args = alloca(len); 00692 touch_filename = alloca(len); 00693 snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id); 00694 snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename); 00695 } 00696 00697 for( x = 0; x < strlen(args); x++) { 00698 if (args[x] == '/') 00699 args[x] = '-'; 00700 } 00701 00702 if (option_verbose > 3) 00703 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args); 00704 00705 pbx_exec(callee_chan, monitor_app, args); 00706 pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename); 00707 pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename); 00708 00709 return FEATURE_RETURN_SUCCESS; 00710 } 00711 00712 ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n"); 00713 return -1; 00714 }
static int builtin_blindtransfer | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
char * | code, | |||
int | sense, | |||
void * | data | |||
) | [static] |
Definition at line 743 of file res_features.c.
References ast_app_dtget(), ast_async_goto(), ast_autoservice_start(), ast_cdr_alloc(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_setdestchan(), ast_cdr_start(), AST_CONTROL_HOLD, AST_DIGIT_ANY, ast_exists_extension(), ast_indicate(), ast_log(), ast_park_call(), ast_parking_ext(), AST_PBX_KEEPALIVE, AST_PBX_NO_HANGUP_PEER, ast_stopstream(), ast_stream_and_wait(), ast_verbose(), check_goto_on_transfer(), ast_channel::cid, ast_callerid::cid_num, FEATURE_RETURN_SUCCESS, finishup(), LOG_WARNING, option_verbose, ast_channel::pbx, pbx_builtin_setvar_helper(), real_ctx(), set_c_e_p(), set_peers(), VERBOSE_PREFIX_2, and VERBOSE_PREFIX_3.
00744 { 00745 struct ast_channel *transferer; 00746 struct ast_channel *transferee; 00747 const char *transferer_real_context; 00748 char xferto[256]; 00749 int res; 00750 00751 set_peers(&transferer, &transferee, peer, chan, sense); 00752 transferer_real_context = real_ctx(transferer, transferee); 00753 /* Start autoservice on chan while we talk to the originator */ 00754 ast_autoservice_start(transferee); 00755 ast_indicate(transferee, AST_CONTROL_HOLD); 00756 00757 memset(xferto, 0, sizeof(xferto)); 00758 00759 /* Transfer */ 00760 res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY); 00761 if (res < 0) { 00762 finishup(transferee); 00763 return -1; /* error ? */ 00764 } 00765 if (res > 0) /* If they've typed a digit already, handle it */ 00766 xferto[0] = (char) res; 00767 00768 ast_stopstream(transferer); 00769 res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout); 00770 if (res < 0) { /* hangup, would be 0 for invalid and 1 for valid */ 00771 finishup(transferee); 00772 return res; 00773 } 00774 if (!strcmp(xferto, ast_parking_ext())) { 00775 res = finishup(transferee); 00776 if (res) 00777 res = -1; 00778 else if (!ast_park_call(transferee, transferer, 0, NULL)) { /* success */ 00779 /* We return non-zero, but tell the PBX not to hang the channel when 00780 the thread dies -- We have to be careful now though. We are responsible for 00781 hanging up the channel, else it will never be hung up! */ 00782 00783 return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER; 00784 } else { 00785 ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name); 00786 } 00787 /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */ 00788 } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) { 00789 pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", transferee->name); 00790 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name); 00791 res=finishup(transferee); 00792 if (!transferer->cdr) { 00793 transferer->cdr=ast_cdr_alloc(); 00794 if (transferer) { 00795 ast_cdr_init(transferer->cdr, transferer); /* initilize our channel's cdr */ 00796 ast_cdr_start(transferer->cdr); 00797 } 00798 } 00799 if (transferer->cdr) { 00800 ast_cdr_setdestchan(transferer->cdr, transferee->name); 00801 ast_cdr_setapp(transferer->cdr, "BLINDTRANSFER",""); 00802 } 00803 if (!transferee->pbx) { 00804 /* Doh! Use our handy async_goto functions */ 00805 if (option_verbose > 2) 00806 ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n" 00807 ,transferee->name, xferto, transferer_real_context); 00808 if (ast_async_goto(transferee, transferer_real_context, xferto, 1)) 00809 ast_log(LOG_WARNING, "Async goto failed :-(\n"); 00810 res = -1; 00811 } else { 00812 /* Set the channel's new extension, since it exists, using transferer context */ 00813 set_c_e_p(transferee, transferer_real_context, xferto, 0); 00814 } 00815 check_goto_on_transfer(transferer); 00816 return res; 00817 } else { 00818 if (option_verbose > 2) 00819 ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context); 00820 } 00821 if (ast_stream_and_wait(transferer, xferfailsound, transferer->language, AST_DIGIT_ANY) < 0 ) { 00822 finishup(transferee); 00823 return -1; 00824 } 00825 ast_stopstream(transferer); 00826 res = finishup(transferee); 00827 if (res) { 00828 if (option_verbose > 1) 00829 ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name); 00830 return res; 00831 } 00832 return FEATURE_RETURN_SUCCESS; 00833 }
static int builtin_disconnect | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
char * | code, | |||
int | sense, | |||
void * | data | |||
) | [static] |
Definition at line 716 of file res_features.c.
References ast_verbose(), FEATURE_RETURN_HANGUP, option_verbose, and VERBOSE_PREFIX_3.
00717 { 00718 if (option_verbose > 3) 00719 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code); 00720 return FEATURE_RETURN_HANGUP; 00721 }
static int builtin_parkcall | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
char * | code, | |||
int | sense, | |||
void * | data | |||
) | [static] |
support routing for one touch call parking
Definition at line 599 of file res_features.c.
References ast_channel::_state, ast_answer(), ast_module_user_add, ast_module_user_remove, ast_park_call(), AST_PBX_KEEPALIVE, AST_PBX_NO_HANGUP_PEER, ast_safe_sleep(), AST_STATE_UP, ast_module_user::chan, ast_channel::exten, FEATURE_SENSE_CHAN, ast_channel::priority, and set_peers().
00600 { 00601 struct ast_channel *parker; 00602 struct ast_channel *parkee; 00603 int res = 0; 00604 struct ast_module_user *u; 00605 00606 u = ast_module_user_add(chan); 00607 00608 set_peers(&parker, &parkee, peer, chan, sense); 00609 /* Setup the exten/priority to be s/1 since we don't know 00610 where this call should return */ 00611 strcpy(chan->exten, "s"); 00612 chan->priority = 1; 00613 if (chan->_state != AST_STATE_UP) 00614 res = ast_answer(chan); 00615 if (!res) 00616 res = ast_safe_sleep(chan, 1000); 00617 if (!res) 00618 res = ast_park_call(parkee, parker, 0, NULL); 00619 00620 ast_module_user_remove(u); 00621 00622 if (!res) { 00623 if (sense == FEATURE_SENSE_CHAN) 00624 res = AST_PBX_NO_HANGUP_PEER; 00625 else 00626 res = AST_PBX_KEEPALIVE; 00627 } 00628 return res; 00629 00630 }
static int check_compat | ( | struct ast_channel * | c, | |
struct ast_channel * | newchan | |||
) | [static] |
Definition at line 835 of file res_features.c.
References ast_channel_make_compatible(), ast_hangup(), ast_log(), and LOG_WARNING.
Referenced by builtin_atxfer().
00836 { 00837 if (ast_channel_make_compatible(c, newchan) < 0) { 00838 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", 00839 c->name, newchan->name); 00840 ast_hangup(newchan); 00841 return -1; 00842 } 00843 return 0; 00844 }
static void check_goto_on_transfer | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 246 of file res_features.c.
References ast_channel::_softhangup, ast_channel::_state, ast_channel_alloc(), ast_channel_masquerade(), ast_clear_flag, AST_FLAGS_ALL, ast_frfree, ast_hangup(), ast_parseable_goto(), ast_pbx_start(), ast_read(), AST_STATE_DOWN, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), f, pbx_builtin_getvar_helper(), ast_channel::readformat, and ast_channel::writeformat.
Referenced by builtin_blindtransfer().
00247 { 00248 struct ast_channel *xferchan; 00249 const char *val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR"); 00250 char *x, *goto_on_transfer; 00251 struct ast_frame *f; 00252 00253 if (ast_strlen_zero(val)) 00254 return; 00255 00256 goto_on_transfer = ast_strdupa(val); 00257 00258 if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, chan->name))) 00259 return; 00260 00261 for (x = goto_on_transfer; x && *x; x++) { 00262 if (*x == '^') 00263 *x = '|'; 00264 } 00265 /* Make formats okay */ 00266 xferchan->readformat = chan->readformat; 00267 xferchan->writeformat = chan->writeformat; 00268 ast_channel_masquerade(xferchan, chan); 00269 ast_parseable_goto(xferchan, goto_on_transfer); 00270 xferchan->_state = AST_STATE_UP; 00271 ast_clear_flag(xferchan, AST_FLAGS_ALL); 00272 xferchan->_softhangup = 0; 00273 if ((f = ast_read(xferchan))) { 00274 ast_frfree(f); 00275 f = NULL; 00276 ast_pbx_start(xferchan); 00277 } else { 00278 ast_hangup(xferchan); 00279 } 00280 }
static void* do_autoanswer_thread | ( | void * | ignore | ) | [static] |
Definition at line 2888 of file res_features.c.
References aalot, ast_clear_flag, ast_context_find(), ast_context_remove_extension2(), AST_CONTROL_HANGUP, AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, ast_frfree, ast_hangup(), ast_log(), AST_MAX_EXTENSION, AST_MAX_FDS, ast_mutex_lock(), ast_read(), ast_set_flag, ast_verbose(), aauser::chan, aauser::context, EVENT_FLAG_CALL, aauser::exten, exten, f, ast_channel::fdno, ast_channel::fds, free, LOG_WARNING, manager_event(), aauser::next, option_verbose, registrar, aauser::start, and VERBOSE_PREFIX_2.
Referenced by load_module().
02889 { 02890 int ms, tms, max; 02891 struct ast_context *con; 02892 char exten[AST_MAX_EXTENSION]; 02893 struct aauser *pu, *pl, *pt = NULL; 02894 struct timeval tv; 02895 struct ast_frame *f; 02896 int x; 02897 fd_set rfds, efds; 02898 fd_set nrfds, nefds; 02899 FD_ZERO(&rfds); 02900 FD_ZERO(&efds); 02901 for (;;) { 02902 ms = -1; 02903 max = -1; 02904 ast_mutex_lock(&autoanswer_lock); 02905 pl = NULL; 02906 pu = aalot; 02907 gettimeofday(&tv, NULL); 02908 FD_ZERO(&nrfds); 02909 FD_ZERO(&nefds); 02910 while(pu) { 02911 tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000; 02912 for (x=0;x<AST_MAX_FDS;x++) { 02913 if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) { 02914 if (FD_ISSET(pu->chan->fds[x], &efds)) 02915 ast_set_flag(pu->chan, AST_FLAG_EXCEPTION); 02916 else 02917 ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION); 02918 pu->chan->fdno = x; 02919 /* See if they need servicing */ 02920 f = ast_read(pu->chan); 02921 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { 02922 /* There's a problem, hang them up*/ 02923 if (option_verbose > 1) 02924 ast_verbose(VERBOSE_PREFIX_2 "%s logged out of autoanswer app\n", pu->chan->name); 02925 manager_event(EVENT_FLAG_CALL, "AutoanswerLogout", 02926 "Channel: %s\r\n" 02927 "Uniqueid: %s\r\n" 02928 "Context: %s\r\n" 02929 "Exten: %s\r\n" 02930 ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten); 02931 ast_hangup(pu->chan); 02932 con = ast_context_find(pu->context); 02933 if (con) { 02934 snprintf(exten, sizeof(exten), "%s", pu->exten); 02935 if (ast_context_remove_extension2(con, exten, 1, registrar)) 02936 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); 02937 } else { 02938 ast_log(LOG_WARNING, "Whoa, no %s context?\n", pu->exten); 02939 } 02940 /* And take them out of the parking lot */ 02941 if (pl) 02942 pl->next = pu->next; 02943 else 02944 aalot = pu->next; 02945 pt = pu; 02946 pu = pu->next; 02947 free(pt); 02948 break; 02949 } else { 02950 /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ 02951 ast_frfree(f); 02952 goto std; /* XXX Ick: jumping into an else statement??? XXX */ 02953 } 02954 } 02955 } 02956 if (x >= AST_MAX_FDS) { 02957 std: for (x=0;x<AST_MAX_FDS;x++) { 02958 /* Keep this one for next one */ 02959 if (pu->chan->fds[x] > -1) { 02960 FD_SET(pu->chan->fds[x], &nrfds); 02961 FD_SET(pu->chan->fds[x], &nefds); 02962 if (pu->chan->fds[x] > max) 02963 max = pu->chan->fds[x]; 02964 } 02965 } 02966 /* Keep track of our longest wait */ 02967 if ((tms < ms) || (ms < 0)) 02968 ms = tms; 02969 pl = pu; 02970 pu = pu->next; 02971 } 02972 } 02973 ast_mutex_unlock(&autoanswer_lock); 02974 rfds = nrfds; 02975 efds = nefds; 02976 tv.tv_sec = ms / 1000; 02977 tv.tv_usec = (ms % 1000) * 1000; 02978 /* Wait for something to happen */ 02979 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); 02980 pthread_testcancel(); 02981 } 02982 return NULL; /* Never reached */ 02983 }
static void do_bridge_masquerade | ( | struct ast_channel * | chan, | |
struct ast_channel * | tmpchan | |||
) | [static] |
Definition at line 2422 of file res_features.c.
References ast_channel::_state, ast_channel_masquerade(), ast_do_masquerade(), ast_explicit_goto(), ast_moh_stop(), ast_mutex_lock(), ast_mutex_unlock(), ast_setstate(), ast_channel::context, ast_channel::exten, ast_channel::lock, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat.
Referenced by action_bridge(), and bridge_exec().
02423 { 02424 ast_moh_stop(chan); 02425 ast_mutex_lock(&chan->lock); 02426 ast_setstate(tmpchan, chan->_state); 02427 tmpchan->readformat = chan->readformat; 02428 tmpchan->writeformat = chan->writeformat; 02429 ast_channel_masquerade(tmpchan, chan); 02430 ast_mutex_lock(&tmpchan->lock); 02431 ast_do_masquerade(tmpchan); 02432 /* when returning from bridge, the channel will continue at the next priority */ 02433 ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1); 02434 ast_mutex_unlock(&tmpchan->lock); 02435 ast_mutex_unlock(&chan->lock); 02436 }
static void* do_holding_thread | ( | void * | ignore | ) | [static] |
Definition at line 2284 of file res_features.c.
References ast_clear_flag, AST_CONTROL_HANGUP, AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, ast_frfree, ast_hangup(), AST_MAX_FDS, ast_mutex_lock(), ast_read(), ast_set_flag, ast_verbose(), holdeduser::chan, f, ast_channel::fdno, ast_channel::fds, free, holdlist, holdeduser::next, option_verbose, holdeduser::start, and VERBOSE_PREFIX_2.
Referenced by load_module().
02285 { 02286 int ms, tms, max; 02287 struct holdeduser *pu, *pl, *pt = NULL; 02288 struct timeval tv; 02289 struct ast_frame *f; 02290 int x; 02291 fd_set rfds, efds; 02292 fd_set nrfds, nefds; 02293 FD_ZERO(&rfds); 02294 FD_ZERO(&efds); 02295 for (;;) { 02296 ms = -1; 02297 max = -1; 02298 ast_mutex_lock(&holding_lock); 02299 pl = NULL; 02300 pu = holdlist; 02301 gettimeofday(&tv, NULL); 02302 FD_ZERO(&nrfds); 02303 FD_ZERO(&nefds); 02304 while(pu) { 02305 tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000; 02306 for (x=0;x<AST_MAX_FDS;x++) { 02307 if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) { 02308 if (FD_ISSET(pu->chan->fds[x], &efds)) 02309 ast_set_flag(pu->chan, AST_FLAG_EXCEPTION); 02310 else 02311 ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION); 02312 pu->chan->fdno = x; 02313 /* See if they need servicing */ 02314 f = ast_read(pu->chan); 02315 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { 02316 /* There's a problem, hang them up*/ 02317 if (option_verbose > 1) 02318 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being onhold\n", pu->chan->name); 02319 ast_hangup(pu->chan); 02320 /* find the corresponding channel and hang them up too! */ 02321 /* but only if it is not bridged yet! */ 02322 /* And take them out of the parking lot */ 02323 if (pl) 02324 pl->next = pu->next; 02325 else 02326 holdlist = pu->next; 02327 pt = pu; 02328 pu = pu->next; 02329 free(pt); 02330 break; 02331 } else { 02332 /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ 02333 ast_frfree(f); 02334 goto std; /* XXX Ick: jumping into an else statement??? XXX */ 02335 } 02336 } 02337 } 02338 if (x >= AST_MAX_FDS) { 02339 std: for (x=0;x<AST_MAX_FDS;x++) { 02340 /* Keep this one for next one */ 02341 if (pu->chan->fds[x] > -1) { 02342 FD_SET(pu->chan->fds[x], &nrfds); 02343 FD_SET(pu->chan->fds[x], &nefds); 02344 if (pu->chan->fds[x] > max) 02345 max = pu->chan->fds[x]; 02346 } 02347 } 02348 /* Keep track of our longest wait */ 02349 if ((tms < ms) || (ms < 0)) 02350 ms = tms; 02351 pl = pu; 02352 pu = pu->next; 02353 } 02354 } 02355 ast_mutex_unlock(&holding_lock); 02356 rfds = nrfds; 02357 efds = nefds; 02358 tv.tv_sec = ms / 1000; 02359 tv.tv_usec = (ms % 1000) * 1000; 02360 /* Wait for something to happen */ 02361 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); 02362 pthread_testcancel(); 02363 } 02364 return NULL; /* Never reached */ 02365 }
static void* do_parking_thread | ( | void * | ignore | ) | [static] |
Take care of parked calls and unpark them if needed.
Definition at line 1753 of file res_features.c.
References ast_add_extension2(), ast_clear_flag, ast_context_create(), ast_context_find(), ast_context_remove_extension2(), AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_UNHOLD, AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, ast_free, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_EXTENSION, AST_MAX_FDS, ast_mutex_lock(), ast_mutex_unlock(), ast_pbx_start(), ast_read(), ast_select(), ast_set_flag, ast_strdupa, ast_strlen_zero(), ast_verbose(), parkeduser::chan, ast_channel::context, parkeduser::context, ast_channel::exten, parkeduser::exten, f, ast_channel::fds, free, LOG_DEBUG, LOG_ERROR, LOG_WARNING, parkeduser::moh_trys, parkeduser::next, notify_metermaids(), parkeduser::notquiteyet, option_debug, option_verbose, parkeduser::parkingexten, parkinglot, parkeduser::parkingnum, parkeduser::parkingtime, parkeduser::peername, post_manager_event(), ast_channel::priority, parkeduser::priority, S_OR, set_c_e_p(), parkeduser::start, strdup, and VERBOSE_PREFIX_2.
Referenced by load_module().
01754 { 01755 fd_set rfds, efds; /* results from previous select, to be preserved across loops. */ 01756 FD_ZERO(&rfds); 01757 FD_ZERO(&efds); 01758 01759 for (;;) { 01760 struct parkeduser *pu, *pl, *pt = NULL; 01761 int ms = -1; /* select timeout, uninitialized */ 01762 int max = -1; /* max fd, none there yet */ 01763 fd_set nrfds, nefds; /* args for the next select */ 01764 FD_ZERO(&nrfds); 01765 FD_ZERO(&nefds); 01766 01767 ast_mutex_lock(&parking_lock); 01768 pl = NULL; 01769 pu = parkinglot; 01770 /* navigate the list with prev-cur pointers to support removals */ 01771 while (pu) { 01772 struct ast_channel *chan = pu->chan; /* shorthand */ 01773 int tms; /* timeout for this item */ 01774 int x; /* fd index in channel */ 01775 struct ast_context *con; 01776 01777 if (pu->notquiteyet) { /* Pretend this one isn't here yet */ 01778 pl = pu; 01779 pu = pu->next; 01780 continue; 01781 } 01782 tms = ast_tvdiff_ms(ast_tvnow(), pu->start); 01783 if (tms > pu->parkingtime) { 01784 ast_indicate(chan, AST_CONTROL_UNHOLD); 01785 /* Get chan, exten from derived kludge */ 01786 if (pu->peername[0]) { 01787 char *peername = ast_strdupa(pu->peername); 01788 char *cp = strrchr(peername, '-'); 01789 if (cp) 01790 *cp = 0; 01791 con = ast_context_find(parking_con_dial); 01792 if (!con) { 01793 con = ast_context_create(NULL, parking_con_dial, registrar); 01794 if (!con) 01795 ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial); 01796 } 01797 if (con) { 01798 char returnexten[AST_MAX_EXTENSION]; 01799 snprintf(returnexten, sizeof(returnexten), "%s|30|t", peername); 01800 ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), ast_free, registrar); 01801 } 01802 set_c_e_p(chan, parking_con_dial, peername, 1); 01803 } else { 01804 /* They've been waiting too long, send them back to where they came. Theoretically they 01805 should have their original extensions and such, but we copy to be on the safe side */ 01806 set_c_e_p(chan, pu->context, pu->exten, pu->priority); 01807 } 01808 01809 post_manager_event("ParkedCallTimeOut", pu->parkingexten, chan); 01810 01811 if (option_verbose > 1) 01812 ast_verbose(VERBOSE_PREFIX_2 "Timeout for %s parked on %d. Returning to %s,%s,%d\n", chan->name, pu->parkingnum, chan->context, chan->exten, chan->priority); 01813 /* Start up the PBX, or hang them up */ 01814 if (ast_pbx_start(chan)) { 01815 ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name); 01816 ast_hangup(chan); 01817 } 01818 /* And take them out of the parking lot */ 01819 if (pl) 01820 pl->next = pu->next; 01821 else 01822 parkinglot = pu->next; 01823 pt = pu; 01824 pu = pu->next; 01825 con = ast_context_find(parking_con); 01826 if (con) { 01827 if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) 01828 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); 01829 else 01830 notify_metermaids(pt->parkingexten, parking_con); 01831 } else 01832 ast_log(LOG_WARNING, "Whoa, no parking context?\n"); 01833 free(pt); 01834 } else { /* still within parking time, process descriptors */ 01835 for (x = 0; x < AST_MAX_FDS; x++) { 01836 struct ast_frame *f; 01837 01838 if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds))) 01839 continue; /* nothing on this descriptor */ 01840 01841 if (FD_ISSET(chan->fds[x], &efds)) 01842 ast_set_flag(chan, AST_FLAG_EXCEPTION); 01843 else 01844 ast_clear_flag(chan, AST_FLAG_EXCEPTION); 01845 chan->fdno = x; 01846 01847 /* See if they need servicing */ 01848 f = ast_read(chan); 01849 if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) { 01850 if (f) 01851 ast_frfree(f); 01852 post_manager_event("ParkedCallGiveUp", pu->parkingexten, chan); 01853 01854 /* There's a problem, hang them up*/ 01855 if (option_verbose > 1) 01856 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name); 01857 ast_hangup(chan); 01858 /* And take them out of the parking lot */ 01859 if (pl) 01860 pl->next = pu->next; 01861 else 01862 parkinglot = pu->next; 01863 pt = pu; 01864 pu = pu->next; 01865 con = ast_context_find(parking_con); 01866 if (con) { 01867 if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) 01868 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); 01869 else 01870 notify_metermaids(pt->parkingexten, parking_con); 01871 } else 01872 ast_log(LOG_WARNING, "Whoa, no parking context?\n"); 01873 free(pt); 01874 break; 01875 } else { 01876 /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ 01877 ast_frfree(f); 01878 if (pu->moh_trys < 3 && !chan->generatordata) { 01879 if (option_debug) 01880 ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source. Restarting.\n"); 01881 ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 01882 S_OR(parkmohclass, NULL), 01883 !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); 01884 pu->moh_trys++; 01885 } 01886 goto std; /*! \todo XXX Ick: jumping into an else statement??? XXX */ 01887 } 01888 01889 } /* end for */ 01890 if (x >= AST_MAX_FDS) { 01891 std: for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */ 01892 if (chan->fds[x] > -1) { 01893 FD_SET(chan->fds[x], &nrfds); 01894 FD_SET(chan->fds[x], &nefds); 01895 if (chan->fds[x] > max) 01896 max = chan->fds[x]; 01897 } 01898 } 01899 /* Keep track of our shortest wait */ 01900 if (tms < ms || ms < 0) 01901 ms = tms; 01902 pl = pu; 01903 pu = pu->next; 01904 } 01905 } 01906 } /* end while */ 01907 ast_mutex_unlock(&parking_lock); 01908 rfds = nrfds; 01909 efds = nefds; 01910 { 01911 struct timeval tv = ast_samp2tv(ms, 1000); 01912 /* Wait for something to happen */ 01913 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); 01914 } 01915 pthread_testcancel(); 01916 } 01917 return NULL; /* Never reached */ 01918 }
static int feature_exec_app | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
char * | code, | |||
int | sense, | |||
void * | data | |||
) | [static] |
exec an app by feature
Definition at line 1058 of file res_features.c.
References ast_call_feature::app, app, ast_call_feature::app_args, ast_autoservice_start(), ast_autoservice_stop(), AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_FLAG_ONSELF, ast_log(), ast_moh_start(), ast_moh_stop(), AST_PBX_KEEPALIVE, AST_PBX_NO_HANGUP_PEER, ast_strlen_zero(), ast_test_flag, FEATURE_RETURN_KEEPTRYING, FEATURE_RETURN_NO_HANGUP_PEER, FEATURE_RETURN_PBX_KEEPALIVE, FEATURE_RETURN_SUCCESS, FEATURE_RETURN_SUCCESSBREAK, FEATURE_SENSE_CHAN, LOG_NOTICE, LOG_WARNING, ast_call_feature::moh_class, pbx_exec(), and pbx_findapp().
01059 { 01060 struct ast_app *app; 01061 struct ast_call_feature *feature = data; 01062 struct ast_channel *work, *idle; 01063 int res; 01064 01065 if (!feature) { /* shouldn't ever happen! */ 01066 ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n"); 01067 return -1; 01068 } 01069 01070 if (sense == FEATURE_SENSE_CHAN) { 01071 if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER)) 01072 return FEATURE_RETURN_KEEPTRYING; 01073 if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) { 01074 work = chan; 01075 idle = peer; 01076 } else { 01077 work = peer; 01078 idle = chan; 01079 } 01080 } else { 01081 if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE)) 01082 return FEATURE_RETURN_KEEPTRYING; 01083 if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) { 01084 work = peer; 01085 idle = chan; 01086 } else { 01087 work = chan; 01088 idle = peer; 01089 } 01090 } 01091 01092 if (!(app = pbx_findapp(feature->app))) { 01093 ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app); 01094 return -2; 01095 } 01096 01097 ast_autoservice_start(idle); 01098 01099 if (!ast_strlen_zero(feature->moh_class)) 01100 ast_moh_start(idle, feature->moh_class, NULL); 01101 01102 res = pbx_exec(work, app, feature->app_args); 01103 01104 if (!ast_strlen_zero(feature->moh_class)) 01105 ast_moh_stop(idle); 01106 01107 ast_autoservice_stop(idle); 01108 01109 if (res == AST_PBX_KEEPALIVE) 01110 return FEATURE_RETURN_PBX_KEEPALIVE; 01111 else if (res == AST_PBX_NO_HANGUP_PEER) 01112 return FEATURE_RETURN_NO_HANGUP_PEER; 01113 else if (res) 01114 return FEATURE_RETURN_SUCCESSBREAK; 01115 01116 return FEATURE_RETURN_SUCCESS; /*! \todo XXX should probably return res */ 01117 }
static struct ast_call_feature* find_dynamic_feature | ( | const char * | name | ) | [static] |
find a feature by name
Definition at line 1045 of file res_features.c.
References AST_LIST_TRAVERSE, and ast_call_feature::sname.
Referenced by ast_feature_interpret(), and set_config_flags().
01046 { 01047 struct ast_call_feature *tmp; 01048 01049 AST_LIST_TRAVERSE(&feature_list, tmp, feature_entry) { 01050 if (!strcasecmp(tmp->sname, name)) 01051 break; 01052 } 01053 01054 return tmp; 01055 }
static int finishup | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 723 of file res_features.c.
References ast_autoservice_stop(), AST_CONTROL_UNHOLD, and ast_indicate().
Referenced by builtin_atxfer(), and builtin_blindtransfer().
00724 { 00725 ast_indicate(chan, AST_CONTROL_UNHOLD); 00726 00727 return ast_autoservice_stop(chan); 00728 }
static int handle_autoanswer | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 2678 of file res_features.c.
References aalot, ast_cli(), ast_mutex_lock(), ast_mutex_unlock(), aauser::chan, aauser::context, aauser::exten, aauser::next, and RESULT_SUCCESS.
02679 { 02680 struct aauser *cur; 02681 02682 ast_cli(fd, "%25s %10s %15s \n", "Channel" 02683 , "Extension", "Context"); 02684 02685 ast_mutex_lock(&autoanswer_lock); 02686 02687 cur=aalot; 02688 while(cur) { 02689 ast_cli(fd, "%25s %10s %15s\n",cur->chan->name, cur->exten, cur->context); 02690 02691 cur = cur->next; 02692 } 02693 02694 ast_mutex_unlock(&autoanswer_lock); 02695 02696 return RESULT_SUCCESS; 02697 }
static int handle_parkedcalls | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 2537 of file res_features.c.
References ast_cli(), ast_mutex_lock(), ast_mutex_unlock(), parkeduser::chan, parkeduser::context, parkeduser::exten, parkeduser::next, parkeduser::parkingexten, parkinglot, parkeduser::parkingtime, parkeduser::priority, RESULT_SUCCESS, and parkeduser::start.
02538 { 02539 struct parkeduser *cur; 02540 int numparked = 0; 02541 02542 ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel" 02543 , "Context", "Extension", "Pri", "Timeout"); 02544 02545 ast_mutex_lock(&parking_lock); 02546 02547 for (cur = parkinglot; cur; cur = cur->next) { 02548 ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n" 02549 ,cur->parkingexten, cur->chan->name, cur->context, cur->exten 02550 ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL)); 02551 02552 numparked++; 02553 } 02554 ast_mutex_unlock(&parking_lock); 02555 ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : ""); 02556 02557 02558 return RESULT_SUCCESS; 02559 }
static int handle_showfeatures | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 2377 of file res_features.c.
References ast_cli(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_pickup_ext(), ast_rwlock_rdlock(), ast_rwlock_unlock(), builtin_features, ast_call_feature::default_exten, ast_call_feature::exten, exten, FEATURES_COUNT, ast_call_feature::fname, format, parking_con, parking_ext, parking_start, parking_stop, RESULT_SUCCESS, and ast_call_feature::sname.
02378 { 02379 int i; 02380 struct ast_call_feature *feature; 02381 char format[] = "%-25s %-7s %-7s\n"; 02382 02383 ast_cli(fd, format, "Builtin Feature", "Default", "Current"); 02384 ast_cli(fd, format, "---------------", "-------", "-------"); 02385 02386 ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */ 02387 02388 ast_rwlock_rdlock(&features_lock); 02389 for (i = 0; i < FEATURES_COUNT; i++) 02390 ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten); 02391 ast_rwlock_unlock(&features_lock); 02392 02393 ast_cli(fd, "\n"); 02394 ast_cli(fd, format, "Dynamic Feature", "Default", "Current"); 02395 ast_cli(fd, format, "---------------", "-------", "-------"); 02396 if (AST_LIST_EMPTY(&feature_list)) 02397 ast_cli(fd, "(none)\n"); 02398 else { 02399 AST_LIST_LOCK(&feature_list); 02400 AST_LIST_TRAVERSE(&feature_list, feature, feature_entry) 02401 ast_cli(fd, format, feature->sname, "no def", feature->exten); 02402 AST_LIST_UNLOCK(&feature_list); 02403 } 02404 ast_cli(fd, "\nCall parking\n"); 02405 ast_cli(fd, "------------\n"); 02406 ast_cli(fd,"%-20s: %s\n", "Parking extension", parking_ext); 02407 ast_cli(fd,"%-20s: %s\n", "Parking context", parking_con); 02408 ast_cli(fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop); 02409 ast_cli(fd,"\n"); 02410 02411 return RESULT_SUCCESS; 02412 }
static int load_config | ( | void | ) | [static] |
Definition at line 3150 of file res_features.c.
References adsipark, ast_config_load(), ast_log(), AST_MAX_EXTENSION, AST_MODULE_LOAD_DECLINE, ast_strlen_zero(), ast_variable_browse(), atxfernoanswertimeout, courtesytone, DEFAULT_FEATURE_DIGIT_TIMEOUT, DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER, DEFAULT_TRANSFER_DIGIT_TIMEOUT, featuredigittimeout, LOG_WARNING, parkaddhints, parkfindnext, parking_con, parking_con_dial, parking_ext, parking_start, parking_stop, parkmohclass, pickup_ext, transferdigittimeout, var, xferfailsound, and xfersound.
03151 { 03152 int start = 0, end = 0; 03153 int res; 03154 struct ast_context *con = NULL; 03155 struct ast_config *cfg = NULL; 03156 struct ast_variable *var = NULL; 03157 char old_parking_ext[AST_MAX_EXTENSION]; 03158 char old_parking_con[AST_MAX_EXTENSION] = ""; 03159 03160 if (!ast_strlen_zero(parking_con)) { 03161 strcpy(old_parking_ext, parking_ext); 03162 strcpy(old_parking_con, parking_con); 03163 } 03164 03165 /* Reset to defaults */ 03166 strcpy(parking_con, "parkedcalls"); 03167 strcpy(parking_con_dial, "park-dial"); 03168 strcpy(parking_ext, "700"); 03169 strcpy(pickup_ext, "*8"); 03170 strcpy(parkmohclass, "default"); 03171 courtesytone[0] = '\0'; 03172 strcpy(xfersound, "beep"); 03173 strcpy(xferfailsound, "pbx-invalid"); 03174 parking_start = 701; 03175 parking_stop = 750; 03176 parkfindnext = 0; 03177 adsipark = 0; 03178 parkaddhints = 0; 03179 03180 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; 03181 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; 03182 atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER; 03183 03184 cfg = ast_config_load("features.conf"); 03185 if (!cfg) { 03186 ast_log(LOG_WARNING,"Could not load features.conf\n"); 03187 return AST_MODULE_LOAD_DECLINE; 03188 } 03189 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { 03190 if (!strcasecmp(var->name, "parkext")) { 03191 ast_copy_string(parking_ext, var->value, sizeof(parking_ext)); 03192 } else if (!strcasecmp(var->name, "context")) { 03193 ast_copy_string(parking_con, var->value, sizeof(parking_con)); 03194 } else if (!strcasecmp(var->name, "parkingtime")) { 03195 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) { 03196 ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value); 03197 parkingtime = DEFAULT_PARK_TIME; 03198 } else 03199 parkingtime = parkingtime * 1000; 03200 } else if (!strcasecmp(var->name, "parkpos")) { 03201 if (sscanf(var->value, "%d-%d", &start, &end) != 2) { 03202 ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno); 03203 } else { 03204 parking_start = start; 03205 parking_stop = end; 03206 } 03207 } else if (!strcasecmp(var->name, "findslot")) { 03208 parkfindnext = (!strcasecmp(var->value, "next")); 03209 } else if (!strcasecmp(var->name, "parkinghints")) { 03210 parkaddhints = ast_true(var->value); 03211 } else if (!strcasecmp(var->name, "adsipark")) { 03212 adsipark = ast_true(var->value); 03213 } else if (!strcasecmp(var->name, "transferdigittimeout")) { 03214 if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) { 03215 ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value); 03216 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; 03217 } else 03218 transferdigittimeout = transferdigittimeout * 1000; 03219 } else if (!strcasecmp(var->name, "featuredigittimeout")) { 03220 if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) { 03221 ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value); 03222 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; 03223 } 03224 } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) { 03225 if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) { 03226 ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value); 03227 atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER; 03228 } else 03229 atxfernoanswertimeout = atxfernoanswertimeout * 1000; 03230 } else if (!strcasecmp(var->name, "courtesytone")) { 03231 ast_copy_string(courtesytone, var->value, sizeof(courtesytone)); 03232 } else if (!strcasecmp(var->name, "parkedplay")) { 03233 if (!strcasecmp(var->value, "both")) 03234 parkedplay = 2; 03235 else if (!strcasecmp(var->value, "parked")) 03236 parkedplay = 1; 03237 else 03238 parkedplay = 0; 03239 } else if (!strcasecmp(var->name, "xfersound")) { 03240 ast_copy_string(xfersound, var->value, sizeof(xfersound)); 03241 } else if (!strcasecmp(var->name, "xferfailsound")) { 03242 ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound)); 03243 } else if (!strcasecmp(var->name, "pickupexten")) { 03244 ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext)); 03245 } else if (!strcasecmp(var->name, "parkedmusicclass")) { 03246 ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass)); 03247 } 03248 } 03249 03250 unmap_features(); 03251 for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) { 03252 if (remap_feature(var->name, var->value)) 03253 ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name); 03254 } 03255 03256 /* Map a key combination to an application*/ 03257 ast_unregister_features(); 03258 for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) { 03259 char *tmp_val = ast_strdupa(var->value); 03260 char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 03261 struct ast_call_feature *feature; 03262 03263 /* strsep() sets the argument to NULL if match not found, and it 03264 * is safe to use it with a NULL argument, so we don't check 03265 * between calls. 03266 */ 03267 exten = strsep(&tmp_val,","); 03268 activatedby = strsep(&tmp_val,","); 03269 app = strsep(&tmp_val,","); 03270 app_args = strsep(&tmp_val,","); 03271 moh_class = strsep(&tmp_val,","); 03272 03273 activateon = strsep(&activatedby, "/"); 03274 03275 /*! \todo XXX var_name or app_args ? */ 03276 if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) { 03277 ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n", 03278 app, exten, activateon, var->name); 03279 continue; 03280 } 03281 03282 AST_LIST_LOCK(&feature_list); 03283 if ((feature = find_dynamic_feature(var->name))) { 03284 AST_LIST_UNLOCK(&feature_list); 03285 ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name); 03286 continue; 03287 } 03288 AST_LIST_UNLOCK(&feature_list); 03289 03290 if (!(feature = ast_calloc(1, sizeof(*feature)))) 03291 continue; 03292 03293 ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN); 03294 ast_copy_string(feature->app, app, FEATURE_APP_LEN); 03295 ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN); 03296 03297 if (app_args) 03298 ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN); 03299 03300 if (moh_class) 03301 ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN); 03302 03303 ast_copy_string(feature->exten, exten, sizeof(feature->exten)); 03304 feature->operation = feature_exec_app; 03305 ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF); 03306 03307 /* Allow caller and calle to be specified for backwards compatability */ 03308 if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller")) 03309 ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF); 03310 else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee")) 03311 ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER); 03312 else { 03313 ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s'," 03314 " must be 'self', or 'peer'\n", var->name); 03315 continue; 03316 } 03317 03318 if (ast_strlen_zero(activatedby)) 03319 ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH); 03320 else if (!strcasecmp(activatedby, "caller")) 03321 ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER); 03322 else if (!strcasecmp(activatedby, "callee")) 03323 ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE); 03324 else if (!strcasecmp(activatedby, "both")) 03325 ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH); 03326 else { 03327 ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s'," 03328 " must be 'caller', or 'callee', or 'both'\n", var->name); 03329 continue; 03330 } 03331 03332 ast_register_feature(feature); 03333 03334 if (option_verbose >= 1) 03335 ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten); 03336 } 03337 ast_config_destroy(cfg); 03338 03339 /* Remove the old parking extension */ 03340 if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) { 03341 if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar)) 03342 notify_metermaids(old_parking_ext, old_parking_con); 03343 if (option_debug) 03344 ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con); 03345 } 03346 03347 if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) { 03348 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); 03349 return -1; 03350 } 03351 res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar); 03352 if (parkaddhints) 03353 park_add_hints(parking_con, parking_start, parking_stop); 03354 if (!res) 03355 notify_metermaids(ast_parking_ext(), parking_con); 03356 return res; 03357 03358 }
static int load_module | ( | void | ) | [static] |
Definition at line 3514 of file res_features.c.
References action_bridge(), ast_cli_register_multiple(), ast_devstate_prov_add(), ast_manager_register, ast_manager_register2(), ast_pthread_create, ast_register_application(), autoanswer, autoanswer_exec(), autoanswer_login_exec(), autoanswer_thread, autoanswerlogin, bridge_exec(), cli_features, descrip, descrip2, descrip3, descrip4, do_autoanswer_thread(), do_holding_thread(), do_parking_thread(), EVENT_FLAG_CALL, EVENT_FLAG_COMMAND, holdedcall, holding_thread, load_config(), manager_park(), manager_parking_status(), mandescr_bridge, mandescr_park, metermaidstate(), park_call_exec(), park_exec(), parkcall, parkedcall, parking_con, parking_ext, parking_thread, retrieve_call_exec(), synopsis, synopsis2, synopsis3, and synopsis4.
03515 { 03516 int res; 03517 03518 ast_register_application(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip); 03519 03520 memset(parking_ext, 0, sizeof(parking_ext)); 03521 memset(parking_con, 0, sizeof(parking_con)); 03522 03523 if ((res = load_config())) 03524 return res; 03525 ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); 03526 ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL); 03527 ast_pthread_create(&holding_thread, NULL, do_holding_thread, NULL); 03528 res = ast_register_application(parkedcall, park_exec, synopsis, descrip); 03529 if (!res) 03530 res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2); 03531 if (!res) { 03532 ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" ); 03533 ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park, 03534 "Park a channel", mandescr_park); 03535 ast_manager_register2("Bridge", EVENT_FLAG_COMMAND, action_bridge, "Bridge two channels already in the PBX", mandescr_bridge); 03536 } 03537 03538 res |= ast_register_application(holdedcall, retrieve_call_exec, synopsis, descrip); 03539 ast_pthread_create(&autoanswer_thread, NULL, do_autoanswer_thread, NULL); 03540 if (!res) 03541 res |= ast_register_application(autoanswerlogin, autoanswer_login_exec, synopsis3, descrip3); 03542 if (!res) 03543 res |= ast_register_application(autoanswer, autoanswer_exec, synopsis4, descrip4); 03544 03545 res |= ast_devstate_prov_add("Park", metermaidstate); 03546 03547 return res; 03548 }
static int manager_park | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 2624 of file res_features.c.
References ast_channel_unlock, ast_get_channel_by_name_locked(), ast_masq_park_call(), ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), and s.
Referenced by load_module().
02625 { 02626 const char *channel = astman_get_header(m, "Channel"); 02627 const char *channel2 = astman_get_header(m, "Channel2"); 02628 const char *timeout = astman_get_header(m, "Timeout"); 02629 char buf[BUFSIZ]; 02630 int to = 0; 02631 int res = 0; 02632 int parkExt = 0; 02633 struct ast_channel *ch1, *ch2; 02634 02635 if (ast_strlen_zero(channel)) { 02636 astman_send_error(s, m, "Channel not specified"); 02637 return 0; 02638 } 02639 02640 if (ast_strlen_zero(channel2)) { 02641 astman_send_error(s, m, "Channel2 not specified"); 02642 return 0; 02643 } 02644 02645 ch1 = ast_get_channel_by_name_locked(channel); 02646 if (!ch1) { 02647 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel); 02648 astman_send_error(s, m, buf); 02649 return 0; 02650 } 02651 02652 ch2 = ast_get_channel_by_name_locked(channel2); 02653 if (!ch2) { 02654 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2); 02655 astman_send_error(s, m, buf); 02656 ast_channel_unlock(ch1); 02657 return 0; 02658 } 02659 02660 if (!ast_strlen_zero(timeout)) { 02661 sscanf(timeout, "%d", &to); 02662 } 02663 02664 res = ast_masq_park_call(ch1, ch2, to, &parkExt); 02665 if (!res) { 02666 ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT); 02667 astman_send_ack(s, m, "Park successful"); 02668 } else { 02669 astman_send_error(s, m, "Park failure"); 02670 } 02671 02672 ast_channel_unlock(ch1); 02673 ast_channel_unlock(ch2); 02674 02675 return 0; 02676 }
static int manager_parking_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Dump lot status.
Definition at line 2576 of file res_features.c.
References ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), parkeduser::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, parkeduser::next, parkinglot, parkeduser::parkingnum, parkeduser::parkingtime, parkeduser::peername, RESULT_SUCCESS, s, S_OR, and parkeduser::start.
Referenced by load_module().
02577 { 02578 struct parkeduser *cur; 02579 const char *id = astman_get_header(m, "ActionID"); 02580 char idText[256] = ""; 02581 02582 if (!ast_strlen_zero(id)) 02583 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 02584 02585 astman_send_ack(s, m, "Parked calls will follow"); 02586 02587 ast_mutex_lock(&parking_lock); 02588 02589 for (cur = parkinglot; cur; cur = cur->next) { 02590 astman_append(s, "Event: ParkedCall\r\n" 02591 "Exten: %d\r\n" 02592 "Channel: %s\r\n" 02593 "From: %s\r\n" 02594 "Timeout: %ld\r\n" 02595 "CallerID: %s\r\n" 02596 "CallerIDName: %s\r\n" 02597 "Uniqueid: %s\r\n\r\n" 02598 "%s" 02599 "\r\n", 02600 cur->parkingnum, cur->chan->name, cur->peername, 02601 (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL), 02602 S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is <unknown> */ 02603 S_OR(cur->chan->cid.cid_name, ""), cur->chan->uniqueid, 02604 idText); 02605 } 02606 02607 astman_append(s, 02608 "Event: ParkedCallsComplete\r\n" 02609 "%s" 02610 "\r\n",idText); 02611 02612 ast_mutex_unlock(&parking_lock); 02613 02614 return RESULT_SUCCESS; 02615 }
static int metermaidstate | ( | const char * | data | ) | [static] |
metermaids callback from devicestate.c
Definition at line 372 of file res_features.c.
References AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, ast_exists_extension(), ast_log(), ast_strdupa, context, exten, LOG_DEBUG, option_debug, and strsep().
Referenced by load_module().
00373 { 00374 int res = AST_DEVICE_INVALID; 00375 char *context = ast_strdupa(data); 00376 char *exten; 00377 00378 exten = strsep(&context, "@"); 00379 if (!context) 00380 return res; 00381 00382 if (option_debug > 3) 00383 ast_log(LOG_DEBUG, "Checking state of exten %s in context %s\n", exten, context); 00384 00385 res = ast_exists_extension(NULL, context, exten, 1, NULL); 00386 00387 if (!res) 00388 return AST_DEVICE_NOT_INUSE; 00389 else 00390 return AST_DEVICE_INUSE; 00391 }
static void notify_metermaids | ( | char * | exten, | |
char * | context | |||
) | [static] |
Notify metermaids that we've changed an extension.
Definition at line 361 of file res_features.c.
References ast_device_state_changed(), ast_log(), LOG_DEBUG, and option_debug.
Referenced by do_parking_thread(), park_call_full(), and park_exec().
00362 { 00363 if (option_debug > 3) 00364 ast_log(LOG_DEBUG, "Notification of state change to metermaids %s@%s\n", exten, context); 00365 00366 /* Send notification to devicestate subsystem */ 00367 ast_device_state_changed("park:%s@%s", exten, context); 00368 return; 00369 }
static void park_add_hints | ( | char * | context, | |
int | start, | |||
int | stop | |||
) | [static] |
Add parking hints for all defined parking lots.
Definition at line 3136 of file res_features.c.
References ast_add_extension(), AST_MAX_EXTENSION, exten, PRIORITY_HINT, and registrar.
03137 { 03138 int numext; 03139 char device[AST_MAX_EXTENSION]; 03140 char exten[10]; 03141 03142 for (numext = start; numext <= stop; numext++) { 03143 snprintf(exten, sizeof(exten), "%d", numext); 03144 snprintf(device, sizeof(device), "park:%s@%s", exten, context); 03145 ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar); 03146 } 03147 }
static int park_call_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Park a call.
Definition at line 1921 of file res_features.c.
References ast_channel::_state, ast_answer(), AST_MAX_EXTENSION, ast_module_user_add, ast_module_user_remove, AST_PBX_KEEPALIVE, ast_safe_sleep(), AST_STATE_UP, ast_strdupa, ast_module_user::chan, ast_channel::exten, orig_exten(), park_call_full(), and ast_channel::priority.
Referenced by load_module().
01922 { 01923 /* Cache the original channel name in case we get masqueraded in the middle 01924 * of a park--it is still theoretically possible for a transfer to happen before 01925 * we get here, but it is _really_ unlikely */ 01926 char *orig_chan_name = ast_strdupa(chan->name); 01927 char orig_exten[AST_MAX_EXTENSION]; 01928 int orig_priority = chan->priority; 01929 01930 /* Data is unused at the moment but could contain a parking 01931 lot context eventually */ 01932 int res = 0; 01933 struct ast_module_user *u; 01934 01935 u = ast_module_user_add(chan); 01936 01937 ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten)); 01938 01939 /* Setup the exten/priority to be s/1 since we don't know 01940 where this call should return */ 01941 strcpy(chan->exten, "s"); 01942 chan->priority = 1; 01943 /* Answer if call is not up */ 01944 if (chan->_state != AST_STATE_UP) 01945 res = ast_answer(chan); 01946 /* Sleep to allow VoIP streams to settle down */ 01947 if (!res) 01948 res = ast_safe_sleep(chan, 1000); 01949 /* Park the call */ 01950 if (!res) { 01951 res = park_call_full(chan, chan, 0, NULL, orig_chan_name); 01952 /* Continue on in the dialplan */ 01953 if (res == 1) { 01954 ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten)); 01955 chan->priority = orig_priority; 01956 res = 0; 01957 } else if (!res) 01958 res = AST_PBX_KEEPALIVE; 01959 } 01960 01961 ast_module_user_remove(u); 01962 01963 return res; 01964 }
static int park_call_full | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
int | timeout, | |||
int * | extout, | |||
char * | orig_chan_name | |||
) | [static] |
Definition at line 393 of file res_features.c.
References adsi_announce_park(), ast_channel::appl, ast_add_extension2(), ast_adsi_available(), ast_adsi_unload_session(), ast_calloc, ast_clear_flag, ast_context_create(), ast_context_find(), AST_CONTROL_HOLD, ast_exists_extension(), AST_FLAG_MASQ_NOSTREAM, ast_free, ast_indicate_data(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_say_digits(), ast_set_flag, ast_strlen_zero(), ast_verbose(), ast_channel::context, ast_channel::data, EVENT_FLAG_CALL, ast_channel::exten, free, LOG_ERROR, LOG_WARNING, ast_channel::macrocontext, ast_channel::macroexten, ast_channel::macropriority, manager_event(), parkeduser::next, notify_metermaids(), parkeduser::notquiteyet, option_verbose, parkinglot, parkeduser::parkingnum, pbx_builtin_getvar_helper(), ast_channel::priority, S_OR, strdup, and VERBOSE_PREFIX_2.
Referenced by ast_masq_park_call(), ast_park_call(), and park_call_exec().
00394 { 00395 struct parkeduser *pu, *cur; 00396 int i, x = -1, parking_range; 00397 struct ast_context *con; 00398 const char *parkingexten; 00399 00400 /* Allocate memory for parking data */ 00401 if (!(pu = ast_calloc(1, sizeof(*pu)))) 00402 return -1; 00403 00404 /* Lock parking lot */ 00405 ast_mutex_lock(&parking_lock); 00406 /* Check for channel variable PARKINGEXTEN */ 00407 parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN"); 00408 if (!ast_strlen_zero(parkingexten)) { 00409 if (ast_exists_extension(NULL, parking_con, parkingexten, 1, NULL)) { 00410 ast_mutex_unlock(&parking_lock); 00411 free(pu); 00412 ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con); 00413 return 1; /* Continue execution if possible */ 00414 } 00415 ast_copy_string(pu->parkingexten, parkingexten, sizeof(pu->parkingexten)); 00416 x = atoi(parkingexten); 00417 } else { 00418 /* Select parking space within range */ 00419 parking_range = parking_stop - parking_start+1; 00420 for (i = 0; i < parking_range; i++) { 00421 x = (i + parking_offset) % parking_range + parking_start; 00422 cur = parkinglot; 00423 while(cur) { 00424 if (cur->parkingnum == x) 00425 break; 00426 cur = cur->next; 00427 } 00428 if (!cur) 00429 break; 00430 } 00431 00432 if (!(i < parking_range)) { 00433 ast_log(LOG_WARNING, "No more parking spaces\n"); 00434 free(pu); 00435 ast_mutex_unlock(&parking_lock); 00436 return -1; 00437 } 00438 /* Set pointer for next parking */ 00439 if (parkfindnext) 00440 parking_offset = x - parking_start + 1; 00441 } 00442 00443 chan->appl = "Parked Call"; 00444 chan->data = NULL; 00445 00446 pu->chan = chan; 00447 00448 /* Put the parked channel on hold if we have two different channels */ 00449 if (chan != peer) { 00450 ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 00451 S_OR(parkmohclass, NULL), 00452 !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); 00453 } 00454 00455 pu->start = ast_tvnow(); 00456 pu->parkingnum = x; 00457 pu->parkingtime = (timeout > 0) ? timeout : parkingtime; 00458 if (extout) 00459 *extout = x; 00460 00461 if (peer) 00462 ast_copy_string(pu->peername, peer->name, sizeof(pu->peername)); 00463 00464 /* Remember what had been dialed, so that if the parking 00465 expires, we try to come back to the same place */ 00466 ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context)); 00467 ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten)); 00468 pu->priority = chan->macropriority ? chan->macropriority : chan->priority; 00469 pu->next = parkinglot; 00470 parkinglot = pu; 00471 00472 /* If parking a channel directly, don't quiet yet get parking running on it */ 00473 if (peer == chan) 00474 pu->notquiteyet = 1; 00475 ast_mutex_unlock(&parking_lock); 00476 /* Wake up the (presumably select()ing) thread */ 00477 pthread_kill(parking_thread, SIGURG); 00478 if (option_verbose > 1) 00479 ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d@%s. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, parking_con, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000)); 00480 00481 if (pu->parkingnum != -1) 00482 snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x); 00483 manager_event(EVENT_FLAG_CALL, "ParkedCall", 00484 "Exten: %s\r\n" 00485 "Channel: %s\r\n" 00486 "From: %s\r\n" 00487 "Timeout: %ld\r\n" 00488 "CallerID: %s\r\n" 00489 "CallerIDName: %s\r\n" 00490 "Uniqueid: %s\r\n", 00491 pu->parkingexten, pu->chan->name, peer ? peer->name : "", 00492 (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL), 00493 S_OR(pu->chan->cid.cid_num, "<unknown>"), 00494 S_OR(pu->chan->cid.cid_name, "<unknown>"), 00495 pu->chan->uniqueid 00496 ); 00497 00498 if (peer && adsipark && ast_adsi_available(peer)) { 00499 adsi_announce_park(peer, pu->parkingexten); /* Only supports parking numbers */ 00500 ast_adsi_unload_session(peer); 00501 } 00502 00503 con = ast_context_find(parking_con); 00504 if (!con) 00505 con = ast_context_create(NULL, parking_con, registrar); 00506 if (!con) /* Still no context? Bad */ 00507 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); 00508 /* Tell the peer channel the number of the parking space */ 00509 if (peer && ((pu->parkingnum != -1 && ast_strlen_zero(orig_chan_name)) || !strcasecmp(peer->name, orig_chan_name))) { /* Only say number if it's a number and the channel hasn't been masqueraded away */ 00510 /* Make sure we don't start saying digits to the channel being parked */ 00511 ast_set_flag(peer, AST_FLAG_MASQ_NOSTREAM); 00512 ast_say_digits(peer, pu->parkingnum, "", peer->language); 00513 ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM); 00514 } 00515 if (con) { 00516 if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), ast_free, registrar)) 00517 notify_metermaids(pu->parkingexten, parking_con); 00518 } 00519 if (pu->notquiteyet) { 00520 /* Wake up parking thread if we're really done */ 00521 ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 00522 S_OR(parkmohclass, NULL), 00523 !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); 00524 pu->notquiteyet = 0; 00525 pthread_kill(parking_thread, SIGURG); 00526 } 00527 return 0; 00528 }
static int park_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Pickup parked call.
Definition at line 1967 of file res_features.c.
References ast_channel::_state, ast_answer(), ast_bridge_call(), ast_cdr_setdestchan(), ast_channel_make_compatible(), ast_context_find(), ast_context_remove_extension2(), AST_CONTROL_UNHOLD, AST_FEATURE_REDIRECT, ast_hangup(), ast_indicate(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), AST_PBX_NO_HANGUP_PEER, ast_set_flag, AST_STATE_UP, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_verbose(), ast_waitstream(), parkeduser::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, config, courtesytone, error(), EVENT_FLAG_CALL, free, LOG_WARNING, manager_event(), parkeduser::next, notify_metermaids(), option_verbose, parkedplay, parking_con, parkeduser::parkingexten, parkinglot, parkeduser::parkingnum, pbx_builtin_setvar_helper(), S_OR, and VERBOSE_PREFIX_3.
Referenced by load_module().
01968 { 01969 int res = 0; 01970 struct ast_module_user *u; 01971 struct ast_channel *peer=NULL; 01972 struct parkeduser *pu, *pl=NULL; 01973 struct ast_context *con; 01974 01975 int park; 01976 struct ast_bridge_config config; 01977 01978 if (!data) { 01979 ast_log(LOG_WARNING, "Parkedcall requires an argument (extension number)\n"); 01980 return -1; 01981 } 01982 01983 u = ast_module_user_add(chan); 01984 01985 park = atoi((char *)data); 01986 ast_mutex_lock(&parking_lock); 01987 pu = parkinglot; 01988 while(pu) { 01989 if (pu->parkingnum == park) { 01990 if (pl) 01991 pl->next = pu->next; 01992 else 01993 parkinglot = pu->next; 01994 break; 01995 } 01996 pl = pu; 01997 pu = pu->next; 01998 } 01999 ast_mutex_unlock(&parking_lock); 02000 if (pu) { 02001 peer = pu->chan; 02002 con = ast_context_find(parking_con); 02003 if (con) { 02004 if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL)) 02005 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); 02006 else 02007 notify_metermaids(pu->parkingexten, parking_con); 02008 } else 02009 ast_log(LOG_WARNING, "Whoa, no parking context?\n"); 02010 02011 manager_event(EVENT_FLAG_CALL, "UnParkedCall", 02012 "Exten: %s\r\n" 02013 "Channel: %s\r\n" 02014 "From: %s\r\n" 02015 "CallerID: %s\r\n" 02016 "CallerIDName: %s\r\n" 02017 "Uniqueid: %s\r\n", 02018 pu->parkingexten, pu->chan->name, chan->name, 02019 S_OR(pu->chan->cid.cid_num, "<unknown>"), 02020 S_OR(pu->chan->cid.cid_name, "<unknown>"), 02021 pu->chan->uniqueid 02022 ); 02023 02024 free(pu); 02025 } 02026 /* JK02: it helps to answer the channel if not already up */ 02027 if (chan->_state != AST_STATE_UP) 02028 ast_answer(chan); 02029 02030 if (peer) { 02031 /* Play a courtesy to the source(s) configured to prefix the bridge connecting */ 02032 02033 if (!ast_strlen_zero(courtesytone)) { 02034 int error = 0; 02035 ast_indicate(peer, AST_CONTROL_UNHOLD); 02036 if (parkedplay == 0) { 02037 error = ast_stream_and_wait(chan, courtesytone, chan->language, ""); 02038 } else if (parkedplay == 1) { 02039 error = ast_stream_and_wait(peer, courtesytone, chan->language, ""); 02040 } else if (parkedplay == 2) { 02041 if (!ast_streamfile(chan, courtesytone, chan->language) && 02042 !ast_streamfile(peer, courtesytone, chan->language)) { 02043 /*! \todo XXX we would like to wait on both! */ 02044 res = ast_waitstream(chan, ""); 02045 if (res >= 0) 02046 res = ast_waitstream(peer, ""); 02047 if (res < 0) 02048 error = 1; 02049 } 02050 } 02051 if (error) { 02052 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); 02053 ast_hangup(peer); 02054 ast_module_user_remove(u); 02055 return -1; 02056 } 02057 } else 02058 ast_indicate(peer, AST_CONTROL_UNHOLD); 02059 02060 res = ast_channel_make_compatible(chan, peer); 02061 if (res < 0) { 02062 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); 02063 ast_hangup(peer); 02064 ast_module_user_remove(u); 02065 return -1; 02066 } 02067 /* This runs sorta backwards, since we give the incoming channel control, as if it 02068 were the person called. */ 02069 if (option_verbose > 2) 02070 ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park); 02071 02072 pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); 02073 ast_cdr_setdestchan(chan->cdr, peer->name); 02074 memset(&config, 0, sizeof(struct ast_bridge_config)); 02075 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); 02076 ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); 02077 res = ast_bridge_call(chan, peer, &config); 02078 02079 pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); 02080 ast_cdr_setdestchan(chan->cdr, peer->name); 02081 02082 /* Simulate the PBX hanging up */ 02083 if (res != AST_PBX_NO_HANGUP_PEER) 02084 ast_hangup(peer); 02085 ast_module_user_remove(u); 02086 return res; 02087 } else { 02088 /*! \todo XXX Play a message XXX */ 02089 if (ast_stream_and_wait(chan, "pbx-invalidpark", chan->language, "")) 02090 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name); 02091 if (option_verbose > 2) 02092 ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park); 02093 res = -1; 02094 } 02095 02096 ast_module_user_remove(u); 02097 02098 return res; 02099 }
static void post_manager_event | ( | const char * | s, | |
char * | parkingexten, | |||
struct ast_channel * | chan | |||
) | [static] |
Definition at line 1736 of file res_features.c.
References ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, EVENT_FLAG_CALL, manager_event(), and S_OR.
Referenced by do_parking_thread().
01737 { 01738 manager_event(EVENT_FLAG_CALL, s, 01739 "Exten: %s\r\n" 01740 "Channel: %s\r\n" 01741 "CallerID: %s\r\n" 01742 "CallerIDName: %s\r\n" 01743 "Uniqueid: %s\r\n\r\n", 01744 parkingexten, 01745 chan->name, 01746 S_OR(chan->cid.cid_num, "<unknown>"), 01747 S_OR(chan->cid.cid_name, "<unknown>"), 01748 chan->uniqueid 01749 ); 01750 }
static const char* real_ctx | ( | struct ast_channel * | transferer, | |
struct ast_channel * | transferee | |||
) | [static] |
Find the context for the transfer.
Definition at line 731 of file res_features.c.
References ast_strlen_zero(), ast_channel::context, ast_channel::macrocontext, pbx_builtin_getvar_helper(), and s.
Referenced by builtin_atxfer(), and builtin_blindtransfer().
00732 { 00733 const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"); 00734 if (ast_strlen_zero(s)) 00735 s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT"); 00736 if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */ 00737 s = transferer->macrocontext; 00738 if (ast_strlen_zero(s)) 00739 s = transferer->context; 00740 return s; 00741 }
static int reload | ( | void | ) | [static] |
Definition at line 3508 of file res_features.c.
References autoanswer_reregister_extensions(), and load_config().
03509 { 03510 autoanswer_reregister_extensions(); 03511 return load_config(); 03512 }
static int remap_feature | ( | const char * | name, | |
const char * | value | |||
) | [static] |
Definition at line 1129 of file res_features.c.
References ast_rwlock_unlock(), ast_rwlock_wrlock(), builtin_features, exten, and FEATURES_COUNT.
01130 { 01131 int x, res = -1; 01132 01133 ast_rwlock_wrlock(&features_lock); 01134 for (x = 0; x < FEATURES_COUNT; x++) { 01135 if (strcasecmp(builtin_features[x].sname, name)) 01136 continue; 01137 01138 ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten)); 01139 res = 0; 01140 break; 01141 } 01142 ast_rwlock_unlock(&features_lock); 01143 01144 return res; 01145 }
static int retrieve_call_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 2367 of file res_features.c.
References ast_module_user_add, ast_module_user_remove, ast_retrieve_call(), and ast_module_user::chan.
Referenced by load_module().
02367 { 02368 int res=0; 02369 struct ast_module_user *u; 02370 char *uniqueid = (char *)data; 02371 u = ast_module_user_add(chan); 02372 res = ast_retrieve_call(chan, uniqueid); 02373 ast_module_user_remove(u); 02374 return res; 02375 }
static void set_c_e_p | ( | struct ast_channel * | chan, | |
const char * | context, | |||
const char * | ext, | |||
int | pri | |||
) | [static] |
store context, priority and extension
Definition at line 239 of file res_features.c.
References ast_channel::context, ast_channel::exten, and ast_channel::priority.
Referenced by ast_masq_park_call(), builtin_blindtransfer(), and do_parking_thread().
00240 { 00241 ast_copy_string(chan->context, context, sizeof(chan->context)); 00242 ast_copy_string(chan->exten, ext, sizeof(chan->exten)); 00243 chan->priority = pri; 00244 }
static void set_config_flags | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config | |||
) | [static] |
Definition at line 1213 of file res_features.c.
References AST_BRIDGE_DTMF_CHANNEL_0, AST_BRIDGE_DTMF_CHANNEL_1, ast_clear_flag, AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_FLAG_NEEDSDTMF, AST_FLAGS_ALL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_rwlock_rdlock(), ast_rwlock_unlock(), ast_set_flag, ast_strdupa, ast_test_flag, builtin_features, config, ast_call_feature::feature_mask, FEATURES_COUNT, find_dynamic_feature(), pbx_builtin_getvar_helper(), and strsep().
Referenced by ast_bridge_call().
01214 { 01215 int x; 01216 01217 ast_clear_flag(config, AST_FLAGS_ALL); 01218 01219 ast_rwlock_rdlock(&features_lock); 01220 for (x = 0; x < FEATURES_COUNT; x++) { 01221 if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF)) 01222 continue; 01223 01224 if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask)) 01225 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0); 01226 01227 if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask)) 01228 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1); 01229 } 01230 ast_rwlock_unlock(&features_lock); 01231 01232 if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) { 01233 const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"); 01234 01235 if (dynamic_features) { 01236 char *tmp = ast_strdupa(dynamic_features); 01237 char *tok; 01238 struct ast_call_feature *feature; 01239 01240 /* while we have a feature */ 01241 while ((tok = strsep(&tmp, "#"))) { 01242 AST_LIST_LOCK(&feature_list); 01243 if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) { 01244 if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER)) 01245 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0); 01246 if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE)) 01247 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1); 01248 } 01249 AST_LIST_UNLOCK(&feature_list); 01250 } 01251 } 01252 } 01253 }
static void set_peers | ( | struct ast_channel ** | caller, | |
struct ast_channel ** | callee, | |||
struct ast_channel * | peer, | |||
struct ast_channel * | chan, | |||
int | sense | |||
) | [static] |
set caller and callee according to the direction
Definition at line 586 of file res_features.c.
References FEATURE_SENSE_PEER.
Referenced by builtin_atxfer(), builtin_automonitor(), builtin_blindtransfer(), and builtin_parkcall().
00588 { 00589 if (sense == FEATURE_SENSE_PEER) { 00590 *caller = peer; 00591 *callee = chan; 00592 } else { 00593 *callee = peer; 00594 *caller = chan; 00595 } 00596 }
static int unload_module | ( | void | ) | [static] |
Definition at line 3551 of file res_features.c.
References ast_cli_unregister_multiple(), ast_devstate_prov_del(), ast_manager_unregister(), ast_module_user_hangup_all, ast_unregister_application(), autoanswer, autoanswerlogin, cli_features, holdedcall, parkcall, and parkedcall.
03552 { 03553 ast_module_user_hangup_all(); 03554 03555 ast_manager_unregister("ParkedCalls"); 03556 ast_manager_unregister("Bridge"); 03557 ast_manager_unregister("Park"); 03558 ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); 03559 ast_unregister_application(parkcall); 03560 ast_unregister_application(app_bridge); 03561 ast_unregister_application(autoanswer); 03562 ast_unregister_application(autoanswerlogin); 03563 ast_unregister_application(holdedcall); 03564 ast_devstate_prov_del("Park"); 03565 return ast_unregister_application(parkedcall); 03566 }
static void unmap_features | ( | void | ) | [static] |
Definition at line 1119 of file res_features.c.
References ast_rwlock_unlock(), ast_rwlock_wrlock(), builtin_features, exten, and FEATURES_COUNT.
01120 { 01121 int x; 01122 01123 ast_rwlock_wrlock(&features_lock); 01124 for (x = 0; x < FEATURES_COUNT; x++) 01125 strcpy(builtin_features[x].exten, builtin_features[x].default_exten); 01126 ast_rwlock_unlock(&features_lock); 01127 }
Definition at line 202 of file res_features.c.
Referenced by ast_autoanswer_login(), autoanswer_exec(), autoanswer_reregister_extensions(), do_autoanswer_thread(), and handle_autoanswer().
int adsipark [static] |
char* app_bridge = "Bridge" [static] |
Definition at line 3360 of file res_features.c.
int atxfernoanswertimeout [static] |
char* autoanswer = "Autoanswer" [static] |
Definition at line 148 of file res_features.c.
pthread_t autoanswer_thread [static] |
Definition at line 204 of file res_features.c.
Referenced by ast_autoanswer_login(), and load_module().
char* autoanswerlogin = "AutoanswerLogin" [static] |
char* bridge_descrip [static] |
Definition at line 3362 of file res_features.c.
char* bridge_synopsis = "Bridge two channels" [static] |
Definition at line 3361 of file res_features.c.
struct ast_call_feature builtin_features[] [static] |
Definition at line 993 of file res_features.c.
Referenced by ast_feature_interpret(), ast_feature_request_and_dial(), handle_showfeatures(), remap_feature(), set_config_flags(), and unmap_features().
struct ast_cli_entry cli_features[] [static] |
Definition at line 2699 of file res_features.c.
struct ast_cli_entry cli_show_features_deprecated [static] |
Initial value:
{ { "show", "features", NULL }, handle_showfeatures, NULL, NULL }
Definition at line 2565 of file res_features.c.
char courtesytone[256] [static] |
Courtesy tone
Definition at line 100 of file res_features.c.
Referenced by autoanswer_exec(), load_config(), and park_exec().
char* descrip [static] |
Initial value:
"ParkedCall(exten):" "Used to connect to a parked call. This application is always\n" "registered internally and does not need to be explicitly added\n" "into the dialplan, although you should include the 'parkedcalls'\n" "context.\n"
Definition at line 120 of file res_features.c.
char* descrip2 [static] |
Definition at line 130 of file res_features.c.
char* descrip3 [static] |
Initial value:
"AutoanswerLogin([context]|exten):" "Used to login to the autoanswer application for an extension.\n"
Definition at line 145 of file res_features.c.
char* descrip4 [static] |
Initial value:
"Autoanswer([context]|exten):" "Used to autoanswer a call for an extension.\n"
Definition at line 152 of file res_features.c.
int featuredigittimeout [static] |
Definition at line 111 of file res_features.c.
char* holdedcall = "HoldedCall" [static] |
pthread_t holding_thread [static] |
struct holdeduser* holdlist [static] |
Definition at line 208 of file res_features.c.
Referenced by ast_get_holded_call(), ast_hold_call(), and do_holding_thread().
char mandescr_bridge[] [static] |
char mandescr_park[] [static] |
struct ast_app* monitor_app = NULL [static] |
Definition at line 155 of file res_features.c.
Referenced by ast_bridge_call(), and builtin_automonitor().
int monitor_ok = 1 [static] |
Definition at line 156 of file res_features.c.
int parkaddhints = 0 [static] |
Add parking hints automatically
Definition at line 90 of file res_features.c.
Referenced by load_config().
char* parkcall = "Park" [static] |
char* parkedcall = "ParkedCall" [static] |
int parkedplay = 0 [static] |
Who to play the courtesy tone to
Definition at line 101 of file res_features.c.
Referenced by park_exec().
int parkfindnext [static] |
char parking_con[AST_MAX_EXTENSION] [static] |
Context for which parking is made accessible
Definition at line 92 of file res_features.c.
Referenced by handle_showfeatures(), load_config(), load_module(), and park_exec().
char parking_con_dial[AST_MAX_EXTENSION] [static] |
Context for dialback for parking (KLUDGE)
Definition at line 93 of file res_features.c.
Referenced by load_config().
char parking_ext[AST_MAX_EXTENSION] [static] |
Extension you type to park the call
Definition at line 94 of file res_features.c.
Referenced by handle_showfeatures(), load_config(), and load_module().
int parking_offset [static] |
Definition at line 105 of file res_features.c.
int parking_start [static] |
First available extension for parking
Definition at line 97 of file res_features.c.
Referenced by handle_showfeatures(), and load_config().
int parking_stop [static] |
Last available extension for parking
Definition at line 98 of file res_features.c.
Referenced by handle_showfeatures(), and load_config().
pthread_t parking_thread [static] |
struct parkeduser* parkinglot [static] |
Definition at line 206 of file res_features.c.
Referenced by do_parking_thread(), handle_parkedcalls(), manager_parking_status(), park_call_full(), and park_exec().
int parkingtime = DEFAULT_PARK_TIME [static] |
No more than 45 seconds parked before you do something with them
Definition at line 91 of file res_features.c.
char parkmohclass[MAX_MUSICCLASS] [static] |
Music class used for parking
Definition at line 96 of file res_features.c.
Referenced by load_config().
char pickup_ext[AST_MAX_EXTENSION] [static] |
char* registrar = "res_features" [static] |
Registrar for operations
Definition at line 115 of file res_features.c.
char showautoanswer_help[] [static] |
Initial value:
"Usage: show autoanswer\n" " Lists currently logged in autoanswer users.\n"
Definition at line 2570 of file res_features.c.
char showfeatures_help[] [static] |
Initial value:
"Usage: feature list\n" " Lists currently configured features.\n"
Definition at line 2533 of file res_features.c.
char showparked_help[] [static] |
Initial value:
"Usage: show parkedcalls\n" " Lists currently parked calls.\n"
Definition at line 2561 of file res_features.c.
char* synopsis = "Answer a parked call" [static] |
Definition at line 118 of file res_features.c.
char* synopsis2 = "Park yourself" [static] |
Definition at line 128 of file res_features.c.
char* synopsis3 = "Log in for autoanswer" [static] |
Definition at line 143 of file res_features.c.
char* synopsis4 = "Autoanswer a call" [static] |
Definition at line 150 of file res_features.c.
int transferdigittimeout [static] |
char xferfailsound[256] [static] |
Call transfer failure sound
Definition at line 103 of file res_features.c.
Referenced by load_config().
char xfersound[256] [static] |
Call transfer sound
Definition at line 102 of file res_features.c.
Referenced by action_bridge(), bridge_exec(), and load_config().