static struct ast_autochan *next_channel(struct ast_channel_iterator *iter, struct ast_autochan *autochan, struct ast_channel *chan) { struct ast_channel *next; struct ast_autochan *autochan_store; const size_t pseudo_len = strlen("DAHDI/pseudo"); if (!iter) { return NULL; } for (; (next = ast_channel_iterator_next(iter)); ast_channel_unref(next)) { if (!strncmp(ast_channel_name(next), "DAHDI/pseudo", pseudo_len) || next == chan) { continue; } autochan_store = ast_autochan_setup(next); ast_channel_unref(next); return autochan_store; } return NULL; }
/* Attempt to pick up specified by partial channel name */ static int pickup_by_part(struct ast_channel *chan, const char *part) { struct ast_channel *target;/*!< Potential pickup target */ int res = -1; /* The found channel is already locked. */ target = ast_channel_callback(find_by_part, NULL, (char *) part, 0); if (target) { res = ast_do_pickup(chan, target); ast_channel_unlock(target); target = ast_channel_unref(target); } return res; }
static int bridgeadd_exec(struct ast_channel *chan, const char *data) { struct ast_channel *c_ref; struct ast_bridge_features chan_features; struct ast_bridge *bridge; char *c_name; /* Answer the channel if needed */ if (ast_channel_state(chan) != AST_STATE_UP) { ast_answer(chan); } if (!(c_ref = ast_channel_get_by_name_prefix(data, strlen(data)))) { ast_log(LOG_WARNING, "Channel %s not found\n", data); return -1; } c_name = ast_strdupa(ast_channel_name(c_ref)); ast_channel_lock(c_ref); bridge = ast_channel_get_bridge(c_ref); ast_channel_unlock(c_ref); ast_channel_unref(c_ref); if (!bridge) { ast_log(LOG_WARNING, "Channel %s is not in a bridge\n", c_name); return -1; } ast_verb(3, "%s is joining %s in bridge %s\n", ast_channel_name(chan), c_name, bridge->uniqueid); if (ast_bridge_features_init(&chan_features) || ast_bridge_join(bridge, chan, NULL, &chan_features, NULL, 0)) { ast_log(LOG_WARNING, "%s failed to join %s in bridge %s\n", ast_channel_name(chan), c_name, bridge->uniqueid); ast_bridge_features_cleanup(&chan_features); ao2_cleanup(bridge); return -1; } ast_bridge_features_cleanup(&chan_features); ao2_cleanup(bridge); return 0; }
static int pickup_by_group(struct ast_channel *chan) { struct ast_channel *target;/*!< Potential pickup target */ int res = -1; /* The found channel is already locked. */ target = ast_pickup_find_by_group(chan); if (target) { ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan)); res = ast_do_pickup(chan, target); ast_channel_unlock(target); target = ast_channel_unref(target); } return res; }
static char *handle_redirect(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { const char *name, *dest; struct ast_channel *chan; int res; switch (cmd) { case CLI_INIT: e->command = "channel redirect"; e->usage = "" "Usage: channel redirect <channel> <[[context,]exten,]priority>\n" " Redirect an active channel to a specified extension.\n"; /*! \todo It would be nice to be able to redirect 2 channels at the same * time like you can with AMI redirect. However, it is not possible to acquire * two channels without the potential for a deadlock with how ast_channel structs * are managed today. Once ast_channel is a refcounted object, this command * will be able to support that. */ return NULL; case CLI_GENERATE: return ast_complete_channels(a->line, a->word, a->pos, a->n, 2); } if (a->argc != e->args + 2) { return CLI_SHOWUSAGE; } name = a->argv[2]; dest = a->argv[3]; if (!(chan = ast_channel_get_by_name(name))) { ast_cli(a->fd, "Channel '%s' not found\n", name); return CLI_FAILURE; } res = ast_async_parseable_goto(chan, dest); chan = ast_channel_unref(chan); if (!res) { ast_cli(a->fd, "Channel '%s' successfully redirected to %s\n", name, dest); } else { ast_cli(a->fd, "Channel '%s' failed to be redirected to %s\n", name, dest); } return res ? CLI_FAILURE : CLI_SUCCESS; }
/*! \brief Attempt to pick up named channel, does not use context */ static int pickup_by_channel(struct ast_channel *chan, char *pickup) { int res = -1; struct ast_channel *target;/*!< Potential pickup target */ target = my_ast_get_channel_by_name_locked(pickup); if (target) { /* Just check that we are not picking up the SAME as target. (i.e. ourself) */ if (chan != target) { res = ast_do_pickup(chan, target); } ast_channel_unlock(target); target = ast_channel_unref(target); } return res; }
/*! * \brief Pickup a call * \param chan channel that initiated pickup. * * Walk list of channels, checking it is not itself, channel is pbx one, * check that the callgroup for both channels are the same and the channel is ringing. * Answer calling channel, flag channel as answered on queue, masq channels together. */ int ast_pickup_call(struct ast_channel *chan) { struct ast_channel *target;/*!< Potential pickup target */ int res = -1; RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup); const char *pickup_sound; const char *fail_sound; ast_debug(1, "Pickup attempt by %s\n", ast_channel_name(chan)); ast_channel_lock(chan); pickup_cfg = ast_get_chan_features_pickup_config(chan); if (!pickup_cfg) { ast_log(LOG_ERROR, "Unable to retrieve pickup configuration. Unable to play pickup sounds\n"); } pickup_sound = ast_strdupa(pickup_cfg ? pickup_cfg->pickupsound : ""); fail_sound = ast_strdupa(pickup_cfg ? pickup_cfg->pickupfailsound : ""); ast_channel_unlock(chan); /* The found channel is already locked. */ target = ast_pickup_find_by_group(chan); if (target) { ast_log(LOG_NOTICE, "Pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan)); res = ast_do_pickup(chan, target); ast_channel_unlock(target); if (!res) { if (!ast_strlen_zero(pickup_sound)) { pbx_builtin_setvar_helper(target, "BRIDGE_PLAY_SOUND", pickup_sound); } } else { ast_log(LOG_WARNING, "Pickup %s failed by %s\n", ast_channel_name(target), ast_channel_name(chan)); } target = ast_channel_unref(target); } if (res < 0) { ast_debug(1, "No call pickup possible... for %s\n", ast_channel_name(chan)); if (!ast_strlen_zero(fail_sound)) { ast_answer(chan); ast_stream_and_wait(chan, fail_sound, ""); } } return res; }
void ast_cel_check_retire_linkedid(struct ast_channel *chan) { const char *linkedid = chan->linkedid; struct channel_find_data find_dat; /* make sure we need to do all this work */ if (!ast_strlen_zero(linkedid) && ast_cel_track_event(AST_CEL_LINKEDID_END)) { struct ast_channel *tmp = NULL; find_dat.chan = chan; find_dat.linkedid = linkedid; if ((tmp = ast_channel_callback(linkedid_match, NULL, &find_dat, 0))) { tmp = ast_channel_unref(tmp); } else { ast_cel_report_event(chan, AST_CEL_LINKEDID_END, NULL, NULL, NULL); } } }
static void call_forwarded_handler(struct stasis_app *app, struct stasis_message *message) { struct ast_multi_channel_blob *payload = stasis_message_data(message); struct ast_channel_snapshot *snapshot = ast_multi_channel_blob_get_channel(payload, "forwarded"); struct ast_channel *chan; if (!snapshot) { return; } chan = ast_channel_get_by_name(snapshot->base->uniqueid); if (!chan) { return; } app_subscribe_channel(app, chan); ast_channel_unref(chan); }
/*! * \brief queue a frame onto either the p->owner or p->chan * * \note the ast_unreal_pvt MUST have it's ref count bumped before entering this function and * decremented after this function is called. This is a side effect of the deadlock * avoidance that is necessary to lock 2 channels and a tech_pvt. Without a ref counted * ast_unreal_pvt, it is impossible to guarantee it will not be destroyed by another thread * during deadlock avoidance. */ static int unreal_queue_frame(struct ast_unreal_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us, int us_locked) { struct ast_channel *other; /* Recalculate outbound channel */ other = isoutbound ? p->owner : p->chan; if (!other) { return 0; } /* do not queue media frames if a generator is on both unreal channels */ if (us && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) && ast_channel_generator(us) && ast_channel_generator(other)) { return 0; } /* grab a ref on the channel before unlocking the pvt, * other can not go away from us now regardless of locking */ ast_channel_ref(other); if (us && us_locked) { ast_channel_unlock(us); } ao2_unlock(p); if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_RINGING) { ast_setstate(other, AST_STATE_RINGING); } ast_queue_frame(other, f); other = ast_channel_unref(other); if (us && us_locked) { ast_channel_lock(us); } ao2_lock(p); return 0; }
void sccpconf_announce_channel_depart(struct ast_channel *chan) { struct announce_pvt *p = ast_channel_tech_pvt(chan); if (!p) { return; } ao2_ref(p, +1); ao2_lock(p); if (!ast_test_flag(&p->base, AST_UNREAL_CARETAKER_THREAD)) { ao2_unlock(p); ao2_ref(p, -1); return; } ast_clear_flag(&p->base, AST_UNREAL_CARETAKER_THREAD); chan = p->base.chan; ao2_unlock(p); ao2_ref(p, -1); if (chan) { ast_bridge_depart(chan); ast_channel_unref(chan); } }
/*! \brief Called when a frame should be written out to a channel */ static int bridge_write(struct ast_channel *ast, struct ast_frame *f) { struct bridge_pvt *p = ast->tech_pvt; struct ast_channel *other = NULL; ao2_lock(p); /* only write frames to output. */ if (p->input == ast) { other = p->output; if (other) { ast_channel_ref(other); } } ao2_unlock(p); if (other) { ast_channel_unlock(ast); ast_queue_frame(other, f); ast_channel_lock(ast); other = ast_channel_unref(other); } return 0; }
static u_char *ast_var_channel_types_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const struct ast_channel_tech *tech = NULL; struct ast_variable *channel_types, *next; static unsigned long long_ret; struct ast_channel *chan; u_long i; if (header_simple_table(vp, name, length, exact, var_len, write_method, -1)) return NULL; channel_types = ast_channeltype_list(); for (i = 1, next = channel_types; next && i != name[*length - 1]; next = next->next, i++) ; if (next != NULL) tech = ast_get_channel_tech(next->name); ast_variables_destroy(channel_types); if (next == NULL || tech == NULL) return NULL; switch (vp->magic) { case ASTCHANTYPEINDEX: long_ret = name[*length - 1]; return (u_char *)&long_ret; case ASTCHANTYPENAME: *var_len = strlen(tech->type); return (u_char *)tech->type; case ASTCHANTYPEDESC: *var_len = strlen(tech->description); return (u_char *)tech->description; case ASTCHANTYPEDEVSTATE: long_ret = tech->devicestate ? 1 : 2; return (u_char *)&long_ret; case ASTCHANTYPEINDICATIONS: long_ret = tech->indicate ? 1 : 2; return (u_char *)&long_ret; case ASTCHANTYPETRANSFER: long_ret = tech->transfer ? 1 : 2; return (u_char *)&long_ret; case ASTCHANTYPECHANNELS: { struct ast_channel_iterator *iter; long_ret = 0; if (!(iter = ast_channel_iterator_all_new())) { return NULL; } while ((chan = ast_channel_iterator_next(iter))) { if (ast_channel_tech(chan) == tech) { long_ret++; } chan = ast_channel_unref(chan); } ast_channel_iterator_destroy(iter); return (u_char *)&long_ret; } default: break; } return NULL; }
static u_char *ast_var_channels_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { static unsigned long long_ret; static u_char bits_ret[2]; static char string_ret[256]; struct ast_channel *chan, *bridge; struct timeval tval; u_char *ret = NULL; int i, bit; struct ast_str *out = ast_str_alloca(2048); struct ast_channel_iterator *iter; if (header_simple_table(vp, name, length, exact, var_len, write_method, ast_active_channels())) return NULL; i = name[*length - 1] - 1; if (!(iter = ast_channel_iterator_all_new())) { return NULL; } while ((chan = ast_channel_iterator_next(iter)) && i) { ast_channel_unref(chan); i--; } iter = ast_channel_iterator_destroy(iter); if (chan == NULL) { return NULL; } *var_len = sizeof(long_ret); ast_channel_lock(chan); switch (vp->magic) { case ASTCHANINDEX: long_ret = name[*length - 1]; ret = (u_char *)&long_ret; break; case ASTCHANNAME: if (!ast_strlen_zero(ast_channel_name(chan))) { ast_copy_string(string_ret, ast_channel_name(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANLANGUAGE: if (!ast_strlen_zero(ast_channel_language(chan))) { ast_copy_string(string_ret, ast_channel_language(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANTYPE: ast_copy_string(string_ret, ast_channel_tech(chan)->type, sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMUSICCLASS: if (!ast_strlen_zero(ast_channel_musicclass(chan))) { ast_copy_string(string_ret, ast_channel_musicclass(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANBRIDGE: if ((bridge = ast_bridged_channel(chan)) != NULL) { ast_copy_string(string_ret, ast_channel_name(bridge), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANMASQ: if (ast_channel_masq(chan) && !ast_strlen_zero(ast_channel_name(ast_channel_masq(chan)))) { ast_copy_string(string_ret, ast_channel_name(ast_channel_masq(chan)), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANMASQR: if (ast_channel_masqr(chan) && !ast_strlen_zero(ast_channel_name(ast_channel_masqr(chan)))) { ast_copy_string(string_ret, ast_channel_name(ast_channel_masqr(chan)), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANWHENHANGUP: if (!ast_tvzero(*ast_channel_whentohangup(chan))) { gettimeofday(&tval, NULL); long_ret = difftime(ast_channel_whentohangup(chan)->tv_sec, tval.tv_sec) * 100 - tval.tv_usec / 10000; ret= (u_char *)&long_ret; } break; case ASTCHANAPP: if (ast_channel_appl(chan)) { ast_copy_string(string_ret, ast_channel_appl(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANDATA: if (ast_channel_data(chan)) { ast_copy_string(string_ret, ast_channel_data(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCONTEXT: ast_copy_string(string_ret, ast_channel_context(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROCONTEXT: ast_copy_string(string_ret, ast_channel_macrocontext(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROEXTEN: ast_copy_string(string_ret, ast_channel_macroexten(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROPRI: long_ret = ast_channel_macropriority(chan); ret = (u_char *)&long_ret; break; case ASTCHANEXTEN: ast_copy_string(string_ret, ast_channel_exten(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANPRI: long_ret = ast_channel_priority(chan); ret = (u_char *)&long_ret; break; case ASTCHANACCOUNTCODE: if (!ast_strlen_zero(ast_channel_accountcode(chan))) { ast_copy_string(string_ret, ast_channel_accountcode(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANFORWARDTO: if (!ast_strlen_zero(ast_channel_call_forward(chan))) { ast_copy_string(string_ret, ast_channel_call_forward(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANUNIQUEID: ast_copy_string(string_ret, ast_channel_uniqueid(chan), sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANCALLGROUP: long_ret = ast_channel_callgroup(chan); ret = (u_char *)&long_ret; break; case ASTCHANPICKUPGROUP: long_ret = ast_channel_pickupgroup(chan); ret = (u_char *)&long_ret; break; case ASTCHANSTATE: long_ret = ast_channel_state(chan) & 0xffff; ret = (u_char *)&long_ret; break; case ASTCHANMUTED: long_ret = ast_channel_state(chan) & AST_STATE_MUTE ? 1 : 2; ret = (u_char *)&long_ret; break; case ASTCHANRINGS: long_ret = ast_channel_rings(chan); ret = (u_char *)&long_ret; break; case ASTCHANCIDDNID: if (ast_channel_dialed(chan)->number.str) { ast_copy_string(string_ret, ast_channel_dialed(chan)->number.str, sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDNUM: if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) { ast_copy_string(string_ret, ast_channel_caller(chan)->id.number.str, sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDNAME: if (ast_channel_caller(chan)->id.name.valid && ast_channel_caller(chan)->id.name.str) { ast_copy_string(string_ret, ast_channel_caller(chan)->id.name.str, sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDANI: if (ast_channel_caller(chan)->ani.number.valid && ast_channel_caller(chan)->ani.number.str) { ast_copy_string(string_ret, ast_channel_caller(chan)->ani.number.str, sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDRDNIS: if (ast_channel_redirecting(chan)->from.number.valid && ast_channel_redirecting(chan)->from.number.str) { ast_copy_string(string_ret, ast_channel_redirecting(chan)->from.number.str, sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDPRES: long_ret = ast_party_id_presentation(&ast_channel_caller(chan)->id); ret = (u_char *)&long_ret; break; case ASTCHANCIDANI2: long_ret = ast_channel_caller(chan)->ani2; ret = (u_char *)&long_ret; break; case ASTCHANCIDTON: long_ret = ast_channel_caller(chan)->id.number.plan; ret = (u_char *)&long_ret; break; case ASTCHANCIDTNS: long_ret = ast_channel_dialed(chan)->transit_network_select; ret = (u_char *)&long_ret; break; case ASTCHANAMAFLAGS: long_ret = ast_channel_amaflags(chan); ret = (u_char *)&long_ret; break; case ASTCHANADSI: long_ret = ast_channel_adsicpe(chan); ret = (u_char *)&long_ret; break; case ASTCHANTONEZONE: if (ast_channel_zone(chan)) { ast_copy_string(string_ret, ast_channel_zone(chan)->country, sizeof(string_ret)); *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANHANGUPCAUSE: long_ret = ast_channel_hangupcause(chan); ret = (u_char *)&long_ret; break; case ASTCHANVARIABLES: if (pbx_builtin_serialize_variables(chan, &out)) { *var_len = ast_str_strlen(out); ret = (u_char *)ast_str_buffer(out); } break; case ASTCHANFLAGS: bits_ret[0] = 0; for (bit = 0; bit < 8; bit++) bits_ret[0] |= ((ast_channel_flags(chan)->flags & (1 << bit)) >> bit) << (7 - bit); bits_ret[1] = 0; for (bit = 0; bit < 8; bit++) bits_ret[1] |= (((ast_channel_flags(chan)->flags >> 8) & (1 << bit)) >> bit) << (7 - bit); *var_len = 2; ret = bits_ret; break; case ASTCHANTRANSFERCAP: long_ret = ast_channel_transfercapability(chan); ret = (u_char *)&long_ret; default: break; } ast_channel_unlock(chan); chan = ast_channel_unref(chan); return ret; }
/*! \brief Initiate new call, part of PBX interface * dest is the dial string */ static int local_call(struct ast_channel *ast, const char *dest, int timeout) { struct local_pvt *p = ast_channel_tech_pvt(ast); int pvt_locked = 0; struct ast_channel *owner = NULL; struct ast_channel *chan = NULL; int res; char *reduced_dest = ast_strdupa(dest); char *slash; const char *chan_cid; if (!p) { return -1; } /* since we are letting go of channel locks that were locked coming into * this function, then we need to give the tech pvt a ref */ ao2_ref(p, 1); ast_channel_unlock(ast); ast_unreal_lock_all(&p->base, &chan, &owner); pvt_locked = 1; if (owner != ast) { res = -1; goto return_cleanup; } if (!owner || !chan) { res = -1; goto return_cleanup; } ast_unreal_call_setup(owner, chan); /* * If the local channel has /n on the end of it, we need to lop * that off for our argument to setting up the CC_INTERFACES * variable. */ if ((slash = strrchr(reduced_dest, '/'))) { *slash = '\0'; } ast_set_cc_interfaces_chanvar(chan, reduced_dest); ao2_unlock(p); pvt_locked = 0; ast_channel_unlock(owner); chan_cid = S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL); if (chan_cid) { chan_cid = ast_strdupa(chan_cid); } ast_channel_unlock(chan); res = -1; switch (p->type) { case LOCAL_CALL_ACTION_DIALPLAN: if (!ast_exists_extension(NULL, p->context, p->exten, 1, chan_cid)) { ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->exten, p->context); } else { publish_local_bridge_message(p); /* Start switch on sub channel */ res = ast_pbx_start(chan); } break; case LOCAL_CALL_ACTION_BRIDGE: publish_local_bridge_message(p); ast_answer(chan); res = ast_bridge_impart(p->action.bridge.join, chan, p->action.bridge.swap, p->action.bridge.features, AST_BRIDGE_IMPART_CHAN_INDEPENDENT); ao2_ref(p->action.bridge.join, -1); p->action.bridge.join = NULL; ao2_cleanup(p->action.bridge.swap); p->action.bridge.swap = NULL; p->action.bridge.features = NULL; break; case LOCAL_CALL_ACTION_MASQUERADE: publish_local_bridge_message(p); ast_answer(chan); res = ast_channel_move(p->action.masq, chan); if (!res) { /* Chan is now an orphaned zombie. Destroy it. */ ast_hangup(chan); } p->action.masq = ast_channel_unref(p->action.masq); break; } if (!res) { ao2_lock(p); ast_set_flag(&p->base, AST_UNREAL_CARETAKER_THREAD); ao2_unlock(p); } /* we already unlocked them, clear them here so the cleanup label won't touch them. */ owner = ast_channel_unref(owner); chan = ast_channel_unref(chan); return_cleanup: if (p) { if (pvt_locked) { ao2_unlock(p); } ao2_ref(p, -1); } if (chan) { ast_channel_unlock(chan); ast_channel_unref(chan); } /* * owner is supposed to be == to ast, if it is, don't unlock it * because ast must exit locked */ if (owner) { if (owner != ast) { ast_channel_unlock(owner); ast_channel_lock(ast); } ast_channel_unref(owner); } else { /* we have to exit with ast locked */ ast_channel_lock(ast); } return res; }
struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event) { struct varshead *headp; struct ast_var_t *newvariable; const char *mixed_name; char timebuf[30]; struct ast_channel *tchan; struct ast_cel_event_record record = { .version = AST_CEL_EVENT_RECORD_VERSION, }; struct ast_datastore *datastore; char *app_data; /* do not call ast_channel_alloc because this is not really a real channel */ if (!(tchan = ast_dummy_channel_alloc())) { return NULL; } headp = ast_channel_varshead(tchan); /* first, get the variables from the event */ if (ast_cel_fill_record(event, &record)) { ast_channel_unref(tchan); return NULL; } /* next, fill the channel with their data */ mixed_name = (record.event_type == AST_CEL_USER_DEFINED) ? record.user_defined_name : record.event_name; if ((newvariable = ast_var_assign("eventtype", mixed_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if (ast_strlen_zero(cel_dateformat)) { snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec, (long) record.event_time.tv_usec); } else { struct ast_tm tm; ast_localtime(&record.event_time, &tm, NULL); ast_strftime(timebuf, sizeof(timebuf), cel_dateformat, &tm); } if ((newvariable = ast_var_assign("eventtime", timebuf))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventenum", record.event_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("userdeftype", record.user_defined_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventextra", record.extra))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } ast_channel_caller(tchan)->id.name.valid = 1; ast_channel_caller(tchan)->id.name.str = ast_strdup(record.caller_id_name); ast_channel_caller(tchan)->id.number.valid = 1; ast_channel_caller(tchan)->id.number.str = ast_strdup(record.caller_id_num); ast_channel_caller(tchan)->ani.number.valid = 1; ast_channel_caller(tchan)->ani.number.str = ast_strdup(record.caller_id_ani); ast_channel_redirecting(tchan)->from.number.valid = 1; ast_channel_redirecting(tchan)->from.number.str = ast_strdup(record.caller_id_rdnis); ast_channel_dialed(tchan)->number.str = ast_strdup(record.caller_id_dnid); ast_channel_exten_set(tchan, record.extension); ast_channel_context_set(tchan, record.context); ast_channel_name_set(tchan, record.channel_name); ast_channel_uniqueid_set(tchan, record.unique_id); ast_channel_linkedid_set(tchan, record.linked_id); ast_channel_accountcode_set(tchan, record.account_code); ast_channel_peeraccount_set(tchan, record.peer_account); ast_channel_userfield_set(tchan, record.user_field); if ((newvariable = ast_var_assign("BRIDGEPEER", record.peer))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } ast_channel_amaflags_set(tchan, record.amaflag); /* We need to store an 'application name' and 'application * data' on the channel for logging purposes, but the channel * structure only provides a place to store pointers, and it * expects these pointers to be pointing to data that does not * need to be freed. This means that the channel's destructor * does not attempt to free any storage that these pointers * point to. However, we can't provide data in that form directly for * these structure members. In order to ensure that these data * elements have a lifetime that matches the channel's * lifetime, we'll put them in a datastore attached to the * channel, and set's the channel's pointers to point into the * datastore. The datastore will then be automatically destroyed * when the channel is destroyed. */ if (!(datastore = ast_datastore_alloc(&fabricated_channel_datastore, NULL))) { ast_channel_unref(tchan); return NULL; } if (!(app_data = ast_malloc(strlen(record.application_name) + strlen(record.application_data) + 2))) { ast_datastore_free(datastore); ast_channel_unref(tchan); return NULL; } ast_channel_appl_set(tchan, strcpy(app_data, record.application_name)); ast_channel_data_set(tchan, strcpy(app_data + strlen(record.application_name) + 1, record.application_data)); datastore->data = app_data; ast_channel_datastore_add(tchan, datastore); return tchan; }
int ast_unreal_hangup(struct ast_unreal_pvt *p, struct ast_channel *ast) { int hangup_chan = 0; int res = 0; int cause; struct ast_channel *owner = NULL; struct ast_channel *chan = NULL; /* the pvt isn't going anywhere, it has a ref */ ast_channel_unlock(ast); /* lock everything */ ast_unreal_lock_all(p, &chan, &owner); if (ast != chan && ast != owner) { res = -1; goto unreal_hangup_cleanup; } cause = ast_channel_hangupcause(ast); if (ast == p->chan) { /* Outgoing side is hanging up. */ ast_clear_flag(p, AST_UNREAL_CARETAKER_THREAD); p->chan = NULL; if (p->owner) { const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS"); if (status) { ast_channel_hangupcause_set(p->owner, cause); pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status); } ast_queue_hangup_with_cause(p->owner, cause); } } else { /* Owner side is hanging up. */ p->owner = NULL; if (p->chan) { if (cause == AST_CAUSE_ANSWERED_ELSEWHERE) { ast_channel_hangupcause_set(p->chan, AST_CAUSE_ANSWERED_ELSEWHERE); ast_debug(2, "%s has AST_CAUSE_ANSWERED_ELSEWHERE set.\n", ast_channel_name(p->chan)); } if (!ast_test_flag(p, AST_UNREAL_CARETAKER_THREAD)) { /* * Need to actually hangup p->chan since nothing else is taking * care of it. */ hangup_chan = 1; } else { ast_queue_hangup_with_cause(p->chan, cause); } } } /* this is one of our locked channels, doesn't matter which */ ast_channel_tech_pvt_set(ast, NULL); ao2_ref(p, -1); unreal_hangup_cleanup: ao2_unlock(p); if (owner) { ast_channel_unlock(owner); ast_channel_unref(owner); } if (chan) { ast_channel_unlock(chan); if (hangup_chan) { ast_hangup(chan); } ast_channel_unref(chan); } /* leave with the channel locked that came in */ ast_channel_lock(ast); return res; }
static int manager_mutestream(struct mansession *s, const struct message *m) { const char *channel = astman_get_header(m, "Channel"); const char *id = astman_get_header(m,"ActionID"); const char *state = astman_get_header(m,"State"); const char *direction = astman_get_header(m,"Direction"); char id_text[256]; struct ast_channel *c = NULL; struct ast_datastore *datastore = NULL; struct mute_information *mute = NULL; int is_new = 0; int turnon; if (ast_strlen_zero(channel)) { astman_send_error(s, m, "Channel not specified"); return 0; } if (ast_strlen_zero(state)) { astman_send_error(s, m, "State not specified"); return 0; } if (ast_strlen_zero(direction)) { astman_send_error(s, m, "Direction not specified"); return 0; } /* Ok, we have everything */ c = ast_channel_get_by_name(channel); if (!c) { astman_send_error(s, m, "No such channel"); return 0; } ast_channel_lock(c); if (!(datastore = ast_channel_datastore_find(c, &mute_datastore, NULL))) { if (!(datastore = initialize_mutehook(c))) { ast_channel_unlock(c); ast_channel_unref(c); astman_send_error(s, m, "Memory allocation failure"); return 0; } is_new = 1; } mute = datastore->data; turnon = ast_true(state); if (!strcasecmp(direction, "in")) { mute->mute_read = turnon; } else if (!strcasecmp(direction, "out")) { mute->mute_write = turnon; } else if (!strcasecmp(direction, "all")) { mute->mute_read = mute->mute_write = turnon; } if (is_new) { if (mute_add_audiohook(c, mute, datastore)) { /* Can't add audiohook */ ast_datastore_free(datastore); ast_free(mute); ast_channel_unlock(c); ast_channel_unref(c); astman_send_error(s, m, "Couldn't add mute audiohook"); return 0; } } ast_channel_unlock(c); ast_channel_unref(c); if (!ast_strlen_zero(id)) { snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id); } else { id_text[0] = '\0'; } astman_append(s, "Response: Success\r\n" "%s" "\r\n", id_text); return 0; }
int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event_type, const char *userdefevname, const char *extra, struct ast_channel *peer2) { struct timeval eventtime; struct ast_event *ev; const char *peername = ""; struct ast_channel *peer; ast_channel_lock(chan); peer = ast_bridged_channel(chan); if (peer) { ast_channel_ref(peer); } ast_channel_unlock(chan); /* Make sure a reload is not occurring while we're checking to see if this * is an event that we care about. We could lose an important event in this * process otherwise. */ ast_mutex_lock(&reload_lock); if (!cel_enabled || !ast_cel_track_event(event_type)) { ast_mutex_unlock(&reload_lock); if (peer) { ast_channel_unref(peer); } return 0; } if (event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END) { char *app; if (!(app = ao2_find(appset, (char *) chan->appl, OBJ_POINTER))) { ast_mutex_unlock(&reload_lock); if (peer) { ast_channel_unref(peer); } return 0; } ao2_ref(app, -1); } ast_mutex_unlock(&reload_lock); if (peer) { ast_channel_lock(peer); peername = ast_strdupa(peer->name); ast_channel_unlock(peer); } else if (peer2) { ast_channel_lock(peer2); peername = ast_strdupa(peer2->name); ast_channel_unlock(peer2); } if (!userdefevname) { userdefevname = ""; } if (!extra) { extra = ""; } eventtime = ast_tvnow(); ast_channel_lock(chan); ev = ast_event_new(AST_EVENT_CEL, AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type, AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec, AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec, AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, userdefevname, AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, ""), AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""), AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, S_COR(chan->caller.ani.number.valid, chan->caller.ani.number.str, ""), AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, S_COR(chan->redirecting.from.number.valid, chan->redirecting.from.number.str, ""), AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->dialed.number.str, ""), AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, chan->exten, AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, chan->context, AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, chan->name, AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->appl, ""), AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->data, ""), AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, chan->amaflags, AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, chan->accountcode, AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, chan->peeraccount, AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, chan->uniqueid, AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, chan->linkedid, AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, chan->userfield, AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, extra, AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, peername, AST_EVENT_IE_END); ast_channel_unlock(chan); if (peer) { peer = ast_channel_unref(peer); } if (ev && ast_event_queue(ev)) { ast_event_destroy(ev); return -1; } return 0; }
static int handle_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { struct ast_datastore *sip_session_datastore; struct ast_channel *other_party; int has_feature; int has_reason; if (!session->channel) { return 0; } has_feature = has_call_feature(rdata); has_reason = has_diversion_reason(rdata); if (!has_feature && !has_reason) { /* If we don't have a call feature or diversion reason or if it's not a feature this module is related to then there is nothing to do. */ return 0; } /* Check bridge status... */ other_party = ast_channel_bridge_peer(session->channel); if (!other_party) { /* The channel wasn't in a two party bridge */ ast_log(LOG_WARNING, "%s (%s) attempted to transfer to voicemail, " "but was not in a two party bridge.\n", ast_sorcery_object_get_id(session->endpoint), ast_channel_name(session->channel)); send_response(session, 400, rdata); return -1; } sip_session_datastore = ast_sip_session_alloc_datastore( &call_feature_info, DATASTORE_NAME); if (!sip_session_datastore) { ast_channel_unref(other_party); send_response(session, 500, rdata); return -1; } sip_session_datastore->data = other_party; if (ast_sip_session_add_datastore(session, sip_session_datastore)) { ast_channel_unref(other_party); ao2_ref(sip_session_datastore, -1); send_response(session, 500, rdata); return -1; } ao2_ref(sip_session_datastore, -1); if (has_feature) { pbx_builtin_setvar_helper(other_party, SEND_TO_VM_HEADER, SEND_TO_VM_HEADER_VALUE); } if (has_reason) { pbx_builtin_setvar_helper(other_party, SEND_TO_VM_REDIRECT, SEND_TO_VM_REDIRECT_VALUE); } return 0; }
/*! * \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_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; 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; /* * 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)); 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_accountcode_set(semi2, ast_channel_accountcode(semi1)); ast_channel_musicclass_set(semi2, ast_channel_musicclass(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_datastore_inherit(semi1, semi2); }
/*! * \internal * \note This function assumes that we're only called from the "outbound" local channel side * * \note it is assummed p is locked and reffed before entering this function */ static void check_bridge(struct ast_channel *ast, struct local_pvt *p) { struct ast_channel *owner; struct ast_channel *chan; struct ast_channel *bridged_chan; struct ast_frame *f; /* Do a few conditional checks early on just to see if this optimization is possible */ if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED) || !p->chan || !p->owner) { return; } /* Safely get the channel bridged to p->chan */ chan = ast_channel_ref(p->chan); ao2_unlock(p); /* don't call bridged channel with the pvt locked */ bridged_chan = ast_bridged_channel(chan); ao2_lock(p); chan = ast_channel_unref(chan); /* since we had to unlock p to get the bridged chan, validate our * data once again and verify the bridged channel is what we expect * it to be in order to perform this optimization */ if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED) || !p->chan || !p->owner || (ast_channel_internal_bridged_channel(p->chan) != bridged_chan)) { return; } /* only do the masquerade if we are being called on the outbound channel, if it has been bridged to another channel and if there are no pending frames on the owner channel (because they would be transferred to the outbound channel during the masquerade) */ if (!ast_channel_internal_bridged_channel(p->chan) /* Not ast_bridged_channel! Only go one step! */ || !AST_LIST_EMPTY(ast_channel_readq(p->owner)) || ast != p->chan /* Sanity check (should always be false) */) { return; } /* Masquerade bridged channel into owner */ /* Lock everything we need, one by one, and give up if we can't get everything. Remember, we'll get another chance in just a little bit */ if (ast_channel_trylock(ast_channel_internal_bridged_channel(p->chan))) { return; } if (ast_check_hangup(ast_channel_internal_bridged_channel(p->chan)) || ast_channel_trylock(p->owner)) { ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan)); return; } /* * At this point we have 4 locks: * p, p->chan (same as ast), p->chan->_bridge, p->owner * * Flush a voice or video frame on the outbound channel to make * the queue empty faster so we can get optimized out. */ f = AST_LIST_FIRST(ast_channel_readq(p->chan)); if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) { AST_LIST_REMOVE_HEAD(ast_channel_readq(p->chan), frame_list); ast_frfree(f); f = AST_LIST_FIRST(ast_channel_readq(p->chan)); } if (f || ast_check_hangup(p->owner) || ast_channel_masquerade(p->owner, ast_channel_internal_bridged_channel(p->chan))) { ast_channel_unlock(p->owner); ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan)); return; } /* Masquerade got setup. */ ast_debug(4, "Masquerading %s <- %s\n", ast_channel_name(p->owner), ast_channel_name(ast_channel_internal_bridged_channel(p->chan))); if (ast_channel_monitor(p->owner) && !ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan))) { struct ast_channel_monitor *tmp; /* If a local channel is being monitored, we don't want a masquerade * to cause the monitor to go away. Since the masquerade swaps the monitors, * pre-swapping the monitors before the masquerade will ensure that the monitor * ends up where it is expected. */ tmp = ast_channel_monitor(p->owner); ast_channel_monitor_set(p->owner, ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan))); ast_channel_monitor_set(ast_channel_internal_bridged_channel(p->chan), tmp); } if (ast_channel_audiohooks(p->chan)) { struct ast_audiohook_list *audiohooks_swapper; audiohooks_swapper = ast_channel_audiohooks(p->chan); ast_channel_audiohooks_set(p->chan, ast_channel_audiohooks(p->owner)); ast_channel_audiohooks_set(p->owner, audiohooks_swapper); } /* If any Caller ID was set, preserve it after masquerade like above. We must check * to see if Caller ID was set because otherwise we'll mistakingly copy info not * set from the dialplan and will overwrite the real channel Caller ID. The reason * for this whole preswapping action is because the Caller ID is set on the channel * thread (which is the to be masqueraded away local channel) before both local * channels are optimized away. */ if (ast_channel_caller(p->owner)->id.name.valid || ast_channel_caller(p->owner)->id.number.valid || ast_channel_caller(p->owner)->id.subaddress.valid || ast_channel_caller(p->owner)->ani.name.valid || ast_channel_caller(p->owner)->ani.number.valid || ast_channel_caller(p->owner)->ani.subaddress.valid) { SWAP(*ast_channel_caller(p->owner), *ast_channel_caller(ast_channel_internal_bridged_channel(p->chan))); } if (ast_channel_redirecting(p->owner)->from.name.valid || ast_channel_redirecting(p->owner)->from.number.valid || ast_channel_redirecting(p->owner)->from.subaddress.valid || ast_channel_redirecting(p->owner)->to.name.valid || ast_channel_redirecting(p->owner)->to.number.valid || ast_channel_redirecting(p->owner)->to.subaddress.valid) { SWAP(*ast_channel_redirecting(p->owner), *ast_channel_redirecting(ast_channel_internal_bridged_channel(p->chan))); } if (ast_channel_dialed(p->owner)->number.str || ast_channel_dialed(p->owner)->subaddress.valid) { SWAP(*ast_channel_dialed(p->owner), *ast_channel_dialed(ast_channel_internal_bridged_channel(p->chan))); } ast_app_group_update(p->chan, p->owner); ast_set_flag(p, LOCAL_ALREADY_MASQED); ast_channel_unlock(p->owner); ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan)); /* Do the masquerade now. */ owner = ast_channel_ref(p->owner); ao2_unlock(p); ast_channel_unlock(ast); ast_do_masquerade(owner); ast_channel_unref(owner); ast_channel_lock(ast); ao2_lock(p); }
struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event) { struct varshead *headp; struct ast_var_t *newvariable; const char *mixed_name; char timebuf[30]; struct ast_channel *tchan; struct ast_cel_event_record record = { .version = AST_CEL_EVENT_RECORD_VERSION, }; /* do not call ast_channel_alloc because this is not really a real channel */ if (!(tchan = ast_dummy_channel_alloc())) { return NULL; } headp = ast_channel_varshead(tchan); /* first, get the variables from the event */ if (ast_cel_fill_record(event, &record)) { ast_channel_unref(tchan); return NULL; } /* next, fill the channel with their data */ mixed_name = (record.event_type == AST_CEL_USER_DEFINED) ? record.user_defined_name : record.event_name; if ((newvariable = ast_var_assign("eventtype", mixed_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if (ast_strlen_zero(cel_dateformat)) { snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec, (long) record.event_time.tv_usec); } else { struct ast_tm tm; ast_localtime(&record.event_time, &tm, NULL); ast_strftime(timebuf, sizeof(timebuf), cel_dateformat, &tm); } if ((newvariable = ast_var_assign("eventtime", timebuf))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventenum", record.event_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("userdeftype", record.user_defined_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventextra", record.extra))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } ast_channel_caller(tchan)->id.name.valid = 1; ast_channel_caller(tchan)->id.name.str = ast_strdup(record.caller_id_name); ast_channel_caller(tchan)->id.number.valid = 1; ast_channel_caller(tchan)->id.number.str = ast_strdup(record.caller_id_num); ast_channel_caller(tchan)->ani.number.valid = 1; ast_channel_caller(tchan)->ani.number.str = ast_strdup(record.caller_id_ani); ast_channel_redirecting(tchan)->from.number.valid = 1; ast_channel_redirecting(tchan)->from.number.str = ast_strdup(record.caller_id_rdnis); ast_channel_dialed(tchan)->number.str = ast_strdup(record.caller_id_dnid); ast_channel_exten_set(tchan, record.extension); ast_channel_context_set(tchan, record.context); ast_channel_name_set(tchan, record.channel_name); ast_channel_uniqueid_set(tchan, record.unique_id); ast_channel_linkedid_set(tchan, record.linked_id); ast_channel_accountcode_set(tchan, record.account_code); ast_channel_peeraccount_set(tchan, record.peer_account); ast_channel_userfield_set(tchan, record.user_field); if ((newvariable = ast_var_assign("BRIDGEPEER", record.peer))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } ast_channel_appl_set(tchan, ast_strdup(record.application_name)); ast_channel_data_set(tchan, ast_strdup(record.application_data)); ast_channel_amaflags_set(tchan, record.amaflag); return tchan; }
/*! * \internal * \brief Determine if an extension is a parking extension */ static int parking_is_exten_park(const char *context, const char *exten) { struct ast_exten *exten_obj; struct pbx_find_info info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ const char *app_at_exten; ast_debug(4, "Checking if %s@%s is a parking exten\n", exten, context); exten_obj = pbx_find_extension(NULL, NULL, &info, context, exten, 1, NULL, NULL, E_MATCH); if (!exten_obj) { return 0; } app_at_exten = ast_get_extension_app(exten_obj); if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) { return 0; } return 1; } /*! * \internal * \since 12.0.0 * \brief Perform a blind transfer to a parking lot * * In general, most parking features should work to call this function. This will safely * park either a channel in the bridge with \ref bridge_channel or will park the entire * bridge if more than one channel is in the bridge. It will create the correct data to * pass to the \ref AstBridging Bridging API to safely park the channel. * * \param bridge_channel The bridge_channel representing the channel performing the park * \param context The context to blind transfer to * \param exten The extension to blind transfer to * \param parked_channel_cb Optional callback executed prior to sending the parked channel into the bridge * \param parked_channel_data Data for the parked_channel_cb * * \retval 0 on success * \retval non-zero on error */ static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel, const char *context, const char *exten, transfer_channel_cb parked_channel_cb, struct transfer_channel_data *parked_channel_data) { RAII_VAR(struct ast_bridge_channel *, other, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, other_chan, NULL, ast_channel_cleanup); struct ast_exten *e; struct pbx_find_info find_info = { .stacklen = 0 }; int peer_count; if (ast_strlen_zero(context) || ast_strlen_zero(exten)) { return -1; } if (!bridge_channel->in_bridge) { return -1; } if (!parking_is_exten_park(context, exten)) { return -1; } ast_bridge_channel_lock_bridge(bridge_channel); peer_count = bridge_channel->bridge->num_channels; if (peer_count == 2) { other = ast_bridge_channel_peer(bridge_channel); ao2_ref(other, +1); other_chan = other->chan; ast_channel_ref(other_chan); } ast_bridge_unlock(bridge_channel->bridge); if (peer_count < 2) { /* There is nothing to do if there is no one to park. */ return -1; } /* With a multiparty bridge, we need to do a regular blind transfer. We link the * existing bridge to the parking lot with a Local channel rather than * transferring others. */ if (peer_count > 2) { struct ast_channel *transfer_chan = NULL; transfer_chan = park_local_transfer(bridge_channel->chan, context, exten, parked_channel_data); if (!transfer_chan) { return -1; } ast_channel_ref(transfer_chan); if (parked_channel_cb) { parked_channel_cb(transfer_chan, parked_channel_data, AST_BRIDGE_TRANSFER_MULTI_PARTY); } if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_hangup(transfer_chan); ast_channel_unref(transfer_chan); return -1; } ast_channel_unref(transfer_chan); return 0; } /* Subscribe to park messages with the other channel entering */ if (create_parked_subscription_full(bridge_channel->chan, ast_channel_uniqueid(other->chan), 1, parked_channel_data)) { return -1; } if (parked_channel_cb) { parked_channel_cb(other_chan, parked_channel_data, AST_BRIDGE_TRANSFER_SINGLE_PARTY); } e = pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH); /* Write the park frame with the intended recipient and other data out to the bridge. */ ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(other_chan), ast_channel_uniqueid(bridge_channel->chan), e ? ast_get_extension_app_data(e) : NULL); return 0; } /*! * \internal * \since 12.0.0 * \brief Perform a direct park on a channel in a bridge * * \note This will be called from within the \ref AstBridging Bridging API * * \param bridge_channel The bridge_channel representing the channel to be parked * \param uuid_parkee The UUID of the channel being parked * \param uuid_parker The UUID of the channel performing the park * \param app_data Application parseable data to pass to the parking application */ static int parking_park_bridge_channel(struct ast_bridge_channel *bridge_channel, const char *uuid_parkee, const char *uuid_parker, const char *app_data) { RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge *, original_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup); if (strcmp(ast_channel_uniqueid(bridge_channel->chan), uuid_parkee)) { /* We aren't the parkee, so ignore this action. */ return -1; } parker = ast_channel_get_by_name(uuid_parker); if (!parker) { ast_log(LOG_NOTICE, "Channel with uuid %s left before we could start parking the call. Parking canceled.\n", uuid_parker); publish_parked_call_failure(bridge_channel->chan); return -1; } if (!(parking_bridge = park_application_setup(bridge_channel->chan, parker, app_data, NULL))) { publish_parked_call_failure(bridge_channel->chan); return -1; } ast_bridge_set_transfer_variables(bridge_channel->chan, ast_channel_name(parker), 0); /* bridge_channel must be locked so we can get a reference to the bridge it is currently on */ ao2_lock(bridge_channel); original_bridge = bridge_channel->bridge; if (!original_bridge) { ao2_unlock(bridge_channel); publish_parked_call_failure(bridge_channel->chan); return -1; } ao2_ref(original_bridge, +1); /* Cleaned by RAII_VAR */ ao2_unlock(bridge_channel); if (ast_bridge_move(parking_bridge, original_bridge, bridge_channel->chan, NULL, 1)) { ast_log(LOG_ERROR, "Failed to move %s into the parking bridge.\n", ast_channel_name(bridge_channel->chan)); return -1; } return 0; }
/* Called with ast locked */ int ast_unreal_setoption(struct ast_channel *ast, int option, void *data, int datalen) { int res = 0; struct ast_unreal_pvt *p; struct ast_channel *otherchan = NULL; ast_chan_write_info_t *write_info; char *info_data; if (option != AST_OPTION_CHANNEL_WRITE) { return -1; } write_info = data; if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) { ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n"); return -1; } info_data = write_info->data; if (!strcmp(write_info->function, "CHANNEL")) { if (!strncasecmp(info_data, "hangup_handler_", 15)) { /* Block CHANNEL(hangup_handler_xxx) writes to the other unreal channel. */ return 0; } /* Crossover the accountcode and peeraccount to cross the unreal bridge. */ if (!strcasecmp(info_data, "accountcode")) { info_data = "peeraccount"; } else if (!strcasecmp(info_data, "peeraccount")) { info_data = "accountcode"; } } /* get the tech pvt */ if (!(p = ast_channel_tech_pvt(ast))) { return -1; } ao2_ref(p, 1); ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */ /* get the channel we are supposed to write to */ ao2_lock(p); otherchan = (write_info->chan == p->owner) ? p->chan : p->owner; if (!otherchan || otherchan == write_info->chan) { res = -1; otherchan = NULL; ao2_unlock(p); goto setoption_cleanup; } ast_channel_ref(otherchan); /* clear the pvt lock before grabbing the channel */ ao2_unlock(p); ast_channel_lock(otherchan); res = write_info->write_fn(otherchan, write_info->function, info_data, write_info->value); ast_channel_unlock(otherchan); setoption_cleanup: ao2_ref(p, -1); if (otherchan) { ast_channel_unref(otherchan); } ast_channel_lock(ast); /* Lock back before we leave */ return res; }
static int manager_log(struct ast_cdr *cdr) { struct ast_tm timeresult; char strStartTime[80] = ""; char strAnswerTime[80] = ""; char strEndTime[80] = ""; char buf[CUSTOM_FIELDS_BUF_SIZE]; if (!enablecdr) return 0; ast_localtime(&cdr->start, &timeresult, NULL); ast_strftime(strStartTime, sizeof(strStartTime), DATE_FORMAT, &timeresult); if (cdr->answer.tv_sec) { ast_localtime(&cdr->answer, &timeresult, NULL); ast_strftime(strAnswerTime, sizeof(strAnswerTime), DATE_FORMAT, &timeresult); } ast_localtime(&cdr->end, &timeresult, NULL); ast_strftime(strEndTime, sizeof(strEndTime), DATE_FORMAT, &timeresult); buf[0] = '\0'; ast_rwlock_rdlock(&customfields_lock); if (customfields && ast_str_strlen(customfields)) { struct ast_channel *dummy = ast_dummy_channel_alloc(); if (!dummy) { ast_log(LOG_ERROR, "Unable to allocate channel for variable substitution.\n"); return 0; } dummy->cdr = ast_cdr_dup(cdr); pbx_substitute_variables_helper(dummy, ast_str_buffer(customfields), buf, sizeof(buf) - 1); ast_channel_unref(dummy); } ast_rwlock_unlock(&customfields_lock); manager_event(EVENT_FLAG_CDR, "Cdr", "AccountCode: %s\r\n" "Source: %s\r\n" "Destination: %s\r\n" "DestinationContext: %s\r\n" "CallerID: %s\r\n" "Channel: %s\r\n" "DestinationChannel: %s\r\n" "LastApplication: %s\r\n" "LastData: %s\r\n" "StartTime: %s\r\n" "AnswerTime: %s\r\n" "EndTime: %s\r\n" "Duration: %ld\r\n" "BillableSeconds: %ld\r\n" "Disposition: %s\r\n" "AMAFlags: %s\r\n" "UniqueID: %s\r\n" "UserField: %s\r\n" "%s", cdr->accountcode, cdr->src, cdr->dst, cdr->dcontext, cdr->clid, cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata, strStartTime, strAnswerTime, strEndTime, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), cdr->uniqueid, cdr->userfield,buf); return 0; }
int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event_type, const char *userdefevname, const char *extra, struct ast_channel *peer2) { struct timeval eventtime; struct ast_event *ev; const char *peername = ""; struct ast_channel *peer; char *linkedid = ast_strdupa(ast_channel_linkedid(chan)); /* Make sure a reload is not occurring while we're checking to see if this * is an event that we care about. We could lose an important event in this * process otherwise. */ ast_mutex_lock(&reload_lock); /* Record the linkedid of new channels if we are tracking LINKEDID_END even if we aren't * reporting on CHANNEL_START so we can track when to send LINKEDID_END */ if (cel_enabled && ast_cel_track_event(AST_CEL_LINKEDID_END) && event_type == AST_CEL_CHANNEL_START && linkedid) { if (ast_cel_linkedid_ref(linkedid)) { ast_mutex_unlock(&reload_lock); return -1; } } if (!cel_enabled || !ast_cel_track_event(event_type)) { ast_mutex_unlock(&reload_lock); return 0; } if (event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END) { char *app; if (!(app = ao2_find(appset, (char *) ast_channel_appl(chan), OBJ_POINTER))) { ast_mutex_unlock(&reload_lock); return 0; } ao2_ref(app, -1); } ast_mutex_unlock(&reload_lock); ast_channel_lock(chan); peer = ast_bridged_channel(chan); if (peer) { ast_channel_ref(peer); } ast_channel_unlock(chan); if (peer) { ast_channel_lock(peer); peername = ast_strdupa(ast_channel_name(peer)); ast_channel_unlock(peer); } else if (peer2) { ast_channel_lock(peer2); peername = ast_strdupa(ast_channel_name(peer2)); ast_channel_unlock(peer2); } if (!userdefevname) { userdefevname = ""; } if (!extra) { extra = ""; } eventtime = ast_tvnow(); ast_channel_lock(chan); ev = ast_event_new(AST_EVENT_CEL, AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type, AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec, AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec, AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, userdefevname, AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""), AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""), AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, ""), AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, ""), AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, S_OR(ast_channel_dialed(chan)->number.str, ""), AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, ast_channel_exten(chan), AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, ast_channel_context(chan), AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, ast_channel_name(chan), AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, S_OR(ast_channel_appl(chan), ""), AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, S_OR(ast_channel_data(chan), ""), AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, ast_channel_amaflags(chan), AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, ast_channel_accountcode(chan), AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, ast_channel_peeraccount(chan), AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, ast_channel_uniqueid(chan), AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, ast_channel_linkedid(chan), AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, ast_channel_userfield(chan), AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, extra, AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, peername, AST_EVENT_IE_END); ast_channel_unlock(chan); if (peer) { peer = ast_channel_unref(peer); } if (ev && ast_event_queue(ev)) { ast_event_destroy(ev); return -1; } 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 common_exec(struct ast_channel *chan, struct ast_flags *flags, int volfactor, const int fd, struct spy_dtmf_options *user_options, const char *mygroup, const char *myenforced, const char *spec, const char *exten, const char *context, const char *mailbox, const char *name_context) { char nameprefix[AST_NAME_STRLEN]; char peer_name[AST_NAME_STRLEN + 5]; char exitcontext[AST_MAX_CONTEXT] = ""; signed char zero_volume = 0; int waitms; int res; char *ptr; int num; int num_spyed_upon = 1; struct ast_channel_iterator *iter = NULL; if (ast_test_flag(flags, OPTION_EXIT)) { const char *c; ast_channel_lock(chan); if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) { ast_copy_string(exitcontext, c, sizeof(exitcontext)); } else if (!ast_strlen_zero(chan->macrocontext)) { ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext)); } else { ast_copy_string(exitcontext, chan->context, sizeof(exitcontext)); } ast_channel_unlock(chan); } if (chan->_state != AST_STATE_UP) ast_answer(chan); ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ waitms = 100; for (;;) { struct ast_autochan *autochan = NULL, *next_autochan = NULL; struct ast_channel *prev = NULL; if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, ""); else if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) goto exit; else ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } } /* Set up the iterator we'll be using during this call */ if (!ast_strlen_zero(spec)) { iter = ast_channel_iterator_by_name_new(spec, strlen(spec)); } else if (!ast_strlen_zero(exten)) { iter = ast_channel_iterator_by_exten_new(exten, context); } else { iter = ast_channel_iterator_all_new(); } if (!iter) { return -1; } res = ast_waitfordigit(chan, waitms); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) goto exit; else ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } /* reset for the next loop around, unless overridden later */ waitms = 100; num_spyed_upon = 0; for (autochan = next_channel(iter, autochan, chan); autochan; prev = autochan->chan, ast_autochan_destroy(autochan), autochan = next_autochan ? next_autochan : next_channel(iter, autochan, chan), next_autochan = NULL) { int igrp = !mygroup; int ienf = !myenforced; char *s; if (autochan->chan == prev) { ast_autochan_destroy(autochan); break; } if (ast_check_hangup(chan)) { ast_autochan_destroy(autochan); break; } if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) { continue; } if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) { continue; } if (mygroup) { int num_groups = 0; int num_mygroups = 0; char dup_group[512]; char dup_mygroup[512]; char *groups[NUM_SPYGROUPS]; char *mygroups[NUM_SPYGROUPS]; const char *group = NULL; int x; int y; ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup)); num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups, ARRAY_LEN(mygroups)); /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable * rather than "SPYGROUP", this check is done to preserve expected behavior */ if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) { group = pbx_builtin_getvar_helper(autochan->chan, "GROUP"); } else { group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP"); } if (!ast_strlen_zero(group)) { ast_copy_string(dup_group, group, sizeof(dup_group)); num_groups = ast_app_separate_args(dup_group, ':', groups, ARRAY_LEN(groups)); } for (y = 0; y < num_mygroups; y++) { for (x = 0; x < num_groups; x++) { if (!strcmp(mygroups[y], groups[x])) { igrp = 1; break; } } } } if (!igrp) { continue; } if (myenforced) { char ext[AST_CHANNEL_NAME + 3]; char buffer[512]; char *end; snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced); ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1); if ((end = strchr(ext, '-'))) { *end++ = ':'; *end = '\0'; } ext[0] = ':'; if (strcasestr(buffer, ext)) { ienf = 1; } } if (!ienf) { continue; } strcpy(peer_name, "spy-"); strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1); ptr = strchr(peer_name, '/'); *ptr++ = '\0'; ptr = strsep(&ptr, "-"); for (s = peer_name; s < ptr; s++) *s = tolower(*s); if (!ast_test_flag(flags, OPTION_QUIET)) { if (ast_test_flag(flags, OPTION_NAME)) { const char *local_context = S_OR(name_context, "default"); const char *local_mailbox = S_OR(mailbox, ptr); res = ast_app_sayname(chan, local_mailbox, local_context); } if (!ast_test_flag(flags, OPTION_NAME) || res < 0) { if (!ast_test_flag(flags, OPTION_NOTECH)) { if (ast_fileexists(peer_name, NULL, NULL) > 0) { res = ast_streamfile(chan, peer_name, chan->language); if (!res) { res = ast_waitstream(chan, ""); } if (res) { ast_autochan_destroy(autochan); break; } } else { res = ast_say_character_str(chan, peer_name, "", chan->language); } } if ((num = atoi(ptr))) ast_say_digits(chan, atoi(ptr), "", chan->language); } } res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext); num_spyed_upon++; if (res == -1) { ast_autochan_destroy(autochan); goto exit; } else if (res == -2) { res = 0; ast_autochan_destroy(autochan); goto exit; } else if (res > 1 && spec) { struct ast_channel *next; snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) { next_autochan = ast_autochan_setup(next); next = ast_channel_unref(next); } else { /* stay on this channel, if it is still valid */ if (!ast_check_hangup(autochan->chan)) { next_autochan = ast_autochan_setup(autochan->chan); } else { /* the channel is gone */ next_autochan = NULL; } } } else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) { goto exit; } } iter = ast_channel_iterator_destroy(iter); if (res == -1 || ast_check_hangup(chan)) break; if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) { break; } } exit: ast_clear_flag(chan, AST_FLAG_SPYING); ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); return res; }
int ast_unreal_channel_push_to_bridge(struct ast_channel *ast, struct ast_bridge *bridge, unsigned int flags) { struct ast_bridge_features *features; struct ast_channel *chan; struct ast_channel *owner; RAII_VAR(struct ast_unreal_pvt *, p, NULL, ao2_cleanup); RAII_VAR(struct ast_callid *, bridge_callid, NULL, ast_callid_cleanup); ast_bridge_lock(bridge); bridge_callid = bridge->callid ? ast_callid_ref(bridge->callid) : NULL; ast_bridge_unlock(bridge); { SCOPED_CHANNELLOCK(lock, ast); p = ast_channel_tech_pvt(ast); if (!p) { return -1; } ao2_ref(p, +1); } { SCOPED_AO2LOCK(lock, p); chan = p->chan; if (!chan) { return -1; } owner = p->owner; if (!owner) { return -1; } ast_channel_ref(chan); ast_channel_ref(owner); } if (bridge_callid) { struct ast_callid *chan_callid; struct ast_callid *owner_callid; /* chan side call ID setting */ ast_channel_lock(chan); chan_callid = ast_channel_callid(chan); if (!chan_callid) { ast_channel_callid_set(chan, bridge_callid); } ast_channel_unlock(chan); ast_callid_cleanup(chan_callid); /* owner side call ID setting */ ast_channel_lock(owner); owner_callid = ast_channel_callid(owner); if (!owner_callid) { ast_channel_callid_set(owner, bridge_callid); } ast_channel_unlock(owner); ast_callid_cleanup(owner_callid); } /* We are done with the owner now that its call ID matches the bridge */ ast_channel_unref(owner); owner = NULL; features = ast_bridge_features_new(); if (!features) { ast_channel_unref(chan); return -1; } ast_set_flag(&features->feature_flags, flags); /* Impart the semi2 channel into the bridge */ if (ast_bridge_impart(bridge, chan, NULL, features, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_bridge_features_destroy(features); ast_channel_unref(chan); return -1; } ao2_lock(p); ast_set_flag(p, AST_UNREAL_CARETAKER_THREAD); ao2_unlock(p); ast_channel_unref(chan); return 0; }