#include <asterisk/module.h>
#include <asterisk/translate.h>
#include <asterisk/say.h>
+#include <asterisk/config.h>
#include <asterisk/parking.h>
#include <asterisk/musiconhold.h>
#include <asterisk/callerid.h>
" 'g' -- goes on in context if the destination channel hangs up\n"
" 'A(x)' -- play an announcement to the called party, using x as file\n"
" 'S(x)' -- hangup the call after x seconds AFTER called party picked up\n"
+" 'L(x[:y][:z])' -- Limit the call to 'x' ms warning when 'y' ms are left (repeated every 'z' ms)\n"
+" -- Only 'x' is required, 'y' and 'z' are optional.\n"
+" -- The following special variables are optional:\n"
+" ** LIMIT_PLAYAUDIO_CALLER (default yes) Play sounds to the caller.\n"
+" ** LIMIT_PLAYAUDIO_CALLEE Play sounds to the callee.\n"
+" ** LIMIT_TIMEOUT_FILE File to play when time is up.\n"
+" ** LIMIT_CONNECT_FILE File to play when call begins.\n"
+" ** LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
+" -- 'timeleft' is a special sound macro to auto-say the time left and is the default.\n\n"
" In addition to transferring the call, a call may be parked and then picked\n"
"up by another user.\n"
" The optional URL will be sent to the called party if the channel supports\n"
unsigned int calldurationlimit=0;
char *cdl;
time_t now;
-
+ struct ast_bridge_config config;
+ long timelimit = 0;
+ long play_warning = 0;
+ long warning_freq=0;
+ char *warning_sound=NULL;
+ char *end_sound=NULL;
+ char *start_sound=NULL;
+ char *limitptr;
+ char limitdata[256];
+ char *stack,*var;
+ int play_to_caller=0,play_to_callee=0;
+ int playargs=0;
+
if (!data) {
ast_log(LOG_WARNING, "Dial requires an argument (technology1/number1&technology2/number2...|optional timeout|options)\n");
return -1;
if (transfer) {
+
/* Extract call duration limit */
if ((cdl = strstr(transfer, "S("))) {
calldurationlimit=atoi(cdl+2);
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %i seconds.\n",calldurationlimit);
}
-
+
+ /* XXX LIMIT SUPPORT */
+ if ((limitptr = strstr(transfer, "L("))) {
+ strncpy(limitdata, limitptr + 2, sizeof(limitdata) - 1);
+ /* Overwrite with X's what was the limit info */
+ while(*limitptr && (*limitptr != ')'))
+ *(limitptr++) = 'X';
+ if (*limitptr)
+ *limitptr = 'X';
+ /* Now find the end of the privdb */
+ limitptr = strchr(limitdata, ')');
+ if (limitptr)
+ *limitptr = '\0';
+ else {
+ ast_log(LOG_WARNING, "Limit Data lacking trailing ')'\n");
+ }
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLER");
+ play_to_caller = var ? ast_true(var) : 1;
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLEE");
+ play_to_callee = var ? ast_true(var) : 0;
+
+ if(! play_to_caller && ! play_to_callee)
+ play_to_caller=1;
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_WARNING_FILE");
+ warning_sound = var ? var : "timeleft";
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_TIMEOUT_FILE");
+ end_sound = var ? var : NULL;
+
+ var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE");
+ start_sound = var ? var : NULL;
+
+ var=stack=limitdata;
+
+ var = strsep(&stack, ":");
+ if(var) {
+ timelimit = atol(var);
+ playargs++;
+ }
+ var = strsep(&stack, ":");
+ if(var) {
+ play_warning = atol(var);
+ playargs++;
+ }
+
+ var = strsep(&stack, ":");
+ if(var) {
+ warning_freq = atol(var);
+ playargs++;
+ }
+
+ if(! timelimit) {
+ timelimit=play_to_caller=play_to_callee=play_warning=warning_freq=0;
+ warning_sound=NULL;
+ }
+ calldurationlimit=0; /* undo effect of S(x) in case they are both used */
+ if(! play_warning && ! start_sound && ! end_sound && timelimit) { /* more efficient do it like S(x) does since no advanced opts*/
+ calldurationlimit=timelimit/1000;
+ timelimit=play_to_caller=play_to_callee=play_warning=warning_freq=0;
+ }
+ else
+ ast_verbose(VERBOSE_PREFIX_3"Limit Data: timelimit=%ld\n -- play_warning=%ld\n -- play_to_caller=%s\n -- play_to_callee=%s\n -- warning_freq=%ld\n -- warning_sound=%s\n -- end_sound=%s\n -- start_sound=%s\n",timelimit,play_warning,play_to_caller ? "yes" : "no",play_to_callee ? "yes" : "no",warning_freq,warning_sound ? warning_sound : "UNDEF",end_sound ? end_sound : "UNDEF",start_sound ? start_sound : "UNDEF");
+
+ }
+
/* XXX ANNOUNCE SUPPORT */
if ((ann = strstr(transfer, "A("))) {
announce = 1;
time(&now);
chan->whentohangup = now + calldurationlimit;
}
- res = ast_bridge_call(chan, peer, allowredir_in, allowredir_out, allowdisconnect);
+
+ memset(&config,0,sizeof(struct ast_bridge_config));
+ config.play_to_caller=play_to_caller;
+ config.play_to_callee=play_to_callee;
+ config.allowredirect_in = allowredir_in;
+ config.allowredirect_out = allowredir_out;
+ config.allowdisconnect = allowdisconnect;
+ config.timelimit = timelimit;
+ config.play_warning = play_warning;
+ config.warning_freq = warning_freq;
+ config.warning_sound = warning_sound;
+ config.end_sound = end_sound;
+ config.start_sound = start_sound;
+ res = ast_bridge_call(chan,peer,&config);
if (res != AST_PBX_NO_HANGUP_PEER)
ast_hangup(peer);
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/logger.h>
+#include <asterisk/say.h>
#include <asterisk/file.h>
#include <asterisk/translate.h>
#include <asterisk/manager.h>
#include <sys/ioctl.h>
#include <linux/zaptel.h>
#ifndef ZT_TIMERPING
-#error "You need newer zaptel! Please cvs update zaptel"
+#error "You need newer zaptel! Please cvs update zaptel"
#endif
#endif
time_t myt;
time(&myt);
- if (offset)
+ if (offset)
chan->whentohangup = myt + offset;
- else
- chan->whentohangup = 0;
+ else
+ chan->whentohangup = 0;
return;
}
+
+
int ast_channel_register(char *type, char *description, int capabilities,
struct ast_channel *(*requester)(char *type, int format, void *data))
{
- return ast_channel_register_ex(type, description, capabilities, requester, NULL);
+ return ast_channel_register_ex(type, description, capabilities, requester, NULL);
}
int ast_channel_register_ex(char *type, char *description, int capabilities,
struct ast_channel_pvt *pvt;
int x;
int flags;
- struct varshead *headp;
-
+ struct varshead *headp;
+
/* If shutting down, don't allocate any new channels */
if (shutting_down)
snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), "%li.%d", (long)time(NULL), uniqueint++);
headp=&tmp->varshead;
ast_mutex_init(&tmp->lock);
- AST_LIST_HEAD_INIT(headp);
+ AST_LIST_HEAD_INIT(headp);
tmp->vars=ast_var_assign("tempvar","tempval");
AST_LIST_INSERT_HEAD(headp,tmp->vars,entries);
strncpy(tmp->context, "default", sizeof(tmp->context)-1);
}
-int ast_safe_sleep_conditional( struct ast_channel *chan, int ms,
+int ast_safe_sleep_conditional( struct ast_channel *chan, int ms,
int (*cond)(void*), void *data )
{
struct ast_frame *f;
/* loop over the variables list, freeing all data and deleting list items */
/* no need to lock the list, as the channel is already locked */
- while (!AST_LIST_EMPTY(headp)) { /* List Deletion. */
- vardata = AST_LIST_FIRST(headp);
- AST_LIST_REMOVE_HEAD(headp, entries);
-// printf("deleting var %s=%s\n",ast_var_name(vardata),ast_var_value(vardata));
- ast_var_delete(vardata);
+ while (!AST_LIST_EMPTY(headp)) { /* List Deletion. */
+ vardata = AST_LIST_FIRST(headp);
+ AST_LIST_REMOVE_HEAD(headp, entries);
+// printf("deleting var %s=%s\n",ast_var_name(vardata),ast_var_value(vardata));
+ ast_var_delete(vardata);
}
-
+
free(chan->pvt);
chan->pvt = NULL;
}
if (chan->blocking) {
ast_log(LOG_WARNING, "Hard hangup called by thread %ld on %s, while fd "
- "is blocked by thread %ld in procedure %s! Expect a failure\n",
+ "is blocked by thread %ld in procedure %s! Expect a failure\n",
(long)pthread_self(), chan->name, (long)chan->blocker, chan->blockproc);
CRASH;
}
ast_mutex_unlock(&chan->lock);
manager_event(EVENT_FLAG_CALL, "Hangup",
"Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Cause: %i\r\n",
- chan->name, chan->uniqueid, chan->hangupcause);
+ "Uniqueid: %s\r\n"
+ "Cause: %i\r\n",
+ chan->name, chan->uniqueid, chan->hangupcause);
ast_channel_free(chan);
return res;
}
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
f = NULL;
} else
- f = &null_frame;
+ f = &null_frame;
ast_mutex_unlock(&chan->lock);
return f;
}
}
if (!chan->deferdtmf && strlen(chan->dtmfq)) {
- /* We have DTMF that has been deferred. Return it now */
+ /* We have DTMF that has been deferred. Return it now */
chan->dtmff.frametype = AST_FRAME_DTMF;
chan->dtmff.subclass = chan->dtmfq[0];
/* Drop first digit */
chan->timingdata = NULL;
ast_mutex_unlock(&chan->lock);
}
- f = &null_frame;
+ f = &null_frame;
return f;
} else
ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
f = ast_read(chan);
if (f == NULL) return -1; /* if hangup */
if ((f->frametype == AST_FRAME_CONTROL) &&
- (f->subclass == AST_CONTROL_HANGUP)) return -1; /* if hangup */
- if (f->frametype == AST_FRAME_TEXT) /* if a text frame */
+ (f->subclass == AST_CONTROL_HANGUP)) return -1; /* if hangup */
+ if (f->frametype == AST_FRAME_TEXT) /* if a text frame */
{
- c = *((char *)f->data); /* get the data */
+ c = *((char *)f->data); /* get the data */
ast_frfree(f);
return(c);
}
* it by doing our own generation. (PM2002)
*/
static const char* dtmf_tones[] = {
- "!941+1336/100,!0/100", /* 0 */
- "!697+1209/100,!0/100", /* 1 */
- "!697+1336/100,!0/100", /* 2 */
- "!697+1477/100,!0/100", /* 3 */
- "!770+1209/100,!0/100", /* 4 */
- "!770+1336/100,!0/100", /* 5 */
- "!770+1477/100,!0/100", /* 6 */
- "!852+1209/100,!0/100", /* 7 */
- "!852+1336/100,!0/100", /* 8 */
- "!852+1477/100,!0/100", /* 9 */
- "!697+1633/100,!0/100", /* A */
- "!770+1633/100,!0/100", /* B */
- "!852+1633/100,!0/100", /* C */
- "!941+1633/100,!0/100", /* D */
- "!941+1209/100,!0/100", /* * */
+ "!941+1336/100,!0/100", /* 0 */
+ "!697+1209/100,!0/100", /* 1 */
+ "!697+1336/100,!0/100", /* 2 */
+ "!697+1477/100,!0/100", /* 3 */
+ "!770+1209/100,!0/100", /* 4 */
+ "!770+1336/100,!0/100", /* 5 */
+ "!770+1477/100,!0/100", /* 6 */
+ "!852+1209/100,!0/100", /* 7 */
+ "!852+1336/100,!0/100", /* 8 */
+ "!852+1477/100,!0/100", /* 9 */
+ "!697+1633/100,!0/100", /* A */
+ "!770+1633/100,!0/100", /* B */
+ "!852+1633/100,!0/100", /* C */
+ "!941+1633/100,!0/100", /* D */
+ "!941+1209/100,!0/100", /* * */
"!941+1477/100,!0/100" }; /* # */
if (digit >= '0' && digit <='9')
ast_playtones_start(chan,0,dtmf_tones[digit-'0'], 0);
struct ast_frame *f = NULL;
/* Stop if we're a zombie or need a soft hangup */
ast_mutex_lock(&chan->lock);
- if (chan->zombie || ast_check_hangup(chan)) {
+ if (chan->zombie || ast_check_hangup(chan)) {
ast_mutex_unlock(&chan->lock);
return -1;
}
return -1;
}
- /* Now we have a good choice for both. We'll write using our native format. */
+ /* Now we have a good choice for both. We'll write using our native format. */
chan->pvt->rawwriteformat = native;
/* User perspective is fmt */
chan->writeformat = fmt;
return -1;
}
- /* Now we have a good choice for both. We'll write using our native format. */
+ /* Now we have a good choice for both. We'll write using our native format. */
chan->pvt->rawreadformat = native;
/* User perspective is fmt */
chan->readformat = fmt;
char *tmp, *var;
/* JDG chanvar */
tmp = oh->variable;
- /* FIXME replace this call with strsep NOT*/
+ /* FIXME replace this call with strsep NOT*/
while( (var = strtok_r(NULL, "|", &tmp)) ) {
pbx_builtin_setvar( chan, var );
} /* /JDG */
strncpy(name, chan->name, sizeof(name)-1);
cut = strchr(name,'-');
if (cut)
- *cut = 0;
+ *cut = 0;
if (!strcmp(name, device))
- return AST_DEVICE_INUSE;
+ return AST_DEVICE_INUSE;
chan = ast_channel_walk(chan);
}
return AST_DEVICE_UNKNOWN;
strncpy(tech, device, sizeof(tech)-1);
number = strchr(tech, '/');
if (!number) {
- return AST_DEVICE_INVALID;
+ return AST_DEVICE_INVALID;
}
*number = 0;
number++;
clone->name, clone->_state, original->name, original->_state);
#endif
/* XXX This is a seriously wacked out operation. We're essentially putting the guts of
- the clone channel into the original channel. Start by killing off the original
- channel's backend. I'm not sure we're going to keep this function, because
+ the clone channel into the original channel. Start by killing off the original
+ channel's backend. I'm not sure we're going to keep this function, because
while the features are nice, the cost is very high in terms of pure nastiness. XXX */
/* We need the clone's lock, too */
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", newn, masqn);
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", orig, newn);
- /* Swap the guts */
+ /* Swap the guts */
p = original->pvt;
original->pvt = clone->pvt;
clone->pvt = p;
strncpy(clone->name, zombn, sizeof(clone->name) - 1);
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", masqn, zombn);
- /* Keep the same language. */
+ /* Keep the same language. */
/* Update the type. */
original->type = clone->type;
/* Copy the FD's */
original->fds[x] = clone->fds[x];
}
/* Append variables from clone channel into original channel */
- /* XXX Is this always correct? We have to in order to keep MACROS working XXX */
+ /* XXX Is this always correct? We have to in order to keep MACROS working XXX */
varptr = original->varshead.first;
if (varptr) {
while(varptr->entries.next) {
/* CDR fields remain the same */
/* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
/* Application and data remain the same */
- /* Clone exception becomes real one, as with fdno */
+ /* Clone exception becomes real one, as with fdno */
original->exception = clone->exception;
original->fdno = clone->fdno;
/* Schedule context remains the same */
/* Stream stuff stays the same */
- /* Keep the original state. The fixup code will need to work with it most likely */
+ /* Keep the original state. The fixup code will need to work with it most likely */
/* dnid and callerid change to become the new, HOWEVER, we also link the original's
fields back into the defunct 'clone' so that they will be freed when
these separate */
original->_state = clone->_state;
- /* Context, extension, priority, app data, jump table, remain the same */
+ /* Context, extension, priority, app data, jump table, remain the same */
/* pvt switches. pbx stays the same, as does next */
/* Set the write format */
return 0;
}
-int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
-{
- /* Copy voice back and forth between the two channels. Give the peer
+
+static long tvdiff(struct timeval *now,struct timeval *then) {
+ return (((now->tv_sec * 1000) + now->tv_usec / 1000) - ((then->tv_sec * 1000) + then->tv_usec / 1000));
+}
+
+static void bridge_playfile(struct ast_channel *chan,char *sound,int remain) {
+ int res=0,min=0,sec=0;
+
+ if(remain > 0) {
+ if(remain / 60 > 1) {
+ min = remain / 60;
+ sec = remain % 60;
+ }
+ else {
+ sec = remain;
+ }
+ }
+
+ if(!strcmp(sound,"timeleft")) {
+ res=ast_streamfile(chan,"vm-youhave",chan->language);
+ res = ast_waitstream(chan, "");
+ if(min) {
+ res = ast_say_number(chan,min, AST_DIGIT_ANY, chan->language);
+ res=ast_streamfile(chan,"minutes",chan->language);
+ res = ast_waitstream(chan, "");
+ }
+ if(sec) {
+ res = ast_say_number(chan,sec, AST_DIGIT_ANY, chan->language);
+ res=ast_streamfile(chan,"seconds",chan->language);
+ res = ast_waitstream(chan, "");
+ }
+ }
+ else {
+ res=ast_streamfile(chan,sound,chan->language);
+ res = ast_waitstream(chan, "");
+ }
+}
+
+int ast_channel_bridge(struct ast_channel *c0,struct ast_channel *c1,struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc) {
+ /* Copy voice back and forth between the two channels. Give the peer
the ability to transfer calls with '#<extension' syntax. */
+ int flags;
struct ast_channel *cs[3];
int to = -1;
struct ast_frame *f;
struct ast_channel *who = NULL;
- int res;
+ int res=0;
int nativefailed=0;
+ struct timeval start_time,precise_now;
+ long elapsed_ms=0,time_left_ms=0;
+ int playit=0,playitagain=1,first_time=1;
+
+
+ flags = (config->allowdisconnect||config->allowredirect_out ? AST_BRIDGE_DTMF_CHANNEL_0 : 0) + (config->allowredirect_in ? AST_BRIDGE_DTMF_CHANNEL_1 : 0);
+
+ /* timestamp */
+ gettimeofday(&start_time,NULL);
+ time_left_ms = config->timelimit;
+
+ if(config->play_to_caller && config->start_sound){
+ bridge_playfile(c0,config->start_sound,time_left_ms / 1000);
+ }
+ if(config->play_to_callee && config->start_sound){
+ bridge_playfile(c1,config->start_sound,time_left_ms / 1000);
+ }
+
+
/* Stop if we're a zombie or need a soft hangup */
if (c0->zombie || ast_check_hangup_locked(c0) || c1->zombie || ast_check_hangup_locked(c1))
c0->name, c1->name, c0->uniqueid, c1->uniqueid);
for (/* ever */;;) {
+
+ /* timestamp */
+ if(config->timelimit) {
+ gettimeofday(&precise_now,NULL);
+ elapsed_ms = tvdiff(&precise_now,&start_time);
+ time_left_ms = config->timelimit - elapsed_ms;
+
+ if(playitagain && (config->play_to_caller || config->play_to_callee) && (config->play_warning && time_left_ms <= config->play_warning)) {
+ /* narrowing down to the end */
+ if(config->warning_freq == 0) {
+ playit = 1;
+ first_time=0;
+ playitagain=0;
+ }
+ else if(first_time) {
+ playit = 1;
+ first_time=0;
+ }
+ else {
+ if((time_left_ms % config->warning_freq) <= 50) {
+ playit = 1;
+ }
+ }
+ }
+
+ if(time_left_ms <= 0) {
+ if(config->play_to_caller && config->end_sound){
+ bridge_playfile(c0,config->end_sound,0);
+ }
+
+ if(config->play_to_callee && config->end_sound){
+ bridge_playfile(c1,config->end_sound,0);
+ }
+ break;
+ }
+
+ if(time_left_ms >= 5000 && playit) {
+ if(config->play_to_caller && config->warning_sound && config->play_warning){
+ bridge_playfile(c0,config->warning_sound,time_left_ms / 1000);
+ }
+
+ if(config->play_to_callee && config->warning_sound && config->play_warning){
+ bridge_playfile(c1,config->warning_sound,time_left_ms / 1000);
+ }
+ playit = 0;
+ }
+
+ }
+
+
+
/* Stop if we're a zombie or need a soft hangup */
if (c0->zombie || ast_check_hangup_locked(c0) || c1->zombie || ast_check_hangup_locked(c1)) {
*fo = NULL;
ast_log(LOG_DEBUG, "Bridge stops because we're zombie or need a soft hangup: c0=%s, c1=%s, flags: %s,%s,%s,%s\n",c0->name,c1->name,c0->zombie?"Yes":"No",ast_check_hangup(c0)?"Yes":"No",c1->zombie?"Yes":"No",ast_check_hangup(c1)?"Yes":"No");
break;
}
- if (c0->pvt->bridge &&
+ if (c0->pvt->bridge && config->timelimit==0 &&
(c0->pvt->bridge == c1->pvt->bridge) && !nativefailed && !c0->monitor && !c1->monitor) {
/* Looks like they share a bridge code */
if (option_verbose > 2)