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