Example #1
0
/*!
 * \brief Set a dial timeout interval hook on the channel.
 *
 * The absolute time that the timeout should occur is stored on
 * a datastore on the channel. This time is converted into a relative
 * number of milliseconds in the future. Then an interval hook is set
 * to trigger in that number of milliseconds.
 *
 * \pre chan is locked
 *
 * \param chan The channel on which to set the interval hook
 */
static void set_interval_hook(struct ast_channel *chan)
{
	struct ast_datastore *datastore;
	struct timeval *hangup_time;
	int64_t ms;
	struct ast_bridge_channel *bridge_channel;

	datastore = ast_channel_datastore_find(chan, &timeout_datastore, NULL);
	if (!datastore) {
		return;
	}

	hangup_time = datastore->data;

	ms = ast_tvdiff_ms(*hangup_time, ast_tvnow());
	bridge_channel = ast_channel_get_bridge_channel(chan);
	if (!bridge_channel) {
		return;
	}

	if (ast_bridge_interval_hook(bridge_channel->features, 0, ms > 0 ? ms : 1,
			bridge_timeout, NULL, NULL, 0)) {
		return;
	}

	ast_queue_frame(bridge_channel->chan, &ast_null_frame);
}
Example #2
0
/*! \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;

	ast_mutex_lock(&p->lock);

	other = (p->input == ast ? p->output : p->input);

	while (other && ast_channel_trylock(other)) {
		ast_mutex_unlock(&p->lock);
		do {
			CHANNEL_DEADLOCK_AVOIDANCE(ast);
		} while (ast_mutex_trylock(&p->lock));
		other = (p->input == ast ? p->output : p->input);
	}

	/* We basically queue the frame up on the other channel if present */
	if (other) {
		ast_queue_frame(other, f);
		ast_channel_unlock(other);
	}

	ast_mutex_unlock(&p->lock);

	return 0;
}
/* Need to post null frames periodically so DTMF emulation can work. */
static void stream_periodic_frames(struct ast_channel *chan, int ms, int interval_ms)
{
	long nanosecs;

	ast_assert(chan != NULL);
	ast_assert(0 < ms);
	ast_assert(0 < interval_ms);

	nanosecs = interval_ms * 1000000L;
	while (0 < ms) {
		ast_queue_frame(chan, &ast_null_frame);

		if (interval_ms < ms) {
			ms -= interval_ms;
		} else {
			nanosecs = ms * 1000000L;
			ms = 0;
		}
		test_nanosleep(0, nanosecs);
	}
}
/*!
 * \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;
}
Example #5
0
/*! \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 int msg_send(void *data)
{
	RAII_VAR(struct msg_data *, mdata, data, ao2_cleanup);

	const struct ast_sip_body body = {
		.type = "text",
		.subtype = "plain",
		.body_text = ast_msg_get_body(mdata->msg)
	};

	pjsip_tx_data *tdata;
	RAII_VAR(char *, uri, NULL, ast_free);
	RAII_VAR(struct ast_sip_endpoint *, endpoint, get_outbound_endpoint(
			 mdata->to, &uri), ao2_cleanup);

	if (!endpoint) {
		ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not find endpoint '%s' and "
			"no default outbound endpoint configured\n", mdata->to);
		return -1;
	}

	if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, NULL, &tdata)) {
		ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not create request\n");
		return -1;
	}

	update_to(tdata, mdata->to);
	update_from(tdata, mdata->from);

	if (ast_sip_add_body(tdata, &body)) {
		pjsip_tx_data_dec_ref(tdata);
		ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not add body to request\n");
		return -1;
	}

	vars_to_headers(mdata->msg, tdata);

	ast_debug(1, "Sending message to '%s' (via endpoint %s) from '%s'\n",
		mdata->to, ast_sorcery_object_get_id(endpoint), mdata->from);

	if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) {
		ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not send request\n");
		return -1;
	}

	return PJ_SUCCESS;
}

static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from)
{
	struct msg_data *mdata;

	if (ast_strlen_zero(to)) {
		ast_log(LOG_ERROR, "SIP MESSAGE - a 'To' URI  must be specified\n");
		return -1;
	}

	if (!(mdata = msg_data_create(msg, to, from)) ||
	    ast_sip_push_task(message_serializer, msg_send, mdata)) {
		ao2_ref(mdata, -1);
		return -1;
	}
	return 0;
}

static const struct ast_msg_tech msg_tech = {
	.name = "pjsip",
	.msg_send = sip_msg_send,
};

static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code code,
				 pjsip_dialog *dlg, pjsip_transaction *tsx)
{
	pjsip_tx_data *tdata;
	pj_status_t status;

	status = ast_sip_create_response(rdata, code, NULL, &tdata);
	if (status != PJ_SUCCESS) {
		ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
		return status;
	}

	if (dlg && tsx) {
		status = pjsip_dlg_send_response(dlg, tsx, tdata);
	} else {
		struct ast_sip_endpoint *endpoint;

		endpoint = ast_pjsip_rdata_get_endpoint(rdata);
		status = ast_sip_send_stateful_response(rdata, tdata, endpoint);
		ao2_cleanup(endpoint);
	}

	if (status != PJ_SUCCESS) {
		ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
	}

	return status;
}

static pj_bool_t module_on_rx_request(pjsip_rx_data *rdata)
{
	enum pjsip_status_code code;
	struct ast_msg *msg;

	/* if not a MESSAGE, don't handle */
	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_message_method)) {
		return PJ_FALSE;
	}

	code = check_content_type(rdata);
	if (code != PJSIP_SC_OK) {
		send_response(rdata, code, NULL, NULL);
		return PJ_TRUE;
	}

	msg = ast_msg_alloc();
	if (!msg) {
		send_response(rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL);
		return PJ_TRUE;
	}

	code = rx_data_to_ast_msg(rdata, msg);
	if (code != PJSIP_SC_OK) {
		send_response(rdata, code, NULL, NULL);
		ast_msg_destroy(msg);
		return PJ_TRUE;
	}

	if (!ast_msg_has_destination(msg)) {
		ast_debug(1, "MESSAGE request received, but no handler wanted it\n");
		send_response(rdata, PJSIP_SC_NOT_FOUND, NULL, NULL);
		ast_msg_destroy(msg);
		return PJ_TRUE;
	}

	/* Send it to the messaging core.
	 *
	 * If we are unable to send a response, the most likely reason is that we
	 * are handling a retransmission of an incoming MESSAGE and were unable to
	 * create a transaction due to a duplicate key. If we are unable to send
	 * a response, we should not queue the message to the dialplan
	 */
	if (!send_response(rdata, PJSIP_SC_ACCEPTED, NULL, NULL)) {
		ast_msg_queue(msg);
	}

	return PJ_TRUE;
}

