#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/global_datastores.h"
Go to the source code of this file.
Data Structures | |
struct | ast_bridge_thread_obj |
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) } |
Functions | |
static int | adsi_announce_park (struct ast_channel *chan, char *parkingexten) |
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) |
static | AST_LIST_HEAD_STATIC (feature_list, ast_call_feature) |
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 (parking_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 | |
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 | builtin_atxfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) |
Attended transfer (). | |
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 | cmd_atxfer (struct ast_channel *a, struct ast_channel *b, struct ast_bridge_config *conf, struct ast_channel *who, char *xferto) |
static int | do_atxfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, int sense, const char *toExt, const char *toCont) |
Attended transfer implementation. | |
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_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 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 int | adsipark |
static int | atxfernoanswertimeout |
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 int | featuredigittimeout |
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 | showfeatures_help [] |
static char | showparked_help [] |
static char * | synopsis = "Answer a parked call" |
static char * | synopsis2 = "Park yourself" |
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 72 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 67 of file res_features.c.
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 |
#define FEATURE_RETURN_HANGUP -1 |
#define FEATURE_RETURN_KEEPTRYING 24 |
Definition at line 527 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 524 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 526 of file res_features.c.
Referenced by ast_bridge_call(), builtin_automonitor(), builtin_blindtransfer(), do_atxfer(), and feature_exec_app().
#define FEATURE_RETURN_SUCCESSBREAK 0 |
#define FEATURE_SENSE_CHAN (1 << 0) |
Definition at line 529 of file res_features.c.
Referenced by ast_bridge_call(), ast_feature_interpret(), builtin_parkcall(), cmd_atxfer(), and feature_exec_app().
#define FEATURE_SENSE_PEER (1 << 1) |
Definition at line 530 of file res_features.c.
Referenced by ast_bridge_call(), cmd_atxfer(), feature_exec_app(), and set_peers().
#define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0])) |
Definition at line 1006 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 74 of file res_features.c.
00074 { 00075 AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0), 00076 AST_FEATURE_FLAG_ONPEER = (1 << 1), 00077 AST_FEATURE_FLAG_ONSELF = (1 << 2), 00078 AST_FEATURE_FLAG_BYCALLEE = (1 << 3), 00079 AST_FEATURE_FLAG_BYCALLER = (1 << 4), 00080 AST_FEATURE_FLAG_BYBOTH = (3 << 3), 00081 };
static int adsi_announce_park | ( | struct ast_channel * | chan, | |
char * | parkingexten | |||
) | [static] |
Definition at line 257 of file res_features.c.
References ADSI_JUST_CENT, ast_adsi_load_session(), ast_adsi_print(), and justify.
Referenced by park_call_full().
00258 { 00259 int res; 00260 int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT}; 00261 char tmp[256]; 00262 char *message[5] = {NULL, NULL, NULL, NULL, NULL}; 00263 00264 snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten); 00265 message[0] = tmp; 00266 res = ast_adsi_load_session(chan, NULL, 0, 1); 00267 if (res == -1) 00268 return res; 00269 return ast_adsi_print(chan, message, justify, 1); 00270 }
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 1446 of file res_features.c.
References ast_cdr::accountcode, ast_cdr::amaflags, ast_cdr::answer, ast_answer(), ast_cdr_alloc(), ast_cdr_answer(), AST_CDR_ANSWERED, ast_cdr_detach(), ast_cdr_dup(), ast_cdr_end(), AST_CDR_FLAG_BRIDGED, AST_CDR_FLAG_MAIN, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), AST_CDR_NULL, ast_cdr_setcid(), ast_cdr_specialized_reset(), ast_cdr_start(), ast_cdr_update(), ast_channel_bridge(), ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, AST_CONTROL_ATXFERCMD, 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_default_amaflags, 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_MAX_EXTENSION, AST_OPTION_FLAG_REQUEST, ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_channel::cdr, ast_cdr::channel, cmd_atxfer(), config, ast_option_header::data, ast_cdr::dcontext, ast_cdr::disposition, ast_cdr::dst, ast_cdr::dstchannel, ast_cdr::end, f, FEATURE_MAX_LEN, FEATURE_RETURN_PASSDIGITS, FEATURE_RETURN_SUCCESS, FEATURE_SENSE_CHAN, FEATURE_SENSE_PEER, ast_option_header::flag, ast_cdr::lastapp, ast_cdr::lastdata, LOG_DEBUG, LOG_WARNING, monitor_app, ast_cdr::next, ast_option_header::option, option_debug, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), set_config_flags(), ast_cdr::start, ast_cdr::uniqueid, and ast_cdr::userfield.
Referenced by app_exec(), ast_bridge_call_thread(), do_atxfer(), park_exec(), and try_calling().
01447 { 01448 /* Copy voice back and forth between the two channels. Give the peer 01449 the ability to transfer calls with '#<extension' syntax. */ 01450 struct ast_frame *f; 01451 struct ast_channel *who; 01452 char chan_featurecode[FEATURE_MAX_LEN + 1]=""; 01453 char peer_featurecode[FEATURE_MAX_LEN + 1]=""; 01454 char orig_channame[AST_MAX_EXTENSION]; 01455 char orig_peername[AST_MAX_EXTENSION]; 01456 01457 int res; 01458 int diff; 01459 int hasfeatures=0; 01460 int hadfeatures=0; 01461 struct ast_option_header *aoh; 01462 struct ast_bridge_config backup_config; 01463 struct ast_cdr *bridge_cdr = NULL; 01464 struct ast_cdr *orig_peer_cdr = NULL; 01465 01466 memset(&backup_config, 0, sizeof(backup_config)); 01467 01468 config->start_time = ast_tvnow(); 01469 01470 if (chan && peer) { 01471 pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name); 01472 pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name); 01473 } else if (chan) 01474 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL); 01475 01476 if (monitor_ok) { 01477 const char *monitor_exec; 01478 struct ast_channel *src = NULL; 01479 if (!monitor_app) { 01480 if (!(monitor_app = pbx_findapp("Monitor"))) 01481 monitor_ok=0; 01482 } 01483 if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 01484 src = chan; 01485 else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR"))) 01486 src = peer; 01487 if (monitor_app && src) { 01488 char *tmp = ast_strdupa(monitor_exec); 01489 pbx_exec(src, monitor_app, tmp); 01490 } 01491 } 01492 01493 set_config_flags(chan, peer, config); 01494 config->firstpass = 1; 01495 01496 /* Answer if need be */ 01497 if (ast_answer(chan)) 01498 return -1; 01499 01500 ast_copy_string(orig_channame,chan->name,sizeof(orig_channame)); 01501 ast_copy_string(orig_peername,peer->name,sizeof(orig_peername)); 01502 orig_peer_cdr = peer->cdr; 01503 01504 if (!chan->cdr || (chan->cdr && !ast_test_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED))) { 01505 01506 if (chan->cdr) { 01507 ast_set_flag(chan->cdr, AST_CDR_FLAG_MAIN); 01508 ast_cdr_update(chan); 01509 bridge_cdr = ast_cdr_dup(chan->cdr); 01510 ast_copy_string(bridge_cdr->lastapp, chan->appl, sizeof(bridge_cdr->lastapp)); 01511 ast_copy_string(bridge_cdr->lastdata, chan->data, sizeof(bridge_cdr->lastdata)); 01512 } else { 01513 /* better yet, in a xfer situation, find out why the chan cdr got zapped (pun unintentional) */ 01514 bridge_cdr = ast_cdr_alloc(); /* this should be really, really rare/impossible? */ 01515 ast_copy_string(bridge_cdr->channel, chan->name, sizeof(bridge_cdr->channel)); 01516 ast_copy_string(bridge_cdr->dstchannel, peer->name, sizeof(bridge_cdr->dstchannel)); 01517 ast_copy_string(bridge_cdr->uniqueid, chan->uniqueid, sizeof(bridge_cdr->uniqueid)); 01518 ast_copy_string(bridge_cdr->lastapp, chan->appl, sizeof(bridge_cdr->lastapp)); 01519 ast_copy_string(bridge_cdr->lastdata, chan->data, sizeof(bridge_cdr->lastdata)); 01520 ast_cdr_setcid(bridge_cdr, chan); 01521 bridge_cdr->disposition = (chan->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NULL; 01522 bridge_cdr->amaflags = chan->amaflags ? chan->amaflags : ast_default_amaflags; 01523 ast_copy_string(bridge_cdr->accountcode, chan->accountcode, sizeof(bridge_cdr->accountcode)); 01524 /* Destination information */ 01525 ast_copy_string(bridge_cdr->dst, chan->exten, sizeof(bridge_cdr->dst)); 01526 ast_copy_string(bridge_cdr->dcontext, chan->context, sizeof(bridge_cdr->dcontext)); 01527 if (peer->cdr) { 01528 bridge_cdr->start = peer->cdr->start; 01529 ast_copy_string(bridge_cdr->userfield, peer->cdr->userfield, sizeof(bridge_cdr->userfield)); 01530 } else { 01531 ast_cdr_start(bridge_cdr); 01532 } 01533 } 01534 /* peer->cdr->answer will be set when a macro runs on the peer; 01535 in that case, the bridge answer will be delayed while the 01536 macro plays on the peer channel. The peer answered the call 01537 before the macro started playing. To the phone system, 01538 this is billable time for the call, even tho the caller 01539 hears nothing but ringing while the macro does its thing. */ 01540 if (peer->cdr && !ast_tvzero(peer->cdr->answer)) { 01541 bridge_cdr->answer = peer->cdr->answer; 01542 chan->cdr->answer = peer->cdr->answer; 01543 } else { 01544 ast_cdr_answer(bridge_cdr); 01545 ast_cdr_answer(chan->cdr); /* for the sake of cli status checks */ 01546 } 01547 ast_set_flag(chan->cdr, AST_CDR_FLAG_BRIDGED); 01548 if (peer->cdr) { 01549 ast_set_flag(peer->cdr, AST_CDR_FLAG_BRIDGED); 01550 } 01551 } 01552 01553 for (;;) { 01554 struct ast_channel *other; /* used later */ 01555 01556 res = ast_channel_bridge(chan, peer, config, &f, &who); 01557 01558 if (config->feature_timer) { 01559 /* Update time limit for next pass */ 01560 diff = ast_tvdiff_ms(ast_tvnow(), config->start_time); 01561 config->feature_timer -= diff; 01562 if (hasfeatures) { 01563 /* Running on backup config, meaning a feature might be being 01564 activated, but that's no excuse to keep things going 01565 indefinitely! */ 01566 if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) { 01567 if (option_debug) 01568 ast_log(LOG_DEBUG, "Timed out, realtime this time!\n"); 01569 config->feature_timer = 0; 01570 who = chan; 01571 if (f) 01572 ast_frfree(f); 01573 f = NULL; 01574 res = 0; 01575 } else if (config->feature_timer <= 0) { 01576 /* Not *really* out of time, just out of time for 01577 digits to come in for features. */ 01578 if (option_debug) 01579 ast_log(LOG_DEBUG, "Timed out for feature!\n"); 01580 if (!ast_strlen_zero(peer_featurecode)) { 01581 ast_dtmf_stream(chan, peer, peer_featurecode, 0); 01582 memset(peer_featurecode, 0, sizeof(peer_featurecode)); 01583 } 01584 if (!ast_strlen_zero(chan_featurecode)) { 01585 ast_dtmf_stream(peer, chan, chan_featurecode, 0); 01586 memset(chan_featurecode, 0, sizeof(chan_featurecode)); 01587 } 01588 if (f) 01589 ast_frfree(f); 01590 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode); 01591 if (!hasfeatures) { 01592 /* Restore original (possibly time modified) bridge config */ 01593 memcpy(config, &backup_config, sizeof(struct ast_bridge_config)); 01594 memset(&backup_config, 0, sizeof(backup_config)); 01595 } 01596 hadfeatures = hasfeatures; 01597 /* Continue as we were */ 01598 continue; 01599 } else if (!f) { 01600 /* The bridge returned without a frame and there is a feature in progress. 01601 * However, we don't think the feature has quite yet timed out, so just 01602 * go back into the bridge. */ 01603 continue; 01604 } 01605 } else { 01606 if (config->feature_timer <=0) { 01607 /* We ran out of time */ 01608 config->feature_timer = 0; 01609 who = chan; 01610 if (f) 01611 ast_frfree(f); 01612 f = NULL; 01613 res = 0; 01614 } 01615 } 01616 } 01617 if (res < 0) { 01618 if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer)) 01619 ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name); 01620 goto before_you_go; 01621 } 01622 01623 if (!f || (f->frametype == AST_FRAME_CONTROL && 01624 (f->subclass == AST_CONTROL_HANGUP || f->subclass == AST_CONTROL_BUSY || 01625 f->subclass == AST_CONTROL_CONGESTION ) ) ) { 01626 res = -1; 01627 break; 01628 } 01629 /* many things should be sent to the 'other' channel */ 01630 other = (who == chan) ? peer : chan; 01631 if (f->frametype == AST_FRAME_CONTROL) { 01632 switch (f->subclass) { 01633 case AST_CONTROL_RINGING: 01634 case AST_CONTROL_FLASH: 01635 case -1: 01636 ast_indicate(other, f->subclass); 01637 break; 01638 case AST_CONTROL_HOLD: 01639 case AST_CONTROL_UNHOLD: 01640 ast_indicate_data(other, f->subclass, f->data, f->datalen); 01641 break; 01642 case AST_CONTROL_OPTION: 01643 aoh = f->data; 01644 /* Forward option Requests */ 01645 if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) { 01646 ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 01647 f->datalen - sizeof(struct ast_option_header), 0); 01648 } 01649 break; 01650 case AST_CONTROL_ATXFERCMD: 01651 cmd_atxfer(chan, peer, config, who, f->data); 01652 break; 01653 } 01654 } else if (f->frametype == AST_FRAME_DTMF_BEGIN) { 01655 /* eat it */ 01656 } else if (f->frametype == AST_FRAME_DTMF) { 01657 char *featurecode; 01658 int sense; 01659 01660 hadfeatures = hasfeatures; 01661 /* This cannot overrun because the longest feature is one shorter than our buffer */ 01662 if (who == chan) { 01663 sense = FEATURE_SENSE_CHAN; 01664 featurecode = chan_featurecode; 01665 } else { 01666 sense = FEATURE_SENSE_PEER; 01667 featurecode = peer_featurecode; 01668 } 01669 /*! append the event to featurecode. we rely on the string being zero-filled, and 01670 * not overflowing it. 01671 * \todo XXX how do we guarantee the latter ? 01672 */ 01673 featurecode[strlen(featurecode)] = f->subclass; 01674 /* Get rid of the frame before we start doing "stuff" with the channels */ 01675 ast_frfree(f); 01676 f = NULL; 01677 config->feature_timer = backup_config.feature_timer; 01678 res = ast_feature_interpret(chan, peer, config, featurecode, sense); 01679 switch(res) { 01680 case FEATURE_RETURN_PASSDIGITS: 01681 ast_dtmf_stream(other, who, featurecode, 0); 01682 /* Fall through */ 01683 case FEATURE_RETURN_SUCCESS: 01684 memset(featurecode, 0, sizeof(chan_featurecode)); 01685 break; 01686 } 01687 if (res >= FEATURE_RETURN_PASSDIGITS) { 01688 res = 0; 01689 } else 01690 break; 01691 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode); 01692 if (hadfeatures && !hasfeatures) { 01693 /* Restore backup */ 01694 memcpy(config, &backup_config, sizeof(struct ast_bridge_config)); 01695 memset(&backup_config, 0, sizeof(struct ast_bridge_config)); 01696 } else if (hasfeatures) { 01697 if (!hadfeatures) { 01698 /* Backup configuration */ 01699 memcpy(&backup_config, config, sizeof(struct ast_bridge_config)); 01700 /* Setup temporary config options */ 01701 config->play_warning = 0; 01702 ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING); 01703 ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING); 01704 config->warning_freq = 0; 01705 config->warning_sound = NULL; 01706 config->end_sound = NULL; 01707 config->start_sound = NULL; 01708 config->firstpass = 0; 01709 } 01710 config->start_time = ast_tvnow(); 01711 config->feature_timer = featuredigittimeout; 01712 if (option_debug) 01713 ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->feature_timer); 01714 } 01715 } 01716 if (f) 01717 ast_frfree(f); 01718 01719 } 01720 before_you_go: 01721 /* obey the NoCDR() wishes. */ 01722 if (!chan->cdr || (chan->cdr && !ast_test_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED))) { 01723 01724 ast_cdr_end(bridge_cdr); 01725 01726 ast_cdr_detach(bridge_cdr); 01727 01728 /* just in case, these channels get bridged again before hangup */ 01729 if (chan->cdr) { 01730 ast_cdr_specialized_reset(chan->cdr,0); 01731 } 01732 if (peer->cdr) { 01733 struct ast_cdr *cur; 01734 01735 ast_channel_lock(peer); 01736 for (cur = peer->cdr; cur; cur = cur->next) { 01737 if (cur == orig_peer_cdr) { 01738 break; 01739 } 01740 } 01741 01742 if (!cur) { 01743 /* orig_peer_cdr is gone, probably because of a masquerade 01744 * during the bridge. */ 01745 ast_channel_unlock(peer); 01746 return res; 01747 } 01748 01749 /* before resetting the peer cdr, throw a copy of it to the 01750 backend, just in case the cdr.conf file is calling for 01751 unanswered CDR's. */ 01752 01753 /* When peer->cdr isn't the same addr as orig_peer_cdr, 01754 this can only happen if there was a transfer, methinks; 01755 at any rate, only pay attention to the original*/ 01756 if (ast_cdr_isset_unanswered()) { 01757 struct ast_cdr *dupd = ast_cdr_dup(orig_peer_cdr); 01758 if (dupd) { 01759 if (ast_tvzero(dupd->end) && ast_cdr_isset_unanswered()) 01760 ast_cdr_end(dupd); 01761 ast_cdr_detach(dupd); 01762 } 01763 } 01764 ast_cdr_specialized_reset(orig_peer_cdr,0); 01765 ast_channel_unlock(peer); 01766 } 01767 } 01768 return res; 01769 }
static void* ast_bridge_call_thread | ( | void * | data | ) | [static] |
Definition at line 226 of file res_features.c.
References ast_channel::appl, ast_bridge_call(), ast_hangup(), ast_bridge_thread_obj::bconfig, ast_bridge_thread_obj::chan, ast_channel::data, free, and ast_bridge_thread_obj::peer.
Referenced by ast_bridge_call_thread_launch().
00227 { 00228 struct ast_bridge_thread_obj *tobj = data; 00229 00230 tobj->chan->appl = "Transferred Call"; 00231 tobj->chan->data = tobj->peer->name; 00232 tobj->peer->appl = "Transferred Call"; 00233 tobj->peer->data = tobj->chan->name; 00234 00235 ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig); 00236 ast_hangup(tobj->chan); 00237 ast_hangup(tobj->peer); 00238 bzero(tobj, sizeof(*tobj)); /*! \todo XXX for safety */ 00239 free(tobj); 00240 return NULL; 00241 }
static void ast_bridge_call_thread_launch | ( | void * | data | ) | [static] |
Definition at line 243 of file res_features.c.
References ast_bridge_call_thread(), ast_pthread_create, and thread.
Referenced by do_atxfer().
00244 { 00245 pthread_t thread; 00246 pthread_attr_t attr; 00247 struct sched_param sched; 00248 00249 pthread_attr_init(&attr); 00250 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00251 ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data); 00252 pthread_attr_destroy(&attr); 00253 memset(&sched, 0, sizeof(sched)); 00254 pthread_setschedparam(thread, SCHED_RR, &sched); 00255 }
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 1169 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().
01170 { 01171 int x; 01172 struct ast_flags features; 01173 struct ast_call_feature *feature; 01174 const char *dynamic_features; 01175 char *tmp, *tok; 01176 int res = FEATURE_RETURN_PASSDIGITS; 01177 int feature_detected = 0; 01178 01179 if (sense == FEATURE_SENSE_CHAN) { 01180 ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL); 01181 dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"); 01182 } else { 01183 ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL); 01184 dynamic_features = pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"); 01185 } 01186 if (option_debug > 2) 01187 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); 01188 01189 ast_rwlock_rdlock(&features_lock); 01190 for (x = 0; x < FEATURES_COUNT; x++) { 01191 if ((ast_test_flag(&features, builtin_features[x].feature_mask)) && 01192 !ast_strlen_zero(builtin_features[x].exten)) { 01193 /* Feature is up for consideration */ 01194 if (!strcmp(builtin_features[x].exten, code)) { 01195 res = builtin_features[x].operation(chan, peer, config, code, sense, NULL); 01196 feature_detected = 1; 01197 break; 01198 } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) { 01199 if (res == FEATURE_RETURN_PASSDIGITS) 01200 res = FEATURE_RETURN_STOREDIGITS; 01201 } 01202 } 01203 } 01204 ast_rwlock_unlock(&features_lock); 01205 01206 if (ast_strlen_zero(dynamic_features) || feature_detected) 01207 return res; 01208 01209 tmp = ast_strdupa(dynamic_features); 01210 01211 while ((tok = strsep(&tmp, "#"))) { 01212 AST_LIST_LOCK(&feature_list); 01213 if (!(feature = find_dynamic_feature(tok))) { 01214 AST_LIST_UNLOCK(&feature_list); 01215 continue; 01216 } 01217 01218 /* Feature is up for consideration */ 01219 if (!strcmp(feature->exten, code)) { 01220 if (option_verbose > 2) 01221 ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok); 01222 res = feature->operation(chan, peer, config, code, sense, feature); 01223 if (res != FEATURE_RETURN_KEEPTRYING) { 01224 AST_LIST_UNLOCK(&feature_list); 01225 break; 01226 } 01227 res = FEATURE_RETURN_PASSDIGITS; 01228 } else if (!strncmp(feature->exten, code, strlen(code))) 01229 res = FEATURE_RETURN_STOREDIGITS; 01230 01231 AST_LIST_UNLOCK(&feature_list); 01232 } 01233 01234 return res; 01235 }
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 1280 of file res_features.c.
References ast_channel::_softhangup, ast_channel::_state, ast_call(), AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, 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_call_feature::exten, f, FEATURES_COUNT, len, LOG_NOTICE, option_verbose, pbx_builtin_setvar_helper(), and VERBOSE_PREFIX_3.
Referenced by do_atxfer().
01281 { 01282 int state = 0; 01283 int cause = 0; 01284 int to; 01285 struct ast_channel *chan; 01286 struct ast_channel *monitor_chans[2]; 01287 struct ast_channel *active_channel; 01288 int res = 0, ready = 0; 01289 01290 if ((chan = ast_request(type, format, data, &cause))) { 01291 ast_set_callerid(chan, cid_num, cid_name, cid_num); 01292 ast_string_field_set(chan, language, language); 01293 ast_channel_inherit_variables(caller, chan); 01294 pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller->name); 01295 01296 if (!ast_call(chan, data, timeout)) { 01297 struct timeval started; 01298 int x, len = 0; 01299 char *disconnect_code = NULL, *dialed_code = NULL; 01300 01301 ast_indicate(caller, AST_CONTROL_RINGING); 01302 /* support dialing of the featuremap disconnect code while performing an attended tranfer */ 01303 ast_rwlock_rdlock(&features_lock); 01304 for (x = 0; x < FEATURES_COUNT; x++) { 01305 if (strcasecmp(builtin_features[x].sname, "disconnect")) 01306 continue; 01307 01308 disconnect_code = builtin_features[x].exten; 01309 len = strlen(disconnect_code) + 1; 01310 dialed_code = alloca(len); 01311 memset(dialed_code, 0, len); 01312 break; 01313 } 01314 ast_rwlock_unlock(&features_lock); 01315 x = 0; 01316 started = ast_tvnow(); 01317 to = timeout; 01318 while (!ast_check_hangup(caller) && timeout && (chan->_state != AST_STATE_UP)) { 01319 struct ast_frame *f = NULL; 01320 01321 monitor_chans[0] = caller; 01322 monitor_chans[1] = chan; 01323 active_channel = ast_waitfor_n(monitor_chans, 2, &to); 01324 01325 /* see if the timeout has been violated */ 01326 if(ast_tvdiff_ms(ast_tvnow(), started) > timeout) { 01327 state = AST_CONTROL_UNHOLD; 01328 ast_log(LOG_NOTICE, "We exceeded our AT-timeout\n"); 01329 break; /*doh! timeout*/ 01330 } 01331 01332 if (!active_channel) 01333 continue; 01334 01335 if (chan && (chan == active_channel)){ 01336 f = ast_read(chan); 01337 if (f == NULL) { /*doh! where'd he go?*/ 01338 state = AST_CONTROL_HANGUP; 01339 res = 0; 01340 break; 01341 } 01342 01343 if (f->frametype == AST_FRAME_CONTROL || f->frametype == AST_FRAME_DTMF || f->frametype == AST_FRAME_TEXT) { 01344 if (f->subclass == AST_CONTROL_RINGING) { 01345 state = f->subclass; 01346 if (option_verbose > 2) 01347 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", chan->name); 01348 ast_indicate(caller, AST_CONTROL_RINGING); 01349 } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) { 01350 state = f->subclass; 01351 if (option_verbose > 2) 01352 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", chan->name); 01353 ast_indicate(caller, AST_CONTROL_BUSY); 01354 ast_frfree(f); 01355 f = NULL; 01356 break; 01357 } else if (f->subclass == AST_CONTROL_ANSWER) { 01358 /* This is what we are hoping for */ 01359 state = f->subclass; 01360 ast_frfree(f); 01361 f = NULL; 01362 ready=1; 01363 break; 01364 } else if (f->subclass != -1) { 01365 ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass); 01366 } 01367 /* else who cares */ 01368 } 01369 01370 } else if (caller && (active_channel == caller)) { 01371 f = ast_read(caller); 01372 if (f == NULL) { /*doh! where'd he go?*/ 01373 if (caller->_softhangup && !chan->_softhangup) { 01374 /* make this a blind transfer */ 01375 ready = 1; 01376 break; 01377 } 01378 state = AST_CONTROL_HANGUP; 01379 res = 0; 01380 break; 01381 } 01382 01383 if (f->frametype == AST_FRAME_DTMF) { 01384 dialed_code[x++] = f->subclass; 01385 dialed_code[x] = '\0'; 01386 if (strlen(dialed_code) == len) { 01387 x = 0; 01388 } else if (x && strncmp(dialed_code, disconnect_code, x)) { 01389 x = 0; 01390 dialed_code[x] = '\0'; 01391 } 01392 if (*dialed_code && !strcmp(dialed_code, disconnect_code)) { 01393 /* Caller Canceled the call */ 01394 state = AST_CONTROL_UNHOLD; 01395 ast_frfree(f); 01396 f = NULL; 01397 break; 01398 } 01399 } 01400 } 01401 if (f) 01402 ast_frfree(f); 01403 } /* end while */ 01404 } else 01405 ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data); 01406 } else { 01407 ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); 01408 switch(cause) { 01409 case AST_CAUSE_BUSY: 01410 state = AST_CONTROL_BUSY; 01411 break; 01412 case AST_CAUSE_CONGESTION: 01413 state = AST_CONTROL_CONGESTION; 01414 break; 01415 } 01416 } 01417 01418 ast_indicate(caller, -1); 01419 if (chan && ready) { 01420 if (chan->_state == AST_STATE_UP) 01421 state = AST_CONTROL_ANSWER; 01422 res = 0; 01423 } else if(chan) { 01424 res = -1; 01425 ast_hangup(chan); 01426 chan = NULL; 01427 } else { 01428 res = -1; 01429 } 01430 01431 if (outstate) 01432 *outstate = state; 01433 01434 return chan; 01435 }
static AST_LIST_HEAD_STATIC | ( | feature_list | , | |
ast_call_feature | ||||
) | [static] |
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 481 of file res_features.c.
References ast_channel::amaflags, ast_channel_alloc(), ast_channel_masquerade(), ast_frfree, ast_hangup(), 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().
00482 { 00483 struct ast_channel *chan; 00484 struct ast_frame *f; 00485 char *orig_chan_name = NULL; 00486 int park_status; 00487 00488 /* Make a new, fake channel that we'll use to masquerade in the real one */ 00489 if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) { 00490 ast_log(LOG_WARNING, "Unable to create parked channel\n"); 00491 return -1; 00492 } 00493 00494 /* Make formats okay */ 00495 chan->readformat = rchan->readformat; 00496 chan->writeformat = rchan->writeformat; 00497 ast_channel_masquerade(chan, rchan); 00498 00499 /* Setup the extensions and such */ 00500 set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority); 00501 00502 /* Make the masq execute */ 00503 f = ast_read(chan); 00504 if (f) 00505 ast_frfree(f); 00506 00507 orig_chan_name = ast_strdupa(chan->name); 00508 00509 park_status = park_call_full(chan, peer, timeout, extout, orig_chan_name); 00510 if (park_status == 1) { 00511 /* would be nice to play: "invalid parking extension" */ 00512 ast_hangup(chan); 00513 return -1; 00514 } 00515 00516 return 0; 00517 }
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 | ( | parking_lock | ) |
protects all static variables above
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 476 of file res_features.c.
References park_call_full().
Referenced by builtin_blindtransfer(), builtin_parkcall(), iax_park_thread(), and sip_park_thread().
00477 { 00478 return park_call_full(chan, peer, timeout, extout, NULL); 00479 }
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 160 of file res_features.c.
Referenced by builtin_blindtransfer(), dp_lookup(), handle_request_refer(), mgcp_ss(), socket_process(), and ss_thread().
00161 { 00162 return parking_ext; 00163 }
int ast_pickup_call | ( | struct ast_channel * | chan | ) |
Pickup a call.
Definition at line 2333 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().
02334 { 02335 struct ast_channel *cur = NULL; 02336 int res = -1; 02337 02338 while ( (cur = ast_channel_walk_locked(cur)) != NULL) { 02339 if (!cur->pbx && 02340 (cur != chan) && 02341 (chan->pickupgroup & cur->callgroup) && 02342 ((cur->_state == AST_STATE_RINGING) || 02343 (cur->_state == AST_STATE_RING))) { 02344 break; 02345 } 02346 ast_channel_unlock(cur); 02347 } 02348 if (cur) { 02349 if (option_debug) 02350 ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name); 02351 res = ast_answer(chan); 02352 if (res) 02353 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name); 02354 res = ast_queue_control(chan, AST_CONTROL_ANSWER); 02355 if (res) 02356 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name); 02357 res = ast_channel_masquerade(cur, chan); 02358 if (res) 02359 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */ 02360 ast_channel_unlock(cur); 02361 } else { 02362 if (option_debug) 02363 ast_log(LOG_DEBUG, "No call pickup possible...\n"); 02364 } 02365 return res; 02366 }
char* ast_pickup_ext | ( | void | ) |
Determine system call pickup extension.
Definition at line 165 of file res_features.c.
Referenced by cb_events(), get_destination(), handle_request_invite(), handle_showfeatures(), mgcp_ss(), and ss_thread().
00166 { 00167 return pickup_ext; 00168 }
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 1023 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.
01024 { 01025 if (!feature) { 01026 ast_log(LOG_NOTICE,"You didn't pass a feature!\n"); 01027 return; 01028 } 01029 01030 AST_LIST_LOCK(&feature_list); 01031 AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry); 01032 AST_LIST_UNLOCK(&feature_list); 01033 01034 if (option_verbose >= 2) 01035 ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname); 01036 }
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 1039 of file res_features.c.
References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, and free.
01040 { 01041 if (!feature) 01042 return; 01043 01044 AST_LIST_LOCK(&feature_list); 01045 AST_LIST_REMOVE(&feature_list,feature,feature_entry); 01046 AST_LIST_UNLOCK(&feature_list); 01047 free(feature); 01048 }
static void ast_unregister_features | ( | void | ) | [static] |
Remove all features in the list.
Definition at line 1051 of file res_features.c.
References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.
01052 { 01053 struct ast_call_feature *feature; 01054 01055 AST_LIST_LOCK(&feature_list); 01056 while ((feature = AST_LIST_REMOVE_HEAD(&feature_list,feature_entry))) 01057 free(feature); 01058 AST_LIST_UNLOCK(&feature_list); 01059 }
static int builtin_atxfer | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
char * | code, | |||
int | sense, | |||
void * | data | |||
) | [static] |
Attended transfer ().
chan | ||
peer | ||
config | ||
code | ||
sense | ||
data | Get extension to transfer to, if you cannot generate channel (or find extension) return to host channel. After called channel answered wait for hangup of transferer, bridge call between transfer peer (taking them off hold) to attended transfer channel. |
Definition at line 1000 of file res_features.c.
References config, and do_atxfer().
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 581 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.
00582 { 00583 char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL; 00584 int x = 0; 00585 size_t len; 00586 struct ast_channel *caller_chan, *callee_chan; 00587 00588 if (!monitor_ok) { 00589 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n"); 00590 return -1; 00591 } 00592 00593 if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) { 00594 monitor_ok = 0; 00595 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n"); 00596 return -1; 00597 } 00598 00599 set_peers(&caller_chan, &callee_chan, peer, chan, sense); 00600 00601 if (!ast_strlen_zero(courtesytone)) { 00602 if (ast_autoservice_start(callee_chan)) 00603 return -1; 00604 if (ast_stream_and_wait(caller_chan, courtesytone, caller_chan->language, "")) { 00605 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); 00606 ast_autoservice_stop(callee_chan); 00607 return -1; 00608 } 00609 if (ast_autoservice_stop(callee_chan)) 00610 return -1; 00611 } 00612 00613 if (callee_chan->monitor) { 00614 if (option_verbose > 3) 00615 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code); 00616 ast_monitor_stop(callee_chan, 1); 00617 return FEATURE_RETURN_SUCCESS; 00618 } 00619 00620 if (caller_chan && callee_chan) { 00621 const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT"); 00622 const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR"); 00623 00624 if (!touch_format) 00625 touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT"); 00626 00627 if (!touch_monitor) 00628 touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR"); 00629 00630 if (touch_monitor) { 00631 len = strlen(touch_monitor) + 50; 00632 args = alloca(len); 00633 touch_filename = alloca(len); 00634 snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor); 00635 snprintf(args, len, "%s|%s|m", (touch_format) ? touch_format : "wav", touch_filename); 00636 } else { 00637 caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name)); 00638 callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name)); 00639 len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50; 00640 args = alloca(len); 00641 touch_filename = alloca(len); 00642 snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id); 00643 snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename); 00644 } 00645 00646 for( x = 0; x < strlen(args); x++) { 00647 if (args[x] == '/') 00648 args[x] = '-'; 00649 } 00650 00651 if (option_verbose > 3) 00652 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args); 00653 00654 pbx_exec(callee_chan, monitor_app, args); 00655 pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename); 00656 pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename); 00657 00658 return FEATURE_RETURN_SUCCESS; 00659 } 00660 00661 ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n"); 00662 return -1; 00663 }
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 692 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.
00693 { 00694 struct ast_channel *transferer; 00695 struct ast_channel *transferee; 00696 const char *transferer_real_context; 00697 char xferto[256]; 00698 int res; 00699 00700 set_peers(&transferer, &transferee, peer, chan, sense); 00701 transferer_real_context = real_ctx(transferer, transferee); 00702 /* Start autoservice on chan while we talk to the originator */ 00703 ast_autoservice_start(transferee); 00704 ast_indicate(transferee, AST_CONTROL_HOLD); 00705 00706 memset(xferto, 0, sizeof(xferto)); 00707 00708 /* Transfer */ 00709 res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY); 00710 if (res < 0) { 00711 finishup(transferee); 00712 return -1; /* error ? */ 00713 } 00714 if (res > 0) /* If they've typed a digit already, handle it */ 00715 xferto[0] = (char) res; 00716 00717 ast_stopstream(transferer); 00718 res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout); 00719 if (res < 0) { /* hangup, would be 0 for invalid and 1 for valid */ 00720 finishup(transferee); 00721 return res; 00722 } 00723 if (!strcmp(xferto, ast_parking_ext())) { 00724 res = finishup(transferee); 00725 if (res) 00726 res = -1; 00727 else if (!ast_park_call(transferee, transferer, 0, NULL)) { /* success */ 00728 /* We return non-zero, but tell the PBX not to hang the channel when 00729 the thread dies -- We have to be careful now though. We are responsible for 00730 hanging up the channel, else it will never be hung up! */ 00731 00732 return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER; 00733 } else { 00734 ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name); 00735 } 00736 /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */ 00737 } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) { 00738 pbx_builtin_setvar_helper(transferer, "BLINDTRANSFER", transferee->name); 00739 pbx_builtin_setvar_helper(transferee, "BLINDTRANSFER", transferer->name); 00740 res=finishup(transferee); 00741 if (!transferer->cdr) { 00742 transferer->cdr=ast_cdr_alloc(); 00743 if (transferer) { 00744 ast_cdr_init(transferer->cdr, transferer); /* initilize our channel's cdr */ 00745 ast_cdr_start(transferer->cdr); 00746 } 00747 } 00748 if (transferer->cdr) { 00749 ast_cdr_setdestchan(transferer->cdr, transferee->name); 00750 ast_cdr_setapp(transferer->cdr, "BLINDTRANSFER",""); 00751 } 00752 if (!transferee->pbx) { 00753 /* Doh! Use our handy async_goto functions */ 00754 if (option_verbose > 2) 00755 ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n" 00756 ,transferee->name, xferto, transferer_real_context); 00757 if (ast_async_goto(transferee, transferer_real_context, xferto, 1)) 00758 ast_log(LOG_WARNING, "Async goto failed :-(\n"); 00759 res = -1; 00760 } else { 00761 /* Set the channel's new extension, since it exists, using transferer context */ 00762 set_c_e_p(transferee, transferer_real_context, xferto, 0); 00763 } 00764 check_goto_on_transfer(transferer); 00765 return res; 00766 } else { 00767 if (option_verbose > 2) 00768 ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context); 00769 } 00770 if (ast_stream_and_wait(transferer, xferfailsound, transferer->language, AST_DIGIT_ANY) < 0 ) { 00771 finishup(transferee); 00772 return -1; 00773 } 00774 ast_stopstream(transferer); 00775 res = finishup(transferee); 00776 if (res) { 00777 if (option_verbose > 1) 00778 ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name); 00779 return res; 00780 } 00781 return FEATURE_RETURN_SUCCESS; 00782 }
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 665 of file res_features.c.
References ast_verbose(), FEATURE_RETURN_HANGUP, option_verbose, and VERBOSE_PREFIX_3.
00666 { 00667 if (option_verbose > 3) 00668 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code); 00669 return FEATURE_RETURN_HANGUP; 00670 }
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 548 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().
00549 { 00550 struct ast_channel *parker; 00551 struct ast_channel *parkee; 00552 int res = 0; 00553 struct ast_module_user *u; 00554 00555 u = ast_module_user_add(chan); 00556 00557 set_peers(&parker, &parkee, peer, chan, sense); 00558 /* Setup the exten/priority to be s/1 since we don't know 00559 where this call should return */ 00560 strcpy(chan->exten, "s"); 00561 chan->priority = 1; 00562 if (chan->_state != AST_STATE_UP) 00563 res = ast_answer(chan); 00564 if (!res) 00565 res = ast_safe_sleep(chan, 1000); 00566 if (!res) 00567 res = ast_park_call(parkee, parker, 0, NULL); 00568 00569 ast_module_user_remove(u); 00570 00571 if (!res) { 00572 if (sense == FEATURE_SENSE_CHAN) 00573 res = AST_PBX_NO_HANGUP_PEER; 00574 else 00575 res = AST_PBX_KEEPALIVE; 00576 } 00577 return res; 00578 00579 }
static int check_compat | ( | struct ast_channel * | c, | |
struct ast_channel * | newchan | |||
) | [static] |
Definition at line 784 of file res_features.c.
References ast_channel_make_compatible(), ast_hangup(), ast_log(), and LOG_WARNING.
Referenced by do_atxfer().
00785 { 00786 if (ast_channel_make_compatible(c, newchan) < 0) { 00787 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", 00788 c->name, newchan->name); 00789 ast_hangup(newchan); 00790 return -1; 00791 } 00792 return 0; 00793 }
static void check_goto_on_transfer | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 187 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().
00188 { 00189 struct ast_channel *xferchan; 00190 const char *val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR"); 00191 char *x, *goto_on_transfer; 00192 struct ast_frame *f; 00193 00194 if (ast_strlen_zero(val)) 00195 return; 00196 00197 goto_on_transfer = ast_strdupa(val); 00198 00199 if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, chan->name))) 00200 return; 00201 00202 for (x = goto_on_transfer; x && *x; x++) { 00203 if (*x == '^') 00204 *x = '|'; 00205 } 00206 /* Make formats okay */ 00207 xferchan->readformat = chan->readformat; 00208 xferchan->writeformat = chan->writeformat; 00209 ast_channel_masquerade(xferchan, chan); 00210 ast_parseable_goto(xferchan, goto_on_transfer); 00211 xferchan->_state = AST_STATE_UP; 00212 ast_clear_flag(xferchan, AST_FLAGS_ALL); 00213 xferchan->_softhangup = 0; 00214 if ((f = ast_read(xferchan))) { 00215 ast_frfree(f); 00216 f = NULL; 00217 ast_pbx_start(xferchan); 00218 } else { 00219 ast_hangup(xferchan); 00220 } 00221 }
static void cmd_atxfer | ( | struct ast_channel * | a, | |
struct ast_channel * | b, | |||
struct ast_bridge_config * | conf, | |||
struct ast_channel * | who, | |||
char * | xferto | |||
) | [static] |
Definition at line 1437 of file res_features.c.
References context, do_atxfer(), FEATURE_SENSE_CHAN, and FEATURE_SENSE_PEER.
Referenced by ast_bridge_call().
01438 { 01439 int sense = (a == who) ? FEATURE_SENSE_CHAN : FEATURE_SENSE_PEER; 01440 char *context = strchr(xferto, '@');; 01441 if (context) 01442 *context++ = '\0'; 01443 do_atxfer(a, b, conf, sense, xferto, context); 01444 }
static int do_atxfer | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config, | |||
int | sense, | |||
const char * | toExt, | |||
const char * | toCont | |||
) | [static] |
Attended transfer implementation.
chan | transfered user | |
peer | person transfering call | |
config | ||
sense | feature options | |
toExt | ||
toCont | This is the actual implementation of attended transfer, it can be activated as a regular feature or through the AMI. "toExt" is the extension to transfer to (default: ask for it on the transferer channel) "toCont" is the context to transfer to (default: the one in which the transferer is) |
Definition at line 810 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_datastore_find(), ast_channel_lock, ast_channel_masquerade(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, AST_CONTROL_BUSY, AST_CONTROL_HOLD, AST_CONTROL_UNHOLD, ast_copy_flags, 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_strlen_zero(), 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_datastore::data, dial_features_info, ast_channel::exten, f, FEATURE_RETURN_SUCCESS, ast_bridge_config::features_callee, ast_dial_features::features_callee, ast_bridge_config::features_caller, finishup(), ast_dial_features::is_caller, LOG_DEBUG, LOG_WARNING, ast_channel::nativeformats, option_debug, ast_bridge_thread_obj::peer, ast_channel::priority, ast_channel::readformat, real_ctx(), S_OR, set_peers(), ast_channel::visible_indication, and ast_channel::writeformat.
Referenced by builtin_atxfer(), and cmd_atxfer().
00811 { 00812 struct ast_channel *transferer; 00813 struct ast_channel *transferee; 00814 const char *transferer_real_context; 00815 const char *transfer_context; 00816 char xferto[256] = ""; 00817 int res; 00818 int outstate=0; 00819 struct ast_channel *newchan; 00820 struct ast_channel *xferchan; 00821 struct ast_bridge_thread_obj *tobj; 00822 struct ast_bridge_config bconfig; 00823 struct ast_frame *f; 00824 int l; 00825 struct ast_datastore *features_datastore; 00826 struct ast_dial_features *dialfeatures = NULL; 00827 00828 if (option_debug) 00829 ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense); 00830 set_peers(&transferer, &transferee, peer, chan, sense); 00831 transferer_real_context = real_ctx(transferer, transferee); 00832 transfer_context = S_OR(toCont, transferer_real_context); 00833 00834 /* Start autoservice on chan while we talk to the originator */ 00835 ast_autoservice_start(transferee); 00836 ast_indicate(transferee, AST_CONTROL_HOLD); 00837 00838 if (!ast_strlen_zero(toExt)) { 00839 ast_copy_string(xferto, toExt, sizeof(xferto)); 00840 } else { 00841 /* Ask for extension to transfer to on the transferer channel */ 00842 res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY); 00843 if (res < 0) { 00844 finishup(transferee); 00845 return res; 00846 } 00847 if (res > 0) /* If they've typed a digit already, handle it */ 00848 xferto[0] = (char) res; 00849 00850 /* this is specific of atxfer */ 00851 res = ast_app_dtget(transferer, transfer_context, xferto, sizeof(xferto), 100, transferdigittimeout); 00852 if (res < 0) { /* hangup, would be 0 for invalid and 1 for valid */ 00853 finishup(transferee); 00854 return res; 00855 } 00856 if (res == 0) { 00857 ast_log(LOG_WARNING, "Did not read data.\n"); 00858 finishup(transferee); 00859 if (ast_stream_and_wait(transferer, "beeperr", transferer->language, "")) 00860 return -1; 00861 return FEATURE_RETURN_SUCCESS; 00862 } 00863 } 00864 00865 /* valid extension, res == 1 */ 00866 if (!ast_exists_extension(transferer, transfer_context, xferto, 1, transferer->cid.cid_num)) { 00867 ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transfer_context); 00868 finishup(transferee); 00869 if (ast_stream_and_wait(transferer, "beeperr", transferer->language, "")) 00870 return -1; 00871 return FEATURE_RETURN_SUCCESS; 00872 } 00873 00874 l = strlen(xferto); 00875 snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transfer_context); /* append context */ 00876 newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats), 00877 xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, transferer->language); 00878 00879 /* If we are the callee and we are being transferred, after the masquerade 00880 * caller features will really be the original callee features */ 00881 ast_channel_lock(transferee); 00882 if ((features_datastore = ast_channel_datastore_find(transferee, &dial_features_info, NULL))) { 00883 dialfeatures = features_datastore->data; 00884 } 00885 ast_channel_unlock(transferee); 00886 00887 if (dialfeatures && !dialfeatures->is_caller) { 00888 ast_copy_flags(&(config->features_caller), &(dialfeatures->features_callee), AST_FLAGS_ALL); 00889 } 00890 00891 ast_indicate(transferer, -1); 00892 if (!newchan) { 00893 finishup(transferee); 00894 /* any reason besides user requested cancel and busy triggers the failed sound */ 00895 if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY && 00896 ast_stream_and_wait(transferer, xferfailsound, transferer->language, "")) 00897 return -1; 00898 return FEATURE_RETURN_SUCCESS; 00899 } 00900 00901 if (check_compat(transferer, newchan)) { 00902 /* we do mean transferee here, NOT transferer */ 00903 finishup(transferee); 00904 return -1; 00905 } 00906 memset(&bconfig,0,sizeof(struct ast_bridge_config)); 00907 ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT); 00908 ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT); 00909 res = ast_bridge_call(transferer, newchan, &bconfig); 00910 if (newchan->_softhangup || !transferer->_softhangup) { 00911 ast_hangup(newchan); 00912 if (ast_stream_and_wait(transferer, xfersound, transferer->language, "")) 00913 ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); 00914 finishup(transferee); 00915 transferer->_softhangup = 0; 00916 return FEATURE_RETURN_SUCCESS; 00917 } 00918 00919 if (check_compat(transferee, newchan)) { 00920 finishup(transferee); 00921 return -1; 00922 } 00923 00924 ast_indicate(transferee, AST_CONTROL_UNHOLD); 00925 00926 if ((ast_autoservice_stop(transferee) < 0) 00927 || (ast_waitfordigit(transferee, 100) < 0) 00928 || (ast_waitfordigit(newchan, 100) < 0) 00929 || ast_check_hangup(transferee) 00930 || ast_check_hangup(newchan)) { 00931 ast_hangup(newchan); 00932 return -1; 00933 } 00934 00935 xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name); 00936 if (!xferchan) { 00937 ast_hangup(newchan); 00938 return -1; 00939 } 00940 /* Make formats okay */ 00941 xferchan->visible_indication = transferer->visible_indication; 00942 xferchan->readformat = transferee->readformat; 00943 xferchan->writeformat = transferee->writeformat; 00944 ast_channel_masquerade(xferchan, transferee); 00945 ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority); 00946 xferchan->_state = AST_STATE_UP; 00947 ast_clear_flag(xferchan, AST_FLAGS_ALL); 00948 xferchan->_softhangup = 0; 00949 00950 if ((f = ast_read(xferchan))) 00951 ast_frfree(f); 00952 00953 newchan->_state = AST_STATE_UP; 00954 ast_clear_flag(newchan, AST_FLAGS_ALL); 00955 newchan->_softhangup = 0; 00956 00957 tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj)); 00958 if (!tobj) { 00959 ast_hangup(xferchan); 00960 ast_hangup(newchan); 00961 return -1; 00962 } 00963 00964 /* For the case where the transfer target is being connected with the original 00965 caller store the target's original features, and apply to the bridge */ 00966 ast_channel_lock(newchan); 00967 if ((features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL))) { 00968 dialfeatures = features_datastore->data; 00969 } 00970 ast_channel_unlock(newchan); 00971 00972 if (dialfeatures) { 00973 ast_copy_flags(&(config->features_callee), &(dialfeatures->features_callee), AST_FLAGS_ALL); 00974 } 00975 00976 tobj->chan = newchan; 00977 tobj->peer = xferchan; 00978 tobj->bconfig = *config; 00979 00980 if (ast_stream_and_wait(newchan, xfersound, newchan->language, "")) 00981 ast_log(LOG_WARNING, "Failed to play transfer sound!\n"); 00982 ast_bridge_call_thread_launch(tobj); 00983 return -1; /* XXX meaning the channel is bridged ? */ 00984 }
static void* do_parking_thread | ( | void * | ignore | ) | [static] |
Take care of parked calls and unpark them if needed.
Definition at line 1786 of file res_features.c.
References ast_add_extension2(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, 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_datastore::data, dial_features_info, 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, ast_dial_features::options, 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().
01787 { 01788 fd_set rfds, efds; /* results from previous select, to be preserved across loops. */ 01789 FD_ZERO(&rfds); 01790 FD_ZERO(&efds); 01791 01792 for (;;) { 01793 struct parkeduser *pu, *pl, *pt = NULL; 01794 int ms = -1; /* select timeout, uninitialized */ 01795 int max = -1; /* max fd, none there yet */ 01796 fd_set nrfds, nefds; /* args for the next select */ 01797 FD_ZERO(&nrfds); 01798 FD_ZERO(&nefds); 01799 01800 ast_mutex_lock(&parking_lock); 01801 pl = NULL; 01802 pu = parkinglot; 01803 /* navigate the list with prev-cur pointers to support removals */ 01804 while (pu) { 01805 struct ast_channel *chan = pu->chan; /* shorthand */ 01806 int tms; /* timeout for this item */ 01807 int x; /* fd index in channel */ 01808 struct ast_context *con; 01809 01810 if (pu->notquiteyet) { /* Pretend this one isn't here yet */ 01811 pl = pu; 01812 pu = pu->next; 01813 continue; 01814 } 01815 tms = ast_tvdiff_ms(ast_tvnow(), pu->start); 01816 if (tms > pu->parkingtime) { 01817 ast_indicate(chan, AST_CONTROL_UNHOLD); 01818 /* Get chan, exten from derived kludge */ 01819 if (pu->peername[0]) { 01820 char *peername = ast_strdupa(pu->peername); 01821 char *cp = strrchr(peername, '-'); 01822 if (cp) 01823 *cp = 0; 01824 con = ast_context_find(parking_con_dial); 01825 if (!con) { 01826 con = ast_context_create(NULL, parking_con_dial, registrar); 01827 if (!con) 01828 ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial); 01829 } 01830 if (con) { 01831 char returnexten[AST_MAX_EXTENSION]; 01832 struct ast_datastore *features_datastore; 01833 struct ast_dial_features *dialfeatures = NULL; 01834 01835 ast_channel_lock(chan); 01836 01837 if ((features_datastore = ast_channel_datastore_find(chan, &dial_features_info, NULL))) 01838 dialfeatures = features_datastore->data; 01839 01840 ast_channel_unlock(chan); 01841 01842 if (dialfeatures) 01843 snprintf(returnexten, sizeof(returnexten), "%s|30|%s", peername, dialfeatures->options); 01844 else /* Existing default */ 01845 snprintf(returnexten, sizeof(returnexten), "%s|30|t", peername); 01846 01847 ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), ast_free, registrar); 01848 } 01849 set_c_e_p(chan, parking_con_dial, peername, 1); 01850 } else { 01851 /* They've been waiting too long, send them back to where they came. Theoretically they 01852 should have their original extensions and such, but we copy to be on the safe side */ 01853 set_c_e_p(chan, pu->context, pu->exten, pu->priority); 01854 } 01855 01856 post_manager_event("ParkedCallTimeOut", pu->parkingexten, chan); 01857 01858 if (option_verbose > 1) 01859 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); 01860 /* Start up the PBX, or hang them up */ 01861 if (ast_pbx_start(chan)) { 01862 ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name); 01863 ast_hangup(chan); 01864 } 01865 /* And take them out of the parking lot */ 01866 if (pl) 01867 pl->next = pu->next; 01868 else 01869 parkinglot = pu->next; 01870 pt = pu; 01871 pu = pu->next; 01872 con = ast_context_find(parking_con); 01873 if (con) { 01874 if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) 01875 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); 01876 else 01877 notify_metermaids(pt->parkingexten, parking_con); 01878 } else 01879 ast_log(LOG_WARNING, "Whoa, no parking context?\n"); 01880 free(pt); 01881 } else { /* still within parking time, process descriptors */ 01882 for (x = 0; x < AST_MAX_FDS; x++) { 01883 struct ast_frame *f; 01884 01885 if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds))) 01886 continue; /* nothing on this descriptor */ 01887 01888 if (FD_ISSET(chan->fds[x], &efds)) 01889 ast_set_flag(chan, AST_FLAG_EXCEPTION); 01890 else 01891 ast_clear_flag(chan, AST_FLAG_EXCEPTION); 01892 chan->fdno = x; 01893 01894 /* See if they need servicing */ 01895 f = ast_read(chan); 01896 if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) { 01897 if (f) 01898 ast_frfree(f); 01899 post_manager_event("ParkedCallGiveUp", pu->parkingexten, chan); 01900 01901 /* There's a problem, hang them up*/ 01902 if (option_verbose > 1) 01903 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name); 01904 ast_hangup(chan); 01905 /* And take them out of the parking lot */ 01906 if (pl) 01907 pl->next = pu->next; 01908 else 01909 parkinglot = pu->next; 01910 pt = pu; 01911 pu = pu->next; 01912 con = ast_context_find(parking_con); 01913 if (con) { 01914 if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) 01915 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); 01916 else 01917 notify_metermaids(pt->parkingexten, parking_con); 01918 } else 01919 ast_log(LOG_WARNING, "Whoa, no parking context?\n"); 01920 free(pt); 01921 break; 01922 } else { 01923 /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ 01924 ast_frfree(f); 01925 if (pu->moh_trys < 3 && !chan->generatordata) { 01926 if (option_debug) 01927 ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source. Restarting.\n"); 01928 ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 01929 S_OR(parkmohclass, NULL), 01930 !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); 01931 pu->moh_trys++; 01932 } 01933 goto std; /*! \todo XXX Ick: jumping into an else statement??? XXX */ 01934 } 01935 01936 } /* end for */ 01937 if (x >= AST_MAX_FDS) { 01938 std: for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */ 01939 if (chan->fds[x] > -1) { 01940 FD_SET(chan->fds[x], &nrfds); 01941 FD_SET(chan->fds[x], &nefds); 01942 if (chan->fds[x] > max) 01943 max = chan->fds[x]; 01944 } 01945 } 01946 /* Keep track of our shortest wait */ 01947 if (tms < ms || ms < 0) 01948 ms = tms; 01949 pl = pu; 01950 pu = pu->next; 01951 } 01952 } 01953 } /* end while */ 01954 ast_mutex_unlock(&parking_lock); 01955 rfds = nrfds; 01956 efds = nefds; 01957 { 01958 struct timeval tv = ast_samp2tv(ms, 1000); 01959 /* Wait for something to happen */ 01960 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); 01961 } 01962 pthread_testcancel(); 01963 } 01964 return NULL; /* Never reached */ 01965 }
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 1075 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_ONPEER, 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, FEATURE_SENSE_PEER, LOG_NOTICE, LOG_WARNING, ast_call_feature::moh_class, pbx_exec(), and pbx_findapp().
01076 { 01077 struct ast_app *app; 01078 struct ast_call_feature *feature = data; 01079 struct ast_channel *work, *idle; 01080 int res; 01081 01082 if (!feature) { /* shouldn't ever happen! */ 01083 ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n"); 01084 return -1; 01085 } 01086 01087 if (sense == FEATURE_SENSE_CHAN) { 01088 if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER)) 01089 return FEATURE_RETURN_KEEPTRYING; 01090 if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) { 01091 work = chan; 01092 idle = peer; 01093 } else { 01094 work = peer; 01095 idle = chan; 01096 } 01097 } else { 01098 if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE)) 01099 return FEATURE_RETURN_KEEPTRYING; 01100 if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) { 01101 work = peer; 01102 idle = chan; 01103 } else { 01104 work = chan; 01105 idle = peer; 01106 } 01107 } 01108 01109 if (!(app = pbx_findapp(feature->app))) { 01110 ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app); 01111 return -2; 01112 } 01113 01114 ast_autoservice_start(idle); 01115 01116 if (!ast_strlen_zero(feature->moh_class)) 01117 ast_moh_start(idle, feature->moh_class, NULL); 01118 01119 res = pbx_exec(work, app, feature->app_args); 01120 01121 if (!ast_strlen_zero(feature->moh_class)) 01122 ast_moh_stop(idle); 01123 01124 ast_autoservice_stop(idle); 01125 01126 if (res == AST_PBX_KEEPALIVE) { 01127 /* do not hangup peer if feature is to be activated on it */ 01128 if ((ast_test_flag(feature, AST_FEATURE_FLAG_ONPEER) && sense == FEATURE_SENSE_CHAN) || (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF) && sense == FEATURE_SENSE_PEER)) 01129 return FEATURE_RETURN_NO_HANGUP_PEER; 01130 else 01131 return FEATURE_RETURN_PBX_KEEPALIVE; 01132 } 01133 else if (res == AST_PBX_NO_HANGUP_PEER) 01134 return FEATURE_RETURN_NO_HANGUP_PEER; 01135 else if (res) 01136 return FEATURE_RETURN_SUCCESSBREAK; 01137 01138 return FEATURE_RETURN_SUCCESS; /*! \todo XXX should probably return res */ 01139 }
static struct ast_call_feature* find_dynamic_feature | ( | const char * | name | ) | [static] |
find a feature by name
Definition at line 1062 of file res_features.c.
References AST_LIST_TRAVERSE, and ast_call_feature::sname.
Referenced by ast_feature_interpret(), and set_config_flags().
01063 { 01064 struct ast_call_feature *tmp; 01065 01066 AST_LIST_TRAVERSE(&feature_list, tmp, feature_entry) { 01067 if (!strcasecmp(tmp->sname, name)) 01068 break; 01069 } 01070 01071 return tmp; 01072 }
static int finishup | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 672 of file res_features.c.
References ast_autoservice_stop(), AST_CONTROL_UNHOLD, and ast_indicate().
Referenced by builtin_blindtransfer(), and do_atxfer().
00673 { 00674 ast_indicate(chan, AST_CONTROL_UNHOLD); 00675 00676 return ast_autoservice_stop(chan); 00677 }
static int handle_parkedcalls | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 2187 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.
02188 { 02189 struct parkeduser *cur; 02190 int numparked = 0; 02191 02192 ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel" 02193 , "Context", "Extension", "Pri", "Timeout"); 02194 02195 ast_mutex_lock(&parking_lock); 02196 02197 for (cur = parkinglot; cur; cur = cur->next) { 02198 ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n" 02199 ,cur->parkingexten, cur->chan->name, cur->context, cur->exten 02200 ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL)); 02201 02202 numparked++; 02203 } 02204 ast_mutex_unlock(&parking_lock); 02205 ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : ""); 02206 02207 02208 return RESULT_SUCCESS; 02209 }
static int handle_showfeatures | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 2146 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.
02147 { 02148 int i; 02149 struct ast_call_feature *feature; 02150 char format[] = "%-25s %-7s %-7s\n"; 02151 02152 ast_cli(fd, format, "Builtin Feature", "Default", "Current"); 02153 ast_cli(fd, format, "---------------", "-------", "-------"); 02154 02155 ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */ 02156 02157 ast_rwlock_rdlock(&features_lock); 02158 for (i = 0; i < FEATURES_COUNT; i++) 02159 ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten); 02160 ast_rwlock_unlock(&features_lock); 02161 02162 ast_cli(fd, "\n"); 02163 ast_cli(fd, format, "Dynamic Feature", "Default", "Current"); 02164 ast_cli(fd, format, "---------------", "-------", "-------"); 02165 if (AST_LIST_EMPTY(&feature_list)) 02166 ast_cli(fd, "(none)\n"); 02167 else { 02168 AST_LIST_LOCK(&feature_list); 02169 AST_LIST_TRAVERSE(&feature_list, feature, feature_entry) 02170 ast_cli(fd, format, feature->sname, "no def", feature->exten); 02171 AST_LIST_UNLOCK(&feature_list); 02172 } 02173 ast_cli(fd, "\nCall parking\n"); 02174 ast_cli(fd, "------------\n"); 02175 ast_cli(fd,"%-20s: %s\n", "Parking extension", parking_ext); 02176 ast_cli(fd,"%-20s: %s\n", "Parking context", parking_con); 02177 ast_cli(fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop); 02178 ast_cli(fd,"\n"); 02179 02180 return RESULT_SUCCESS; 02181 }
static int load_config | ( | void | ) | [static] |
Definition at line 2383 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.
02384 { 02385 int start = 0, end = 0; 02386 int res; 02387 struct ast_context *con = NULL; 02388 struct ast_config *cfg = NULL; 02389 struct ast_variable *var = NULL; 02390 char old_parking_ext[AST_MAX_EXTENSION]; 02391 char old_parking_con[AST_MAX_EXTENSION] = ""; 02392 02393 if (!ast_strlen_zero(parking_con)) { 02394 strcpy(old_parking_ext, parking_ext); 02395 strcpy(old_parking_con, parking_con); 02396 } 02397 02398 /* Reset to defaults */ 02399 strcpy(parking_con, "parkedcalls"); 02400 strcpy(parking_con_dial, "park-dial"); 02401 strcpy(parking_ext, "700"); 02402 strcpy(pickup_ext, "*8"); 02403 strcpy(parkmohclass, "default"); 02404 courtesytone[0] = '\0'; 02405 strcpy(xfersound, "beep"); 02406 strcpy(xferfailsound, "pbx-invalid"); 02407 parking_start = 701; 02408 parking_stop = 750; 02409 parkfindnext = 0; 02410 adsipark = 0; 02411 parkaddhints = 0; 02412 02413 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; 02414 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; 02415 atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER; 02416 02417 cfg = ast_config_load("features.conf"); 02418 if (!cfg) { 02419 ast_log(LOG_WARNING,"Could not load features.conf\n"); 02420 return AST_MODULE_LOAD_DECLINE; 02421 } 02422 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { 02423 if (!strcasecmp(var->name, "parkext")) { 02424 ast_copy_string(parking_ext, var->value, sizeof(parking_ext)); 02425 } else if (!strcasecmp(var->name, "context")) { 02426 ast_copy_string(parking_con, var->value, sizeof(parking_con)); 02427 } else if (!strcasecmp(var->name, "parkingtime")) { 02428 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) { 02429 ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value); 02430 parkingtime = DEFAULT_PARK_TIME; 02431 } else 02432 parkingtime = parkingtime * 1000; 02433 } else if (!strcasecmp(var->name, "parkpos")) { 02434 if (sscanf(var->value, "%d-%d", &start, &end) != 2) { 02435 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); 02436 } else { 02437 parking_start = start; 02438 parking_stop = end; 02439 } 02440 } else if (!strcasecmp(var->name, "findslot")) { 02441 parkfindnext = (!strcasecmp(var->value, "next")); 02442 } else if (!strcasecmp(var->name, "parkinghints")) { 02443 parkaddhints = ast_true(var->value); 02444 } else if (!strcasecmp(var->name, "adsipark")) { 02445 adsipark = ast_true(var->value); 02446 } else if (!strcasecmp(var->name, "transferdigittimeout")) { 02447 if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) { 02448 ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value); 02449 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; 02450 } else 02451 transferdigittimeout = transferdigittimeout * 1000; 02452 } else if (!strcasecmp(var->name, "featuredigittimeout")) { 02453 if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) { 02454 ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value); 02455 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; 02456 } 02457 } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) { 02458 if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) { 02459 ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value); 02460 atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER; 02461 } else 02462 atxfernoanswertimeout = atxfernoanswertimeout * 1000; 02463 } else if (!strcasecmp(var->name, "courtesytone")) { 02464 ast_copy_string(courtesytone, var->value, sizeof(courtesytone)); 02465 } else if (!strcasecmp(var->name, "parkedplay")) { 02466 if (!strcasecmp(var->value, "both")) 02467 parkedplay = 2; 02468 else if (!strcasecmp(var->value, "parked")) 02469 parkedplay = 1; 02470 else 02471 parkedplay = 0; 02472 } else if (!strcasecmp(var->name, "xfersound")) { 02473 ast_copy_string(xfersound, var->value, sizeof(xfersound)); 02474 } else if (!strcasecmp(var->name, "xferfailsound")) { 02475 ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound)); 02476 } else if (!strcasecmp(var->name, "pickupexten")) { 02477 ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext)); 02478 } else if (!strcasecmp(var->name, "parkedmusicclass")) { 02479 ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass)); 02480 } 02481 } 02482 02483 unmap_features(); 02484 for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) { 02485 if (remap_feature(var->name, var->value)) 02486 ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name); 02487 } 02488 02489 /* Map a key combination to an application*/ 02490 ast_unregister_features(); 02491 for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) { 02492 char *tmp_val = ast_strdupa(var->value); 02493 char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 02494 struct ast_call_feature *feature; 02495 02496 /* strsep() sets the argument to NULL if match not found, and it 02497 * is safe to use it with a NULL argument, so we don't check 02498 * between calls. 02499 */ 02500 exten = strsep(&tmp_val,","); 02501 activatedby = strsep(&tmp_val,","); 02502 app = strsep(&tmp_val,","); 02503 app_args = strsep(&tmp_val,","); 02504 moh_class = strsep(&tmp_val,","); 02505 02506 activateon = strsep(&activatedby, "/"); 02507 02508 /*! \todo XXX var_name or app_args ? */ 02509 if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) { 02510 ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n", 02511 app, exten, activateon, var->name); 02512 continue; 02513 } 02514 02515 AST_LIST_LOCK(&feature_list); 02516 if ((feature = find_dynamic_feature(var->name))) { 02517 AST_LIST_UNLOCK(&feature_list); 02518 ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name); 02519 continue; 02520 } 02521 AST_LIST_UNLOCK(&feature_list); 02522 02523 if (!(feature = ast_calloc(1, sizeof(*feature)))) 02524 continue; 02525 02526 ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN); 02527 ast_copy_string(feature->app, app, FEATURE_APP_LEN); 02528 ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN); 02529 02530 if (app_args) 02531 ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN); 02532 02533 if (moh_class) 02534 ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN); 02535 02536 ast_copy_string(feature->exten, exten, sizeof(feature->exten)); 02537 feature->operation = feature_exec_app; 02538 ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF); 02539 02540 /* Allow caller and calle to be specified for backwards compatability */ 02541 if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller")) 02542 ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF); 02543 else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee")) 02544 ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER); 02545 else { 02546 ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s'," 02547 " must be 'self', or 'peer'\n", var->name); 02548 continue; 02549 } 02550 02551 if (ast_strlen_zero(activatedby)) 02552 ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH); 02553 else if (!strcasecmp(activatedby, "caller")) 02554 ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER); 02555 else if (!strcasecmp(activatedby, "callee")) 02556 ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE); 02557 else if (!strcasecmp(activatedby, "both")) 02558 ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH); 02559 else { 02560 ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s'," 02561 " must be 'caller', or 'callee', or 'both'\n", var->name); 02562 continue; 02563 } 02564 02565 ast_register_feature(feature); 02566 02567 if (option_verbose >= 1) 02568 ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten); 02569 } 02570 ast_config_destroy(cfg); 02571 02572 /* Remove the old parking extension */ 02573 if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) { 02574 if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar)) 02575 notify_metermaids(old_parking_ext, old_parking_con); 02576 if (option_debug) 02577 ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con); 02578 } 02579 02580 if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) { 02581 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); 02582 return -1; 02583 } 02584 res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar); 02585 if (parkaddhints) 02586 park_add_hints(parking_con, parking_start, parking_stop); 02587 if (!res) 02588 notify_metermaids(ast_parking_ext(), parking_con); 02589 return res; 02590 02591 }
static int load_module | ( | void | ) | [static] |
Definition at line 2598 of file res_features.c.
References ast_cli_register_multiple(), ast_devstate_prov_add(), ast_manager_register, ast_manager_register2(), ast_pthread_create, ast_register_application(), cli_features, descrip, descrip2, do_parking_thread(), EVENT_FLAG_CALL, load_config(), manager_park(), manager_parking_status(), mandescr_park, metermaidstate(), park_call_exec(), park_exec(), parkcall, parkedcall, parking_con, parking_ext, parking_thread, synopsis, and synopsis2.
02599 { 02600 int res; 02601 02602 memset(parking_ext, 0, sizeof(parking_ext)); 02603 memset(parking_con, 0, sizeof(parking_con)); 02604 02605 if ((res = load_config())) 02606 return res; 02607 ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); 02608 ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL); 02609 res = ast_register_application(parkedcall, park_exec, synopsis, descrip); 02610 if (!res) 02611 res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2); 02612 if (!res) { 02613 ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" ); 02614 ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park, 02615 "Park a channel", mandescr_park); 02616 } 02617 02618 res |= ast_devstate_prov_add("Park", metermaidstate); 02619 02620 return res; 02621 }
static int manager_park | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 2278 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().
02279 { 02280 const char *channel = astman_get_header(m, "Channel"); 02281 const char *channel2 = astman_get_header(m, "Channel2"); 02282 const char *timeout = astman_get_header(m, "Timeout"); 02283 char buf[BUFSIZ]; 02284 int to = 0; 02285 int res = 0; 02286 int parkExt = 0; 02287 struct ast_channel *ch1, *ch2; 02288 02289 if (ast_strlen_zero(channel)) { 02290 astman_send_error(s, m, "Channel not specified"); 02291 return 0; 02292 } 02293 02294 if (ast_strlen_zero(channel2)) { 02295 astman_send_error(s, m, "Channel2 not specified"); 02296 return 0; 02297 } 02298 02299 ch1 = ast_get_channel_by_name_locked(channel); 02300 if (!ch1) { 02301 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel); 02302 astman_send_error(s, m, buf); 02303 return 0; 02304 } 02305 02306 ch2 = ast_get_channel_by_name_locked(channel2); 02307 if (!ch2) { 02308 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2); 02309 astman_send_error(s, m, buf); 02310 ast_channel_unlock(ch1); 02311 return 0; 02312 } 02313 02314 if (!ast_strlen_zero(timeout)) { 02315 sscanf(timeout, "%d", &to); 02316 } 02317 02318 res = ast_masq_park_call(ch1, ch2, to, &parkExt); 02319 if (!res) { 02320 ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT); 02321 astman_send_ack(s, m, "Park successful"); 02322 } else { 02323 astman_send_error(s, m, "Park failure"); 02324 } 02325 02326 ast_channel_unlock(ch1); 02327 ast_channel_unlock(ch2); 02328 02329 return 0; 02330 }
static int manager_parking_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Dump lot status.
Definition at line 2231 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().
02232 { 02233 struct parkeduser *cur; 02234 const char *id = astman_get_header(m, "ActionID"); 02235 char idText[256] = ""; 02236 02237 if (!ast_strlen_zero(id)) 02238 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 02239 02240 astman_send_ack(s, m, "Parked calls will follow"); 02241 02242 ast_mutex_lock(&parking_lock); 02243 02244 for (cur = parkinglot; cur; cur = cur->next) { 02245 astman_append(s, "Event: ParkedCall\r\n" 02246 "Exten: %d\r\n" 02247 "Channel: %s\r\n" 02248 "From: %s\r\n" 02249 "Timeout: %ld\r\n" 02250 "CallerID: %s\r\n" 02251 "CallerIDName: %s\r\n" 02252 "%s" 02253 "\r\n", 02254 cur->parkingnum, cur->chan->name, cur->peername, 02255 (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL), 02256 S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is <unknown> */ 02257 S_OR(cur->chan->cid.cid_name, ""), 02258 idText); 02259 } 02260 02261 astman_append(s, 02262 "Event: ParkedCallsComplete\r\n" 02263 "%s" 02264 "\r\n",idText); 02265 02266 ast_mutex_unlock(&parking_lock); 02267 02268 return RESULT_SUCCESS; 02269 }
static int metermaidstate | ( | const char * | data | ) | [static] |
metermaids callback from devicestate.c
Definition at line 284 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().
00285 { 00286 int res = AST_DEVICE_INVALID; 00287 char *context = ast_strdupa(data); 00288 char *exten; 00289 00290 exten = strsep(&context, "@"); 00291 if (!context) 00292 return res; 00293 00294 if (option_debug > 3) 00295 ast_log(LOG_DEBUG, "Checking state of exten %s in context %s\n", exten, context); 00296 00297 res = ast_exists_extension(NULL, context, exten, 1, NULL); 00298 00299 if (!res) 00300 return AST_DEVICE_NOT_INUSE; 00301 else 00302 return AST_DEVICE_INUSE; 00303 }
static void notify_metermaids | ( | char * | exten, | |
char * | context | |||
) | [static] |
Notify metermaids that we've changed an extension.
Definition at line 273 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().
00274 { 00275 if (option_debug > 3) 00276 ast_log(LOG_DEBUG, "Notification of state change to metermaids %s@%s\n", exten, context); 00277 00278 /* Send notification to devicestate subsystem */ 00279 ast_device_state_changed("park:%s@%s", exten, context); 00280 return; 00281 }
static void park_add_hints | ( | char * | context, | |
int | start, | |||
int | stop | |||
) | [static] |
Add parking hints for all defined parking lots.
Definition at line 2369 of file res_features.c.
References ast_add_extension(), AST_MAX_EXTENSION, exten, PRIORITY_HINT, and registrar.
02370 { 02371 int numext; 02372 char device[AST_MAX_EXTENSION]; 02373 char exten[10]; 02374 02375 for (numext = start; numext <= stop; numext++) { 02376 snprintf(exten, sizeof(exten), "%d", numext); 02377 snprintf(device, sizeof(device), "park:%s@%s", exten, context); 02378 ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar); 02379 } 02380 }
static int park_call_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Park a call.
Definition at line 1968 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().
01969 { 01970 /* Cache the original channel name in case we get masqueraded in the middle 01971 * of a park--it is still theoretically possible for a transfer to happen before 01972 * we get here, but it is _really_ unlikely */ 01973 char *orig_chan_name = ast_strdupa(chan->name); 01974 char orig_exten[AST_MAX_EXTENSION]; 01975 int orig_priority = chan->priority; 01976 01977 /* Data is unused at the moment but could contain a parking 01978 lot context eventually */ 01979 int res = 0; 01980 struct ast_module_user *u; 01981 01982 u = ast_module_user_add(chan); 01983 01984 ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten)); 01985 01986 /* Setup the exten/priority to be s/1 since we don't know 01987 where this call should return */ 01988 strcpy(chan->exten, "s"); 01989 chan->priority = 1; 01990 /* Answer if call is not up */ 01991 if (chan->_state != AST_STATE_UP) 01992 res = ast_answer(chan); 01993 /* Sleep to allow VoIP streams to settle down */ 01994 if (!res) 01995 res = ast_safe_sleep(chan, 1000); 01996 /* Park the call */ 01997 if (!res) { 01998 res = park_call_full(chan, chan, 0, NULL, orig_chan_name); 01999 /* Continue on in the dialplan */ 02000 if (res == 1) { 02001 ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten)); 02002 chan->priority = orig_priority; 02003 res = 0; 02004 } else if (!res) 02005 res = AST_PBX_KEEPALIVE; 02006 } 02007 02008 ast_module_user_remove(u); 02009 02010 return res; 02011 }
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 305 of file res_features.c.
References adsi_announce_park(), ast_channel::appl, ast_add_extension2(), ast_adsi_available(), ast_adsi_unload_session(), ast_bridged_channel(), ast_calloc, AST_CHANNEL_NAME, ast_channel_unlock, ast_clear_flag, ast_context_create(), ast_context_find(), AST_CONTROL_HOLD, ast_exists_extension(), AST_FLAG_MASQ_NOSTREAM, ast_free, ast_get_channel_by_name_locked(), 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, ast_channel::tech, ast_channel_tech::type, and VERBOSE_PREFIX_2.
Referenced by ast_masq_park_call(), ast_park_call(), and park_call_exec().
00306 { 00307 struct parkeduser *pu, *cur; 00308 int i, x = -1, parking_range; 00309 struct ast_context *con; 00310 const char *parkingexten; 00311 00312 /* Allocate memory for parking data */ 00313 if (!(pu = ast_calloc(1, sizeof(*pu)))) 00314 return -1; 00315 00316 /* Lock parking lot */ 00317 ast_mutex_lock(&parking_lock); 00318 /* Check for channel variable PARKINGEXTEN */ 00319 parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN"); 00320 if (!ast_strlen_zero(parkingexten)) { 00321 /*!\note The API forces us to specify a numeric parking slot, even 00322 * though the architecture would tend to support non-numeric extensions 00323 * (as are possible with SIP, for example). Hence, we enforce that 00324 * limitation here. If extout was not numeric, we could permit 00325 * arbitrary non-numeric extensions. 00326 */ 00327 if (sscanf(parkingexten, "%d", &x) != 1 || x < 0) { 00328 ast_log(LOG_WARNING, "PARKINGEXTEN does not indicate a valid parking slot: '%s'.\n", parkingexten); 00329 ast_mutex_unlock(&parking_lock); 00330 free(pu); 00331 return 1; /* Continue execution if possible */ 00332 } 00333 snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x); 00334 00335 if (ast_exists_extension(NULL, parking_con, pu->parkingexten, 1, NULL)) { 00336 ast_mutex_unlock(&parking_lock); 00337 free(pu); 00338 ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con); 00339 return 1; /* Continue execution if possible */ 00340 } 00341 } else { 00342 /* Select parking space within range */ 00343 parking_range = parking_stop - parking_start+1; 00344 for (i = 0; i < parking_range; i++) { 00345 x = (i + parking_offset) % parking_range + parking_start; 00346 cur = parkinglot; 00347 while(cur) { 00348 if (cur->parkingnum == x) 00349 break; 00350 cur = cur->next; 00351 } 00352 if (!cur) 00353 break; 00354 } 00355 00356 if (!(i < parking_range)) { 00357 ast_log(LOG_WARNING, "No more parking spaces\n"); 00358 free(pu); 00359 ast_mutex_unlock(&parking_lock); 00360 return -1; 00361 } 00362 /* Set pointer for next parking */ 00363 if (parkfindnext) 00364 parking_offset = x - parking_start + 1; 00365 snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x); 00366 } 00367 00368 chan->appl = "Parked Call"; 00369 chan->data = NULL; 00370 00371 pu->chan = chan; 00372 00373 /* Put the parked channel on hold if we have two different channels */ 00374 if (chan != peer) { 00375 ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 00376 S_OR(parkmohclass, NULL), 00377 !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); 00378 } 00379 00380 pu->start = ast_tvnow(); 00381 pu->parkingnum = x; 00382 pu->parkingtime = (timeout > 0) ? timeout : parkingtime; 00383 if (extout) 00384 *extout = x; 00385 00386 if (peer) { 00387 /* This is so ugly that it hurts, but implementing get_base_channel() on local channels 00388 could have ugly side effects. We could have transferer<->local,1<->local,2<->parking 00389 and we need the callback name to be that of transferer. Since local,1/2 have the same 00390 name we can be tricky and just grab the bridged channel from the other side of the local 00391 */ 00392 if (!strcasecmp(peer->tech->type, "Local")) { 00393 struct ast_channel *tmpchan, *base_peer; 00394 char other_side[AST_CHANNEL_NAME]; 00395 char *c; 00396 ast_copy_string(other_side, peer->name, sizeof(other_side)); 00397 if ((c = strrchr(other_side, ','))) { 00398 *++c = '1'; 00399 } 00400 if ((tmpchan = ast_get_channel_by_name_locked(other_side))) { 00401 if ((base_peer = ast_bridged_channel(tmpchan))) { 00402 ast_copy_string(pu->peername, base_peer->name, sizeof(pu->peername)); 00403 } 00404 ast_channel_unlock(tmpchan); 00405 } 00406 } else { 00407 ast_copy_string(pu->peername, peer->name, sizeof(pu->peername)); 00408 } 00409 } 00410 00411 /* Remember what had been dialed, so that if the parking 00412 expires, we try to come back to the same place */ 00413 ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context)); 00414 ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten)); 00415 pu->priority = chan->macropriority ? chan->macropriority : chan->priority; 00416 pu->next = parkinglot; 00417 parkinglot = pu; 00418 00419 /* If parking a channel directly, don't quiet yet get parking running on it */ 00420 if (peer == chan) 00421 pu->notquiteyet = 1; 00422 ast_mutex_unlock(&parking_lock); 00423 /* Wake up the (presumably select()ing) thread */ 00424 pthread_kill(parking_thread, SIGURG); 00425 if (option_verbose > 1) 00426 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)); 00427 00428 manager_event(EVENT_FLAG_CALL, "ParkedCall", 00429 "Exten: %s\r\n" 00430 "Channel: %s\r\n" 00431 "From: %s\r\n" 00432 "Timeout: %ld\r\n" 00433 "CallerID: %s\r\n" 00434 "CallerIDName: %s\r\n", 00435 pu->parkingexten, pu->chan->name, peer ? peer->name : "", 00436 (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL), 00437 S_OR(pu->chan->cid.cid_num, "<unknown>"), 00438 S_OR(pu->chan->cid.cid_name, "<unknown>") 00439 ); 00440 00441 if (peer && adsipark && ast_adsi_available(peer)) { 00442 adsi_announce_park(peer, pu->parkingexten); /* Only supports parking numbers */ 00443 ast_adsi_unload_session(peer); 00444 } 00445 00446 con = ast_context_find(parking_con); 00447 if (!con) 00448 con = ast_context_create(NULL, parking_con, registrar); 00449 if (!con) /* Still no context? Bad */ 00450 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); 00451 /* Tell the peer channel the number of the parking space */ 00452 if (peer && (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 */ 00453 /* Make sure we don't start saying digits to the channel being parked */ 00454 ast_set_flag(peer, AST_FLAG_MASQ_NOSTREAM); 00455 ast_say_digits(peer, pu->parkingnum, "", peer->language); 00456 ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM); 00457 } 00458 if (con) { 00459 if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), ast_free, registrar)) 00460 notify_metermaids(pu->parkingexten, parking_con); 00461 } 00462 if (pu->notquiteyet) { 00463 /* Wake up parking thread if we're really done */ 00464 ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 00465 S_OR(parkmohclass, NULL), 00466 !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); 00467 pu->notquiteyet = 0; 00468 pthread_kill(parking_thread, SIGURG); 00469 } 00470 return 0; 00471 }
static int park_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Pickup parked call.
Definition at line 2014 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, 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().
02015 { 02016 int res = 0; 02017 struct ast_module_user *u; 02018 struct ast_channel *peer=NULL; 02019 struct parkeduser *pu, *pl=NULL; 02020 struct ast_context *con; 02021 02022 int park; 02023 struct ast_bridge_config config; 02024 02025 if (!data) { 02026 ast_log(LOG_WARNING, "Parkedcall requires an argument (extension number)\n"); 02027 return -1; 02028 } 02029 02030 u = ast_module_user_add(chan); 02031 02032 park = atoi((char *)data); 02033 ast_mutex_lock(&parking_lock); 02034 pu = parkinglot; 02035 while(pu) { 02036 if (pu->parkingnum == park) { 02037 if (pl) 02038 pl->next = pu->next; 02039 else 02040 parkinglot = pu->next; 02041 break; 02042 } 02043 pl = pu; 02044 pu = pu->next; 02045 } 02046 ast_mutex_unlock(&parking_lock); 02047 if (pu) { 02048 peer = pu->chan; 02049 con = ast_context_find(parking_con); 02050 if (con) { 02051 if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL)) 02052 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); 02053 else 02054 notify_metermaids(pu->parkingexten, parking_con); 02055 } else 02056 ast_log(LOG_WARNING, "Whoa, no parking context?\n"); 02057 02058 manager_event(EVENT_FLAG_CALL, "UnParkedCall", 02059 "Exten: %s\r\n" 02060 "Channel: %s\r\n" 02061 "From: %s\r\n" 02062 "CallerID: %s\r\n" 02063 "CallerIDName: %s\r\n", 02064 pu->parkingexten, pu->chan->name, chan->name, 02065 S_OR(pu->chan->cid.cid_num, "<unknown>"), 02066 S_OR(pu->chan->cid.cid_name, "<unknown>") 02067 ); 02068 02069 free(pu); 02070 } 02071 /* JK02: it helps to answer the channel if not already up */ 02072 if (chan->_state != AST_STATE_UP) 02073 ast_answer(chan); 02074 02075 if (peer) { 02076 /* Play a courtesy to the source(s) configured to prefix the bridge connecting */ 02077 02078 if (!ast_strlen_zero(courtesytone)) { 02079 int error = 0; 02080 ast_indicate(peer, AST_CONTROL_UNHOLD); 02081 if (parkedplay == 0) { 02082 error = ast_stream_and_wait(chan, courtesytone, chan->language, ""); 02083 } else if (parkedplay == 1) { 02084 error = ast_stream_and_wait(peer, courtesytone, chan->language, ""); 02085 } else if (parkedplay == 2) { 02086 if (!ast_streamfile(chan, courtesytone, chan->language) && 02087 !ast_streamfile(peer, courtesytone, chan->language)) { 02088 /*! \todo XXX we would like to wait on both! */ 02089 res = ast_waitstream(chan, ""); 02090 if (res >= 0) 02091 res = ast_waitstream(peer, ""); 02092 if (res < 0) 02093 error = 1; 02094 } 02095 } 02096 if (error) { 02097 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); 02098 ast_hangup(peer); 02099 ast_module_user_remove(u); 02100 return -1; 02101 } 02102 } else 02103 ast_indicate(peer, AST_CONTROL_UNHOLD); 02104 02105 res = ast_channel_make_compatible(chan, peer); 02106 if (res < 0) { 02107 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); 02108 ast_hangup(peer); 02109 ast_module_user_remove(u); 02110 return -1; 02111 } 02112 /* This runs sorta backwards, since we give the incoming channel control, as if it 02113 were the person called. */ 02114 if (option_verbose > 2) 02115 ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park); 02116 02117 pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); 02118 ast_cdr_setdestchan(chan->cdr, peer->name); 02119 memset(&config, 0, sizeof(struct ast_bridge_config)); 02120 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); 02121 ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); 02122 res = ast_bridge_call(chan, peer, &config); 02123 02124 pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); 02125 ast_cdr_setdestchan(chan->cdr, peer->name); 02126 02127 /* Simulate the PBX hanging up */ 02128 if (res != AST_PBX_NO_HANGUP_PEER) 02129 ast_hangup(peer); 02130 ast_module_user_remove(u); 02131 return res; 02132 } else { 02133 /*! \todo XXX Play a message XXX */ 02134 if (ast_stream_and_wait(chan, "pbx-invalidpark", chan->language, "")) 02135 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name); 02136 if (option_verbose > 2) 02137 ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park); 02138 res = -1; 02139 } 02140 02141 ast_module_user_remove(u); 02142 02143 return res; 02144 }
static void post_manager_event | ( | const char * | s, | |
char * | parkingexten, | |||
struct ast_channel * | chan | |||
) | [static] |
Definition at line 1771 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().
01772 { 01773 manager_event(EVENT_FLAG_CALL, s, 01774 "Exten: %s\r\n" 01775 "Channel: %s\r\n" 01776 "CallerID: %s\r\n" 01777 "CallerIDName: %s\r\n\r\n", 01778 parkingexten, 01779 chan->name, 01780 S_OR(chan->cid.cid_num, "<unknown>"), 01781 S_OR(chan->cid.cid_name, "<unknown>") 01782 ); 01783 }
static const char* real_ctx | ( | struct ast_channel * | transferer, | |
struct ast_channel * | transferee | |||
) | [static] |
Find the context for the transfer.
Definition at line 680 of file res_features.c.
References ast_strlen_zero(), ast_channel::context, ast_channel::macrocontext, pbx_builtin_getvar_helper(), and s.
Referenced by builtin_blindtransfer(), and do_atxfer().
00681 { 00682 const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"); 00683 if (ast_strlen_zero(s)) 00684 s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT"); 00685 if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */ 00686 s = transferer->macrocontext; 00687 if (ast_strlen_zero(s)) 00688 s = transferer->context; 00689 return s; 00690 }
static int reload | ( | void | ) | [static] |
Definition at line 2593 of file res_features.c.
References load_config().
02594 { 02595 return load_config(); 02596 }
static int remap_feature | ( | const char * | name, | |
const char * | value | |||
) | [static] |
Definition at line 1151 of file res_features.c.
References ast_rwlock_unlock(), ast_rwlock_wrlock(), builtin_features, exten, and FEATURES_COUNT.
01152 { 01153 int x, res = -1; 01154 01155 ast_rwlock_wrlock(&features_lock); 01156 for (x = 0; x < FEATURES_COUNT; x++) { 01157 if (strcasecmp(builtin_features[x].sname, name)) 01158 continue; 01159 01160 ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten)); 01161 res = 0; 01162 break; 01163 } 01164 ast_rwlock_unlock(&features_lock); 01165 01166 return res; 01167 }
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 180 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().
00181 { 00182 ast_copy_string(chan->context, context, sizeof(chan->context)); 00183 ast_copy_string(chan->exten, ext, sizeof(chan->exten)); 00184 chan->priority = pri; 00185 }
static void set_config_flags | ( | struct ast_channel * | chan, | |
struct ast_channel * | peer, | |||
struct ast_bridge_config * | config | |||
) | [static] |
Definition at line 1237 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().
01238 { 01239 int x; 01240 01241 ast_clear_flag(config, AST_FLAGS_ALL); 01242 01243 ast_rwlock_rdlock(&features_lock); 01244 for (x = 0; x < FEATURES_COUNT; x++) { 01245 if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF)) 01246 continue; 01247 01248 if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask)) 01249 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0); 01250 01251 if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask)) 01252 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1); 01253 } 01254 ast_rwlock_unlock(&features_lock); 01255 01256 if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) { 01257 const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"); 01258 01259 if (dynamic_features) { 01260 char *tmp = ast_strdupa(dynamic_features); 01261 char *tok; 01262 struct ast_call_feature *feature; 01263 01264 /* while we have a feature */ 01265 while ((tok = strsep(&tmp, "#"))) { 01266 AST_LIST_LOCK(&feature_list); 01267 if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) { 01268 if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER)) 01269 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0); 01270 if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE)) 01271 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1); 01272 } 01273 AST_LIST_UNLOCK(&feature_list); 01274 } 01275 } 01276 } 01277 }
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 535 of file res_features.c.
References FEATURE_SENSE_PEER.
Referenced by builtin_automonitor(), builtin_blindtransfer(), builtin_parkcall(), and do_atxfer().
00537 { 00538 if (sense == FEATURE_SENSE_PEER) { 00539 *caller = peer; 00540 *callee = chan; 00541 } else { 00542 *callee = peer; 00543 *caller = chan; 00544 } 00545 }
static int unload_module | ( | void | ) | [static] |
Definition at line 2624 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(), cli_features, parkcall, and parkedcall.
02625 { 02626 ast_module_user_hangup_all(); 02627 02628 ast_manager_unregister("ParkedCalls"); 02629 ast_manager_unregister("Park"); 02630 ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); 02631 ast_unregister_application(parkcall); 02632 ast_devstate_prov_del("Park"); 02633 return ast_unregister_application(parkedcall); 02634 }
static void unmap_features | ( | void | ) | [static] |
Definition at line 1141 of file res_features.c.
References ast_rwlock_unlock(), ast_rwlock_wrlock(), builtin_features, exten, and FEATURES_COUNT.
01142 { 01143 int x; 01144 01145 ast_rwlock_wrlock(&features_lock); 01146 for (x = 0; x < FEATURES_COUNT; x++) 01147 strcpy(builtin_features[x].exten, builtin_features[x].default_exten); 01148 ast_rwlock_unlock(&features_lock); 01149 }
int adsipark [static] |
int atxfernoanswertimeout [static] |
struct ast_call_feature builtin_features[] [static] |
Definition at line 1010 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 2220 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 2215 of file res_features.c.
char courtesytone[256] [static] |
Courtesy tone
Definition at line 95 of file res_features.c.
Referenced by 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 115 of file res_features.c.
char* descrip2 [static] |
Definition at line 125 of file res_features.c.
int featuredigittimeout [static] |
Definition at line 106 of file res_features.c.
char mandescr_park[] [static] |
struct ast_app* monitor_app = NULL [static] |
Definition at line 136 of file res_features.c.
Referenced by ast_bridge_call(), and builtin_automonitor().
int monitor_ok = 1 [static] |
Definition at line 137 of file res_features.c.
int parkaddhints = 0 [static] |
Add parking hints automatically
Definition at line 85 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 96 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 87 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 88 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 89 of file res_features.c.
Referenced by handle_showfeatures(), load_config(), and load_module().
int parking_offset [static] |
Definition at line 100 of file res_features.c.
int parking_start [static] |
First available extension for parking
Definition at line 92 of file res_features.c.
Referenced by handle_showfeatures(), and load_config().
int parking_stop [static] |
Last available extension for parking
Definition at line 93 of file res_features.c.
Referenced by handle_showfeatures(), and load_config().
pthread_t parking_thread [static] |
struct parkeduser* parkinglot [static] |
Definition at line 154 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 86 of file res_features.c.
char parkmohclass[MAX_MUSICCLASS] [static] |
Music class used for parking
Definition at line 91 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 110 of file res_features.c.
char showfeatures_help[] [static] |
Initial value:
"Usage: feature list\n" " Lists currently configured features.\n"
Definition at line 2183 of file res_features.c.
char showparked_help[] [static] |
Initial value:
"Usage: show parkedcalls\n" " Lists currently parked calls.\n"
Definition at line 2211 of file res_features.c.
char* synopsis = "Answer a parked call" [static] |
Definition at line 113 of file res_features.c.
char* synopsis2 = "Park yourself" [static] |
Definition at line 123 of file res_features.c.
int transferdigittimeout [static] |
char xferfailsound[256] [static] |
Call transfer failure sound
Definition at line 98 of file res_features.c.
Referenced by load_config().
char xfersound[256] [static] |