예제 #1
0
/*! \brief
 * the string is 'prefix:data' or prefix:fmt:data'
 * with ':' being invalid in strings.
 */
static int do_say(say_args_t *a, const char *s, const char *options, int depth)
{
    struct ast_variable *v;
    char *lang, *x, *rule = NULL;
    int ret = 0;
    struct varshead head = { .first = NULL, .last = NULL };
    struct ast_var_t *n;

    ast_debug(2, "string <%s> depth <%d>\n", s, depth);
    if (depth++ > 10) {
        ast_log(LOG_WARNING, "recursion too deep, exiting\n");
        return -1;
    } else if (!say_cfg) {
        ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
        return -1;
    }

    /* scan languages same as in file.c */
    if (a->language == NULL)
        a->language = "en";     /* default */
    ast_debug(2, "try <%s> in <%s>\n", s, a->language);
    lang = ast_strdupa(a->language);
    for (;;) {
        for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
            if (ast_extension_match(v->name, s)) {
                rule = ast_strdupa(v->value);
                break;
            }
        }
        if (rule)
            break;
        if ( (x = strchr(lang, '_')) )
            *x = '\0';      /* try without suffix */
        else if (strcmp(lang, "en"))
            lang = "en";    /* last resort, try 'en' if not done yet */
        else
            break;
    }
    if (!rule)
        return 0;

    /* skip up to two prefixes to get the value */
    if ( (x = strchr(s, ':')) )
        s = x + 1;
    if ( (x = strchr(s, ':')) )
        s = x + 1;
    ast_debug(2, "value is <%s>\n", s);
    n = ast_var_assign("SAY", s);
    AST_LIST_INSERT_HEAD(&head, n, entries);

    /* scan the body, one piece at a time */
    while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
        char fn[128];
        const char *p, *fmt, *data; /* format and data pointers */

        /* prepare a decent file name */
        x = ast_skip_blanks(x);
        ast_trim_blanks(x);

        /* replace variables */
        pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
        ast_debug(2, "doing [%s]\n", fn);

        /* locate prefix and data, if any */
        fmt = strchr(fn, ':');
        if (!fmt || fmt == fn)	{	/* regular filename */
            ret = s_streamwait3(a, fn);
            continue;
        }
        fmt++;
        data = strchr(fmt, ':');	/* colon before data */
        if (!data || data == fmt) {	/* simple prefix-fmt */
            ret = do_say(a, fn, options, depth);
            continue;
        }
        /* prefix:fmt:data */
        for (p = fmt; p < data && ret <= 0; p++) {
            char fn2[sizeof(fn)];
            if (*p == ' ' || *p == '\t')	/* skip blanks */
                continue;
            if (*p == '\'') {/* file name - we trim them */
                char *y;
                strcpy(fn2, ast_skip_blanks(p+1));	/* make a full copy */
                y = strchr(fn2, '\'');
                if (!y) {
                    p = data;	/* invalid. prepare to end */
                    break;
                }
                *y = '\0';
                ast_trim_blanks(fn2);
                p = strchr(p+1, '\'');
                ret = s_streamwait3(a, fn2);
            } else {
                int l = fmt-fn;
                strcpy(fn2, fn); /* copy everything */
                /* after prefix, append the format */
                fn2[l++] = *p;
                strcpy(fn2 + l, data);
                ret = do_say(a, fn2, options, depth);
            }

            if (ret) {
                break;
            }
        }
    }
    ast_var_delete(n);
    return ret;
}
예제 #2
0
파일: cel.c 프로젝트: kg6zgj/asterisk
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;
}
예제 #3
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);
}
예제 #4
0
/*! \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;
	struct ast_var_t *varptr;
	struct ast_var_t *clone_var;
	char *reduced_dest = ast_strdupa(dest);
	char *slash;
	const char *exten;
	const char *context;

	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);

	awesome_locking(p, &chan, &owner);
	pvt_locked = 1;

	if (owner != ast) {
		res = -1;
		goto return_cleanup;
	}

	if (!owner || !chan) {
		res = -1;
		goto return_cleanup;
	}

	/*
	 * Note that cid_num and cid_name aren't passed in the ast_channel_alloc
	 * call, so it's done here instead.
	 *
	 * All these failure points just return -1. The individual strings will
	 * be cleared when we destroy the channel.
	 */
	ast_party_redirecting_copy(ast_channel_redirecting(chan), ast_channel_redirecting(owner));

	ast_party_dialed_copy(ast_channel_dialed(chan), ast_channel_dialed(owner));

	ast_connected_line_copy_to_caller(ast_channel_caller(chan), ast_channel_connected(owner));
	ast_connected_line_copy_from_caller(ast_channel_connected(chan), ast_channel_caller(owner));

	ast_channel_language_set(chan, ast_channel_language(owner));
	ast_channel_accountcode_set(chan, ast_channel_accountcode(owner));
	ast_channel_musicclass_set(chan, ast_channel_musicclass(owner));
	ast_cdr_update(chan);

	ast_channel_cc_params_init(chan, ast_channel_get_cc_config_params(owner));

	/* 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(ast) == AST_CAUSE_ANSWERED_ELSEWHERE) {
		ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE);
	}

	/* copy the channel variables from the incoming channel to the outgoing channel */
	/* Note that due to certain assumptions, they MUST be in the same order */
	AST_LIST_TRAVERSE(ast_channel_varshead(owner), varptr, entries) {
		clone_var = ast_var_assign(varptr->name, varptr->value);
		if (clone_var) {
			AST_LIST_INSERT_TAIL(ast_channel_varshead(chan), clone_var, entries);
		}
	}
예제 #5
0
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;
}