Compare commits

...

10 Commits

Author SHA1 Message Date
Riza Sulistyo 70a3e2e1a4 Add call_id to the callback param 2023-10-03 07:43:26 +07:00
Riza Sulistyo eb77f1f7dc Fix build error on C89 compiler (e.g: VS2005) and change the status code to using defined/enum error for uniformity 2023-09-25 19:26:51 +07:00
Riza Sulistyo f7e28fcc2a Add doc 2023-09-25 12:27:27 +07:00
Riza Sulistyo c2ff1540c3 Modification based on comments 2023-09-22 18:11:29 +07:00
Riza Sulistyo b4565c3374 Modification based on comments 2023-09-18 13:48:44 +07:00
Riza Sulistyo e74e036ae5 Remove tdata from callback param 2023-09-15 18:54:55 +07:00
Riza Sulistyo c8bde05f9b Call the callback from pjsua_call_on_incoming() 2023-09-14 23:06:56 +07:00
Riza Sulistyo dc723740db Fix failed unit test 2023-09-12 07:37:05 +07:00
Riza Sulistyo c780fe7cb2 Modification based on comments
- save incoming rdata to be passed as callback parameter
2023-09-09 16:47:57 +07:00
Riza Sulistyo 2fa315e321 Add on_rejected_incoming_call() callback 2023-09-04 23:26:20 +07:00
4 changed files with 273 additions and 37 deletions

View File

@ -1080,6 +1080,63 @@ typedef struct pjsua_call_setting
} pjsua_call_setting;
/**
* This will contain the information passed from the callback
* \a pjsua_on_rejected_incoming_call_cb.
*/
typedef struct pjsua_on_rejected_incoming_call_param {
/**
* The incoming call id. This will be set to PJSUA_INVALID_ID when there is
* no available call slot at the time.
*/
pjsua_call_id call_id;
/**
* Local URI.
*/
pj_str_t local_info;
/**
* Remote URI.
*/
pj_str_t remote_info;
/**
* Rejection code.
*/
int st_code;
/**
* Rejection text.
*/
pj_str_t st_text;
/**
* The original INVITE message, if it's not available this will be set
* to NULL.
*/
pjsip_rx_data *rdata;
/**
* Internal.
*/
struct {
char local_info[PJSIP_MAX_URL_SIZE];
char remote_info[PJSIP_MAX_URL_SIZE];
} buf_;
} pjsua_on_rejected_incoming_call_param;
/**
* Type of callback to be called when incoming call is rejected.
*
* @param param The rejected call information.
*
*/
typedef void (*pjsua_on_rejected_incoming_call_cb)(
const pjsua_on_rejected_incoming_call_param *param);
/**
* This structure describes application callback to receive various event
* notification from PJSUA-API. All of these callbacks are OPTIONAL,
@ -1967,6 +2024,23 @@ typedef struct pjsua_callback
*/
void (*on_media_event)(pjmedia_event *event);
/**
* This callback will be invoked when the library implicitly rejects
* an incoming call.
*
* In addition to being declined explicitly using the
* #pjsua_call_answer()/#pjsua_call_answer2() method,
* the library may also automatically reject the incoming call due
* to different scenarios, e.g:
* - no available call slot.
* - no available account to handle the call.
* - when an incoming INVITE is received with, for instance, a message
* containing invalid SDP.
*
* See also #pjsua_on_rejected_incoming_call_cb.
*/
pjsua_on_rejected_incoming_call_cb on_rejected_incoming_call;
} pjsua_callback;

View File