static int incoming_in_dialog_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
{
	char buf[MAX_BODY_SIZE];
	enum pjsip_status_code code;
	struct ast_frame f;
	pjsip_dialog *dlg = session->inv_session->dlg;
	pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);

	if (!session->channel) {
		send_response(rdata, PJSIP_SC_NOT_FOUND, dlg, tsx);
		return 0;
	}

	if ((code = check_content_type(rdata)) != PJSIP_SC_OK) {
		send_response(rdata, code, dlg, tsx);
		return 0;
	}

	if (print_body(rdata, buf, sizeof(buf)-1) < 1) {
		/* invalid body size */
		send_response(rdata, PJSIP_SC_REQUEST_ENTITY_TOO_LARGE, dlg, tsx);
		return 0;
	}

	ast_debug(3, "Received in dialog SIP message\n");

	memset(&f, 0, sizeof(f));
	f.frametype = AST_FRAME_TEXT;
	f.subclass.integer = 0;
	f.offset = 0;
	f.data.ptr = buf;
	f.datalen = strlen(buf) + 1;
	ast_queue_frame(session->channel, &f);

	send_response(rdata, PJSIP_SC_ACCEPTED, dlg, tsx);
	return 0;
}
Example #7
0
static int msg_send(void *data)
{
	RAII_VAR(struct msg_data *, mdata, data, ao2_cleanup);

	const struct ast_sip_body body = {
		.type = "text",
		.subtype = "plain",
		.body_text = ast_msg_get_body(mdata->msg)
	};

	pjsip_tx_data *tdata;
	RAII_VAR(char *, uri, NULL, ast_free);
	RAII_VAR(struct ast_sip_endpoint *, endpoint, get_outbound_endpoint(
			 mdata->to, &uri), ao2_cleanup);

	if (!endpoint) {
		ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not find endpoint and "
			"no default outbound endpoint configured\n");
		return -1;
	}

	if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, NULL, &tdata)) {
		ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not create request\n");
		return -1;
	}

	update_to(tdata, mdata->to);
	update_from(tdata, mdata->from);

	if (ast_sip_add_body(tdata, &body)) {
		pjsip_tx_data_dec_ref(tdata);
		ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not add body to request\n");
		return -1;
	}

	vars_to_headers(mdata->msg, tdata);

	if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) {
		ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not send request\n");
		return -1;
	}

	return PJ_SUCCESS;
}

static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from)
{
	struct msg_data *mdata;

	if (ast_strlen_zero(to)) {
		ast_log(LOG_ERROR, "SIP MESSAGE - a 'To' URI  must be specified\n");
		return -1;
	}

	if (!(mdata = msg_data_create(msg, to, from)) ||
	    ast_sip_push_task(NULL, msg_send, mdata)) {
		ao2_ref(mdata, -1);
		return -1;
	}
	return 0;
}

