If pjsua_call_on_incoming rejects an incoming call for any reason, the application won't be notified through the on_incoming_call callback. This patch adds an on_missed_call callback, so that the application has a chance to at least log the call that couldn't be accepted (for example in a call history database). Without such a callback, the only trace of the missed call would be in the PJSIP logs The function that prepares the call to the user callback duplicates some code from pjsip_dlg_create_uas to initialize a pjsua_call_info structure, but it's inevitable: if the call was rejected because of one malformed header, pjsip_dlg_create_uas will fail, and won't provide even the partial information that _is_ well-formed -------------- next part -------------- Index: pjsip/include/pjsua-lib/pjsua.h =================================================================== --- pjsip/include/pjsua-lib/pjsua.h (revision 3711) +++ pjsip/include/pjsua-lib/pjsua.h (working copy) @@ -283,6 +283,8 @@ /** Forward declaration for pjsua_msg_data */ typedef struct pjsua_msg_data pjsua_msg_data; +/** Forward declaration for pjsua_call_info */ +typedef struct pjsua_call_info pjsua_call_info; /** * Maximum proxies in account. @@ -681,6 +683,25 @@ pjsip_rx_data *rdata); /** + * Notify application on rejected incoming call. + * + * @param acc_id The account which match the incoming call. + * @param call_info Infomation on the rejected call. + * @param rdata The incoming INVITE request. + * @param status The internal error that caused the call to + * be rejected. + * @param status_code + * The SIP code used to reject the call. + * @param status_text + * The SIP reason string used to reject the call. + */ + void (*on_missed_call)(pjsua_acc_id acc_id, + const pjsua_call_info *call_info, + pjsip_rx_data *rdata, pj_status_t status, + pjsip_status_code status_code, + const pj_str_t *status_text); + + /** * This is a general notification callback which is called whenever * a transaction within the call has changed state. Application can * implement this callback for example to monitor the state of Index: pjsip/src/pjsua-lib/pjsua_call.c =================================================================== --- pjsip/src/pjsua-lib/pjsua_call.c (revision 3711) +++ pjsip/src/pjsua-lib/pjsua_call.c (working copy) @@ -1074,6 +1074,100 @@ /** + * Notifies user of calls rejected by pjsua_call_on_incoming. + */ +static +void on_missed_call(pjsip_rx_data *rdata, pj_status_t status, pjsip_status_code status_code, const pj_str_t *status_text) +{ + pjsua_call_info call_info; + int len; + pjsip_contact_hdr *contact_hdr; + pjsip_hdr *pos = NULL; + + if (!pjsua_var.ua_cfg.cb.on_missed_call) + return; + + if (status_text == NULL) + status_text = pjsip_get_status_text(status_code); + + pj_bzero(&call_info, sizeof(call_info)); + + call_info.id = PJSUA_INVALID_ID; + call_info.role = PJSIP_ROLE_UAS; + call_info.acc_id = pjsua_acc_find_for_incoming(rdata);; + pjsua_call_setting_default(&call_info.setting); + call_info.state = PJSIP_INV_STATE_INCOMING; + call_info.state_text = pj_str((char *)pjsip_inv_state_name(call_info.state)); + + /* Local info (To: header) */ + call_info.local_info.ptr = call_info.buf_.local_info; + call_info.local_info.slen = 0; + + len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.to->uri, call_info.buf_.local_info, sizeof(call_info.buf_.local_info)); + + if (len >= 0) + call_info.local_info.slen = len; + + /* Local contact (outgoing Contact: header) */ + call_info.local_contact.ptr = call_info.buf_.local_contact; + call_info.local_contact.slen = 0; + + if (pjsua_var.acc[call_info.acc_id].contact.slen) { + pj_strncpy(&call_info.local_contact, &pjsua_var.acc[call_info.acc_id].contact, sizeof(call_info.buf_.local_contact)); + } else { + pj_str_t contact; + pj_status_t status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact, call_info.acc_id, rdata); + + if (status == PJ_SUCCESS) { + pj_strncpy(&call_info.local_contact, &contact, sizeof(call_info.buf_.local_contact)); + } + } + + /* Remote info (From: header) */ + call_info.remote_info.ptr = call_info.buf_.remote_info; + call_info.remote_info.slen = 0; + + len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, call_info.buf_.remote_info, sizeof(call_info.buf_.remote_info)); + + if (len >= 0) + call_info.remote_info.slen = len; + + /* Remote contact (Contact: header) */ + call_info.remote_contact.ptr = call_info.buf_.remote_contact; + call_info.remote_contact.slen = 0; + + do { + contact_hdr = (pjsip_contact_hdr *)pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, pos); + if (contact_hdr) { + if (!contact_hdr->uri || (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri))) { + pos = (pjsip_hdr *)contact_hdr->next; + if (pos == &rdata->msg_info.msg->hdr) + contact_hdr = NULL; + } + else { + break; + } + } + } + while (contact_hdr); + + if (contact_hdr) { + len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri, call_info.buf_.remote_contact, sizeof(call_info.buf_.remote_contact)); + + if (len >= 0) + call_info.remote_contact.slen = len; + } + + /* Call id (Call-ID: header) */ + call_info.call_id.ptr = call_info.buf_.call_id; + pj_strncpy(&call_info.call_id, &rdata->msg_info.cid->id, sizeof(call_info.buf_.call_id)); + + PJ_ASSERT_RETURN(pjsua_var.ua_cfg.cb.on_missed_call,); + pjsua_var.ua_cfg.cb.on_missed_call(call_info.acc_id, &call_info, rdata, status, status_code, status_text); +} + + +/** * Handle incoming INVITE request. * Called by pjsua_core.c */ @@ -1126,6 +1220,8 @@ NULL, NULL); PJ_LOG(2,(THIS_FILE, "Unable to accept incoming call (too many calls)")); + + on_missed_call(rdata, PJ_SUCCESS, PJSIP_SC_BUSY_HERE, NULL); goto on_return; } @@ -1153,11 +1249,15 @@ pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response, NULL, NULL); + on_missed_call(rdata, status, response->msg->line.status.code, + &response->msg->line.status.reason); } else { /* Respond with 500 (Internal Server Error) */ pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); + + on_missed_call(rdata, status, 500, NULL); } goto on_return; @@ -1267,6 +1367,9 @@ pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE, NULL, &hdr_list, NULL, NULL); + + on_missed_call(rdata, status, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE, + NULL); } else { const pj_str_t reason = pj_str("Bad SDP"); pjsip_warning_hdr *w; @@ -1282,6 +1385,8 @@ pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason, &hdr_list, NULL, NULL); + + on_missed_call(rdata, status, 400, &reason); } goto on_return; } @@ -1293,6 +1398,7 @@ const pj_str_t reason = pj_str("Missing media in SDP"); pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason, NULL, NULL, NULL); + on_missed_call(rdata, PJ_SUCCESS, 400, &reason); goto on_return; } @@ -1326,10 +1432,14 @@ pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response, NULL, NULL); + on_missed_call(rdata, status, response->msg->line.status.code, + &response->msg->line.status.reason); } else { /* Respond with 500 (Internal Server Error) */ pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL, NULL, NULL, NULL); + + on_missed_call(rdata, status, 500, NULL); } goto on_return; @@ -1346,6 +1456,7 @@ status); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); + on_missed_call(rdata, status, 500, NULL); goto on_return; } } @@ -1356,6 +1467,7 @@ if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); + on_missed_call(rdata, status, 500, NULL); goto on_return; } @@ -1434,6 +1546,7 @@ pj_list_push_back(&hdr_list, w); pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL); + on_missed_call(rdata, status, 500, NULL); /* Can't terminate dialog because transaction is in progress. pjsip_dlg_terminate(dlg); @@ -1486,6 +1599,7 @@ } call->inv = NULL; call->async_call.dlg = NULL; + on_missed_call(rdata, status, sip_err_code, NULL); goto on_return; } } else if (status != PJ_EPENDING) { @@ -1496,6 +1610,7 @@ } call->inv = NULL; call->async_call.dlg = NULL; + on_missed_call(rdata, status, sip_err_code, NULL); goto on_return; } } @@ -1508,6 +1623,7 @@ pjsua_perror(THIS_FILE, "Error creating SDP answer", status); pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, sip_err_code, NULL, NULL, NULL, NULL); + on_missed_call(rdata, status, sip_err_code, NULL); goto on_return; } */ @@ -1524,6 +1640,7 @@ call->inv = NULL; call->async_call.dlg = NULL; + on_missed_call(rdata, status, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL); goto on_return; } @@ -1555,10 +1672,13 @@ status); pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL); pjsip_inv_terminate(inv, 500, PJ_FALSE); + on_missed_call(rdata, status, 500, NULL); } else { pjsip_inv_send_msg(inv, response); pjsip_inv_terminate(inv, response->msg->line.status.code, PJ_FALSE); + on_missed_call(rdata, status, response->msg->line.status.code, + &response->msg->line.status.reason); } pjsua_media_channel_deinit(call->index); call->inv = NULL; @@ -1572,6 +1692,7 @@ pjsua_media_channel_deinit(call->index); call->inv = NULL; call->async_call.dlg = NULL; + on_missed_call(rdata, status, 0, NULL); goto on_return; } } @@ -1605,6 +1726,8 @@ } else { pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL, NULL); + on_missed_call(rdata, PJ_SUCCESS, PJSIP_SC_TEMPORARILY_UNAVAILABLE, + NULL); } }