Newer
Older
*/
if (condition >= 0) {
const struct tone_zone_sound *ts = NULL;
switch (condition) {
ts = ast_get_indication_tone(chan->zone, "ring");
break;
ts = ast_get_indication_tone(chan->zone, "busy");
break;
ts = ast_get_indication_tone(chan->zone, "congestion");
break;
}
if (ts && ts->data[0]) {
ast_log(LOG_DEBUG, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
ast_playtones_start(chan,0,ts->data, 1);
} else if (condition == AST_CONTROL_PROGRESS) {
/* ast_playtones_stop(chan); */
} else if (condition == AST_CONTROL_PROCEEDING) {
/* Do nothing, really */
} else if (condition == AST_CONTROL_HOLD) {
/* Do nothing.... */
} else if (condition == AST_CONTROL_UNHOLD) {
/* Do nothing.... */
} else if (condition == AST_CONTROL_VIDUPDATE) {
/* Do nothing.... */
/* not handled */
ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
}
}
else ast_playtones_stop(chan);
}
int ast_recvchar(struct ast_channel *chan, int timeout)
{
int c;
char *buf = ast_recvtext(chan, timeout);
if (buf == NULL)
return -1; /* error or timeout */
c = *(unsigned char *)buf;
free(buf);
return c;
char *ast_recvtext(struct ast_channel *chan, int timeout)
{
int res, done = 0;
char *buf = NULL;
while (!done) {
struct ast_frame *f;
if (ast_check_hangup(chan))
break;
res = ast_waitfor(chan, timeout);
if (res <= 0) /* timeout or error */
break;
timeout = res; /* update timeout */
if (f == NULL)
break; /* no frame */
if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)
done = 1; /* force a break */
else if (f->frametype == AST_FRAME_TEXT) { /* what we want */
buf = strndup((char *) f->data, f->datalen); /* dup and break */
}
ast_frfree(f);
}
int ast_sendtext(struct ast_channel *chan, const char *text)
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
if (chan->tech->send_text)
res = chan->tech->send_text(chan, text);
ast_clear_flag(chan, AST_FLAG_BLOCKING);
static int do_senddigit(struct ast_channel *chan, char digit)
{
int res = -1;
if (chan->tech->send_digit)
res = chan->tech->send_digit(chan, digit);
if (!chan->tech->send_digit || res) {
/*
* Device does not support DTMF tones, lets fake
* 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+1477/100,!0/100" }; /* # */
ast_playtones_start(chan, 0, dtmf_tones[digit-'0'], 0);
ast_playtones_start(chan, 0, dtmf_tones[digit-'A'+10], 0);
ast_playtones_start(chan, 0, dtmf_tones[14], 0);
ast_playtones_start(chan, 0, dtmf_tones[15], 0);
ast_log(LOG_DEBUG, "Unable to generate DTMF tone '%c' for '%s'\n", digit, chan->name);
int ast_senddigit(struct ast_channel *chan, char digit)
{
int ast_prod(struct ast_channel *chan)
{
struct ast_frame a = { AST_FRAME_VOICE };
char nothing[128];
/* Send an empty audio frame to get things moving */
if (chan->_state != AST_STATE_UP) {
ast_log(LOG_DEBUG, "Prodding channel '%s'\n", chan->name);
a.subclass = chan->rawwriteformat;
a.data = nothing + AST_FRIENDLY_OFFSET;
if (ast_write(chan, &a))
ast_log(LOG_WARNING, "Prodding channel '%s' failed\n", chan->name);
int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
{
int res;
if (!chan->tech->write_video)
return 0;
res = ast_write(chan, fr);
if (!res)
res = 1;
return res;
}
int ast_write(struct ast_channel *chan, struct ast_frame *fr)
{
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)) {
ast_mutex_unlock(&chan->lock);
/* Handle any pending masquerades */
if (chan->masq) {
Mark Spencer
committed
if (ast_do_masquerade(chan)) {
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
ast_mutex_unlock(&chan->lock);
ast_mutex_unlock(&chan->lock);
if (ast_test_flag(chan, AST_FLAG_WRITE_INT))
ast_mutex_unlock(&chan->lock);
/* High bit prints debugging */
if (chan->fout & 0x80000000)
ast_frame_dump(chan->name, fr, ">>");
CHECK_BLOCKING(chan);
switch(fr->frametype) {
case AST_FRAME_CONTROL:
/* XXX Interpret control frames XXX */
ast_log(LOG_WARNING, "Don't know how to handle control frames yet\n");
break;
case AST_FRAME_DTMF:
ast_clear_flag(chan, AST_FLAG_BLOCKING);
if (chan->tech->send_text)
res = chan->tech->send_text(chan, (char *) fr->data);
else
res = 0;
break;
case AST_FRAME_HTML:
if (chan->tech->send_html)
res = chan->tech->send_html(chan, fr->subclass, (char *) fr->data, fr->datalen);
case AST_FRAME_VIDEO:
/* XXX Handle translation of video codecs one day XXX */
if (chan->tech->write_video)
res = chan->tech->write_video(chan, fr);
else
res = 0;
break;
if (chan->tech->write) {
Kevin P. Fleming
committed
f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
Kevin P. Fleming
committed
if (f->frametype == AST_FRAME_VOICE && chan->spies)
queue_frame_to_spies(chan, f, SPY_WRITE);
if( chan->monitor && chan->monitor->write_stream &&
f && ( f->frametype == AST_FRAME_VOICE ) ) {
#ifndef MONITOR_CONSTANT_DELAY
int jump = chan->insmpl - chan->outsmpl - 2 * f->samples;
if (jump >= 0) {
if (ast_seekstream(chan->monitor->write_stream, jump + f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
chan->outsmpl += jump + 2 * f->samples;
} else
chan->outsmpl += f->samples;
#else
int jump = chan->insmpl - chan->outsmpl;
if (jump - MONITOR_DELAY >= 0) {
if (ast_seekstream(chan->monitor->write_stream, jump - f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
chan->outsmpl += jump;
} else
chan->outsmpl += f->samples;
#endif
if (ast_writestream(chan->monitor->write_stream, f) < 0)
ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
}
Kevin P. Fleming
committed
res = chan->tech->write(chan, f);
Kevin P. Fleming
committed
/* It's possible this is a translated frame */
if (f && f->frametype == AST_FRAME_DTMF) {
ast_log(LOG_DTMF, "%s : %c\n", chan->name, f->subclass);
} else if (fr->frametype == AST_FRAME_DTMF) {
ast_log(LOG_DTMF, "%s : %c\n", chan->name, fr->subclass);
}
if (f && (f != fr))
ast_frfree(f);
ast_clear_flag(chan, AST_FLAG_BLOCKING);
/* Consider a write failure to force a soft hangup */
if (res < 0)
else {
if ((chan->fout & 0x7fffffff) == 0x7fffffff)
chan->fout &= 0x80000000;
else
chan->fout++;
}
ast_mutex_unlock(&chan->lock);
static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *format,
struct ast_trans_pvt **trans, const int direction)
{
int native;
int res;
native = chan->nativeformats;
/* Find a translation path from the native format to one of the desired formats */
if (!direction)
/* reading */
res = ast_translator_best_choice(&fmt, &native);
else
/* writing */
res = ast_translator_best_choice(&native, &fmt);
ast_log(LOG_WARNING, "Unable to find a codec translation path from %s to %s\n",
ast_getformatname(native), ast_getformatname(fmt));
/* Now we have a good choice for both. */
ast_mutex_lock(&chan->lock);
*rawformat = native;
*format = fmt;
/* Free any read translation we have right now */
if (*trans)
ast_translator_free_path(*trans);
/* Build a translation path from the raw format to the desired format */
if (!direction)
/* reading */
*trans = ast_translator_build_path(*format, *rawformat);
else
/* writing */
*trans = ast_translator_build_path(*rawformat, *format);
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
if (option_debug)
ast_log(LOG_DEBUG, "Set channel %s to %s format %s\n", chan->name,
direction ? "write" : "read", ast_getformatname(fmt));
int ast_set_read_format(struct ast_channel *chan, int fmt)
return set_format(chan, fmt, &chan->rawreadformat, &chan->readformat,
&chan->readtrans, 0);
}
int ast_set_write_format(struct ast_channel *chan, int fmt)
{
return set_format(chan, fmt, &chan->rawwriteformat, &chan->writeformat,
&chan->writetrans, 1);
struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
Mark Spencer
committed
int cause = 0;
struct ast_channel *chan;
struct ast_frame *f;
Mark Spencer
committed
chan = ast_request(type, format, data, &cause);
Martin Pycko
committed
if (oh) {
ast_set_variables(chan, oh->vars);
ast_set_callerid(chan, oh->cid_num, oh->cid_name, oh->cid_num);
Martin Pycko
committed
}
ast_set_callerid(chan, cid_num, cid_name, cid_num);
Martin Pycko
committed
if (!ast_call(chan, data, 0)) {
while(timeout && (chan->_state != AST_STATE_UP)) {
res = ast_waitfor(chan, timeout);
if (res < 0) {
/* Something not cool, or timed out */
break;
}
/* If done, break out */
if (!res)
break;
if (timeout > -1)
timeout = res;
f = ast_read(chan);
if (!f) {
state = AST_CONTROL_HANGUP;
break;
}
if (f->frametype == AST_FRAME_CONTROL) {
if (f->subclass == AST_CONTROL_RINGING)
state = AST_CONTROL_RINGING;
else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
state = f->subclass;
break;
} else if (f->subclass == AST_CONTROL_ANSWER) {
state = f->subclass;
} else if (f->subclass == AST_CONTROL_PROGRESS) {
/* Ignore */
} else if (f->subclass == -1) {
/* Ignore -- just stopping indications */
} else {
ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass);
}
}
ast_frfree(f);
}
Mark Spencer
committed
ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
} else {
ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
Mark Spencer
committed
switch(cause) {
case AST_CAUSE_BUSY:
state = AST_CONTROL_BUSY;
break;
case AST_CAUSE_CONGESTION:
state = AST_CONTROL_CONGESTION;
break;
}
}
if (chan) {
/* Final fixups */
if (oh) {
if (oh->context && *oh->context)
ast_copy_string(chan->context, oh->context, sizeof(chan->context));
ast_copy_string(chan->exten, oh->exten, sizeof(chan->exten));
if (oh->priority)
chan->priority = oh->priority;
}
if (chan->_state == AST_STATE_UP)
state = AST_CONTROL_ANSWER;
}
if (chan && res <= 0) {
if (!chan->cdr) {
chan->cdr = ast_cdr_alloc();
if (chan->cdr)
ast_cdr_init(chan->cdr, chan);
}
if (chan->cdr) {
char tmp[256];
snprintf(tmp, 256, "%s/%s", type, (char *)data);
ast_cdr_setapp(chan->cdr,"Dial",tmp);
ast_cdr_update(chan);
ast_cdr_start(chan->cdr);
ast_cdr_end(chan->cdr);
/* If the cause wasn't handled properly */
if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
ast_cdr_failed(chan->cdr);
} else
ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
ast_hangup(chan);
chan = NULL;
}
struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname)
Martin Pycko
committed
{
return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL);
Martin Pycko
committed
}
Mark Spencer
committed
struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
Mark Spencer
committed
int foo;
Mark Spencer
committed
if (!cause)
cause = &foo;
*cause = AST_CAUSE_NOTDEFINED;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return NULL;
}
for (chan = backends; chan; chan = chan->next) {
if (strcasecmp(type, chan->tech->type))
continue;
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)
return NULL;
if (!(c = chan->tech->requester(type, capabilities, data, cause)))
return NULL;
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"
"Uniqueid: %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);
}
return c;
Mark Spencer
committed
}
ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
*cause = AST_CAUSE_NOSUCHDRIVER;
ast_mutex_unlock(&chlock);
}
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);
/*--- ast_transfer: Transfer a call to dest, if the channel supports transfer */
/* called by app_transfer or the manager interface */
int ast_transfer(struct ast_channel *chan, char *dest)
{
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;
Kevin P. Fleming
committed
int 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;
Kevin P. Fleming
committed
int 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 {
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
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)
{
Kevin P. Fleming
committed
int src;
int dst;
/* Set up translation from the chan to the peer */
Kevin P. Fleming
committed
src = chan->nativeformats;
dst = peer->nativeformats;
if (ast_translator_best_choice(&dst, &src) < 0) {
ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan->name, src, peer->name, dst);
Kevin P. Fleming
committed
/* if the best path is not 'pass through', then
transcoding is needed; if desired, force transcode path
to use SLINEAR between channels */
if ((src != dst) && option_transcode_slin)
dst = AST_FORMAT_SLINEAR;
if (ast_set_read_format(chan, dst) < 0) {
ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan->name, dst);
Kevin P. Fleming
committed
if (ast_set_write_format(peer, dst) < 0) {
ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, dst);
Kevin P. Fleming
committed
/* Set up translation from the peer to the chan */
src = peer->nativeformats;
dst = chan->nativeformats;
if (ast_translator_best_choice(&dst, &src) < 0) {
ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer->name, src, chan->name, dst);
Kevin P. Fleming
committed
/* if the best path is not 'pass through', then
transcoding is needed; if desired, force transcode path
to use SLINEAR between channels */
if ((src != dst) && option_transcode_slin)
dst = AST_FORMAT_SLINEAR;
if (ast_set_read_format(peer, dst) < 0) {
ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer->name, dst);
Kevin P. Fleming
committed
if (ast_set_write_format(chan, dst) < 0) {
ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, dst);
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 channel %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 channel %s into the structure of %s\n", clone->name, original->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];
ast_copy_string(tmp, chan->name, sizeof(tmp));
ast_copy_string(chan->name, newname, sizeof(chan->name));
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", tmp, chan->name, chan->uniqueid);
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
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_TAIL(&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_TAIL(&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;
}
}
}
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
/* 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);
/*--- ast_do_masquerade: Masquerade a channel */
/* 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];
if (option_debug > 3)
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);
ast_log(LOG_DEBUG, "Got clone lock for masquerade 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;
ast_copy_string(orig, original->name, sizeof(orig));
ast_copy_string(newn, clone->name, sizeof(newn));
/* Create the masq name */
snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn);
ast_copy_string(original->name, newn, sizeof(original->name));
ast_copy_string(clone->name, masqn, sizeof(clone->name));
/* 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);
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
2912
/* 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);
ast_copy_string(clone->name, zombn, sizeof(clone->name));
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;
t_pvt = original->monitor;
original->monitor = clone->monitor;
clone->monitor = t_pvt;
ast_copy_string(original->language, clone->language, sizeof(original->language));
clone_variables(original, clone);
Kevin P. Fleming
committed
AST_LIST_HEAD_INIT_NOLOCK(&clone->varshead);
/* 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;