Newer
Older
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return NULL;
}
chan = backends;
while(chan) {
if (!strcasecmp(type, chan->tech->type)) {
capabilities = chan->tech->capabilities;
fmt = format;
res = ast_translator_best_choice(&fmt, &capabilities);
if (res < 0) {
ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %d) to %d\n", type, chan->tech->capabilities, format);
ast_mutex_unlock(&chlock);
ast_mutex_unlock(&chlock);
if (chan->tech->requester)
c = chan->tech->requester(type, capabilities, data, cause);
if (c->_state == AST_STATE_DOWN) {
manager_event(EVENT_FLAG_CALL, "Newchannel",
"Channel: %s\r\n"
"State: %s\r\n"
"CallerID: %s\r\n"
"CallerIDName: %s\r\n"
c->name, ast_state2str(c->_state), c->cid.cid_num ? c->cid.cid_num : "<unknown>", c->cid.cid_name ? c->cid.cid_name : "<unknown>",c->uniqueid);
Mark Spencer
committed
if (!chan) {
ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
Mark Spencer
committed
*cause = AST_CAUSE_NOSUCHDRIVER;
}
ast_mutex_unlock(&chlock);
int ast_parse_device_state(char *device)
{
char name[AST_CHANNEL_NAME] = "";
char *cut;
struct ast_channel *chan;
chan = ast_channel_walk_locked(NULL);
while (chan) {
strncpy(name, chan->name, sizeof(name)-1);
ast_mutex_unlock(&chan->lock);
cut = strchr(name,'-');
if (cut)
if (!strcmp(name, device))
chan = ast_channel_walk_locked(chan);
}
return AST_DEVICE_UNKNOWN;
}
int ast_device_state(char *device)
{
char tech[AST_MAX_EXTENSION] = "";
char *number;
struct chanlist *chanls;
int res = 0;
strncpy(tech, device, sizeof(tech)-1);
number = strchr(tech, '/');
if (!number) {
}
*number = 0;
number++;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
chanls = backends;
while(chanls) {
if (!strcasecmp(tech, chanls->tech->type)) {
ast_mutex_unlock(&chlock);
if (!chanls->tech->devicestate)
return ast_parse_device_state(device);
else {
res = chanls->tech->devicestate(number);
if (res == AST_DEVICE_UNKNOWN)
return ast_parse_device_state(device);
else
return res;
}
}
chanls = chanls->next;
}
ast_mutex_unlock(&chlock);
return AST_DEVICE_INVALID;
}
int ast_call(struct ast_channel *chan, char *addr, int timeout)
{
/* Place an outgoing call, but don't wait any longer than timeout ms before returning.
If the remote end does not answer within the timeout, then do NOT hang up, but
return anyway. */
int res = -1;
/* Stop if we're a zombie or need a soft hangup */
ast_mutex_lock(&chan->lock);
if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan))
if (chan->tech->call)
res = chan->tech->call(chan, addr, timeout);
ast_mutex_unlock(&chan->lock);
int ast_transfer(struct ast_channel *chan, char *dest)
{
/* Place an outgoing call, but don't wait any longer than timeout ms before returning.
If the remote end does not answer within the timeout, then do NOT hang up, but
return anyway. */
int res = -1;
/* Stop if we're a zombie or need a soft hangup */
ast_mutex_lock(&chan->lock);
if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
if (chan->tech->transfer) {
res = chan->tech->transfer(chan, dest);
if (!res)
res = 1;
} else
res = 0;
}
ast_mutex_unlock(&chan->lock);
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
{
int pos=0;
int to = ftimeout;
char d;
/* XXX Merge with full version? XXX */
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
d = ast_waitstream(c, AST_DIGIT_ANY);
ast_stopstream(c);
if (!d)
d = ast_waitfordigit(c, to);
} else {
d = ast_waitfordigit(c, to);
}
if (d < 0)
return -1;
if (d == 0) {
s[pos]='\0';
return 1;
}
return 0;
}
to = timeout;
} while(1);
/* Never reached */
return 0;
}
int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders, int audiofd, int ctrlfd)
{
int pos=0;
int to = ftimeout;
char d;
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
return -1;
if (!len)
return -1;
do {
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
d = ast_waitstream_full(c, AST_DIGIT_ANY, audiofd, ctrlfd);
ast_stopstream(c);
usleep(1000);
if (!d)
d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
} else {
d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
}
if (d < 0)
return -1;
if (d == 0) {
s[pos]='\0';
return 1;
}
if (d == 1) {
s[pos]='\0';
return 2;
}
if (!strchr(enders, d))
s[pos++] = d;
if (strchr(enders, d) || (pos >= len)) {
s[pos]='\0';
return 0;
}
to = timeout;
} while(1);
/* Never reached */
return 0;
}
int ast_channel_supports_html(struct ast_channel *chan)
{
if (chan->tech->send_html)
Mark Spencer
committed
int ast_channel_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
if (chan->tech->send_html)
return chan->tech->send_html(chan, subclass, data, datalen);
Mark Spencer
committed
int ast_channel_sendurl(struct ast_channel *chan, const char *url)
if (chan->tech->send_html)
return chan->tech->send_html(chan, AST_HTML_URL, url, strlen(url) + 1);
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
{
int peerf;
int chanf;
int res;
ast_mutex_lock(&peer->lock);
ast_mutex_unlock(&peer->lock);
ast_mutex_lock(&chan->lock);
ast_mutex_unlock(&chan->lock);
res = ast_translator_best_choice(&peerf, &chanf);
if (res < 0) {
ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan->name, chan->nativeformats, peer->name, peer->nativeformats);
return -1;
}
/* Set read format on channel */
Mark Spencer
committed
res = ast_set_read_format(chan, peerf);
ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan->name, chanf);
return -1;
}
/* Set write format on peer channel */
Mark Spencer
committed
res = ast_set_write_format(peer, peerf);
ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, peerf);
return -1;
}
/* Now we go the other way */
peerf = peer->nativeformats;
chanf = chan->nativeformats;
res = ast_translator_best_choice(&chanf, &peerf);
if (res < 0) {
ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer->name, peer->nativeformats, chan->name, chan->nativeformats);
return -1;
}
/* Set writeformat on channel */
Mark Spencer
committed
res = ast_set_write_format(chan, chanf);
ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, chanf);
return -1;
}
/* Set read format on peer channel */
Mark Spencer
committed
res = ast_set_read_format(peer, chanf);
ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer->name, peerf);
int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone)
{
struct ast_frame null = { AST_FRAME_NULL, };
int res = -1;
if (original == clone) {
ast_log(LOG_WARNING, "Can't masquerade channel '%s' into itself!\n", original->name);
return -1;
}
ast_mutex_lock(&original->lock);
while(ast_mutex_trylock(&clone->lock)) {
ast_mutex_unlock(&original->lock);
usleep(1);
ast_mutex_lock(&original->lock);
}
ast_log(LOG_DEBUG, "Planning to masquerade %s into the structure of %s\n",
clone->name, original->name);
if (original->masq) {
ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
original->masq->name, original->name);
} else if (clone->masqr) {
ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
clone->name, clone->masqr->name);
} else {
original->masq = clone;
clone->masqr = original;
ast_queue_frame(original, &null);
ast_queue_frame(clone, &null);
ast_log(LOG_DEBUG, "Done planning to masquerade %s into the structure of %s\n", original->name, clone->name);
res = 0;
}
ast_mutex_unlock(&clone->lock);
ast_mutex_unlock(&original->lock);
return res;
void ast_change_name(struct ast_channel *chan, char *newname)
{
char tmp[256];
strncpy(tmp, chan->name, sizeof(tmp) - 1);
strncpy(chan->name, newname, sizeof(chan->name) - 1);
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", tmp, chan->name, chan->uniqueid);
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
{
struct ast_var_t *current, *newvar;
char *varname;
AST_LIST_TRAVERSE(&parent->varshead, current, entries) {
int vartype = 0;
varname = ast_var_full_name(current);
if (!varname)
continue;
if (varname[0] == '_') {
vartype = 1;
if (varname[1] == '_')
vartype = 2;
}
switch (vartype) {
case 1:
newvar = ast_var_assign(&varname[1], ast_var_value(current));
if (newvar) {
AST_LIST_INSERT_HEAD(&child->varshead, newvar, entries);
if (option_debug)
ast_log(LOG_DEBUG, "Copying soft-transferable variable %s.\n", ast_var_name(newvar));
}
break;
case 2:
newvar = ast_var_assign(ast_var_full_name(current), ast_var_value(current));
if (newvar) {
AST_LIST_INSERT_HEAD(&child->varshead, newvar, entries);
if (option_debug)
ast_log(LOG_DEBUG, "Copying hard-transferable variable %s.\n", ast_var_name(newvar));
}
break;
default:
if (option_debug)
ast_log(LOG_DEBUG, "Not copying variable %s.\n", ast_var_name(current));
break;
}
}
}
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
/* Clone channel variables from 'clone' channel into 'original' channel
All variables except those related to app_groupcount are cloned
Variables are actually _removed_ from 'clone' channel, presumably
because it will subsequently be destroyed.
Assumes locks will be in place on both channels when called.
*/
static void clone_variables(struct ast_channel *original, struct ast_channel *clone)
{
struct ast_var_t *varptr;
/* we need to remove all app_groupcount related variables from the original
channel before merging in the clone's variables; any groups assigned to the
original channel should be released, only those assigned to the clone
should remain
*/
AST_LIST_TRAVERSE_SAFE_BEGIN(&original->varshead, varptr, entries) {
if (!strncmp(ast_var_name(varptr), GROUP_CATEGORY_PREFIX, strlen(GROUP_CATEGORY_PREFIX))) {
AST_LIST_REMOVE(&original->varshead, varptr, entries);
ast_var_delete(varptr);
}
}
AST_LIST_TRAVERSE_SAFE_END;
/* Append variables from clone channel into original channel */
/* XXX Is this always correct? We have to in order to keep MACROS working XXX */
if (AST_LIST_FIRST(&clone->varshead))
AST_LIST_INSERT_TAIL(&original->varshead, AST_LIST_FIRST(&clone->varshead), entries);
}
/* Assumes channel will be locked when called */
Mark Spencer
committed
int ast_do_masquerade(struct ast_channel *original)
const struct ast_channel_tech *t;
void *t_pvt;
struct ast_callerid tmpcid;
int rformat = original->readformat;
int wformat = original->writeformat;
char newn[100];
char orig[100];
char masqn[100];
char zombn[100];
ast_log(LOG_DEBUG, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
clone->name, clone->_state, original->name, original->_state);
/* 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
while the features are nice, the cost is very high in terms of pure nastiness. XXX */
Mark Spencer
committed
/* We need the clone's lock, too */
ast_mutex_lock(&clone->lock);
Mark Spencer
committed
ast_log(LOG_DEBUG, "Got clone lock on '%s' at %p\n", clone->name, &clone->lock);
/* Having remembered the original read/write formats, we turn off any translation on either
one */
free_translation(clone);
free_translation(original);
/* Unlink the masquerade */
original->masq = NULL;
clone->masqr = NULL;
/* Save the original name */
strncpy(orig, original->name, sizeof(orig) - 1);
/* Save the new name */
strncpy(newn, clone->name, sizeof(newn) - 1);
/* Create the masq name */
snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn);
strncpy(original->name, newn, sizeof(original->name)-1);
strncpy(clone->name, masqn, sizeof(clone->name) - 1);
/* Notify any managers of the change, first the masq then the other */
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clone->uniqueid);
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid);
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
/* Swap the technlogies */
t = original->tech;
original->tech = clone->tech;
clone->tech = t;
t_pvt = original->tech_pvt;
original->tech_pvt = clone->tech_pvt;
clone->tech_pvt = t_pvt;
/* Swap the readq's */
cur = original->readq;
original->readq = clone->readq;
clone->readq = cur;
/* Swap the alertpipes */
for (i = 0; i < 2; i++) {
x = original->alertpipe[i];
original->alertpipe[i] = clone->alertpipe[i];
clone->alertpipe[i] = x;
}
/* Swap the raw formats */
x = original->rawreadformat;
original->rawreadformat = clone->rawreadformat;
clone->rawreadformat = x;
x = original->rawwriteformat;
original->rawwriteformat = clone->rawwriteformat;
clone->rawwriteformat = x;
/* Save any pending frames on both sides. Start by counting
* how many we're going to need... */
prev = NULL;
cur = clone->readq;
x = 0;
while(cur) {
x++;
prev = cur;
cur = cur->next;
}
/* If we had any, prepend them to the ones already in the queue, and
* load up the alertpipe */
if (prev) {
prev->next = original->readq;
original->readq = clone->readq;
clone->readq = NULL;
if (original->alertpipe[1] > -1) {
write(original->alertpipe[1], &x, sizeof(x));
/* And of course, so does our current state. Note we need not
call ast_setstate since the event manager doesn't really consider
these separate. We do this early so that the clone has the proper
state of the original channel. */
origstate = original->_state;
original->_state = clone->_state;
clone->_state = origstate;
if (clone->tech->fixup){
res = clone->tech->fixup(original, clone);
if (res)
ast_log(LOG_WARNING, "Fixup failed on channel %s, strange things may happen.\n", clone->name);
}
/* Start by disconnecting the original's physical side */
if (clone->tech->hangup)
res = clone->tech->hangup(clone);
if (res) {
ast_log(LOG_WARNING, "Hangup failed! Strange things may happen!\n");
Mark Spencer
committed
ast_mutex_unlock(&clone->lock);
snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
strncpy(clone->name, zombn, sizeof(clone->name) - 1);
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", masqn, zombn, clone->uniqueid);
/* Update the type. */
original->type = clone->type;
/* Keep the same language. */
strncpy(original->language, clone->language, sizeof(original->language));
clone_variables(original, clone);
/* Presense of ADSI capable CPE follows clone */
original->adsicpe = clone->adsicpe;
/* Bridge remains the same */
/* 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 */
ast_copy_flags(original, clone, AST_FLAG_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 */
/* Just swap the whole structures, nevermind the allocations, they'll work themselves
out. */
tmpcid = original->cid;
original->cid = clone->cid;
clone->cid = tmpcid;
/* Restore original timing file descriptor */
original->fds[AST_MAX_FDS - 2] = original->timingfd;
/* Our native formats are different now */
original->nativeformats = clone->nativeformats;
/* Context, extension, priority, app data, jump table, remain the same */
/* pvt switches. pbx stays the same, as does next */
/* Set the write format */
Mark Spencer
committed
ast_set_write_format(original, wformat);
Mark Spencer
committed
ast_set_read_format(original, rformat);
/* Copy the music class */
strncpy(original->musicclass, clone->musicclass, sizeof(original->musicclass) - 1);
ast_log(LOG_DEBUG, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat);
/* Okay. Last thing is to let the channel driver know about all this mess, so he
can fix up everything as best as possible */
if (original->tech->fixup) {
res = original->tech->fixup(clone, original);
if (res) {
ast_log(LOG_WARNING, "Driver for '%s' could not fixup channel %s\n",
original->type, original->name);
Mark Spencer
committed
ast_mutex_unlock(&clone->lock);
return -1;
}
} else
ast_log(LOG_WARNING, "Driver '%s' does not have a fixup routine (for %s)! Bad things may happen.\n",
original->type, original->name);
/* Now, at this point, the "clone" channel is totally F'd up. We mark it as
a zombie so nothing tries to touch it. If it's already been marked as a
zombie, then free it now (since it already is considered invalid). */
if (ast_test_flag(clone, AST_FLAG_ZOMBIE)) {
ast_log(LOG_DEBUG, "Destroying clone '%s'\n", clone->name);
Mark Spencer
committed
ast_mutex_unlock(&clone->lock);
ast_channel_free(clone);
manager_event(EVENT_FLAG_CALL, "Hangup", "Channel: %s\r\n", zombn);
} else {
struct ast_frame null_frame = { AST_FRAME_NULL, };
ast_log(LOG_DEBUG, "Released clone lock on '%s'\n", clone->name);
ast_queue_frame(clone, &null_frame);
Mark Spencer
committed
ast_mutex_unlock(&clone->lock);
if (ast_test_flag(original, AST_FLAG_BLOCKING))
ast_log(LOG_DEBUG, "Done Masquerading %s (%d)\n",
original->name, original->_state);
void ast_set_callerid(struct ast_channel *chan, const char *callerid, const char *calleridname, const char *ani)
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
if (chan->cid.cid_num)
free(chan->cid.cid_num);
if (ast_strlen_zero(callerid))
chan->cid.cid_num = NULL;
else
chan->cid.cid_num = strdup(callerid);
}
if (calleridname) {
if (chan->cid.cid_name)
free(chan->cid.cid_name);
if (ast_strlen_zero(calleridname))
chan->cid.cid_name = NULL;
else
chan->cid.cid_name = strdup(calleridname);
}
if (ani) {
if (chan->cid.cid_ani)
free(chan->cid.cid_ani);
if (ast_strlen_zero(ani))
chan->cid.cid_ani = NULL;
else
chan->cid.cid_ani = strdup(ani);
}
if (chan->cdr)
ast_cdr_setcid(chan->cdr, chan);
manager_event(EVENT_FLAG_CALL, "Newcallerid",
"Channel: %s\r\n"
"CallerID: %s\r\n"
"CallerIDName: %s\r\n"
chan->name, chan->cid.cid_num ?
chan->cid.cid_num : "<Unknown>",
chan->cid.cid_name ?
chan->cid.cid_name : "<Unknown>",
}
int ast_setstate(struct ast_channel *chan, int state)
{
if (chan->_state != state) {
int oldstate = chan->_state;
chan->_state = state;
if (oldstate == AST_STATE_DOWN) {
ast_device_state_changed(chan->name);
manager_event(EVENT_FLAG_CALL, "Newchannel",
"Channel: %s\r\n"
"State: %s\r\n"
"CallerID: %s\r\n"
"CallerIDName: %s\r\n"
chan->name, ast_state2str(chan->_state),
chan->cid.cid_num ? chan->cid.cid_num : "<unknown>",
chan->cid.cid_name ? chan->cid.cid_name : "<unknown>",
chan->uniqueid);
} else {
manager_event(EVENT_FLAG_CALL, "Newstate",
"Channel: %s\r\n"
"State: %s\r\n"
"CallerID: %s\r\n"
"CallerIDName: %s\r\n"
chan->name, ast_state2str(chan->_state),
chan->cid.cid_num ? chan->cid.cid_num : "<unknown>",
chan->cid.cid_name ? chan->cid.cid_name : "<unknown>",
chan->uniqueid);
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));
#else
return (now->tv_sec - then->tv_sec) * 1000 + (now->tv_usec - then->tv_usec) / 1000;
#endif
Mark Spencer
committed
struct ast_channel *ast_bridged_channel(struct ast_channel *chan)
{
struct ast_channel *bridged;
bridged = chan->_bridge;
if (bridged && bridged->tech->bridged_channel)
bridged = bridged->tech->bridged_channel(chan, bridged);
Mark Spencer
committed
return bridged;
}
Anthony Minessale II
committed
static void bridge_playfile(struct ast_channel *chan, struct ast_channel *peer, char *sound, int remain)
Anthony Minessale II
committed
int res=0, min=0, sec=0,check=0;
check = ast_autoservice_start(peer);
if(check)
return;
if (remain > 0) {
if (remain / 60 > 1) {
min = remain / 60;
sec = remain % 60;
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, (char *) NULL);
res = ast_streamfile(chan, "queue-minutes", chan->language);
res = ast_waitstream(chan, "");
}
if (sec) {
res = ast_say_number(chan, sec, AST_DIGIT_ANY, chan->language, (char *) NULL);
res = ast_streamfile(chan, "queue-seconds", chan->language);
res = ast_waitstream(chan, "");
}
} else {
res = ast_streamfile(chan, sound, chan->language);
res = ast_waitstream(chan, "");
}
Anthony Minessale II
committed
check = ast_autoservice_stop(peer);
static int ast_generic_bridge(int *playitagain, int *playit, struct timeval *start_time, 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. */
struct ast_channel *cs[3];
int to = -1;
struct ast_frame *f;
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
void *pvt0, *pvt1;
int res=0;
int o0nativeformats;
int o1nativeformats;
struct timeval precise_now;
long elapsed_ms=0, time_left_ms=0;
cs[0] = c0;
cs[1] = c1;
pvt0 = c0->pvt;
pvt1 = c1->pvt;
o0nativeformats = c0->nativeformats;
o1nativeformats = c1->nativeformats;
for (;;) {
if ((c0->pvt != pvt0) || (c1->pvt != pvt1) ||
(o0nativeformats != c0->nativeformats) ||
(o1nativeformats != c1->nativeformats)) {
/* Check for Masquerade, codec changes, etc */
res = -3;
break;
}
/* timestamp */
if (config->timelimit) {
/* If there is a time limit, return now */
gettimeofday(&precise_now,NULL);
elapsed_ms = tvdiff(&precise_now,start_time);
time_left_ms = config->timelimit - elapsed_ms;
if (*playitagain && ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) || (ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING))) && (config->play_warning && time_left_ms <= config->play_warning)) {
res = -3;
break;
}
if (time_left_ms <= 0) {
res = -3;
break;
}
if (time_left_ms >= 5000 && *playit) {
res = -3;
break;
}
}
who = ast_waitfor_n(cs, 2, &to);
if (!who) {
ast_log(LOG_DEBUG, "Nobody there, continuing...\n");
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
c0->_softhangup = 0;
if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
c1->_softhangup = 0;
c0->_bridge = c1;
c1->_bridge = c0;
continue;
}
continue;
}
f = ast_read(who);
if (!f) {
*fo = NULL;
*rc = who;
res = 0;
ast_log(LOG_DEBUG, "Didn't get a frame from channel: %s\n",who->name);
break;
}
if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
if ((f->subclass == AST_CONTROL_HOLD) || (f->subclass == AST_CONTROL_UNHOLD)) {
ast_indicate(who == c0 ? c1 : c0, f->subclass);
} else {
*fo = f;
*rc = who;
res = 0;
ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass, who->name);
break;
}
}
if ((f->frametype == AST_FRAME_VOICE) ||
(f->frametype == AST_FRAME_TEXT) ||
(f->frametype == AST_FRAME_VIDEO) ||
(f->frametype == AST_FRAME_IMAGE) ||
(f->frametype == AST_FRAME_HTML) ||
(f->frametype == AST_FRAME_DTMF)) {
if ((f->frametype == AST_FRAME_DTMF) &&
(config->flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) {
if ((who == c0)) {
if ((config->flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
*rc = c0;
*fo = f;
/* Take out of conference mode */
res = 0;
ast_log(LOG_DEBUG, "Got AST_BRIDGE_DTMF_CHANNEL_0 on c0 (%s)\n",c0->name);
break;
} else
goto tackygoto;
} else
if ((who == c1)) {
if (config->flags & AST_BRIDGE_DTMF_CHANNEL_1) {
*rc = c1;
*fo = f;
res = 0;
ast_log(LOG_DEBUG, "Got AST_BRIDGE_DTMF_CHANNEL_1 on c1 (%s)\n",c1->name);
break;
} else
goto tackygoto;
}
} else {
#if 0
ast_log(LOG_DEBUG, "Read from %s\n", who->name);
if (who == last)
ast_log(LOG_DEBUG, "Servicing channel %s twice in a row?\n", last->name);
last = who;
#endif
tackygoto:
/* Don't copy packets if there is a generator on either one, since they're
not supposed to be listening anyway */
if (who == c0)
ast_write(c1, f);
else
ast_write(c0, f);
}
}
ast_frfree(f);
/* Swap who gets priority */
cs[2] = cs[0];
cs[0] = cs[1];
cs[1] = cs[2];
}
return res;
}
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. */
struct ast_channel *cs[3];
struct ast_channel *who = NULL;
int res=0;
int o0nativeformats;
int o1nativeformats;
struct timeval start_time,precise_now;
long elapsed_ms=0, time_left_ms=0;
int playit=0, playitagain=1, first_time=1;
firstpass = config->firstpass;
config->firstpass = 0;
/* timestamp */
gettimeofday(&start_time,NULL);
time_left_ms = config->timelimit;
if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->start_sound && firstpass)
Anthony Minessale II
committed
bridge_playfile(c0,c1,config->start_sound,time_left_ms / 1000);
if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->start_sound && firstpass)
Anthony Minessale II
committed
bridge_playfile(c1,c0,config->start_sound,time_left_ms / 1000);
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) || ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1))
Mark Spencer
committed
if (c0->_bridge) {
ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
Mark Spencer
committed
c0->name, c0->_bridge->name);
Mark Spencer
committed
if (c1->_bridge) {
ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
Mark Spencer
committed
c1->name, c1->_bridge->name);
return -1;
}
/* Keep track of bridge */
Mark Spencer
committed
c0->_bridge = c1;
c1->_bridge = c0;
manager_event(EVENT_FLAG_CALL, "Link",
"Channel1: %s\r\n"
James Golovich
committed
"Channel2: %s\r\n"
"Uniqueid1: %s\r\n"
"Uniqueid2: %s\r\n",
c0->name, c1->name, c0->uniqueid, c1->uniqueid);
o1nativeformats = c1->nativeformats;
o0nativeformats = c0->nativeformats;
/* timestamp */
gettimeofday(&precise_now,NULL);
elapsed_ms = tvdiff(&precise_now,&start_time);
time_left_ms = config->timelimit - elapsed_ms;
if (playitagain && ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) || (ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING))) && (config->play_warning && time_left_ms <= config->play_warning)) {
/* narrowing down to the end */
playit = 1;
first_time=0;
playitagain=0;
playit = 1;
first_time=0;
} else {
if ((time_left_ms % config->warning_freq) <= 50) {
playit = 1;
}
}
}
if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->end_sound)
Anthony Minessale II
committed
bridge_playfile(c0,c1,config->end_sound,0);
if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->end_sound)
Anthony Minessale II
committed
bridge_playfile(c1,c0,config->end_sound,0);
break;
}
if (time_left_ms >= 5000 && playit) {
if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->warning_sound && config->play_warning)
Anthony Minessale II
committed
bridge_playfile(c0,c1,config->warning_sound,time_left_ms / 1000);
if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->warning_sound && config->play_warning)
Anthony Minessale II
committed
bridge_playfile(c1,c0,config->warning_sound,time_left_ms / 1000);
playit = 0;
}