@ -436,6 +436,43 @@ struct OnMediaEventParam
MediaEvent ev;
};
/**
* Parameter of Endpoint::onRejectedIncomingCall() callback.
*/
struct OnRejectedIncomingCallParam
{
/**
* The incoming call id. This will be set to PJSUA_INVALID_ID when there is
* no available call slot at the time.
*/
pjsua_call_id callId;
/**
* Local URI.
*/
std::string localInfo;
/**
* Remote URI.
*/
std::string remoteInfo;
/**
* Rejection code.
*/
int statusCode;
/**
* Rejection text.
*/
std::string reason;
/**
* The original INVITE message, on some cases it is not available.
*/
SipRxData rdata;
};
/**
* This structure describes authentication challenge used in Proxy-Authenticate
* or WWW-Authenticate for digest authentication scheme.
@ -1898,6 +1935,23 @@ public:
*/
virtual pj_status_t onCredAuth(OnCredAuthParam &prm);
/**
* This callback will be invoked when the library implicitly rejects
* an incoming call.
*
* In addition to being declined explicitly using the Call::answer()
* method, the library may also automatically reject the incoming call
* due to different scenarios, e.g:
* - no available call slot.
* - no available account to handle the call.
* - when an incoming INVITE is received with, for instance, a message
* containing invalid SDP.
*
* @param prm Callback parameters.
*/
virtual void onRejectedIncomingCall(OnRejectedIncomingCallParam &prm)
{ PJ_UNUSED_ARG(prm); }
private:
static Endpoint *instance_; // static instance
LogWriter *writer; // Custom writer, if any
@ -2078,6 +2132,10 @@ private:
const pjsip_cred_info *cred,
const pj_str_t *method,
pjsip_digest_credential *auth);
static void on_rejected_incoming_call(
const pjsua_on_rejected_incoming_call_param *param);
friend class Account;

View File

