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; }
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 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; 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)); /* 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_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_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); }