static const struct ast_msg_tech msg_tech = {
	.name = "pjsip",
	.msg_send = sip_msg_send,
};

static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code code,
				 pjsip_dialog *dlg, pjsip_transaction *tsx)
{
	pjsip_tx_data *tdata;
	pj_status_t status;
	pjsip_response_addr res_addr;

	status = ast_sip_create_response(rdata, code, NULL, &tdata);
	if (status != PJ_SUCCESS) {
		ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
		return status;
	}

	if (dlg && tsx) {
		status = pjsip_dlg_send_response(dlg, tsx, tdata);
	} else {
		/* Get where to send request. */
		status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
		if (status != PJ_SUCCESS) {
			ast_log(LOG_ERROR, "Unable to get response address (%d)\n", status);
			return status;
		}
		status = ast_sip_send_response(&res_addr, tdata, ast_pjsip_rdata_get_endpoint(rdata));
	}

	if (status != PJ_SUCCESS) {
		ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
	}

	return status;
}

static pj_bool_t module_on_rx_request(pjsip_rx_data *rdata)
{
	enum pjsip_status_code code;
	struct ast_msg *msg;

	/* if not a MESSAGE, don't handle */
	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_message_method)) {
		return PJ_FALSE;
	}

	msg = ast_msg_alloc();
	if (!msg) {
		send_response(rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL);
		return PJ_TRUE;
	}

	if ((code = check_content_type(rdata)) != PJSIP_SC_OK) {
		send_response(rdata, code, NULL, NULL);
		return PJ_TRUE;
	}

	if ((code = rx_data_to_ast_msg(rdata, msg)) == PJSIP_SC_OK) {
		/* send it to the dialplan */
		ast_msg_queue(msg);
		code = PJSIP_SC_ACCEPTED;
	}

	send_response(rdata, code, NULL, NULL);
	return PJ_TRUE;
}

static int incoming_in_dialog_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
{
	char buf[MAX_BODY_SIZE];
	enum pjsip_status_code code;
	struct ast_frame f;

	pjsip_dialog *dlg = session->inv_session->dlg;
	pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);

	if ((code = check_content_type(rdata)) != PJSIP_SC_OK) {
		send_response(rdata, code, dlg, tsx);
		return 0;
	}

	if (print_body(rdata, buf, sizeof(buf)-1) < 1) {
		/* invalid body size */
		return 0;
	}

	ast_debug(3, "Received in dialog SIP message\n");

	memset(&f, 0, sizeof(f));
	f.frametype = AST_FRAME_TEXT;
	f.subclass.integer = 0;
	f.offset = 0;
	f.data.ptr = buf;
	f.datalen = strlen(buf) + 1;
	ast_queue_frame(session->channel, &f);

	send_response(rdata, PJSIP_SC_ACCEPTED, dlg, tsx);
	return 0;
}
static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
{
	pjsip_msg_body *body = rdata->msg_info.msg->body;
	char buf[body ? body->len : 0];
	char *cur = buf;
	char *line;

	char event = '\0';
	unsigned int duration = 100;

	char is_dtmf = is_media_type(rdata, "dtmf");

	if (!is_dtmf && !is_media_type(rdata, "dtmf-relay")) {
		return 0;
	}

	if (!body || !body->len) {
		/* need to return 200 OK on empty body */
		send_response(session, rdata, 200);
		return 0;
	}

	body->print_body(body, buf, body->len);

	if (is_dtmf) {
		/* directly use what is in the message body */
		event = get_event(cur);
	} else { /* content type = application/dtmf-relay */
		while ((line = strsep(&cur, "\r\n"))) {
			char *c;

			if (!(c = strchr(line, '='))) {
				continue;
			}

			*c++ = '\0';
			c = ast_skip_blanks(c);

			if (!strcasecmp(line, "signal")) {
				if (!(event = get_event(c))) {
					break;
				}
			} else if (!strcasecmp(line, "duration")) {
				sscanf(c, "%30u", &duration);
			}
		}
	}

	if (event == '!') {
		struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_FLASH, } };
		ast_queue_frame(session->channel, &f);
	} else if (event != '\0') {
		struct ast_frame f = { AST_FRAME_DTMF, };
		f.len = duration;
		f.subclass.integer = event;
		ast_queue_frame(session->channel, &f);
	} else {
		ast_log(LOG_ERROR, "Invalid DTMF event signal in INFO message.\n");
	}

	send_response(session, rdata, event ? 200 : 500);
	return event ? 0 : -1;
}