@ -1352,6 +1352,59 @@ static pj_status_t verify_request(const pjsua_call *call,
return status;
}
static void rejected_incoming_call_cb(pjsua_call_id call_id,
pjsip_rx_data *rdata,
pjsip_tx_data *tdata,
int st_code,
pj_str_t *st_text)
{
pjsip_from_hdr *from_hdr = NULL;
pjsip_to_hdr *to_hdr = NULL;
pjsip_sip_uri *uri;
pjsua_on_rejected_incoming_call_param param;
if (!pjsua_var.ua_cfg.cb.on_rejected_incoming_call)
return;
pj_bzero(&param, sizeof(pjsua_on_rejected_incoming_call_param));
param.call_id = call_id;
param.st_code = st_code;
if (rdata) {
param.rdata = rdata;
to_hdr = rdata->msg_info.to;
from_hdr = rdata->msg_info.from;
} else if (tdata) {
pjsip_msg *msg = tdata->msg;
to_hdr = PJSIP_MSG_TO_HDR(msg);
from_hdr = PJSIP_MSG_FROM_HDR(msg);
}
if (to_hdr) {
param.local_info.ptr = param.buf_.local_info;
uri = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri);
param.local_info.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
uri,
param.buf_.local_info,
sizeof(param.buf_.local_info));
}
if (from_hdr) {
param.remote_info.ptr = param.buf_.remote_info;
uri = (pjsip_sip_uri*)pjsip_uri_get_uri(from_hdr->uri);
param.remote_info.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
uri,
param.buf_.remote_info,
sizeof(param.buf_.remote_info));
}
if (st_text && st_text->slen) {
pj_strassign(&param.st_text, st_text);
} else {
param.st_text = *pjsip_get_status_text(st_code);
}
pjsua_var.ua_cfg.cb.on_rejected_incoming_call(&param);
}
/* Incoming call callback when media transport creation is completed. */
static pj_status_t
on_incoming_call_med_tp_complete2(pjsua_call_id call_id,
@ -1406,15 +1459,21 @@ on_return:
*/
if (call->inv->state > PJSIP_INV_STATE_NULL) {
pj_status_t status_ = PJ_SUCCESS;
pj_str_t reason = pj_str("");
if (response == NULL) {
pj_str_t reason = pj_str("Failed creating media transport");
reason = pj_str("Failed creating media transport");
status_ = pjsip_inv_end_session(call->inv, err_code, &reason,
&response);
}
if (status_ == PJ_SUCCESS && response)
status_ = pjsip_inv_send_msg(call->inv, response);
if ((err_code / 100 != 1) && (err_code / 100 != 2)) {
rejected_incoming_call_cb(call->index, rdata, response,
err_code, &reason);
}
}
pjsua_media_channel_deinit(call->index);
}
@ -1453,7 +1512,6 @@ on_incoming_call_med_tp_complete(pjsua_call_id call_id,
return on_incoming_call_med_tp_complete2(call_id, info, NULL, NULL, NULL);
}
/**
* Handle incoming INVITE request.
* Called by pjsua_core.c
@ -1470,11 +1528,13 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
pjsip_inv_session *inv = NULL;
int acc_id;
pjsua_call *call = NULL;
int call_id = -1;
int call_id = PJSUA_INVALID_ID;
int sip_err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
pjmedia_sdp_session *offer=NULL;
pj_bool_t should_dec_dlg = PJ_FALSE;
pjsip_tpselector tp_sel;
pj_str_t st_reason = pj_str("");
int ret_st_code = 0;
pj_status_t status;
/* Don't want to handle anything but INVITE */
@ -1504,9 +1564,9 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
call_id = alloc_call_id();
if (call_id == PJSUA_INVALID_ID) {
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
PJSIP_SC_BUSY_HERE, NULL,
NULL, NULL);
ret_st_code = PJSIP_SC_BUSY_HERE;
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, ret_st_code,
NULL, NULL, NULL);
PJ_LOG(2,(THIS_FILE,
"Unable to accept incoming call (too many calls)"));
goto on_return;
@ -1534,16 +1594,17 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
*/
if (response) {
pjsip_response_addr res_addr;
ret_st_code = response->msg->line.status.code;
pjsip_get_response_addr(response->pool, rdata, &res_addr);
status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
NULL, NULL);
status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr,
response, NULL, NULL);
if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(response);
} else {
ret_st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
/* Respond with 500 (Internal Server Error) */
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, ret_st_code,
NULL, NULL, NULL);
}
goto on_return;
@ -1597,6 +1658,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
st_code, &st_text, NULL, NULL, NULL);
ret_st_code = st_code;
goto on_return;
}
@ -1625,8 +1687,8 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
} else {
acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
if (acc_id == PJSUA_INVALID_ID) {
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
PJSIP_SC_TEMPORARILY_UNAVAILABLE,
ret_st_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, ret_st_code,
NULL, NULL, NULL);
PJ_LOG(2,(THIS_FILE,
@ -1681,13 +1743,13 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
acc->values[acc->count++] = pj_str("application/sdp");
pj_list_init(&hdr_list);
pj_list_push_back(&hdr_list, acc);
ret_st_code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
PJSIP_SC_UNSUPPORTED_MEDIA_TYPE,
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, ret_st_code,
NULL, &hdr_list, NULL, NULL);
} else {
const pj_str_t reason = pj_str("Bad SDP");
pjsip_warning_hdr *w;
st_reason = pj_str("Bad SDP");
pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
status);
@ -1697,9 +1759,10 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
status);
pj_list_init(&hdr_list);
pj_list_push_back(&hdr_list, w);
ret_st_code = PJSIP_SC_BAD_REQUEST;
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
&reason, &hdr_list, NULL, NULL);
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, ret_st_code,
&st_reason, &hdr_list, NULL, NULL);
}
goto on_return;
}
@ -1708,9 +1771,11 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
* checks will be done in pjsip_inv_verify_request2() below.
*/
if ((offer) && (offer->media_count==0)) {
const pj_str_t reason = pj_str("Missing media in SDP");
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
NULL, NULL, NULL);
st_reason = pj_str("Missing media in SDP");
ret_st_code = PJSIP_SC_BAD_REQUEST;
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, ret_st_code,
&st_reason, NULL, NULL, NULL);
goto on_return;
}
@ -1745,16 +1810,18 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
*/
if (response) {
pjsip_response_addr res_addr;
ret_st_code = response->msg->line.status.code;
pjsip_get_response_addr(response->pool, rdata, &res_addr);
status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
NULL, NULL);
status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr,
response, NULL, NULL);
if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(response);
} else {
/* Respond with 500 (Internal Server Error) */
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
NULL, NULL, NULL);
ret_st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, ret_st_code,
NULL, NULL, NULL, NULL);
}
goto on_return;
@ -1767,10 +1834,11 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
acc_id, rdata);
if (status != PJ_SUCCESS) {
ret_st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
pjsua_perror(THIS_FILE, "Unable to generate Contact header",
status);
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, ret_st_code,
NULL, NULL, NULL);
goto on_return;
}
}
@ -1779,8 +1847,9 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
status = pjsip_dlg_create_uas_and_inc_lock( pjsip_ua_instance(), rdata,
&contact, &dlg);
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
ret_st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, ret_st_code,
NULL, NULL, NULL);
goto on_return;
}
@ -1859,8 +1928,8 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
status);
pj_list_init(&hdr_list);
pj_list_push_back(&hdr_list, w);
pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
ret_st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
pjsip_dlg_respond(dlg, rdata, ret_st_code, NULL, &hdr_list, NULL);
/* Can't terminate dialog because transaction is in progress.
pjsip_dlg_terminate(dlg);
@ -1901,8 +1970,12 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
pjsip_dlg_inc_lock(dlg);
if (response) {
ret_st_code = response->msg->line.status.code;
pjsip_dlg_send_response(dlg, call->inv->invite_tsx, response);
} else {
ret_st_code = sip_err_code;
pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
}
@ -1944,10 +2017,14 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
pjsip_dlg_inc_lock(dlg);
if (response) {
ret_st_code = response->msg->line.status.code;
pjsip_dlg_send_response(dlg, call->inv->invite_tsx,
response);
} else {
ret_st_code = sip_err_code;
pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL,
NULL);
}
@ -1965,6 +2042,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
pjsua_perror(THIS_FILE, "Error initializing media channel", status);
pjsip_dlg_inc_lock(dlg);
ret_st_code = sip_err_code;
pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
if (call->inv->dlg) {
pjsip_inv_terminate(call->inv, sip_err_code, PJ_FALSE);
@ -1994,8 +2072,9 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
&pjsua_var.acc[acc_id].cfg.timer_setting);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Session Timer init failed", status);
pjsip_dlg_respond(dlg, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL);
pjsip_inv_terminate(inv, PJSIP_SC_INTERNAL_SERVER_ERROR, PJ_FALSE);
ret_st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
pjsip_dlg_respond(dlg, rdata, ret_st_code, NULL, NULL, NULL);
pjsip_inv_terminate(inv, ret_st_code, PJ_FALSE);
pjsua_media_channel_deinit(call->index);
call->inv = NULL;
@ -2030,12 +2109,14 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
if (response == NULL) {
pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
status);
pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
pjsip_inv_terminate(inv, 500, PJ_FALSE);
ret_st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
pjsip_dlg_respond(dlg, rdata, ret_st_code, NULL, NULL, NULL);
pjsip_inv_terminate(inv, ret_st_code, PJ_FALSE);
} else {
ret_st_code = response->msg->line.status.code;
pjsip_inv_send_msg(inv, response);
pjsip_inv_terminate(inv, response->msg->line.status.code,
PJ_FALSE);
pjsip_inv_terminate(inv, ret_st_code, PJ_FALSE);
}
pjsua_media_channel_deinit(call->index);
call->inv = NULL;
@ -2122,6 +2203,13 @@ on_return:
pjsip_rx_data_free_cloned(call->incoming_data);
call->incoming_data = NULL;
}
if ((ret_st_code != 0) && (ret_st_code / 100 != 1) &&
(ret_st_code / 100 != 2))
{
rejected_incoming_call_cb(call_id, rdata, NULL, ret_st_code,
&st_reason);
}
pj_log_pop_indent();
PJSUA_UNLOCK();

View File

@ -1962,6 +1962,7 @@ void Endpoint::libInit(const EpConfig &prmEpConfig) PJSUA2_THROW(Error)
ua_cfg.cb.on_create_media_transport = &Endpoint::on_create_media_transport;
ua_cfg.cb.on_stun_resolution_complete =
&Endpoint::stun_resolve_cb;
ua_cfg.cb.on_rejected_incoming_call = &Endpoint::on_rejected_incoming_call;
/* Init! */
PJSUA2_CHECK_EXPR( pjsua_init(&ua_cfg, &log_cfg, &med_cfg) );
@ -2687,3 +2688,18 @@ pj_status_t Endpoint::on_auth_create_aka_response_callback(pj_pool_t *pool,
#endif
return status;
}
void Endpoint::on_rejected_incoming_call(
const pjsua_on_rejected_incoming_call_param *param)
{
OnRejectedIncomingCallParam prm;
prm.callId = param->call_id;
prm.localInfo = pj2Str(param->local_info);
prm.remoteInfo = pj2Str(param->remote_info);
prm.statusCode = param->st_code;
prm.reason = pj2Str(param->st_text);
if (param->rdata)
prm.rdata.fromPj(*param->rdata);
Endpoint::instance().onRejectedIncomingCall(prm);
}