/*! * \internal * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly * identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the * local channel and the channel that instigated the park. */ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten, struct transfer_channel_data *parked_channel_data) { char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; struct ast_channel *parkee; struct ast_channel *parkee_side_2; int cause; /* Fill the variable with the extension and context we want to call */ snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ parkee = ast_request("Local", ast_channel_nativeformats(parker), NULL, parker, destination, &cause); if (!parkee) { return NULL; } /* Before we actually dial out let's inherit appropriate information. */ ast_channel_lock_both(parker, parkee); ast_channel_req_accountcodes(parkee, parker, AST_CHANNEL_REQUESTOR_REPLACEMENT); ast_channel_parkinglot_set(parkee, ast_channel_parkinglot(parker)); ast_connected_line_copy_from_caller(ast_channel_connected(parkee), ast_channel_caller(parker)); ast_channel_inherit_variables(parker, parkee); ast_bridge_set_transfer_variables(parkee, ast_channel_name(parker), 0); ast_channel_datastore_inherit(parker, parkee); ast_channel_unlock(parker); parkee_side_2 = ast_local_get_peer(parkee); ast_assert(parkee_side_2 != NULL); ast_channel_unlock(parkee); /* We need to have the parker subscribe to the new local channel before hand. */ if (create_parked_subscription_full(parker, ast_channel_uniqueid(parkee_side_2), 1, parked_channel_data)) { ast_channel_unref(parkee_side_2); ast_hangup(parkee); return NULL; } ast_channel_unref(parkee_side_2); /* Since the above worked fine now we actually call it and return the channel */ if (ast_call(parkee, destination, 0)) { ast_hangup(parkee); return NULL; } return parkee; }
static int serialize_showchan(struct ast_channel *c, char *buf, size_t size) { long elapsed_seconds = 0; int hour = 0, min = 0, sec = 0; struct ast_str *format_buf = ast_str_alloca(64); char cgrp[256]; char pgrp[256]; struct ast_str *write_transpath = ast_str_alloca(256); struct ast_str *read_transpath = ast_str_alloca(256); struct ast_bridge *bridge; memset(buf, 0, size); if (!c) return 0; elapsed_seconds = ast_channel_get_duration(c); hour = elapsed_seconds / 3600; min = (elapsed_seconds % 3600) / 60; sec = elapsed_seconds % 60; ast_channel_lock(c); bridge = ast_channel_get_bridge(c); ast_channel_unlock(c); snprintf(buf,size, "Name= %s\n" "Type= %s\n" "UniqueID= %s\n" "LinkedID= %s\n" "CallerIDNum= %s\n" "CallerIDName= %s\n" "ConnectedLineIDNum= %s\n" "ConnectedLineIDName=%s\n" "DNIDDigits= %s\n" "RDNIS= %s\n" "Parkinglot= %s\n" "Language= %s\n" "State= %s (%u)\n" "Rings= %d\n" "NativeFormat= %s\n" "WriteFormat= %s\n" "ReadFormat= %s\n" "RawWriteFormat= %s\n" "RawReadFormat= %s\n" "WriteTranscode= %s %s\n" "ReadTranscode= %s %s\n" "1stFileDescriptor= %d\n" "Framesin= %u %s\n" "Framesout= %u %s\n" "TimetoHangup= %ld\n" "ElapsedTime= %dh%dm%ds\n" "BridgeID= %s\n" "Context= %s\n" "Extension= %s\n" "Priority= %d\n" "CallGroup= %s\n" "PickupGroup= %s\n" "Application= %s\n" "Data= %s\n" "Blocking_in= %s\n", ast_channel_name(c), ast_channel_tech(c)->type, ast_channel_uniqueid(c), ast_channel_linkedid(c), S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, "(N/A)"), S_COR(ast_channel_caller(c)->id.name.valid, ast_channel_caller(c)->id.name.str, "(N/A)"), S_COR(ast_channel_connected(c)->id.number.valid, ast_channel_connected(c)->id.number.str, "(N/A)"), S_COR(ast_channel_connected(c)->id.name.valid, ast_channel_connected(c)->id.name.str, "(N/A)"), S_OR(ast_channel_dialed(c)->number.str, "(N/A)"), S_COR(ast_channel_redirecting(c)->from.number.valid, ast_channel_redirecting(c)->from.number.str, "(N/A)"), ast_channel_parkinglot(c), ast_channel_language(c), ast_state2str(ast_channel_state(c)), ast_channel_state(c), ast_channel_rings(c), ast_format_cap_get_names(ast_channel_nativeformats(c), &format_buf), ast_format_get_name(ast_channel_writeformat(c)), ast_format_get_name(ast_channel_readformat(c)), ast_format_get_name(ast_channel_rawwriteformat(c)), ast_format_get_name(ast_channel_rawreadformat(c)), ast_channel_writetrans(c) ? "Yes" : "No", ast_translate_path_to_str(ast_channel_writetrans(c), &write_transpath), ast_channel_readtrans(c) ? "Yes" : "No", ast_translate_path_to_str(ast_channel_readtrans(c), &read_transpath), ast_channel_fd(c, 0), ast_channel_fin(c) & ~DEBUGCHAN_FLAG, (ast_channel_fin(c) & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", ast_channel_fout(c) & ~DEBUGCHAN_FLAG, (ast_channel_fout(c) & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", (long)ast_channel_whentohangup(c)->tv_sec, hour, min, sec, bridge ? bridge->uniqueid : "(Not bridged)", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_print_group(cgrp, sizeof(cgrp), ast_channel_callgroup(c)), ast_print_group(pgrp, sizeof(pgrp), ast_channel_pickupgroup(c)), ast_channel_appl(c) ? ast_channel_appl(c) : "(N/A)", ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)") : "(None)", (ast_test_flag(ast_channel_flags(c), AST_FLAG_BLOCKING) ? ast_channel_blockproc(c) : "(Not Blocking)")); ao2_cleanup(bridge); return 0; }
static int func_channel_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len) { int ret = 0; struct ast_format_cap *tmpcap; if (!chan) { ast_log(LOG_WARNING, "No channel was provided to %s function.\n", function); return -1; } if (!strcasecmp(data, "audionativeformat")) { tmpcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (tmpcap) { struct ast_str *codec_buf = ast_str_alloca(128); ast_channel_lock(chan); ast_format_cap_append_from_cap(tmpcap, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_AUDIO); ast_channel_unlock(chan); ast_copy_string(buf, ast_format_cap_get_names(tmpcap, &codec_buf), len); ao2_ref(tmpcap, -1); } } else if (!strcasecmp(data, "videonativeformat")) { tmpcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (tmpcap) { struct ast_str *codec_buf = ast_str_alloca(128); ast_channel_lock(chan); ast_format_cap_append_from_cap(tmpcap, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_VIDEO); ast_channel_unlock(chan); ast_copy_string(buf, ast_format_cap_get_names(tmpcap, &codec_buf), len); ao2_ref(tmpcap, -1); } } else if (!strcasecmp(data, "audioreadformat")) { locked_copy_string(chan, buf, ast_format_get_name(ast_channel_readformat(chan)), len); } else if (!strcasecmp(data, "audiowriteformat")) { locked_copy_string(chan, buf, ast_format_get_name(ast_channel_writeformat(chan)), len); #ifdef CHANNEL_TRACE } else if (!strcasecmp(data, "trace")) { locked_copy_string(chan, buf, ast_channel_trace_is_enabled(chan) ? "1" : "0", len); #endif } else if (!strcasecmp(data, "tonezone") && ast_channel_zone(chan)) { locked_copy_string(chan, buf, ast_channel_zone(chan)->country, len); } else if (!strcasecmp(data, "dtmf_features")) { if (ast_bridge_features_ds_get_string(chan, buf, len)) { buf[0] = '\0'; } } else if (!strcasecmp(data, "language")) locked_copy_string(chan, buf, ast_channel_language(chan), len); else if (!strcasecmp(data, "musicclass")) locked_copy_string(chan, buf, ast_channel_musicclass(chan), len); else if (!strcasecmp(data, "name")) { locked_copy_string(chan, buf, ast_channel_name(chan), len); } else if (!strcasecmp(data, "parkinglot")) locked_copy_string(chan, buf, ast_channel_parkinglot(chan), len); else if (!strcasecmp(data, "state")) locked_copy_string(chan, buf, ast_state2str(ast_channel_state(chan)), len); else if (!strcasecmp(data, "onhold")) { locked_copy_string(chan, buf, ast_channel_hold_state(chan) == AST_CONTROL_HOLD ? "1" : "0", len); } else if (!strcasecmp(data, "channeltype")) locked_copy_string(chan, buf, ast_channel_tech(chan)->type, len); else if (!strcasecmp(data, "accountcode")) locked_copy_string(chan, buf, ast_channel_accountcode(chan), len); else if (!strcasecmp(data, "checkhangup")) { locked_copy_string(chan, buf, ast_check_hangup(chan) ? "1" : "0", len); } else if (!strcasecmp(data, "peeraccount")) locked_copy_string(chan, buf, ast_channel_peeraccount(chan), len); else if (!strcasecmp(data, "hangupsource")) locked_copy_string(chan, buf, ast_channel_hangupsource(chan), len); else if (!strcasecmp(data, "appname") && ast_channel_appl(chan)) locked_copy_string(chan, buf, ast_channel_appl(chan), len); else if (!strcasecmp(data, "appdata") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_data(chan), len); else if (!strcasecmp(data, "exten") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_exten(chan), len); else if (!strcasecmp(data, "context") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_context(chan), len); else if (!strcasecmp(data, "userfield") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_userfield(chan), len); else if (!strcasecmp(data, "channame") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_name(chan), len); else if (!strcasecmp(data, "linkedid")) { ast_channel_lock(chan); if (ast_strlen_zero(ast_channel_linkedid(chan))) { /* fall back on the channel's uniqueid if linkedid is unset */ ast_copy_string(buf, ast_channel_uniqueid(chan), len); } else { ast_copy_string(buf, ast_channel_linkedid(chan), len); } ast_channel_unlock(chan); } else if (!strcasecmp(data, "peer")) { struct ast_channel *peer; peer = ast_channel_bridge_peer(chan); if (peer) { /* Only real channels could have a bridge peer this way. */ ast_channel_lock(peer); ast_copy_string(buf, ast_channel_name(peer), len); ast_channel_unlock(peer); ast_channel_unref(peer); } else { buf[0] = '\0'; ast_channel_lock(chan); if (!ast_channel_tech(chan)) { const char *pname; /* * A dummy channel can still pass along bridged peer info * via the BRIDGEPEER variable. * * A horrible kludge, but... how else? */ pname = pbx_builtin_getvar_helper(chan, "BRIDGEPEER"); if (!ast_strlen_zero(pname)) { ast_copy_string(buf, pname, len); } } ast_channel_unlock(chan); } } else if (!strcasecmp(data, "uniqueid")) { locked_copy_string(chan, buf, ast_channel_uniqueid(chan), len); } else if (!strcasecmp(data, "transfercapability")) { locked_copy_string(chan, buf, transfercapability_table[ast_channel_transfercapability(chan) & 0x1f], len); } else if (!strcasecmp(data, "callgroup")) { char groupbuf[256]; locked_copy_string(chan, buf, ast_print_group(groupbuf, sizeof(groupbuf), ast_channel_callgroup(chan)), len); } else if (!strcasecmp(data, "pickupgroup")) { char groupbuf[256]; locked_copy_string(chan, buf, ast_print_group(groupbuf, sizeof(groupbuf), ast_channel_pickupgroup(chan)), len); } else if (!strcasecmp(data, "namedcallgroup")) { struct ast_str *tmp_str = ast_str_alloca(1024); locked_copy_string(chan, buf, ast_print_namedgroups(&tmp_str, ast_channel_named_callgroups(chan)), len); } else if (!strcasecmp(data, "namedpickupgroup")) { struct ast_str *tmp_str = ast_str_alloca(1024); locked_copy_string(chan, buf, ast_print_namedgroups(&tmp_str, ast_channel_named_pickupgroups(chan)), len); } else if (!strcasecmp(data, "after_bridge_goto")) { ast_bridge_read_after_goto(chan, buf, len); } else if (!strcasecmp(data, "amaflags")) { ast_channel_lock(chan); snprintf(buf, len, "%u", ast_channel_amaflags(chan)); ast_channel_unlock(chan); } else if (!strncasecmp(data, "secure_bridge_", 14)) { struct ast_datastore *ds; buf[0] = '\0'; ast_channel_lock(chan); if ((ds = ast_channel_datastore_find(chan, &secure_call_info, NULL))) { struct ast_secure_call_store *encrypt = ds->data; if (!strcasecmp(data, "secure_bridge_signaling")) { snprintf(buf, len, "%s", encrypt->signaling ? "1" : ""); } else if (!strcasecmp(data, "secure_bridge_media")) { snprintf(buf, len, "%s", encrypt->media ? "1" : ""); } } ast_channel_unlock(chan); } else if (!strcasecmp(data, "max_forwards")) { ast_channel_lock(chan); snprintf(buf, len, "%d", ast_max_forwards_get(chan)); ast_channel_unlock(chan); } else if (!ast_channel_tech(chan) || !ast_channel_tech(chan)->func_channel_read || ast_channel_tech(chan)->func_channel_read(chan, function, data, buf, len)) { ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data); ret = -1; } return ret; }
static int func_channel_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len) { int ret = 0; char tmp[512]; struct ast_format_cap *tmpcap; if (!strcasecmp(data, "audionativeformat")) { if ((tmpcap = ast_format_cap_get_type(ast_channel_nativeformats(chan), AST_FORMAT_TYPE_AUDIO))) { ast_copy_string(buf, ast_getformatname_multiple(tmp, sizeof(tmp), tmpcap), len); tmpcap = ast_format_cap_destroy(tmpcap); } } else if (!strcasecmp(data, "videonativeformat")) { if ((tmpcap = ast_format_cap_get_type(ast_channel_nativeformats(chan), AST_FORMAT_TYPE_VIDEO))) { ast_copy_string(buf, ast_getformatname_multiple(tmp, sizeof(tmp), tmpcap), len); tmpcap = ast_format_cap_destroy(tmpcap); } } else if (!strcasecmp(data, "audioreadformat")) { ast_copy_string(buf, ast_getformatname(ast_channel_readformat(chan)), len); } else if (!strcasecmp(data, "audiowriteformat")) { ast_copy_string(buf, ast_getformatname(ast_channel_writeformat(chan)), len); #ifdef CHANNEL_TRACE } else if (!strcasecmp(data, "trace")) { ast_channel_lock(chan); ast_copy_string(buf, ast_channel_trace_is_enabled(chan) ? "1" : "0", len); ast_channel_unlock(chan); #endif } else if (!strcasecmp(data, "tonezone") && ast_channel_zone(chan)) locked_copy_string(chan, buf, ast_channel_zone(chan)->country, len); else if (!strcasecmp(data, "language")) locked_copy_string(chan, buf, ast_channel_language(chan), len); else if (!strcasecmp(data, "musicclass")) locked_copy_string(chan, buf, ast_channel_musicclass(chan), len); else if (!strcasecmp(data, "name")) { locked_copy_string(chan, buf, ast_channel_name(chan), len); } else if (!strcasecmp(data, "parkinglot")) locked_copy_string(chan, buf, ast_channel_parkinglot(chan), len); else if (!strcasecmp(data, "state")) locked_copy_string(chan, buf, ast_state2str(ast_channel_state(chan)), len); else if (!strcasecmp(data, "channeltype")) locked_copy_string(chan, buf, ast_channel_tech(chan)->type, len); else if (!strcasecmp(data, "accountcode")) locked_copy_string(chan, buf, ast_channel_accountcode(chan), len); else if (!strcasecmp(data, "checkhangup")) { ast_channel_lock(chan); ast_copy_string(buf, ast_check_hangup(chan) ? "1" : "0", len); ast_channel_unlock(chan); } else if (!strcasecmp(data, "peeraccount")) locked_copy_string(chan, buf, ast_channel_peeraccount(chan), len); else if (!strcasecmp(data, "hangupsource")) locked_copy_string(chan, buf, ast_channel_hangupsource(chan), len); else if (!strcasecmp(data, "appname") && ast_channel_appl(chan)) locked_copy_string(chan, buf, ast_channel_appl(chan), len); else if (!strcasecmp(data, "appdata") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_data(chan), len); else if (!strcasecmp(data, "exten") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_exten(chan), len); else if (!strcasecmp(data, "context") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_context(chan), len); else if (!strcasecmp(data, "userfield") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_userfield(chan), len); else if (!strcasecmp(data, "channame") && ast_channel_data(chan)) locked_copy_string(chan, buf, ast_channel_name(chan), len); else if (!strcasecmp(data, "linkedid")) { ast_channel_lock(chan); if (ast_strlen_zero(ast_channel_linkedid(chan))) { /* fall back on the channel's uniqueid if linkedid is unset */ ast_copy_string(buf, ast_channel_uniqueid(chan), len); } else { ast_copy_string(buf, ast_channel_linkedid(chan), len); } ast_channel_unlock(chan); } else if (!strcasecmp(data, "peer")) { struct ast_channel *p; ast_channel_lock(chan); p = ast_bridged_channel(chan); if (p || ast_channel_tech(chan) || ast_channel_cdr(chan)) /* dummy channel? if so, we hid the peer name in the language */ ast_copy_string(buf, (p ? ast_channel_name(p) : ""), len); else { /* a dummy channel can still pass along bridged peer info via the BRIDGEPEER variable */ const char *pname = pbx_builtin_getvar_helper(chan, "BRIDGEPEER"); if (!ast_strlen_zero(pname)) ast_copy_string(buf, pname, len); /* a horrible kludge, but... how else? */ else buf[0] = 0; } ast_channel_unlock(chan); } else if (!strcasecmp(data, "uniqueid")) { locked_copy_string(chan, buf, ast_channel_uniqueid(chan), len); } else if (!strcasecmp(data, "transfercapability")) { locked_copy_string(chan, buf, transfercapability_table[ast_channel_transfercapability(chan) & 0x1f], len); } else if (!strcasecmp(data, "callgroup")) { char groupbuf[256]; locked_copy_string(chan, buf, ast_print_group(groupbuf, sizeof(groupbuf), ast_channel_callgroup(chan)), len); } else if (!strcasecmp(data, "pickupgroup")) { char groupbuf[256]; locked_copy_string(chan, buf, ast_print_group(groupbuf, sizeof(groupbuf), ast_channel_pickupgroup(chan)), len); } else if (!strcasecmp(data, "amaflags")) { char amabuf[256]; snprintf(amabuf,sizeof(amabuf), "%d", ast_channel_amaflags(chan)); locked_copy_string(chan, buf, amabuf, len); } else if (!strncasecmp(data, "secure_bridge_", 14)) { struct ast_datastore *ds; ast_channel_lock(chan); if ((ds = ast_channel_datastore_find(chan, &secure_call_info, NULL))) { struct ast_secure_call_store *encrypt = ds->data; if (!strcasecmp(data, "secure_bridge_signaling")) { snprintf(buf, len, "%s", encrypt->signaling ? "1" : ""); } else if (!strcasecmp(data, "secure_bridge_media")) { snprintf(buf, len, "%s", encrypt->media ? "1" : ""); } } ast_channel_unlock(chan); } else if (!ast_channel_tech(chan) || !ast_channel_tech(chan)->func_channel_read || ast_channel_tech(chan)->func_channel_read(chan, function, data, buf, len)) { ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data); ret = -1; } return ret; }
/*! * \internal * \brief Handle COLP and redirecting conditions. * \since 12.0.0 * * \param p Unreal private structure. * \param ast Channel indicating the condition. * \param condition What is being indicated. * * \retval 0 on success. * \retval -1 on error. */ static int unreal_colp_redirect_indicate(struct ast_unreal_pvt *p, struct ast_channel *ast, int condition) { struct ast_channel *my_chan; struct ast_channel *my_owner; struct ast_channel *this_channel; struct ast_channel *the_other_channel; int isoutbound; int res = 0; unsigned char frame_data[1024]; struct ast_frame f = { .frametype = AST_FRAME_CONTROL, .subclass.integer = condition, .data.ptr = frame_data, }; /* * A connected line update frame may only contain a partial * amount of data, such as just a source, or just a ton, and not * the full amount of information. However, the collected * information is all stored in the outgoing channel's * connectedline structure, so when receiving a connected line * update on an outgoing unreal channel, we need to transmit the * collected connected line information instead of whatever * happens to be in this control frame. The same applies for * redirecting information, which is why it is handled here as * well. */ ast_channel_unlock(ast); ast_unreal_lock_all(p, &my_chan, &my_owner); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); if (isoutbound) { this_channel = p->chan; the_other_channel = p->owner; } else { this_channel = p->owner; the_other_channel = p->chan; } if (the_other_channel) { if (condition == AST_CONTROL_CONNECTED_LINE) { ast_connected_line_copy_to_caller(ast_channel_caller(the_other_channel), ast_channel_connected(this_channel)); f.datalen = ast_connected_line_build_data(frame_data, sizeof(frame_data), ast_channel_connected(this_channel), NULL); } else { f.datalen = ast_redirecting_build_data(frame_data, sizeof(frame_data), ast_channel_redirecting(this_channel), NULL); } } if (my_chan) { ast_channel_unlock(my_chan); ast_channel_unref(my_chan); } if (my_owner) { ast_channel_unlock(my_owner); ast_channel_unref(my_owner); } if (the_other_channel) { res = unreal_queue_frame(p, isoutbound, &f, ast, 0); } ao2_unlock(p); ast_channel_lock(ast); return res; } int ast_unreal_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = 0; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ switch (condition) { case AST_CONTROL_MASQUERADE_NOTIFY: /* * Always block this because this is the channel being * masqueraded; not anything down the chain. */ break; case AST_CONTROL_CONNECTED_LINE: case AST_CONTROL_REDIRECTING: res = unreal_colp_redirect_indicate(p, ast, condition); break; case AST_CONTROL_HOLD: if (ast_test_flag(p, AST_UNREAL_MOH_INTERCEPT)) { ast_moh_start(ast, data, NULL); break; } res = unreal_queue_indicate(p, ast, condition, data, datalen); break; case AST_CONTROL_UNHOLD: if (ast_test_flag(p, AST_UNREAL_MOH_INTERCEPT)) { ast_moh_stop(ast); break; } res = unreal_queue_indicate(p, ast, condition, data, datalen); break; case AST_CONTROL_RINGING: /* Don't queue ringing frames if the channel is not in a "ring" state. Otherwise, * the real channel on the other end will likely start a playtones generator. It is * possible that this playtones generator will never be stopped under certain * circumstances. */ if (ast_channel_state(ast) == AST_STATE_RING) { res = unreal_queue_indicate(p, ast, condition, data, datalen); } else { res = -1; } break; case AST_CONTROL_PVT_CAUSE_CODE: /* Return -1 so that asterisk core will correctly set up hangupcauses. */ unreal_queue_indicate(p, ast, condition, data, datalen); res = -1; break; default: res = unreal_queue_indicate(p, ast, condition, data, datalen); break; } ao2_ref(p, -1); return res; } int ast_unreal_digit_begin(struct ast_channel *ast, char digit) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = -1; struct ast_frame f = { AST_FRAME_DTMF_BEGIN, }; int isoutbound; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ ao2_lock(p); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); f.subclass.integer = digit; res = unreal_queue_frame(p, isoutbound, &f, ast, 0); ao2_unlock(p); ao2_ref(p, -1); return res; } int ast_unreal_digit_end(struct ast_channel *ast, char digit, unsigned int duration) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = -1; struct ast_frame f = { AST_FRAME_DTMF_END, }; int isoutbound; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ ao2_lock(p); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); f.subclass.integer = digit; f.len = duration; res = unreal_queue_frame(p, isoutbound, &f, ast, 0); ao2_unlock(p); ao2_ref(p, -1); return res; } int ast_unreal_sendtext(struct ast_channel *ast, const char *text) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = -1; struct ast_frame f = { AST_FRAME_TEXT, }; int isoutbound; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ ao2_lock(p); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); f.data.ptr = (char *) text; f.datalen = strlen(text) + 1; res = unreal_queue_frame(p, isoutbound, &f, ast, 0); ao2_unlock(p); ao2_ref(p, -1); return res; } int ast_unreal_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = -1; struct ast_frame f = { AST_FRAME_HTML, }; int isoutbound; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ ao2_lock(p); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); f.subclass.integer = subclass; f.data.ptr = (char *)data; f.datalen = datalen; res = unreal_queue_frame(p, isoutbound, &f, ast, 0); ao2_unlock(p); ao2_ref(p, -1); return res; } void ast_unreal_call_setup(struct ast_channel *semi1, struct ast_channel *semi2) { struct ast_var_t *varptr; struct ast_var_t *clone_var; ast_channel_stage_snapshot(semi2); /* * Note that cid_num and cid_name aren't passed in the * ast_channel_alloc calls in ast_unreal_new_channels(). It's * done here instead. */ ast_party_redirecting_copy(ast_channel_redirecting(semi2), ast_channel_redirecting(semi1)); ast_party_dialed_copy(ast_channel_dialed(semi2), ast_channel_dialed(semi1)); /* Crossover the CallerID and conected-line to cross the unreal bridge. */ ast_connected_line_copy_to_caller(ast_channel_caller(semi2), ast_channel_connected(semi1)); ast_connected_line_copy_from_caller(ast_channel_connected(semi2), ast_channel_caller(semi1)); ast_channel_language_set(semi2, ast_channel_language(semi1)); ast_channel_musicclass_set(semi2, ast_channel_musicclass(semi1)); ast_channel_parkinglot_set(semi2, ast_channel_parkinglot(semi1)); /* Crossover the accountcode and peeraccount to cross the unreal bridge. */ ast_channel_accountcode_set(semi2, ast_channel_peeraccount(semi1)); ast_channel_peeraccount_set(semi2, ast_channel_accountcode(semi1)); ast_channel_cc_params_init(semi2, ast_channel_get_cc_config_params(semi1)); /* * Make sure we inherit the AST_CAUSE_ANSWERED_ELSEWHERE if it's * set on the queue/dial call request in the dialplan. */ if (ast_channel_hangupcause(semi1) == AST_CAUSE_ANSWERED_ELSEWHERE) { ast_channel_hangupcause_set(semi2, AST_CAUSE_ANSWERED_ELSEWHERE); } /* * Copy the channel variables from the semi1 channel to the * outgoing channel. * * Note that due to certain assumptions, they MUST be in the * same order. */ AST_LIST_TRAVERSE(ast_channel_varshead(semi1), varptr, entries) { clone_var = ast_var_assign(varptr->name, varptr->value); if (clone_var) { AST_LIST_INSERT_TAIL(ast_channel_varshead(semi2), clone_var, entries); ast_channel_publish_varset(semi2, ast_var_full_name(clone_var), ast_var_value(clone_var)); } } ast_channel_datastore_inherit(semi1, semi2); ast_channel_stage_snapshot_done(semi2); }