mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-10-30 15:14:17 +00:00
897 lines
31 KiB
C
897 lines
31 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ccsip_publish.h"
|
|
#include "phntask.h"
|
|
#include "sip_common_transport.h"
|
|
#include "ccsip_task.h"
|
|
#include "ccsip_callinfo.h"
|
|
#include "ccsip_macros.h"
|
|
#include "util_string.h"
|
|
#include "cpr_rand.h"
|
|
#include "platform_api.h"
|
|
#include "ccsip_register.h"
|
|
#include "ccsip_reldev.h"
|
|
#include "debug.h"
|
|
|
|
static sll_handle_t s_PCB_list = NULL; // signly linked list handle of PCBs
|
|
static int outgoingPublishes;
|
|
|
|
static boolean sipSPISendPublish(ccsip_publish_cb_t *pcb_p, boolean authen);
|
|
static void free_pending_reqs (sll_handle_t list);
|
|
|
|
/**
|
|
* This function will generate a new handle and return it.
|
|
*
|
|
* @return a non-zero integer value
|
|
*/
|
|
static pub_handle_t generate_new_pub_handle (void)
|
|
{
|
|
static pub_handle_t handle = 0;
|
|
|
|
handle++;
|
|
if (handle == NULL_PUBLISH_HANDLE) {
|
|
handle++;
|
|
}
|
|
return handle;
|
|
}
|
|
|
|
/**
|
|
* This function will check if it is the PCB we are looking for based on the key.
|
|
* This is only invoked by sll_find().
|
|
*
|
|
* @param[in] key - pointer to the key.
|
|
* @param[in] data - pointer to a node data.
|
|
*
|
|
* @return SLL_MATCH_FOUND if the node matches.
|
|
* Otherwise, SLL_MATCH_NOT_FOUND is returned.
|
|
*
|
|
* @pre (key != NULL) and (data != NULL)
|
|
*/
|
|
static sll_match_e is_matching_pcb (void *key, void *data)
|
|
{
|
|
pub_handle_t pub_handle = *((pub_handle_t *)key);
|
|
ccsip_publish_cb_t *pcb_p = (ccsip_publish_cb_t *)data;
|
|
|
|
if (pub_handle == pcb_p->pub_handle) {
|
|
return SLL_MATCH_FOUND;
|
|
}
|
|
return SLL_MATCH_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
* This function will create a PCB. It will also create the PCB linked list.
|
|
*
|
|
* @return NULL if there are no resources to create a PCB.
|
|
* Otherwise, pointer to a new PCB is returned.
|
|
*
|
|
* @pre (key != NULL) and (data != NULL)
|
|
*/
|
|
static ccsip_publish_cb_t *get_new_pcb (void)
|
|
{
|
|
ccsip_publish_cb_t *pcb_p;
|
|
|
|
/*
|
|
* If PCB list is not created yet, create the list.
|
|
*/
|
|
if (s_PCB_list == NULL) {
|
|
s_PCB_list = sll_create(is_matching_pcb);
|
|
if (s_PCB_list == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
pcb_p = (ccsip_publish_cb_t *)cpr_malloc(sizeof(ccsip_publish_cb_t));
|
|
if (pcb_p == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(pcb_p, 0, sizeof(ccsip_publish_cb_t));
|
|
pcb_p->pub_handle = generate_new_pub_handle();
|
|
pcb_p->hb.cb_type = PUBLISH_CB;
|
|
pcb_p->hb.dn_line = 1; // for now set it to primary line. This will change when we do line based PUBLISH.
|
|
/*
|
|
* set up dest & src ip addr and port number.
|
|
*/
|
|
ccsip_common_util_set_dest_ipaddr_port(&pcb_p->hb);
|
|
ccsip_common_util_set_src_ipaddr(&pcb_p->hb);
|
|
pcb_p->hb.local_port = sipTransportGetListenPort(pcb_p->hb.dn_line, NULL);
|
|
pcb_p->retry_timer.timer = cprCreateTimer("PUBLISH retry timer",
|
|
SIP_PUBLISH_RETRY_TIMER,
|
|
TIMER_EXPIRATION,
|
|
sip_msgq);
|
|
if (pcb_p->retry_timer.timer == NULL) {
|
|
cpr_free(pcb_p);
|
|
return NULL;
|
|
}
|
|
pcb_p->pending_reqs = sll_create(NULL);
|
|
if (pcb_p->pending_reqs == NULL) {
|
|
(void)cprDestroyTimer(pcb_p->retry_timer.timer);
|
|
cpr_free(pcb_p);
|
|
return NULL;
|
|
}
|
|
(void) sll_append(s_PCB_list, pcb_p);
|
|
|
|
return pcb_p;
|
|
}
|
|
|
|
/**
|
|
* This function will find matching PCB by the key in the PCB list.
|
|
*
|
|
* @param[in] pub_handle - publish handle.
|
|
*
|
|
* @return NULL if there is no matching PCB
|
|
* Otherwise, pointer to the found PCB is returned.
|
|
*
|
|
* @pre (pub_handle > 0) and (s_PCB_list != NULL)
|
|
*/
|
|
static ccsip_publish_cb_t *find_pcb (pub_handle_t pub_handle)
|
|
{
|
|
ccsip_publish_cb_t *pcb_p;
|
|
|
|
pcb_p = (ccsip_publish_cb_t *)sll_find(s_PCB_list, &pub_handle);
|
|
|
|
return pcb_p;
|
|
}
|
|
|
|
/**
|
|
* This function will find matching PCB by the SIP Call-ID in the PCB list.
|
|
*
|
|
* @param[in] callID_p - SIP Call-ID
|
|
*
|
|
* @return NULL if there is no matching PCB
|
|
* Otherwise, pointer to the found PCB is returned.
|
|
*
|
|
* @pre (callID_p != NULL)
|
|
*/
|
|
static ccsip_publish_cb_t *find_pcb_by_sip_callid (const char *callID_p)
|
|
{
|
|
ccsip_publish_cb_t *pcb_p;
|
|
|
|
pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, NULL);
|
|
while (pcb_p != NULL) {
|
|
if (strncmp(callID_p, pcb_p->hb.sipCallID, (sizeof(pcb_p->hb.sipCallID) -1)) == 0) {
|
|
return pcb_p;
|
|
}
|
|
pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, pcb_p);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* This function will free up the PCB resources and removes it from the PCB list.
|
|
*
|
|
* @param[in] pcb_p - pointer to a PCB.
|
|
*
|
|
* @return none
|
|
*
|
|
* @pre (pcb_p != NULL)
|
|
*/
|
|
static void free_pcb (ccsip_publish_cb_t *pcb_p)
|
|
{
|
|
if (pcb_p->hb.authen.authorization != NULL) {
|
|
cpr_free(pcb_p->hb.authen.authorization);
|
|
}
|
|
if (pcb_p->hb.authen.sip_authen != NULL) {
|
|
sippmh_free_authen(pcb_p->hb.authen.sip_authen);
|
|
}
|
|
|
|
cpr_free(pcb_p->entity_tag);
|
|
free_pending_reqs(pcb_p->pending_reqs);
|
|
(void)cprDestroyTimer(pcb_p->retry_timer.timer);
|
|
free_event_data(pcb_p->hb.event_data_p);
|
|
(void)sll_remove(s_PCB_list, (void *)pcb_p);
|
|
cpr_free(pcb_p);
|
|
}
|
|
|
|
/**
|
|
* This function will append the application request in a pending list.
|
|
*
|
|
* @param[in] pcb_p - pointer to a PCB.
|
|
* @param[in] msg_p - pointer to an application request.
|
|
*
|
|
* @return TRUE if it is successful.
|
|
* Otherwise, FALSE is returned.
|
|
*
|
|
* @pre (pcb_p != NULL) and (msg_p != NULL)
|
|
* @post ((slink_list_t *)s_pres_req_list->count++)
|
|
*/
|
|
static boolean append_pending_reqs (ccsip_publish_cb_t *pcb_p, pub_req_t *msg_p)
|
|
{
|
|
pub_req_t *temp_msg_p;
|
|
|
|
temp_msg_p = (pub_req_t *)cpr_malloc(sizeof(pub_req_t));
|
|
if (temp_msg_p == NULL) {
|
|
return FALSE;
|
|
}
|
|
(*temp_msg_p) = (*msg_p);
|
|
(void) sll_append(pcb_p->pending_reqs, temp_msg_p);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* This function will free up entire list of pending requests
|
|
*
|
|
* @param[in] list - pending requests list handle
|
|
*
|
|
* @return none
|
|
*
|
|
*/
|
|
static void free_pending_reqs (sll_handle_t list)
|
|
{
|
|
pub_req_t *msg_p;
|
|
|
|
if (list == NULL) {
|
|
return;
|
|
}
|
|
|
|
msg_p = (pub_req_t *)sll_next(list, NULL);
|
|
while (msg_p != NULL) {
|
|
free_event_data(msg_p->event_data_p);
|
|
(void)sll_remove(list, (void *)msg_p);
|
|
cpr_free(msg_p);
|
|
msg_p = (pub_req_t *)sll_next(list, NULL);
|
|
}
|
|
sll_destroy(list);
|
|
}
|
|
|
|
/**
|
|
* This function will send the PUBLSIH request response to the application.
|
|
*
|
|
* @param[in] resp_code - this also includes error responses that start at 1000
|
|
* @param[in] pub_handle - handle to a PCB
|
|
* @param[in] app_handle - application generated handle to this PUBLISH context.
|
|
* @param[in] callback_task - task in which the application resides.
|
|
* @param[in] resp_msg_id - messageID posted to the task.
|
|
*
|
|
* @return none
|
|
*/
|
|
static void send_resp_to_app (int resp_code, pub_handle_t pub_handle, pub_handle_t app_handle,
|
|
cc_srcs_t callback_task, int resp_msg_id)
|
|
{
|
|
static const char fname[] = "send_resp_to_app";
|
|
pub_rsp_t rsp;
|
|
|
|
rsp.resp_code = resp_code;
|
|
rsp.pub_handle = pub_handle;
|
|
rsp.app_handle = app_handle;
|
|
if (publish_int_response(&rsp, callback_task, resp_msg_id) != CC_RC_SUCCESS) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Failed to post PUBLISH response to the application", fname);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function will process SIPSPI_EV_CC_PUBLISH posted by applications.
|
|
* If there is an outstanding transaction, it will hold the request in the pending list.
|
|
*
|
|
* @param[in] buf - inter-process message buffer.
|
|
*
|
|
* @return SIP_OK if it successfully processes the request.
|
|
* SIP_ERROR if it fails to process the request.
|
|
* SIP_DEFER if it defers the processing.
|
|
*
|
|
* @note This will not free buf.
|
|
*
|
|
* @pre (buf != NULL)
|
|
*/
|
|
int publish_handle_ev_app_publish (cprBuffer_t buf)
|
|
{
|
|
static const char fname[] = "publish_handle_ev_app_publish";
|
|
pub_req_t *msg_p = (pub_req_t *)buf;
|
|
ccsip_publish_cb_t *pcb_p;
|
|
|
|
|
|
/*
|
|
* If this is initial PUBLISH, allocate a PCB.
|
|
* Otherwise, look up for PCB based on pub_handle.
|
|
*/
|
|
if (msg_p->pub_handle != NULL_PUBLISH_HANDLE) {
|
|
pcb_p = find_pcb(msg_p->pub_handle);
|
|
|
|
if (pcb_p == NULL) {
|
|
send_resp_to_app(PUBLISH_FAILED_NOCONTEXT, msg_p->pub_handle, msg_p->app_handle,
|
|
msg_p->callback_task, msg_p->resp_msg_id);
|
|
free_event_data(msg_p->event_data_p);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX
|
|
"Modification PUBLISH cannot be sent as the PCB is missing\n",
|
|
fname);
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Check if there is an outstanding transaction.
|
|
* if so, put the request in pending request queue.
|
|
*/
|
|
if (pcb_p->outstanding_trxn == TRUE) {
|
|
if (append_pending_reqs(pcb_p, msg_p) == TRUE) {
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"deffering as there is an outstanding transaction\n",
|
|
DEB_F_PREFIX_ARGS(SIP_PUB, fname));
|
|
return SIP_DEFER;
|
|
}
|
|
/* free up PCB and respond with error */
|
|
free_pcb (pcb_p);
|
|
send_resp_to_app(PUBLISH_FAILED_NORESOURCE, msg_p->pub_handle, msg_p->app_handle,
|
|
msg_p->callback_task, msg_p->resp_msg_id);
|
|
free_event_data(msg_p->event_data_p);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Queueing outgoing PUBLISH request failed\n", fname);
|
|
return SIP_ERROR;
|
|
}
|
|
/*
|
|
* if event_data_p is NULL, this is terminating PUBLISH.
|
|
* otherwise, it is a modifying PUBLISH.
|
|
*/
|
|
free_event_data(pcb_p->hb.event_data_p);
|
|
pcb_p->hb.event_data_p = msg_p->event_data_p;
|
|
if ((msg_p->event_data_p == NULL) && (msg_p->expires == 0)) { // removing PUBLISH
|
|
pcb_p->hb.orig_expiration = 0;
|
|
}
|
|
} else {
|
|
pcb_p = get_new_pcb();
|
|
if (pcb_p == NULL) {
|
|
send_resp_to_app(PUBLISH_FAILED_NORESOURCE, msg_p->pub_handle, msg_p->app_handle,
|
|
msg_p->callback_task, msg_p->resp_msg_id);
|
|
free_event_data(msg_p->event_data_p);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"PCB allocation failed\n", fname);
|
|
return SIP_ERROR;
|
|
}
|
|
pcb_p->app_handle = msg_p->app_handle;
|
|
sstrncpy(pcb_p->ruri, msg_p->ruri, MAX_URI_LENGTH);
|
|
sstrncpy(pcb_p->esc, msg_p->esc, MAX_URI_LENGTH);
|
|
pcb_p->hb.orig_expiration = msg_p->expires;
|
|
pcb_p->hb.event_type = msg_p->event_type;
|
|
pcb_p->hb.event_data_p = msg_p->event_data_p;
|
|
pcb_p->callback_task = msg_p->callback_task;
|
|
pcb_p->resp_msg_id = msg_p->resp_msg_id;
|
|
}
|
|
|
|
pcb_p->hb.authen.cred_type = 0;
|
|
|
|
if (sipSPISendPublish(pcb_p, FALSE) == TRUE) {
|
|
pcb_p->outstanding_trxn = TRUE;
|
|
outgoingPublishes++;
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"PUBLISH request sent successfully\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
|
|
return SIP_OK;
|
|
}
|
|
|
|
/* free up PCB and respond with error */
|
|
free_pcb (pcb_p);
|
|
send_resp_to_app(PUBLISH_FAILED_SEND, msg_p->pub_handle, msg_p->app_handle,
|
|
msg_p->callback_task, msg_p->resp_msg_id);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Failed to send PUBLISH request\n", fname);
|
|
return SIP_ERROR;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* This function will create the PUBLISH message and send it. it also starts the retry timer.
|
|
*
|
|
* @param[in] pcb_p - pointer to PCB
|
|
* @param[in] authen - boolean that indicates whether to add authorization header.
|
|
*
|
|
* @return TRUE if it successfully sent PUBLISH
|
|
* Otherwise, FALSE is returned
|
|
*
|
|
* @pre (pcb_p != NULL)
|
|
*/
|
|
static boolean sipSPISendPublish (ccsip_publish_cb_t *pcb_p, boolean authen)
|
|
{
|
|
static const char fname[] = "sipSPISendPublish";
|
|
static uint32_t cseq = 0;
|
|
char dest_sip_addr_str[MAX_IPADDR_STR_LEN];
|
|
char *domainloc;
|
|
char src_addr_str[MAX_IPADDR_STR_LEN];
|
|
char sip_temp_str[MAX_SIP_URL_LENGTH];
|
|
char sip_temp_tag[MAX_SIP_URL_LENGTH];
|
|
uint8_t mac_address[MAC_ADDRESS_LENGTH];
|
|
char via[SIP_MAX_VIA_LENGTH];
|
|
int max_forwards_value = 70;
|
|
static uint16_t count = 1;
|
|
sipMessage_t *request = NULL;
|
|
int timeout = 0;
|
|
|
|
request = GET_SIP_MESSAGE();
|
|
if (!request) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Populate full RURI if it is not yet. Sometimes, applications may only provide user part.
|
|
*/
|
|
if (pcb_p->full_ruri[0] == 0) {
|
|
sstrncpy(pcb_p->full_ruri, "sip:", MAX_SIP_URL_LENGTH);
|
|
sstrncat(pcb_p->full_ruri, pcb_p->ruri, MAX_SIP_URL_LENGTH - sizeof("sip:"));
|
|
/* check if it has host part */
|
|
domainloc = strchr(pcb_p->full_ruri, '@');
|
|
if (domainloc == NULL) {
|
|
domainloc = pcb_p->full_ruri + strlen(pcb_p->full_ruri);
|
|
if ((domainloc - pcb_p->full_ruri) < (MAX_SIP_URL_LENGTH - 1)) {
|
|
/* Do not include @ when there is no user part */
|
|
if (pcb_p->ruri[0] != '\0') {
|
|
*domainloc++ = '@';
|
|
}
|
|
ipaddr2dotted(dest_sip_addr_str, &pcb_p->hb.dest_sip_addr);
|
|
sstrncpy(domainloc, dest_sip_addr_str,
|
|
MAX_SIP_URL_LENGTH - (domainloc - (pcb_p->full_ruri)));
|
|
}
|
|
}
|
|
}
|
|
|
|
ipaddr2dotted(src_addr_str, &pcb_p->hb.src_addr);
|
|
|
|
// Add request line
|
|
if (HSTATUS_SUCCESS != sippmh_add_request_line(request,
|
|
sipGetMethodString(sipMethodPublish),
|
|
pcb_p->full_ruri, SIP_VERSION)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Request line\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Add local Via
|
|
snprintf(via, sizeof(via), "SIP/2.0/%s %s:%d;%s=%s%.8x",
|
|
sipTransportGetTransportType(1, TRUE, NULL),
|
|
src_addr_str, pcb_p->hb.local_port, VIA_BRANCH,
|
|
VIA_BRANCH_START, (unsigned int) cpr_rand());
|
|
if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_VIA, via)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding VIA header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Add To Header
|
|
snprintf(sip_temp_str, MAX_SIP_URL_LENGTH, "<%s>", pcb_p->full_ruri);
|
|
if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_TO, sip_temp_str)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding TO header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Add From Header.
|
|
sstrncat(sip_temp_str, ";tag=", MAX_SIP_URL_LENGTH - strlen(sip_temp_str));
|
|
sip_util_make_tag(sip_temp_tag);
|
|
sstrncat(sip_temp_str, sip_temp_tag, MAX_SIP_URL_LENGTH - strlen(sip_temp_str));
|
|
if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_FROM, sip_temp_str)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding FROM header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Add Call-ID Header.
|
|
platform_get_wired_mac_address(mac_address);
|
|
count++;
|
|
snprintf(pcb_p->hb.sipCallID, MAX_SIP_CALL_ID, "%.4x%.4x-%.4x%.4x-%.8x-%.8x@%s", // was MAX_SIP_URL_LENGTH
|
|
mac_address[0] * 256 + mac_address[1],
|
|
mac_address[2] * 256 + mac_address[3],
|
|
mac_address[4] * 256 + mac_address[5], count,
|
|
(unsigned int) cpr_rand(),
|
|
(unsigned int) cpr_rand(),
|
|
src_addr_str);
|
|
if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_CALLID, pcb_p->hb.sipCallID)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CALLID header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Add Contact header. Contact header is not needed as per RFC. BUT CCM needs it.
|
|
snprintf(sip_temp_str, MAX_SIP_URL_LENGTH, "<sip:%.4x%.4x%.4x@%s:%d>",
|
|
mac_address[0] * 256 + mac_address[1],
|
|
mac_address[2] * 256 + mac_address[3],
|
|
mac_address[4] * 256 + mac_address[5],
|
|
src_addr_str, pcb_p->hb.local_port);
|
|
if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_CONTACT, sip_temp_str)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Contact header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Add Cseq.
|
|
cseq++;
|
|
if (cseq == 0) {
|
|
cseq = 1;
|
|
}
|
|
if (HSTATUS_SUCCESS != sippmh_add_cseq(request, sipGetMethodString(sipMethodPublish), cseq)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding CSEQ header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Add UserAgent header
|
|
(void) sippmh_add_text_header(request, SIP_HEADER_USER_AGENT,
|
|
sipHeaderUserAgent);
|
|
|
|
|
|
// Add SIP-If-Match header.
|
|
if (pcb_p->entity_tag != NULL) {
|
|
if (HSTATUS_SUCCESS != sippmh_add_text_header(request, SIP_HEADER_SIPIFMATCH,
|
|
pcb_p->entity_tag)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Event header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
// Add Expires Header
|
|
if (HSTATUS_SUCCESS != sippmh_add_int_header(request, SIP_HEADER_EXPIRES,
|
|
pcb_p->hb.orig_expiration)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Expires header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Add max-forwards header
|
|
config_get_value(CFGID_SIP_MAX_FORWARDS, &max_forwards_value,
|
|
sizeof(max_forwards_value));
|
|
if (HSTATUS_SUCCESS !=
|
|
sippmh_add_int_header(request, SIP_HEADER_MAX_FORWARDS,
|
|
max_forwards_value)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Max-Forwards header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
|
|
// add Authorization header
|
|
if (authen) {
|
|
if (HSTATUS_SUCCESS != sippmh_add_text_header(request, AUTHOR_HDR(pcb_p->hb.authen.status_code),
|
|
pcb_p->hb.authen.authorization)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Authorization header\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
// Add content, if any
|
|
if (pcb_p->hb.event_data_p) {
|
|
if (add_content(pcb_p->hb.event_data_p, request, fname) == FALSE) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Content\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
} else {
|
|
if (HSTATUS_SUCCESS != sippmh_add_int_header(request, SIP_HEADER_CONTENT_LENGTH, 0)) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in adding Content-Len\n", fname);
|
|
free_sip_message(request);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
ccsip_common_util_set_retry_settings(&pcb_p->hb, &timeout);
|
|
if (sipTransportCreateSendMessage(NULL, request, sipMethodPublish,
|
|
&(pcb_p->hb.dest_sip_addr),
|
|
(int16_t) pcb_p->hb.dest_sip_port,
|
|
FALSE, TRUE, timeout, pcb_p,
|
|
RELDEV_NO_STORED_MSG) < 0) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send PUBLISH message\n", fname);
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
/**
|
|
* This function will handle retry-timer expiration
|
|
*
|
|
* @param[in] handle - handle to PCB
|
|
*
|
|
* @return 0 if it is successful in handling the timer
|
|
* Otherwise, -1 is returned
|
|
*
|
|
* @pre (handle != 0)
|
|
*/
|
|
int publish_handle_retry_timer_expire (uint32_t handle)
|
|
{
|
|
static const char fname[] = "publish_handle_retry_timer_expire";
|
|
pub_handle_t pub_handle = handle;
|
|
ccsip_publish_cb_t *pcb_p;
|
|
uint32_t max_retx = 0;
|
|
uint32_t time_t1 = 0;
|
|
uint32_t time_t2 = 0;
|
|
uint32_t timeout = 0;
|
|
|
|
/*
|
|
* find the PCB
|
|
*/
|
|
pcb_p = find_pcb(pub_handle);
|
|
if (pcb_p == NULL) {
|
|
/* No PCB. So do nothing. */
|
|
return 0;
|
|
}
|
|
if (pcb_p->hb.retx_flag == FALSE) {
|
|
/* probably we got some response. so do nothing */
|
|
return 0;
|
|
}
|
|
config_get_value(CFGID_SIP_RETX, &max_retx, sizeof(max_retx));
|
|
if (max_retx > MAX_NON_INVITE_RETRY_ATTEMPTS) {
|
|
max_retx = MAX_NON_INVITE_RETRY_ATTEMPTS;
|
|
}
|
|
if (pcb_p->hb.retx_counter < max_retx) {
|
|
pcb_p->hb.retx_counter++;
|
|
config_get_value(CFGID_TIMER_T1, &time_t1, sizeof(time_t1));
|
|
timeout = time_t1 * (1 << pcb_p->hb.retx_counter);
|
|
config_get_value(CFGID_TIMER_T2, &time_t2, sizeof(time_t2));
|
|
if (timeout > time_t2) {
|
|
timeout = time_t2;
|
|
}
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"Resending message #%d\n",
|
|
DEB_F_PREFIX_ARGS(SIP_PUB, fname), pcb_p->hb.retx_counter);
|
|
if (sipTransportSendMessage(NULL,
|
|
pcb_p->retry_timer.message_buffer,
|
|
pcb_p->retry_timer.message_buffer_len,
|
|
pcb_p->retry_timer.message_type,
|
|
&(pcb_p->retry_timer.ipaddr),
|
|
pcb_p->retry_timer.port,
|
|
FALSE, TRUE, timeout, pcb_p) < 0) {
|
|
/* free up PCB and respond with error */
|
|
send_resp_to_app(PUBLISH_FAILED_SEND, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
free_pcb (pcb_p);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to send message", fname);
|
|
return (-1);
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* send timeout response and free up PCB
|
|
*/
|
|
send_resp_to_app(SIP_CLI_ERR_REQ_TIMEOUT, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
free_pcb (pcb_p);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"reached MAX retries", fname);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* This function will check if wee need to send refresh PUBLISH.
|
|
* If so, it will send refresh PUBLISH
|
|
*
|
|
* @param[in] none
|
|
*
|
|
* @return none
|
|
*/
|
|
void publish_handle_periodic_timer_expire (void)
|
|
{
|
|
static const char fname[] = "publish_handle_periodic_timer_expire";
|
|
int delta = 0;
|
|
ccsip_publish_cb_t *pcb_p;
|
|
pub_req_t msg;
|
|
|
|
config_get_value(CFGID_TIMER_SUBSCRIBE_DELTA, &delta,
|
|
sizeof(delta));
|
|
pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, NULL);
|
|
while (pcb_p != NULL) {
|
|
if (pcb_p->outstanding_trxn == FALSE) {
|
|
if (pcb_p->hb.expires >= TMR_PERIODIC_PUBLISH_INTERVAL) {
|
|
pcb_p->hb.expires -= TMR_PERIODIC_PUBLISH_INTERVAL;
|
|
}
|
|
if (pcb_p->hb.expires <= (delta + TMR_PERIODIC_PUBLISH_INTERVAL)) {
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"sending REFRESH PUBLISH", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
|
|
memset (&msg, 0, sizeof(msg));
|
|
/* refresh is triggered by NULL event data and non-zero expires value */
|
|
msg.pub_handle = pcb_p->pub_handle;
|
|
msg.expires = pcb_p->hb.orig_expiration;
|
|
(void)publish_handle_ev_app_publish(&msg);
|
|
}
|
|
}
|
|
pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, pcb_p);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function will process the response to PUBLISH request sent by us.
|
|
*
|
|
* @param[in] pSipMessage - pointer to received SIP response msg
|
|
*
|
|
* @return SIP_OK if it successfully processes the response.
|
|
* SIP_ERROR if it fails to process the response.
|
|
*
|
|
* @pre (pSipMessage != NULL)
|
|
*/
|
|
int publish_handle_ev_sip_response (sipMessage_t *pSipMessage)
|
|
{
|
|
static const char fname[] = "publish_handle_ev_sip_response";
|
|
const char *callID_p = NULL;
|
|
int response_code = 0;
|
|
const char *expires = NULL;
|
|
const char *sip_etag = NULL;
|
|
long expiry_time;
|
|
pub_req_t *msg_p;
|
|
ccsip_publish_cb_t *pcb_p;
|
|
int entity_tag_size;
|
|
|
|
callID_p = sippmh_get_cached_header_val(pSipMessage, CALLID);
|
|
if (!callID_p) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Cannot obtain SIP Call ID.\n", fname);
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Find PCB by Call-ID. The Call-IDs generated by the phone ae unique.
|
|
*/
|
|
pcb_p = find_pcb_by_sip_callid(callID_p);
|
|
if (pcb_p == NULL) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"No matching PCB found\n", fname);
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Cancel the retry_timer and set the retx_flag to FALSE.
|
|
*/
|
|
sip_platform_msg_timer_subnot_stop(&(pcb_p->retry_timer));
|
|
pcb_p->hb.retx_flag = FALSE;
|
|
|
|
// Parse the return code
|
|
(void) sipGetResponseCode(pSipMessage, &response_code);
|
|
|
|
if (response_code >= 200) {
|
|
pcb_p->outstanding_trxn = FALSE;
|
|
}
|
|
|
|
|
|
if ((response_code == SIP_CLI_ERR_UNAUTH) ||
|
|
(response_code == SIP_CLI_ERR_PROXY_REQD)) {
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"Authentication Required\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
|
|
if (ccsip_common_util_generate_auth(pSipMessage, &pcb_p->hb, SIP_METHOD_PUBLISH,
|
|
response_code, pcb_p->full_ruri) == TRUE) {
|
|
if (sipSPISendPublish(pcb_p, TRUE) == TRUE) {
|
|
pcb_p->outstanding_trxn = TRUE;
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"sent request with Auth header\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
|
|
return SIP_OK;
|
|
}
|
|
}
|
|
/*
|
|
* Since we failed to resend the PUBLISH request, free up the PCB and let the app know.
|
|
*/
|
|
send_resp_to_app(PUBLISH_FAILED_SEND, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
free_pcb (pcb_p);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to respond to auth challenge\n", fname);
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
/*
|
|
* if response code is 423, grab Min-Expires and send new PUBLISH with new expires value
|
|
*/
|
|
if (response_code == SIP_CLI_ERR_INTERVAL_TOO_SMALL) {
|
|
expires = sippmh_get_header_val(pSipMessage,
|
|
(const char *)SIP_HEADER_MIN_EXPIRES,
|
|
NULL);
|
|
if (expires) {
|
|
expiry_time = strtoul(expires, NULL, 10);
|
|
//ensure new Min-Expires is > what we set before in Expires
|
|
if ((long) expiry_time > pcb_p->hb.expires) {
|
|
pcb_p->hb.expires = expiry_time;
|
|
pcb_p->hb.orig_expiration = expiry_time;
|
|
}
|
|
if (sipSPISendPublish(pcb_p, FALSE) == TRUE) {
|
|
pcb_p->outstanding_trxn = TRUE;
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"sent request with increased expires\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
|
|
return SIP_OK;
|
|
}
|
|
}
|
|
/*
|
|
* Since we failed to resend the PUBLISH request, free up the PCB and let the app know.
|
|
*/
|
|
send_resp_to_app(PUBLISH_FAILED_SEND, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
free_pcb (pcb_p);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to respond to 423\n", fname);
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
/*
|
|
* if the response_code is > 299, free up the PCB and let the app know.
|
|
*/
|
|
if (response_code > 299) {
|
|
send_resp_to_app(response_code, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
free_pcb (pcb_p);
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"received %d response\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname), response_code);
|
|
return SIP_OK;
|
|
}
|
|
|
|
/*
|
|
* if the response is < 200, do nothing.
|
|
*/
|
|
if (response_code < 200) {
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"received %d response\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname), response_code);
|
|
return SIP_OK;
|
|
}
|
|
|
|
/*
|
|
* If it is PUBLISH remove operation, free up PCB
|
|
*/
|
|
if (pcb_p->hb.orig_expiration == 0) {
|
|
send_resp_to_app(response_code, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
free_pcb (pcb_p);
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"removed PCB as this was a terminating PUBLISH\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname));
|
|
return SIP_OK;
|
|
}
|
|
|
|
/*
|
|
* extract Expires and SIP-ETag headers and save them.
|
|
*/
|
|
expires = sippmh_get_header_val(pSipMessage, SIP_HEADER_EXPIRES, NULL);
|
|
if (expires) {
|
|
expiry_time = strtoul(expires, NULL, 10);
|
|
pcb_p->hb.expires = expiry_time;
|
|
}
|
|
sip_etag = sippmh_get_header_val(pSipMessage, SIP_HEADER_SIPETAG, NULL);
|
|
if (sip_etag != NULL) {
|
|
cpr_free(pcb_p->entity_tag);
|
|
entity_tag_size = strlen(sip_etag) + 1;
|
|
pcb_p->entity_tag = cpr_malloc(entity_tag_size);
|
|
if (pcb_p->entity_tag != NULL) {
|
|
sstrncpy(pcb_p->entity_tag, sip_etag, entity_tag_size);
|
|
} else {
|
|
free_pcb (pcb_p);
|
|
send_resp_to_app(PUBLISH_FAILED_NORESOURCE, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"memory allocation failed\n", fname);
|
|
return SIP_ERROR;
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there are no pending requests, provide the response to the application.
|
|
*/
|
|
msg_p = (pub_req_t *)sll_next(pcb_p->pending_reqs, NULL);
|
|
if (msg_p != NULL) {
|
|
(void)sll_remove(pcb_p->pending_reqs, msg_p);
|
|
(void)publish_handle_ev_app_publish(msg_p);
|
|
cpr_free(msg_p);
|
|
return SIP_OK;
|
|
}
|
|
send_resp_to_app(response_code, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
CCSIP_DEBUG_TASK(DEB_F_PREFIX"sent response %d to app\n", DEB_F_PREFIX_ARGS(SIP_PUB, fname), response_code);
|
|
return SIP_OK;
|
|
}
|
|
|
|
/**
|
|
* This function will inform the application that phone is either
|
|
* 1. restarting or
|
|
* 2. failing over/ falling back
|
|
*
|
|
* @note detection of CCM reboot will be handled by PUBLISH ETag mechanism.
|
|
*
|
|
* @param[in] none
|
|
*
|
|
* @return none
|
|
*/
|
|
void publish_reset (void)
|
|
{
|
|
ccsip_publish_cb_t *pcb_p;
|
|
|
|
pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, NULL);
|
|
while (pcb_p != NULL) {
|
|
send_resp_to_app(PUBLISH_FAILED_RESET, pcb_p->pub_handle, pcb_p->app_handle,
|
|
pcb_p->callback_task, pcb_p->resp_msg_id);
|
|
free_pcb(pcb_p);
|
|
pcb_p = (ccsip_publish_cb_t *)sll_next(s_PCB_list, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function will print the stats (invoked by show command)
|
|
*
|
|
* @param[in] none
|
|
*
|
|
* @return 0 always
|
|
*/
|
|
cc_int32_t show_publish_stats (cc_int32_t argc, const char *argv[])
|
|
{
|
|
debugif_printf("------ Current PUBLISH Statistics ------\n");
|
|
if (s_PCB_list != NULL) {
|
|
debugif_printf("Number of PCBs allocated: %d\n", sll_count(s_PCB_list));
|
|
} else {
|
|
debugif_printf("Number of PCBs allocated: 0\n");
|
|
}
|
|
debugif_printf("Total outgoing PUBLISH requests: %d\n", outgoingPublishes);
|
|
return 0;
|
|
}
|
|
|