mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-10-23 11:44:14 +00:00
5752 lines
182 KiB
C
5752 lines
182 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/. */
|
|
|
|
/*
|
|
* Routines that parse SIP messages into individual components
|
|
* defined in ccsip_pmh.h. Used by the code that receives the
|
|
* messages to decipher them and then effect callstate as
|
|
* appropriate.
|
|
* Some SIP messages are exactly the same as HTTP/1.1 messages,
|
|
* and these are parsed using wrapper functions to those in
|
|
* httpish.c
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
|
|
#include "plstr.h"
|
|
#include "cpr_types.h"
|
|
#include "cpr_stdio.h"
|
|
#include "cpr_stdlib.h"
|
|
#include "cpr_string.h"
|
|
#include "cpr_in.h"
|
|
#include "cpr_memory.h"
|
|
#include "ccsip_pmh.h"
|
|
#include "phone_debug.h"
|
|
#include "ccapi.h"
|
|
#include "text_strings.h"
|
|
#include "util_string.h"
|
|
#include "ccsip_spi_utils.h"
|
|
#include "ccsip_callinfo.h"
|
|
#include "ccsip_core.h"
|
|
|
|
/* Skip linear whitespace */
|
|
#define SKIP_LWS(p) while (*p == SPACE || *p == TAB) { \
|
|
p++; \
|
|
}
|
|
|
|
#define SKIP_WHITE_SPACE(p) while (*p == SPACE || *p == TAB || \
|
|
*p == '\n') { \
|
|
p++; \
|
|
}
|
|
|
|
#define AUTHENTICATION_BASIC "Basic"
|
|
#define AUTHENTICATION_DIGEST "Digest"
|
|
#define AUTHENTICATION_REALM "realm"
|
|
#define AUTHENTICATION_NONCE "nonce"
|
|
#define AUTHENTICATION_URI "uri"
|
|
#define AUTHENTICATION_DOMAIN "domain"
|
|
#define AUTHENTICATION_ALGORITHM "algorithm"
|
|
#define AUTHENTICATION_OPAQUE "opaque"
|
|
#define AUTHENTICATION_USERNAME "username"
|
|
#define AUTHENTICATION_RESPONSE "response"
|
|
|
|
|
|
/* Trim trailing space from end of string */
|
|
static void
|
|
trim_right (char *pstr)
|
|
{
|
|
char *pend;
|
|
|
|
if (!pstr) {
|
|
return;
|
|
}
|
|
|
|
pend = (pstr + (strlen(pstr) - 1));
|
|
while (pend > pstr) {
|
|
if (!isspace((int) *pend)) {
|
|
break;
|
|
}
|
|
*pend = '\0';
|
|
pend--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remove square brackets from IPv6 address
|
|
*
|
|
* @param pstr address string
|
|
*
|
|
* @return none
|
|
*
|
|
* @pre none
|
|
*
|
|
*/
|
|
|
|
static void trim_ipv6_host(char *pstr)
|
|
{
|
|
if (!pstr) {
|
|
return;
|
|
}
|
|
|
|
if (*pstr == '[') {
|
|
pstr++;
|
|
}
|
|
|
|
if (*(pstr + strlen(pstr)-1) == ']') {
|
|
*(pstr + strlen(pstr)-1) = '\0';
|
|
}
|
|
}
|
|
|
|
int parse_errno = 0;
|
|
|
|
/* The order of these error messages corresponds to parse error codes
|
|
* defined in ccsip_pmh.h
|
|
*/
|
|
static char *parse_errors[] = { "",
|
|
ERROR_1,
|
|
ERROR_2,
|
|
ERROR_3,
|
|
ERROR_4,
|
|
ERROR_5,
|
|
ERROR_6,
|
|
ERROR_7,
|
|
ERROR_8
|
|
};
|
|
|
|
/*
|
|
* These headers are defined as per the values of Header indexes ( in
|
|
* ccsip_protocol.h. To include a header in the cached list, increase
|
|
* the HEADER_CACHE_SIZE in httpish.h and add its entry here. Its index
|
|
* should be consistent with the index value in ccsip_protocol.h.
|
|
* For example, #define VIA 2, implies sip_cached_headers[2] has Via header
|
|
* related stuff.
|
|
*/
|
|
sip_header_t sip_cached_headers[] = {
|
|
/* 0 */ {"From", "f"},
|
|
/* 1 */ {"To", "t"},
|
|
/* 2 */ {HTTPISH_HEADER_VIA, "v"},
|
|
/* 3 */ {"Call-ID", "i"},
|
|
/* 4 */ {"CSeq", NULL},
|
|
/* 5 */ {"Contact", "m"},
|
|
/* 6 */ {HTTPISH_HEADER_CONTENT_LENGTH, HTTPISH_C_HEADER_CONTENT_LENGTH},
|
|
/* 7 */ {HTTPISH_HEADER_CONTENT_TYPE, "c"},
|
|
/* 8 */ {"Record-Route", NULL},
|
|
/* 9 */ {"Require", NULL},
|
|
/* 10 */ {"Route", NULL},
|
|
/* 11 */ {"Supported", "k"}
|
|
};
|
|
|
|
static boolean
|
|
is_dtmf_or_pause (char *digit)
|
|
{
|
|
if (!(*digit)) {
|
|
return FALSE;
|
|
}
|
|
switch (*digit) {
|
|
case 'A':
|
|
case 'B':
|
|
case 'C':
|
|
case 'D':
|
|
case '*':
|
|
case '#':
|
|
case 'p':
|
|
case 'w':
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Inefficient ? Somewhat... */
|
|
sipMethod_t
|
|
sippmh_get_method_code (const char *method)
|
|
{
|
|
sipMethod_t ret = sipMethodInvalid;
|
|
|
|
if (method) {
|
|
|
|
ret = sipMethodUnknown;
|
|
|
|
if (strcmp(method, SIP_METHOD_INVITE) == 0)
|
|
ret = sipMethodInvite;
|
|
else if (strcmp(method, SIP_METHOD_BYE) == 0)
|
|
ret = sipMethodBye;
|
|
else if (strcmp(method, SIP_METHOD_ACK) == 0)
|
|
ret = sipMethodAck;
|
|
else if (strcmp(method, SIP_METHOD_PRACK) == 0)
|
|
ret = sipMethodPrack;
|
|
else if (strcmp(method, SIP_METHOD_COMET) == 0)
|
|
ret = sipMethodComet;
|
|
else if (strcmp(method, SIP_METHOD_OPTIONS) == 0)
|
|
ret = sipMethodOptions;
|
|
else if (strcmp(method, SIP_METHOD_CANCEL) == 0)
|
|
ret = sipMethodCancel;
|
|
else if (strcmp(method, SIP_METHOD_NOTIFY) == 0)
|
|
ret = sipMethodNotify;
|
|
else if (strcmp(method, SIP_METHOD_REFER) == 0)
|
|
ret = sipMethodRefer;
|
|
else if (strcmp(method, SIP_METHOD_SUBSCRIBE) == 0)
|
|
ret = sipMethodSubscribe;
|
|
else if (strcmp(method, SIP_METHOD_REGISTER) == 0)
|
|
ret = sipMethodRegister;
|
|
else if (strcmp(method, SIP_METHOD_UPDATE) == 0)
|
|
ret = sipMethodUpdate;
|
|
else if (strcmp(method, SIP_METHOD_INFO) == 0)
|
|
ret = sipMethodInfo;
|
|
else if (strcmp(method, SIP_METHOD_PUBLISH) == 0)
|
|
ret = sipMethodPublish;
|
|
else if (strcmp(method, SIP_METHOD_MESSAGE) == 0)
|
|
ret = sipMethodMessage;
|
|
else if (strcmp(method, SIP_METHOD_INFO) == 0)
|
|
ret = sipMethodInfo;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define SKIP_SIP_TOKEN(inp_str) \
|
|
while (isalnum((int)*inp_str) || *inp_str == DASH || \
|
|
*inp_str == DOT || *inp_str == EXCLAMATION || \
|
|
*inp_str == PERCENT || *inp_str == STAR || \
|
|
*inp_str == UNDERSCORE || *inp_str == PLUS || \
|
|
*inp_str == '`' || *inp_str == SINGLE_QUOTE || \
|
|
*inp_str == COLON || *inp_str == TILDA || \
|
|
*inp_str == AT_SIGN) { \
|
|
inp_str++; \
|
|
}
|
|
|
|
static char *
|
|
parse_generic_param (char *param, char **param_val)
|
|
{
|
|
boolean match_found;
|
|
char *tok_start;
|
|
|
|
/*
|
|
* param is pointing to the first character after the name of the
|
|
* parameter. It could be
|
|
* white space or
|
|
* equal sign or
|
|
* semi-colon or
|
|
* comma or
|
|
* end of string
|
|
*/
|
|
if (*param == SPACE || *param == TAB) {
|
|
*param++ = 0;
|
|
SKIP_LWS(param);
|
|
}
|
|
|
|
if (*param != EQUAL_SIGN) {
|
|
/*
|
|
* Parameter exists but its value is empty
|
|
*/
|
|
*param_val = "";
|
|
return param;
|
|
}
|
|
|
|
*param++ = 0;
|
|
SKIP_LWS(param);
|
|
*param_val = param;
|
|
if (*param == DOUBLE_QUOTE) {
|
|
param++;
|
|
match_found = FALSE;
|
|
while (*param) {
|
|
if (*param == DOUBLE_QUOTE && *(param - 1) != ESCAPE_CHAR) {
|
|
param++;
|
|
match_found = TRUE;
|
|
break;
|
|
}
|
|
param++;
|
|
}
|
|
if (match_found == FALSE) {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
tok_start = param;
|
|
SKIP_SIP_TOKEN(param);
|
|
if (param == tok_start) {
|
|
return NULL;
|
|
}
|
|
}
|
|
return param;
|
|
}
|
|
|
|
/*
|
|
* The syntax of other-param in SIP URL is same as extension-attribute in
|
|
* Contact header. A pointer to the start of the token=value or
|
|
* token="value" string is returned in other_param_value. This allows
|
|
* the calling code to store this value if needed such as in the
|
|
* case of supporting contact-extensions. The function returns
|
|
* a pointer to the next character to be parsed.
|
|
*/
|
|
static char *
|
|
parse_other_param (char *inp_str, char **other_param_value)
|
|
{
|
|
char temp;
|
|
char *token_string;
|
|
|
|
|
|
SKIP_LWS(inp_str);
|
|
token_string = inp_str;
|
|
SKIP_SIP_TOKEN(inp_str);
|
|
switch (*inp_str) {
|
|
case COMMA:
|
|
case '\0':
|
|
case SEMI_COLON:
|
|
/*
|
|
* Either we have encountered end of string or another
|
|
* parameter follows this other-param. In either case we
|
|
* are done with parsing other-param. Since this is
|
|
* not in the form of token=value or token="value" discard
|
|
* it.
|
|
*/
|
|
*other_param_value = NULL;
|
|
|
|
/* check for loose routing specifier */
|
|
temp = *inp_str;
|
|
*inp_str = 0;
|
|
if (!cpr_strcasecmp(token_string, "lr")) {
|
|
*other_param_value = (char *) cpr_malloc(4);
|
|
if (*other_param_value != NULL) {
|
|
sstrncpy(*other_param_value, token_string, 4);
|
|
}
|
|
}
|
|
*inp_str = temp;
|
|
break;
|
|
|
|
case EQUAL_SIGN:
|
|
/* It is either token=token form or token=quoted-string form */
|
|
inp_str++;
|
|
if (*inp_str == DOUBLE_QUOTE) {
|
|
/* quoted-string follows */
|
|
inp_str++; /* skip the " char */
|
|
while (*inp_str) {
|
|
if (*inp_str == DOUBLE_QUOTE && *(inp_str - 1) != ESCAPE_CHAR) {
|
|
inp_str++;
|
|
break;
|
|
}
|
|
inp_str++;
|
|
}
|
|
} else {
|
|
/* token follows */
|
|
// SKIP_SIP_TOKEN(inp_str);
|
|
// Continue until another semicolon is found or end of string is
|
|
// reached
|
|
while (*inp_str != '\0' && *inp_str != SEMI_COLON) {
|
|
inp_str++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Terminate the string that token_string is pointing
|
|
* to while keeping the value of *inp_str unchanged.
|
|
* Then assign the null terminated string to other_param_value
|
|
*/
|
|
*other_param_value = (char *) cpr_malloc(SIP_MAX_OTHER_PARAM_LENGTH + 1 * sizeof(char));
|
|
if (*other_param_value != NULL) {
|
|
temp = *inp_str;
|
|
*inp_str = 0;
|
|
sstrncpy(*other_param_value, token_string, SIP_MAX_OTHER_PARAM_LENGTH);
|
|
*inp_str = temp;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
CCSIP_DEBUG_ERROR(ERROR_3, "parse_other_param", inp_str);
|
|
*other_param_value = NULL;
|
|
return NULL;
|
|
}
|
|
return inp_str;
|
|
}
|
|
|
|
/*
|
|
* This function creates an array of url headers. Each
|
|
* element of the array has two member pointers:
|
|
* char pointer to header name,
|
|
* char pointer to header value
|
|
*/
|
|
static int
|
|
url_add_headers_to_list (char *url_strp, sipUrl_t *sip_url)
|
|
{
|
|
char *tmp_ptr = NULL;
|
|
char num_head = 1;
|
|
char *lasts = NULL;
|
|
|
|
if (!url_strp) {
|
|
return (PARSE_ERR_NULL_PTR);
|
|
}
|
|
|
|
tmp_ptr = strchr(url_strp, AMPERSAND);
|
|
while (tmp_ptr != NULL) {
|
|
num_head++;
|
|
tmp_ptr++;
|
|
tmp_ptr = strchr(tmp_ptr, AMPERSAND);
|
|
}
|
|
|
|
sip_url->headerp = (attr_value_pair_t *)
|
|
cpr_malloc(sizeof(attr_value_pair_t) * num_head);
|
|
if (!sip_url->headerp) {
|
|
return (PARSE_ERR_NO_MEMORY);
|
|
}
|
|
|
|
sip_url->num_headers = num_head;
|
|
num_head = 0;
|
|
url_strp = PL_strtok_r(url_strp, "&?", &lasts);
|
|
while ((url_strp != NULL) && (num_head < sip_url->num_headers)) {
|
|
|
|
tmp_ptr = strchr(url_strp, EQUAL_SIGN);
|
|
if (!tmp_ptr) {
|
|
return (PARSE_ERR_SYNTAX);
|
|
}
|
|
*tmp_ptr++ = 0;
|
|
|
|
sip_url->headerp[(uint16_t)num_head].attr = url_strp;
|
|
sip_url->headerp[(uint16_t)num_head].value = tmp_ptr;
|
|
|
|
num_head++;
|
|
url_strp = PL_strtok_r(NULL, "&", &lasts);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
parseUrlParams (char *url_param, sipUrl_t *sipUrl, genUrl_t *genUrl)
|
|
{
|
|
static const char fname[] = "parseUrlParams";
|
|
char *param_val;
|
|
char *url_other_param = NULL;
|
|
uint16_t i;
|
|
// uint32_t ttl_val;
|
|
unsigned long strtoul_result;
|
|
char *strtoul_end;
|
|
|
|
|
|
/*
|
|
* url-parameters = *( ";" url-parameter )
|
|
* url-parameter = transport-param | user-param | method-param
|
|
* | ttl-param | maddr-param | other-param
|
|
* transport-param = "transport=" ( "udp" | "tcp" | "sctp" | "tls"
|
|
* | other-transport )
|
|
* ttl-param = "ttl=" ttl
|
|
* ttl = 1*3DIGIT ; 0 to 255
|
|
* maddr-param = "maddr=" host
|
|
* user-param = "user=" ( "phone" | "ip" )
|
|
* method-param = "method=" Method
|
|
* tag-param = "tag=" UUID
|
|
* lr-param = "lr"
|
|
* UUID = 1*( hex | "-" )
|
|
* other-param = ( token | ( token "=" ( token | quoted-string )))
|
|
* other-transport = token
|
|
*/
|
|
|
|
/*
|
|
* url_param is pointing to the first character after the ';'
|
|
* This routine only prints error message for SYNTAX error, since we
|
|
* want to pinpoint the error location. For other errors it simply
|
|
* returns the error code.
|
|
*/
|
|
SKIP_LWS(url_param);
|
|
if (*url_param == 0) {
|
|
return PARSE_ERR_UNEXPECTED_EOS;
|
|
}
|
|
while (1) {
|
|
if (cpr_strncasecmp(url_param, "transport=", 10) == 0) {
|
|
url_param += 10;
|
|
SKIP_LWS(url_param);
|
|
if (cpr_strncasecmp(url_param, "tcp", 3) == 0) {
|
|
sipUrl->transport = TRANSPORT_TCP;
|
|
url_param += 3;
|
|
} else if (cpr_strncasecmp(url_param, "tls", 3) == 0) {
|
|
sipUrl->transport = TRANSPORT_TLS;
|
|
url_param += 3;
|
|
} else if (cpr_strncasecmp(url_param, "sctp", 4) == 0) {
|
|
sipUrl->transport = TRANSPORT_SCTP;
|
|
url_param += 4;
|
|
} else if (cpr_strncasecmp(url_param, "udp", 3) == 0) {
|
|
sipUrl->transport = TRANSPORT_UDP;
|
|
url_param += 3;
|
|
} else {
|
|
SKIP_SIP_TOKEN(url_param);
|
|
SKIP_LWS(url_param);
|
|
if (*url_param != SEMI_COLON && *url_param != 0) {
|
|
CCSIP_DEBUG_ERROR(ERROR_3, fname, url_param);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
}
|
|
} else if (cpr_strncasecmp(url_param, "user=", 5) == 0) {
|
|
url_param += 5;
|
|
SKIP_LWS(url_param);
|
|
if (cpr_strncasecmp(url_param, "phone", 5) == 0) {
|
|
sipUrl->is_phone = 1;
|
|
url_param += 5;
|
|
} else if (cpr_strncasecmp(url_param, "ip", 2) == 0) {
|
|
url_param += 2;
|
|
} else {
|
|
CCSIP_DEBUG_ERROR(ERROR_3, fname, url_param);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
} else if (cpr_strncasecmp(url_param, "method=", 7) == 0) {
|
|
url_param += 7;
|
|
SKIP_LWS(url_param);
|
|
param_val = url_param;
|
|
while (isalpha((int) *url_param)) {
|
|
url_param++;
|
|
}
|
|
if (param_val == url_param) {
|
|
return PARSE_ERR_UNEXPECTED_EOS;
|
|
}
|
|
sipUrl->method = param_val;
|
|
/* Note that we have not terminated the method str yet */
|
|
} else if (cpr_strncasecmp(url_param, "ttl=", 4) == 0) {
|
|
char save_ch;
|
|
|
|
url_param += 4;
|
|
SKIP_LWS(url_param);
|
|
param_val = url_param;
|
|
while (isdigit((int) *url_param)) {
|
|
url_param++;
|
|
/* Atmost 3 digits allowed in ttl value */
|
|
if ((url_param - param_val) > 3) {
|
|
CCSIP_DEBUG_ERROR(ERROR_3, fname, url_param);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
}
|
|
if (url_param == param_val) {
|
|
/* Did not find any digit after "ttl=" */
|
|
return PARSE_ERR_UNEXPECTED_EOS;
|
|
}
|
|
save_ch = *url_param;
|
|
*url_param = 0; /* Terminate the string for strtoul */
|
|
|
|
errno = 0;
|
|
strtoul_result = strtoul(param_val, &strtoul_end, 10);
|
|
|
|
if (errno || param_val == strtoul_end || strtoul_result > MAX_TTL_VAL) {
|
|
CCSIP_DEBUG_ERROR(ERROR_7, fname, sipUrl->ttl_val);
|
|
return PARSE_ERR_INVALID_TTL_VAL;
|
|
}
|
|
sipUrl->ttl_val = (unsigned char) strtoul_result;
|
|
*url_param = save_ch; /* Restore string state */
|
|
} else if (cpr_strncasecmp(url_param, "maddr=", 6) == 0) {
|
|
url_param += 6;
|
|
SKIP_LWS(url_param);
|
|
param_val = url_param; /* maddr now points to a host */
|
|
while (isalnum((int) *url_param) || *url_param == DOT ||
|
|
*url_param == DASH) {
|
|
url_param++;
|
|
}
|
|
if (url_param == param_val) {
|
|
/* Empty value of maddr */
|
|
return PARSE_ERR_UNEXPECTED_EOS;
|
|
}
|
|
sipUrl->maddr = param_val;
|
|
} else if (cpr_strncasecmp(url_param, "phone-context=", 14) == 0) {
|
|
url_param += 14;
|
|
SKIP_LWS(url_param);
|
|
param_val = url_param;
|
|
|
|
while (isalpha((int) *url_param)) {
|
|
url_param++;
|
|
}
|
|
if (param_val == url_param) {
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
|
|
/* If the next character is a space, we want to zero the
|
|
* string, otherwise the next character is EOF or ; in
|
|
* which case the code below will handle these cases.
|
|
*/
|
|
if (isspace((int) *url_param)) {
|
|
*url_param++ = 0;
|
|
}
|
|
genUrl->phone_context = param_val;
|
|
} else if (cpr_strncasecmp(url_param, "lr", 2) == 0) {
|
|
// skip to the following SEMI_COLON or end
|
|
while (*url_param && *url_param != SEMI_COLON) {
|
|
url_param++;
|
|
}
|
|
sipUrl->lr_flag = TRUE;
|
|
} else {
|
|
/* other-param */
|
|
url_param = parse_other_param(url_param, &url_other_param);
|
|
|
|
if (url_param == NULL) {
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
|
|
if (url_other_param != NULL) {
|
|
/* Store it in first free slot */
|
|
i = 0;
|
|
while (i < SIP_MAX_LOCATIONS) {
|
|
if (genUrl->other_params[i] == NULL) {
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (i == SIP_MAX_LOCATIONS) {
|
|
cpr_free(url_other_param);
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"parseUrlParams: Too many unknown parameters"
|
|
" in URL of Contact header", fname);
|
|
} else {
|
|
genUrl->other_params[i] = url_other_param;
|
|
url_other_param = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
SKIP_LWS(url_param);
|
|
/* We either expect - end of string or ';' */
|
|
if (*url_param == 0) {
|
|
return 0;
|
|
}
|
|
if (*url_param != SEMI_COLON) {
|
|
CCSIP_DEBUG_ERROR(ERROR_3, fname, url_param);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
*url_param++ = 0; /* Zero the ';' and advance pointer */
|
|
SKIP_LWS(url_param);
|
|
if (*url_param == 0) {
|
|
return PARSE_ERR_UNEXPECTED_EOS;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* This routine is based on a very simple and valid assumption. SIP URL can be
|
|
* of the following forms (before the parameters, which start with ';')
|
|
* My understanding is that it is NOT a context-free grammar
|
|
* sip:host
|
|
* sip:host:port
|
|
* sip:user@host
|
|
* sip:user@host:port
|
|
* sip:user:password@host
|
|
* sip:user:password@host:port
|
|
* sip:user:password@[ipv6host]:port
|
|
*
|
|
* e.g. "1218@[2001:db8:c18:1:211:11ff:feb1:fb65]"
|
|
*
|
|
* Parse the SIP URL and zero the separators ( @ : ; etc)
|
|
* Point fields of sipUrl to appropriate places in the duplicated string
|
|
* This saves multiple mallocs for different fields of the sipUrl
|
|
* For a SIP URL of the form sip:user:password@host:port;params...
|
|
* loc_ptr[0] = Beginning of user part
|
|
* loc_ptr[1] = Beginning of password
|
|
* loc_ptr[2] = Beginning of host
|
|
* loc_ptr[3] = Beginning of port
|
|
* loc_ptr[4] = Beginning of parameters
|
|
*/
|
|
static int
|
|
parseSipUrl (char *url_start, genUrl_t *genUrl)
|
|
{
|
|
static const char fname[] = "parseSipUrl";
|
|
sipUrl_t *sipUrl = genUrl->u.sipUrl;
|
|
char ch = 0;
|
|
int token_cnt, separator_cnt;
|
|
char *url_main;
|
|
char *tokens[4];
|
|
char separator[4];
|
|
char *endptr;
|
|
uint16_t port;
|
|
boolean parsing_user_part;
|
|
char *temp_url;
|
|
boolean ipv6_addr = FALSE;
|
|
|
|
/* initializing separator */
|
|
separator[0] = '\0';
|
|
|
|
/*
|
|
* SIP-URL = "sip:" [ userinfo "@" ] hostport
|
|
* url-parameters [ headers ]
|
|
* userinfo = user [ ":" password ]
|
|
* user = *( unreserved | escaped
|
|
* | "&" | "=" | "+" | "$" | "," )
|
|
* password = *( unreserved | escaped
|
|
* | "&" | "=" | "+" | "$" | "," )
|
|
* hostport = host [ ":" port ]
|
|
* host = hostname | IPv4address | IPv6reference
|
|
* IPv6reference = "[" IPv6address "]"
|
|
* IPv6address = hexpart [ ":" IPv4address ]
|
|
* hexpart = hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
|
|
* hexseq = hex4 *( ":" hex4)
|
|
* hex4 = 1*4HEXDIG
|
|
* port = 1*DIGIT
|
|
* hostname = *( domainlabel "." ) toplabel [ "." ]
|
|
* domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
|
* toplabel = alpha | alpha *( alphanum | "-" ) alphanum
|
|
* IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
|
|
* port = *digit
|
|
*/
|
|
|
|
/* Ignore any leading white spaces too, */
|
|
SKIP_LWS(url_start);
|
|
|
|
/* we are pointing at whatever is after "sip:" */
|
|
url_main = url_start;
|
|
|
|
tokens[0] = url_main;
|
|
token_cnt = 0;
|
|
separator_cnt = 0;
|
|
|
|
/* Scan the string for : and @ and store their pointers. Terminate
|
|
* the search when you reach end of string or beginning of parameters
|
|
* indicated by ;. However the bis02 version of the spec allows ; in
|
|
* the user part of the URL. Thus the code must differentiate between
|
|
* a ; in the user part and a ; indicating the start of URL params.
|
|
*/
|
|
while (1) {
|
|
if (*url_main == 0 || *url_main == SEMI_COLON ||
|
|
*url_main == QUESTION_MARK) {
|
|
/* Either end of string or beginning of SIP URL parameters
|
|
* or a ; encountered in the user part
|
|
*/
|
|
parsing_user_part = FALSE;
|
|
temp_url = url_main;
|
|
while (*temp_url != 0) {
|
|
if (*temp_url == AT_SIGN) {
|
|
parsing_user_part = TRUE;
|
|
break;
|
|
} else if (*temp_url == QUESTION_MARK) {
|
|
/* overloaded headers in url */
|
|
break;
|
|
}
|
|
temp_url++;
|
|
}
|
|
temp_url = NULL;
|
|
|
|
if (!parsing_user_part) {
|
|
ch = *url_main;
|
|
*url_main++ = 0; /* Terminate the current token */
|
|
token_cnt++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* For IPv6 address colon present so skip that */
|
|
if ((*url_main == COLON && ipv6_addr == FALSE)|| *url_main == AT_SIGN || *url_main == SPACE ||
|
|
*url_main == TAB) {
|
|
|
|
if (*url_main == SPACE || *url_main == TAB) {
|
|
*url_main++ = 0; /* Terminate current token */
|
|
SKIP_LWS(url_main);
|
|
}
|
|
/*
|
|
* Now we should be pointing to end of string or a separator
|
|
* (even ';' - beginning of parameters
|
|
*/
|
|
if (*url_main == COLON || *url_main == AT_SIGN) {
|
|
if (separator_cnt == 3) {
|
|
/* Too many separators */
|
|
CCSIP_DEBUG_ERROR(ERROR_3_1, fname, *url_main);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
separator[separator_cnt++] = *url_main;
|
|
*url_main++ = 0; /* Zero the separator */
|
|
SKIP_LWS(url_main);
|
|
|
|
if (*url_main == LEFT_SQUARE_BRACKET) {
|
|
*url_main++ = 0; /* Must be IPv6 address */
|
|
ipv6_addr = TRUE;
|
|
}
|
|
tokens[++token_cnt] = url_main;
|
|
if (*url_main == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else if (*url_main == RIGHT_SQUARE_BRACKET && ipv6_addr == TRUE) {
|
|
|
|
*url_main++ = 0; /* Found complete IPv6 address*/
|
|
} else {
|
|
url_main++;
|
|
}
|
|
}
|
|
|
|
sipUrl->port = SIP_WELL_KNOWN_PORT;
|
|
sipUrl->port_present = FALSE;
|
|
|
|
/* token_cnt contains the number of entries in the tokens array */
|
|
switch (token_cnt) {
|
|
|
|
case 1:
|
|
/* sip:host */
|
|
sipUrl->host = tokens[0];
|
|
break;
|
|
|
|
case 2:
|
|
if (separator[0] == AT_SIGN) {
|
|
/* sip:user@host */
|
|
sipUrl->user = tokens[0];
|
|
sipUrl->host = tokens[1];
|
|
sipUrl->is_ipv6 = ipv6_addr;
|
|
} else {
|
|
/* sip:host:port */
|
|
sipUrl->host = tokens[0];
|
|
port = (uint16_t) strtol(tokens[1], &endptr, 10);
|
|
if (*endptr == 0) {
|
|
sipUrl->port = port;
|
|
sipUrl->port_present = TRUE;
|
|
} else {
|
|
sipUrl->port = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
if (separator[0] == separator[1]) {
|
|
/* Cannot have 2 successive entries of : or @ */
|
|
CCSIP_DEBUG_ERROR(ERROR_3_1, fname, separator[1]);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
if (separator[0] == AT_SIGN) {
|
|
/* sip:user@host:port */
|
|
sipUrl->user = tokens[0];
|
|
sipUrl->host = tokens[1];
|
|
port = (uint16_t) strtol(tokens[2], &endptr, 10);
|
|
if (*endptr == 0) {
|
|
sipUrl->port = port;
|
|
sipUrl->port_present = TRUE;
|
|
} else {
|
|
sipUrl->port = 0;
|
|
}
|
|
} else {
|
|
/* sip:user:password@host */
|
|
sipUrl->user = tokens[0];
|
|
sipUrl->password = tokens[1];
|
|
sipUrl->host = tokens[2];
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
/* sip:user:password@host:port */
|
|
|
|
if (separator[0] == COLON && separator[1] == AT_SIGN &&
|
|
separator[2] == COLON) {
|
|
sipUrl->user = tokens[0];
|
|
sipUrl->password = tokens[1];
|
|
sipUrl->host = tokens[2];
|
|
port = (uint16_t) strtol(tokens[3], &endptr, 10);
|
|
if (*endptr == 0) {
|
|
sipUrl->port = port;
|
|
sipUrl->port_present = TRUE;
|
|
} else {
|
|
sipUrl->port = 0;
|
|
}
|
|
} else {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad separator sequence", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
/* Remove [ and ] from IPv6 address*/
|
|
trim_ipv6_host(sipUrl->host);
|
|
|
|
/* Verify the Port */
|
|
if (sipUrl->port == 0) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received Bad Port", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
|
|
/* Verify the host portion */
|
|
if (sipSPI_validate_ip_addr_name(sipUrl->host) == FALSE) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Received Bad Host", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
|
|
/* Set default transport to UNSPECIFIED */
|
|
sipUrl->transport = TRANSPORT_UNSPECIFIED;
|
|
if (ch == SEMI_COLON) { /* Process URL parameters */
|
|
return parseUrlParams(url_main, sipUrl, genUrl);
|
|
} else if (ch == QUESTION_MARK) { /* Process URL headers */
|
|
return url_add_headers_to_list(url_main, sipUrl);
|
|
}
|
|
return 0; /* SUCCESS */
|
|
}
|
|
|
|
/*
|
|
* This routine is based on a very simple and valid assumption. TEL URL can be
|
|
* of the following forms (before the parameters, which start with ';')
|
|
* tel:user
|
|
*
|
|
* Parse the TEL URL and zero the separators (;)
|
|
* Point fields of sipUrl to appropriate places in the duplicated string
|
|
* This saves multiple mallocs for different fields of the telUrl
|
|
* For a TEL URL of the form tel:user;params...
|
|
*/
|
|
static int
|
|
parseTelUrl (char *url_start, genUrl_t *genUrl)
|
|
{
|
|
static const char fname[] = "parseTelUrl";
|
|
char *url_main;
|
|
telUrl_t *telUrl = genUrl->u.telUrl;
|
|
boolean is_local_subscriber = FALSE;
|
|
|
|
/*
|
|
* TEL-URL = telephone-scheme ":" telephone-subscriber
|
|
* telephone-scheme = "tel"
|
|
* telephone-subscriber = global-phone-number/local-phone-number
|
|
* global-phone-number = "+" base-phone-number [isdn-subaddress]
|
|
* [post-dial] *(area-specifier/service-provider/
|
|
* future-extension)
|
|
* base-phone-number = 1*phonedigit
|
|
* local-phone-number = 1*(phonedigit / dtmf-digit /pause-character)
|
|
* [isdn-subaddress] [post-dial] area-specifier
|
|
* *(are-specifier/service-provider/
|
|
* future-extension)
|
|
* isdn-subaddress = ";isub=" 1*phonedigit
|
|
* post-dial = ";postd=" 1*(phonedigit / dtmf-digit /
|
|
* pause-character)
|
|
* area-specifier = ";" phone-context-tag "=" phone-context-ident
|
|
* phone-context-tag = "phone-context"
|
|
* phone-context-ident = network-prefix / private-prefix
|
|
* network-prefix = global-network-prefix / local-network-prefix
|
|
* global-network-prefix = "+" 1*phonedigit
|
|
* local-network-prefix = 1*(phonedigit / dtmf-digit / pause-character)
|
|
* private-prefix = (%x21-22 / %x24-27 / %x2C / %x2F / %x3A /
|
|
* %x3C-40 /%x45-4F / %x51-56 / %x58-60 /
|
|
* %x65-6F / %x71-76 / %x78-7E /) *(%x21-3A /
|
|
* %x3C-7E)
|
|
* service-provider = ";" provider-tag "=" provider-hostname
|
|
* provider-tag = "tsp"
|
|
* provider-hostname = domain ; <domain> is defined in [RFC1035]
|
|
* future-extension = ";" token ["=" token]
|
|
* phonedigit = DIGIT / visual-separator
|
|
* visual-separator = "-" / "." / "(" / ")"
|
|
* pause-character = "p" / "w"
|
|
* dtmf-digit = "*" / "#" / "A" / "B" / "C" / "D"
|
|
*/
|
|
|
|
if (!url_start) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Missing user field", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
|
|
url_main = url_start;
|
|
SKIP_LWS(url_main);
|
|
/*
|
|
* User field is the only mandatory field. The rest are optional
|
|
*/
|
|
|
|
if (*url_main == PLUS) {
|
|
/* global-phone number */
|
|
url_main++;
|
|
telUrl->user = url_main;
|
|
|
|
while ((url_main) && ((*url_main == DASH) ||
|
|
(isdigit((int)*url_main)) || (*url_main == DOT))) {
|
|
url_main++;
|
|
}
|
|
} else {
|
|
/* local-phone-number */
|
|
telUrl->user = url_main;
|
|
is_local_subscriber = TRUE;
|
|
|
|
while ((url_main) && ((is_dtmf_or_pause(url_main)) ||
|
|
(*url_main == DASH) || (isdigit((int)*url_main)) ||
|
|
(*url_main == DOT))) {
|
|
url_main++;
|
|
}
|
|
}
|
|
if (url_main) {
|
|
SKIP_LWS(url_main);
|
|
}
|
|
if ((url_main) && (!(*url_main))) {
|
|
if (!is_local_subscriber) {
|
|
return 0; /* no fields to parse */
|
|
} else {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"local-phone-number must have area-specifier",
|
|
fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
}
|
|
if (url_main) {
|
|
if ((*url_main) && (*url_main == SEMI_COLON)) {
|
|
*url_main++ = 0; /* skip separator */
|
|
} else {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Need ';' before parameters", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
}
|
|
|
|
while (url_main) {
|
|
SKIP_LWS(url_main);
|
|
if (cpr_strncasecmp(url_main, "isub=", 5) == 0) {
|
|
url_main += 5;
|
|
SKIP_LWS(url_main);
|
|
telUrl->isdn_subaddr = url_main;
|
|
} else if (cpr_strncasecmp(url_main, "postd=", 6) == 0) {
|
|
url_main += 6;
|
|
SKIP_LWS(url_main);
|
|
telUrl->post_dial = url_main;
|
|
} else if (cpr_strncasecmp(url_main, "phone-context", 13) == 0) {
|
|
url_main += 13;
|
|
SKIP_LWS(url_main);
|
|
if (*url_main != EQUAL_SIGN) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad syntax in phone_context field", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
*url_main++ = 0; /* skip "=" */
|
|
SKIP_LWS(url_main);
|
|
genUrl->phone_context = url_main;
|
|
} else if (cpr_strncasecmp(url_main, "tsp", 3) == 0) {
|
|
url_main += 3;
|
|
SKIP_LWS(url_main);
|
|
if (*url_main != EQUAL_SIGN) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad syntax in service-provider field", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
*url_main++ = 0; /* skip "=" */
|
|
SKIP_LWS(url_main);
|
|
telUrl->unparsed_tsp = url_main;
|
|
} else {
|
|
/* future extension */
|
|
if (telUrl->future_ext) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Only one future extension allowed", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
SKIP_LWS(url_main);
|
|
url_main = strchr(url_main, EQUAL_SIGN);
|
|
if (!url_main) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Bad field value", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
} else {
|
|
*url_main++ = 0;
|
|
SKIP_LWS(url_main);
|
|
telUrl->future_ext = url_main;
|
|
}
|
|
}
|
|
url_main = strchr(url_main, SEMI_COLON);
|
|
if (url_main) {
|
|
*url_main++ = 0; /* skip and zero separator */
|
|
}
|
|
}
|
|
if ((is_local_subscriber) && (!genUrl->phone_context)) {
|
|
/* local-phone-number must have area-specifier */
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"local-phone-number requires area-specifier", fname);
|
|
return PARSE_ERR_SYNTAX;
|
|
}
|
|
return 0; /* Success */
|
|
}
|
|
|
|
/*
|
|
* Parses a URL
|
|
* (eg. "sip:14085266593@sip.cisco.com;transport=UDP;user=phone")
|
|
* (eg. "tel:+1-555-555-5555")
|
|
* into a usable genUrl_t struct. Memory is created by this function.
|
|
* To free first free internal members(sippmh_genurl_free(genUrl)).
|
|
* Returns NULL if there is a parse error or malloc fails. Components/tokens
|
|
* not found in the URL are set to NULL, unless they have a default value
|
|
* defined by the protocol ( for example port 5060 is default)
|
|
* Parse error will set parse_errno variable to indicate the error.
|
|
* Expects a NULL terminated string in url.
|
|
* See comment block before parseSipUrl() and parseTelUrl()
|
|
* for some other details.
|
|
*
|
|
* url - Pointer to URL to be parsed
|
|
* dup_flag - Should this URL be duplicated and modified ??
|
|
*/
|
|
genUrl_t *
|
|
sippmh_parse_url (char *url, boolean dup_flag)
|
|
{
|
|
genUrl_t *genUrl;
|
|
char *url_main;
|
|
uint16_t i;
|
|
uint16_t skipValue;
|
|
|
|
if (url == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
genUrl = (genUrl_t *) cpr_calloc(1, sizeof(genUrl_t));
|
|
if (genUrl == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Ensure clean other params pointers
|
|
*/
|
|
i = 0;
|
|
while (i < SIP_MAX_LOCATIONS) {
|
|
genUrl->other_params[i] = NULL;
|
|
i++;
|
|
}
|
|
|
|
if (dup_flag) {
|
|
url_main = cpr_strdup(url);
|
|
if (!url_main) {
|
|
cpr_free(genUrl);
|
|
return NULL;
|
|
}
|
|
genUrl->str_start = url_main;
|
|
} else {
|
|
url_main = url;
|
|
}
|
|
|
|
SKIP_LWS(url_main);
|
|
if (!cpr_strncasecmp(url_main, "sips", 4)) {
|
|
genUrl->schema = URL_TYPE_SIP;
|
|
genUrl->sips = TRUE;
|
|
skipValue = 4;
|
|
} else if (!cpr_strncasecmp(url_main, "sip", 3)) {
|
|
genUrl->schema = URL_TYPE_SIP;
|
|
skipValue = 3;
|
|
} else if (!cpr_strncasecmp(url_main, "tel", 3)) {
|
|
genUrl->schema = URL_TYPE_TEL;
|
|
skipValue = 3;
|
|
} else if (!cpr_strncasecmp(url_main, "cid", 3)) {
|
|
genUrl->schema = URL_TYPE_CID;
|
|
skipValue = 3;
|
|
} else {
|
|
genUrl->schema = URL_TYPE_UNKNOWN;
|
|
skipValue = 0;
|
|
}
|
|
url_main += skipValue; /* skip "sip", "sips", cid, or "tel" */
|
|
if (*url_main != COLON) {
|
|
if (dup_flag) {
|
|
cpr_free(genUrl->str_start);
|
|
}
|
|
cpr_free(genUrl);
|
|
return NULL;
|
|
}
|
|
*url_main++ = 0; /* terminate ":" for schema */
|
|
switch (genUrl->schema) {
|
|
case URL_TYPE_CID:
|
|
case URL_TYPE_SIP:
|
|
genUrl->u.sipUrl = (sipUrl_t *) cpr_calloc(1, sizeof(sipUrl_t));
|
|
if (!genUrl->u.sipUrl) {
|
|
if (dup_flag) {
|
|
cpr_free(genUrl->str_start);
|
|
}
|
|
cpr_free(genUrl);
|
|
return NULL;
|
|
}
|
|
parse_errno = parseSipUrl(url_main, genUrl);
|
|
if (parse_errno == 0) {
|
|
//remove_visual_separators(genUrl);
|
|
if (genUrl->u.sipUrl->port == 0) {
|
|
/* If no port specified in SIP-URL, then use 5060 */
|
|
genUrl->u.sipUrl->port = SIP_WELL_KNOWN_PORT;
|
|
genUrl->u.sipUrl->port_present = FALSE;
|
|
}
|
|
return genUrl;
|
|
}
|
|
/* For syntax error, we would already have printed the error msg */
|
|
if (parse_errno != PARSE_ERR_SYNTAX) {
|
|
if (parse_errno == PARSE_ERR_NO_MEMORY) {
|
|
parse_errno = 0; /* Not really a parse error (out of mem) */
|
|
} else {
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], "sippmh_parse_url");
|
|
}
|
|
}
|
|
sippmh_genurl_free(genUrl);
|
|
return NULL;
|
|
case URL_TYPE_TEL:
|
|
genUrl->u.telUrl = (telUrl_t *) cpr_calloc(1, sizeof(telUrl_t));
|
|
if (!genUrl->u.telUrl) {
|
|
if (dup_flag) {
|
|
cpr_free(genUrl->str_start);
|
|
}
|
|
cpr_free(genUrl);
|
|
return NULL;
|
|
}
|
|
parse_errno = parseTelUrl(url_main, genUrl);
|
|
if (parse_errno == 0) {
|
|
//remove_visual_separators(genUrl);
|
|
return genUrl;
|
|
}
|
|
/* For syntax error, we would already have printed the error msg */
|
|
if (parse_errno != PARSE_ERR_SYNTAX) {
|
|
if (parse_errno == PARSE_ERR_NO_MEMORY) {
|
|
parse_errno = 0; /* Not really a parse error (out of mem) */
|
|
} else {
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], "sippmh_parse_url");
|
|
}
|
|
}
|
|
sippmh_genurl_free(genUrl);
|
|
return NULL;
|
|
default:
|
|
sippmh_genurl_free(genUrl);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* If this is a SIP URL, free the sipUrl_t. If this is
|
|
* a telUrl_t free the telUrl_t. Also, free memory allocated by creation
|
|
* of the genUrl_t then free the genUrl_t.
|
|
*/
|
|
void
|
|
sippmh_genurl_free (genUrl_t *genUrl)
|
|
{
|
|
uint16_t i;
|
|
|
|
if (!genUrl) {
|
|
return;
|
|
}
|
|
if (genUrl->str_start) {
|
|
cpr_free(genUrl->str_start);
|
|
}
|
|
if ((genUrl->schema == URL_TYPE_SIP) ||
|
|
(genUrl->schema == URL_TYPE_CID)) {
|
|
if (genUrl->u.sipUrl->headerp) {
|
|
cpr_free(genUrl->u.sipUrl->headerp);
|
|
}
|
|
cpr_free(genUrl->u.sipUrl);
|
|
} else if (genUrl->schema == URL_TYPE_TEL) {
|
|
cpr_free(genUrl->u.telUrl);
|
|
}
|
|
|
|
/* Free any "other" parameters that were saved */
|
|
i = 0;
|
|
while (i < SIP_MAX_LOCATIONS) {
|
|
if (genUrl->other_params[i] != NULL) {
|
|
cpr_free(genUrl->other_params[i]);
|
|
}
|
|
i++;
|
|
}
|
|
|
|
cpr_free(genUrl);
|
|
}
|
|
|
|
/* Parse display name:
|
|
* "Mr. Watson" <sip:watson@bell-telephone.com>
|
|
* "Mr. Watson" is the display-name
|
|
* Return pointer to the '<'
|
|
*/
|
|
|
|
static char *
|
|
parse_display_name (char *ptr)
|
|
{
|
|
static const char fname[] = "parse_display_name";
|
|
|
|
while (*ptr) {
|
|
if (*ptr == DOUBLE_QUOTE && *(ptr - 1) != ESCAPE_CHAR) {
|
|
/* We reached end of quoted-string, mark end of display name */
|
|
*ptr++ = 0;
|
|
SKIP_LWS(ptr); /* Skip spaces till '<' */
|
|
/* ptr should now be pointing to '<' */
|
|
if (*ptr != LEFT_ANGULAR_BRACKET) {
|
|
parse_errno = PARSE_ERR_UNMATCHED_BRACKET;
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname);
|
|
|
|
return NULL;
|
|
}
|
|
return ptr;
|
|
}
|
|
ptr++;
|
|
}
|
|
|
|
parse_errno = PARSE_ERR_UNTERMINATED_STRING;
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname);
|
|
|
|
return NULL; /* Unmatched " */
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse the name-addr form or the addr-spec form. These parse entities can
|
|
* be present in the From, To and Contact headers. So it is called either
|
|
* by sippmh_parse_contact() or sippmh_parse_from_or_to().
|
|
*
|
|
* input_loc_ptr - Pointer to the string to be parsed
|
|
* dup_flag - Should the input string be duplicated before parsing
|
|
* more_ptr - Return pointer to the next
|
|
* Tokens are extracted in place, i.e. we do not do a strdup for every token.
|
|
* We have on string and the output struct has pointers pointing to various
|
|
* tokens in the string.
|
|
* Note that this routine relies on the caller setting parse_errno to 0
|
|
* before calling.
|
|
*
|
|
* input_loc_ptr - Pointer to input string to be parsed
|
|
* start_ptr - Pointer to beginning of the string, which will be used for
|
|
* cleanup.
|
|
* dup_flag - Should the input string be duplicated before parsing
|
|
* name_addr_only_flag - If TRUE, means only name-addr form is allowed
|
|
* more_ptr - Return pointer to start of remaining string ( string that
|
|
* remains after parsing name-addr or addr-spec
|
|
*
|
|
* Output: A pointer to a valid SIP location OR
|
|
* NULL (in case of error)
|
|
*
|
|
*/
|
|
sipLocation_t *
|
|
sippmh_parse_nameaddr_or_addrspec (char *input_loc_ptr,
|
|
char *start_ptr,
|
|
boolean dup_flag,
|
|
boolean name_addr_only_flag,
|
|
char **more_ptr)
|
|
{
|
|
const char *fname = "sippmh_parse_nameaddr_or_addrspec";
|
|
char *addr_param;
|
|
char *loc_ptr, *addr_spec, *left_bracket;
|
|
sipLocation_t *sipLoc = NULL;
|
|
char *right_bracket = NULL;
|
|
char save_ch = 0;
|
|
char *displayNameStart;
|
|
|
|
*more_ptr = NULL;
|
|
|
|
/*
|
|
* name-addr = [ display-name ] "<" addr-spec ">"
|
|
* addr-spec = SIP-URL | URI
|
|
* display-name = *token | quoted-string
|
|
*/
|
|
if (dup_flag) {
|
|
/* Duplicate the string and work with it */
|
|
start_ptr = loc_ptr = cpr_strdup(input_loc_ptr);
|
|
if (loc_ptr == NULL) {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
loc_ptr = input_loc_ptr;
|
|
}
|
|
|
|
if (*loc_ptr == DOUBLE_QUOTE) {
|
|
displayNameStart = loc_ptr + 1;
|
|
left_bracket = parse_display_name(loc_ptr + 1);
|
|
if (left_bracket == NULL) {
|
|
/* Could not find matching " or reached end of string or could not
|
|
* find <
|
|
*/
|
|
if (dup_flag) {
|
|
cpr_free(loc_ptr);
|
|
}
|
|
/* parse_display_name has already set the parse_errno */
|
|
|
|
return NULL;
|
|
}
|
|
} else {
|
|
|
|
displayNameStart = loc_ptr;
|
|
/* Either we have token(s) preceding '<' or start of addr-spec */
|
|
left_bracket = strpbrk(loc_ptr, ",<");
|
|
if (left_bracket) {
|
|
if (*left_bracket == COMMA) {
|
|
*left_bracket = 0;
|
|
*more_ptr = left_bracket;
|
|
left_bracket = NULL;
|
|
save_ch = COMMA;
|
|
}
|
|
} else {
|
|
*more_ptr = NULL;
|
|
}
|
|
}
|
|
|
|
sipLoc = (sipLocation_t *) cpr_calloc(1, sizeof(sipLocation_t));
|
|
if (sipLoc == NULL) {
|
|
if (dup_flag) {
|
|
cpr_free(loc_ptr);
|
|
}
|
|
return NULL;
|
|
}
|
|
sipLoc->loc_start = start_ptr; /* Save pointer to start of allocated mem */
|
|
|
|
if (left_bracket) {
|
|
/* This is a name-addr form */
|
|
|
|
*left_bracket = 0; /* Terminate the display-name portion */
|
|
sipLoc->name = displayNameStart;
|
|
addr_spec = left_bracket + 1;
|
|
right_bracket = strchr(addr_spec, RIGHT_ANGULAR_BRACKET);
|
|
if (right_bracket == NULL) {
|
|
if (dup_flag) {
|
|
cpr_free(loc_ptr);
|
|
}
|
|
cpr_free(sipLoc);
|
|
parse_errno = PARSE_ERR_UNMATCHED_BRACKET;
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno],
|
|
"sippmh_parse_nameaddr_or_addrspec");
|
|
return NULL;
|
|
}
|
|
|
|
/* Terminate the addr_spec at the > */
|
|
*right_bracket++ = 0;
|
|
|
|
/* Look for the next non-whitespace character */
|
|
SKIP_LWS(right_bracket);
|
|
*more_ptr = right_bracket;
|
|
} else {
|
|
|
|
/* This is addr-spec format */
|
|
|
|
if (name_addr_only_flag) {
|
|
if (dup_flag) {
|
|
cpr_free(loc_ptr);
|
|
}
|
|
cpr_free(sipLoc);
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\n%s: Bad name-addr format", fname);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
addr_spec = loc_ptr;
|
|
/* In addr-spec form, it is illegal to have URL special
|
|
* chars such as SEMI_COLON, QUESTION MARK
|
|
*/
|
|
addr_param = addr_spec;
|
|
while (*addr_param) {
|
|
if (*addr_param == QUESTION_MARK || *addr_param == SEMI_COLON) {
|
|
|
|
if ((save_ch != 0) && *more_ptr) {
|
|
/* restore the saved char mostly COMMA */
|
|
**more_ptr = save_ch;
|
|
}
|
|
|
|
save_ch = *addr_param;
|
|
*more_ptr = addr_param;
|
|
|
|
/* Terminate the addr-spec form at Special char */
|
|
*addr_param = 0;
|
|
break;
|
|
}
|
|
addr_param++;
|
|
}
|
|
}
|
|
|
|
sipLoc->genUrl = sippmh_parse_url(addr_spec, FALSE);
|
|
if (sipLoc->genUrl == NULL) {
|
|
if (dup_flag) {
|
|
cpr_free(loc_ptr);
|
|
}
|
|
cpr_free(sipLoc);
|
|
/* sippmh_parse_url would have set the parse_errno */
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* There is more stuff to follow since *more_ptr is not NULL
|
|
* replace the special chars (such as ? or ;) in original string
|
|
*/
|
|
if ((save_ch != 0) && (*more_ptr != NULL)) {
|
|
**more_ptr = save_ch;
|
|
}
|
|
return sipLoc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Validate the tag. The tag is composed of tokens. The tag_ptr
|
|
* points to the start of the tag and a list of semicolon
|
|
* separated addr-extensions or just the tag itself.
|
|
*
|
|
* Returns 0 (no error) if the tag is valid, else
|
|
* returns PARSE_ERR_SYNTAX.
|
|
*
|
|
*/
|
|
static int
|
|
validate_tag (sipLocation_t *sipLoc, char *tag_ptr)
|
|
{
|
|
static const char fname[] = "validate_tag";
|
|
int ret_val = 0;
|
|
char *term_ptr;
|
|
|
|
/* Skip "tag=", points to value */
|
|
tag_ptr += 4;
|
|
SKIP_LWS(tag_ptr);
|
|
if (*tag_ptr == 0) {
|
|
ret_val = PARSE_ERR_UNEXPECTED_EOS;
|
|
CCSIP_ERR_DEBUG {
|
|
buginf(parse_errors[ret_val], fname);
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
sipLoc->tag = tag_ptr;
|
|
|
|
/* Walk the string until we encounter a non-token
|
|
* character. The only valid character at the end of
|
|
* string is either semicolon or end-of-line.
|
|
*/
|
|
|
|
SKIP_SIP_TOKEN(tag_ptr);
|
|
term_ptr = tag_ptr;
|
|
/* skip trailing spaces */
|
|
SKIP_LWS(tag_ptr);
|
|
if ((*tag_ptr != SEMI_COLON) && (*tag_ptr != 0)) {
|
|
ret_val = PARSE_ERR_SYNTAX;
|
|
CCSIP_ERR_DEBUG {
|
|
buginf(parse_errors[ret_val], fname, tag_ptr);
|
|
}
|
|
} else {
|
|
/* terminate the tag */
|
|
*term_ptr = 0;
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
|
|
sipLocation_t *
|
|
sippmh_parse_from_or_to (char *input_loc_ptr, boolean dup_flag)
|
|
{
|
|
static const char fname[] = "sippmh_parse_from_or_to";
|
|
char *more_ptr;
|
|
sipLocation_t *sipLoc;
|
|
boolean tag_found = FALSE;
|
|
char *lasts = NULL;
|
|
|
|
/*
|
|
* From = ( "From" | "f" ) ":" ( name-addr | addr-spec )
|
|
* *( ";" addr-params )
|
|
* addr-params = tag-param | addr-extension
|
|
* tag-param = "tag=" token
|
|
* addr-exten = token = ["=" (token | quoted-string)]
|
|
*
|
|
* NOTE: Only the tag parameter in the addr-params is relevant to
|
|
* SIP so we skip all addr-extensions when parsing addr-params.
|
|
*/
|
|
|
|
parse_errno = 0;
|
|
more_ptr = NULL;
|
|
sipLoc = sippmh_parse_nameaddr_or_addrspec(input_loc_ptr, input_loc_ptr,
|
|
dup_flag, FALSE, &more_ptr);
|
|
|
|
if (sipLoc) { /* valid sipLocation */
|
|
|
|
if (more_ptr == NULL) {
|
|
return sipLoc;
|
|
}
|
|
|
|
/* initialize the tag */
|
|
sipLoc->tag = NULL;
|
|
if (*more_ptr == SEMI_COLON) {
|
|
*more_ptr++ = 0;
|
|
more_ptr = PL_strtok_r(more_ptr, ";", &lasts);
|
|
/* if we had a ; without any addr-params */
|
|
if (more_ptr == NULL) {
|
|
parse_errno = PARSE_ERR_UNEXPECTED_EOS;
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname);
|
|
} else {
|
|
/* parse the tag but skip any addr-extensions */
|
|
while (more_ptr && tag_found == FALSE) {
|
|
SKIP_LWS(more_ptr);
|
|
if (strncmp(more_ptr, "tag=", 4) == 0) {
|
|
tag_found = TRUE;
|
|
parse_errno = validate_tag(sipLoc, more_ptr);
|
|
} else {
|
|
more_ptr = PL_strtok_r(NULL, ";", &lasts);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (*more_ptr) {
|
|
parse_errno = PARSE_ERR_SYNTAX;
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname, more_ptr);
|
|
}
|
|
}
|
|
|
|
if (parse_errno) {
|
|
sippmh_free_location(sipLoc);
|
|
sipLoc = NULL;
|
|
}
|
|
|
|
return sipLoc;
|
|
}
|
|
|
|
void
|
|
sippmh_free_location (sipLocation_t * sipLoc)
|
|
{
|
|
if (sipLoc) {
|
|
cpr_free(sipLoc->loc_start);
|
|
/* make sure to free contents in genUrl_t. */
|
|
sippmh_genurl_free(sipLoc->genUrl);
|
|
cpr_free(sipLoc);
|
|
}
|
|
}
|
|
|
|
static char *
|
|
sippmh_parse_contact_params (char *params, sipContactParams_t *contact_params)
|
|
{
|
|
char *param_value;
|
|
char *contact_other_param = NULL;
|
|
boolean good_params;
|
|
boolean good_qval;
|
|
char tmp_char;
|
|
|
|
|
|
/*
|
|
* contact-params = "q" "=" qvalue
|
|
* | "action" "=" "proxy" | "redirect"
|
|
* | "expires" "=" delta-seconds | <"> SIP-date <">
|
|
* | extension-attribute
|
|
*
|
|
* params is pointing to first character after ';'
|
|
*/
|
|
SKIP_LWS(params);
|
|
if (*params == 0) {
|
|
return params;
|
|
}
|
|
while (1) {
|
|
good_params = FALSE;
|
|
|
|
/* Parse qval parameter */
|
|
if (*params == 'q' || *params == 'Q') {
|
|
params++;
|
|
SKIP_LWS(params);
|
|
if (*params == EQUAL_SIGN) {
|
|
params++;
|
|
SKIP_LWS(params);
|
|
if (*params) {
|
|
param_value = params;
|
|
good_qval = TRUE;
|
|
tmp_char = *params;
|
|
/* the qval must be in one of these forms */
|
|
if ((!strncmp(params, "0", 1)) ||
|
|
(!strncmp(params, "1", 1))) {
|
|
params++; /* move up to "." if present */
|
|
if (*params == DOT) {
|
|
params++;
|
|
if (isdigit((int) *params)) {
|
|
while (isdigit((int) *params)) {
|
|
if ((tmp_char == '1') &&
|
|
(*params != '0')) {
|
|
good_qval = FALSE;
|
|
break;
|
|
}
|
|
params++;
|
|
}
|
|
} else {
|
|
good_qval = FALSE;
|
|
}
|
|
} else if (((*params)) && ((*params != SPACE) &&
|
|
(*params != SEMI_COLON) &&
|
|
(*params != TAB))) {
|
|
good_qval = FALSE;
|
|
}
|
|
} else {
|
|
good_qval = FALSE;
|
|
}
|
|
if (good_qval) {
|
|
good_params = TRUE;
|
|
contact_params->qval = param_value;
|
|
if (*params == SPACE || *params == TAB) {
|
|
*params++ = 0; /* Terminate the qval */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Parse action paramater */
|
|
} else if (cpr_strncasecmp(params, "action", 6) == 0) {
|
|
params += 6;
|
|
SKIP_LWS(params);
|
|
if (*params == EQUAL_SIGN) {
|
|
params++;
|
|
SKIP_LWS(params);
|
|
if (cpr_strncasecmp(params, "proxy", 5) == 0) {
|
|
contact_params->action = PROXY;
|
|
params += 5;
|
|
good_params = TRUE;
|
|
} else if (cpr_strncasecmp(params, "redirect", 8) == 0) {
|
|
contact_params->action = REDIRECT;
|
|
params += 8;
|
|
good_params = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Parse expires paramater */
|
|
} else if (cpr_strncasecmp(params, "expires", 7) == 0) {
|
|
params += 7;
|
|
SKIP_LWS(params);
|
|
if (*params == EQUAL_SIGN) {
|
|
params++;
|
|
SKIP_LWS(params);
|
|
if (*params) {
|
|
char save_ch;
|
|
boolean is_int = FALSE;
|
|
|
|
/* Expires can be given in delta seconds or date format.
|
|
* If date is used, the date must be enclosed in quotes.
|
|
* IOS does not use this parameter, but the SIP phones
|
|
* do.
|
|
*/
|
|
contact_params->expires_gmt = '\0';
|
|
good_params = TRUE;
|
|
param_value = params;
|
|
while (isdigit((int) *params)) {
|
|
params++;
|
|
is_int = TRUE;
|
|
}
|
|
if (is_int == TRUE) {
|
|
save_ch = *params;
|
|
*params = 0; /* terminate the string for strtoul */
|
|
contact_params->expires = strtoul(param_value, NULL, 10);
|
|
*params = save_ch;
|
|
} else {
|
|
/* expires may be in SIP-date format */
|
|
char *gmt_str;
|
|
|
|
if (*params == DOUBLE_QUOTE) { /* skip the quote */
|
|
params++;
|
|
}
|
|
if ((gmt_str = strstr(params, "GMT")) != NULL) {
|
|
contact_params->expires_gmt = params;
|
|
params = gmt_str + 3;
|
|
contact_params->expires = 0;
|
|
|
|
if (*params == SPACE || *params == TAB || *params == DOUBLE_QUOTE) {
|
|
*params++ = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Parse x-cisco-newreg */
|
|
} else if (cpr_strncasecmp(params, REQ_CONT_PARAM_CISCO_NEWREG,
|
|
sizeof(REQ_CONT_PARAM_CISCO_NEWREG) - 1) == 0) {
|
|
/*
|
|
* x-cisco-newreg in the contact is set by CCM in the 200 OK
|
|
* response to the 1st. line registration message as information
|
|
* only to the endpoint that its registration is a new
|
|
* registration to the CCM.
|
|
*/
|
|
|
|
/*
|
|
* Move parameter pointer pass this parm (-1 is to exclude
|
|
* null terminating character of the parameter name).
|
|
*/
|
|
params += sizeof(REQ_CONT_PARAM_CISCO_NEWREG) - 1;
|
|
good_params = TRUE;
|
|
contact_params->flags |= SIP_CONTACT_PARM_X_CISCO_NEWREG;
|
|
|
|
/* Parse contact-extension paramater */
|
|
} else {
|
|
if (params) {
|
|
params = parse_other_param(params, &contact_other_param);
|
|
if (params) {
|
|
good_params = TRUE;
|
|
/*
|
|
* Contact extensions are not supported. Throw away
|
|
* any that were just parsed.
|
|
*/
|
|
if (contact_other_param != NULL) {
|
|
cpr_free(contact_other_param);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (good_params == FALSE) {
|
|
parse_errno = PARSE_ERR_SYNTAX;
|
|
return params;
|
|
}
|
|
SKIP_LWS(params);
|
|
if (*params == SEMI_COLON) {
|
|
/* More parameters follow */
|
|
*params++ = 0;
|
|
SKIP_LWS(params);
|
|
} else {
|
|
/* Either we have encountered end of string or start of next
|
|
* location in this contact header.
|
|
*/
|
|
return params;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Parses limit parameter for CC-Diversion or CC-Redirect
|
|
* header. The parsed value is in sipDiversion_t structure.
|
|
* Returns pointer to remaining characters in the buffer, if fails NULL
|
|
*/
|
|
static char *
|
|
sippmh_parse_limit_params (char *limit_t, sipDiversion_t *sipdiversion)
|
|
{
|
|
boolean params_good = FALSE;
|
|
char save_ch;
|
|
char *param_value = NULL;
|
|
int digit_count = 0;
|
|
|
|
if ((limit_t == NULL) || (sipdiversion == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
SKIP_LWS(limit_t);
|
|
if (*limit_t == EQUAL_SIGN) {
|
|
limit_t++;
|
|
SKIP_LWS(limit_t);
|
|
if (*limit_t) {
|
|
params_good = TRUE;
|
|
param_value = limit_t;
|
|
while (isdigit((int) *limit_t)) {
|
|
digit_count++;
|
|
limit_t++;
|
|
}
|
|
if (digit_count > 2) {
|
|
/* More than two digits */
|
|
params_good = FALSE;
|
|
return NULL;
|
|
}
|
|
digit_count = 0;
|
|
save_ch = *limit_t;
|
|
*limit_t = 0;
|
|
sipdiversion->limit = strtoul(param_value, NULL, 10);
|
|
*limit_t = save_ch;
|
|
}
|
|
} else {
|
|
/* Equal to sign missing */
|
|
params_good = FALSE;
|
|
}
|
|
|
|
if (params_good) {
|
|
return limit_t;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/* Parses counter parameter for CC-Diversion or CC-Redirect
|
|
* header. The parsed value is in sipDiversion_t structure.
|
|
* Returns pointer to remaining characters in the buffer, if fails NULL
|
|
*/
|
|
static char *
|
|
sippmh_parse_counter_params (char *counter_t, sipDiversion_t *sipdiversion)
|
|
{
|
|
boolean params_good = FALSE;
|
|
char save_ch;
|
|
char *param_value = NULL;
|
|
int digit_count = 0;
|
|
|
|
if ((counter_t == NULL) || (sipdiversion == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
SKIP_LWS(counter_t);
|
|
if (*counter_t == EQUAL_SIGN) {
|
|
counter_t++;
|
|
SKIP_LWS(counter_t);
|
|
if (*counter_t) {
|
|
params_good = TRUE;
|
|
param_value = counter_t;
|
|
while (isdigit((int) *counter_t)) {
|
|
digit_count++;
|
|
counter_t++;
|
|
}
|
|
if (digit_count > 2) {
|
|
/* More than two digits */
|
|
params_good = FALSE;
|
|
return NULL;
|
|
}
|
|
digit_count = 0;
|
|
save_ch = *counter_t;
|
|
*counter_t = 0;
|
|
sipdiversion->counter = strtoul(param_value, NULL, 10);
|
|
*counter_t = save_ch;
|
|
|
|
}
|
|
} else {
|
|
/* Equal to sign missing */
|
|
params_good = FALSE;
|
|
}
|
|
|
|
if (params_good) {
|
|
return counter_t;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static boolean
|
|
sippmh_parse_diversion_params (char *diversion_t, sipDiversion_t *sipdiversion)
|
|
{
|
|
|
|
while (1) {
|
|
/* Parsing diversion-reason parameter */
|
|
if (strncasecmp(diversion_t, DIVERSION_REASON, 6) == 0) {
|
|
// We don't use reason parameter, so comment out at this time.
|
|
// diversion_t += 6;
|
|
// if ((diversion_t = sippmh_parse_reason_params(diversion_t, sipdiversion)) == NULL)
|
|
// return FALSE;
|
|
/*
|
|
* Since we do not use reason parameter, search for semi-colan to mark
|
|
* start of next parameter. If no semi-colon, then we are done.
|
|
*/
|
|
diversion_t = strchr(diversion_t, SEMI_COLON);
|
|
if (diversion_t == NULL) {
|
|
// End of parameter list
|
|
return TRUE;
|
|
}
|
|
/* Parse diversion-limit parameter */
|
|
} else if (strncasecmp(diversion_t, DIVERSION_LIMIT, 5) == 0) {
|
|
diversion_t += 5;
|
|
if ((diversion_t = sippmh_parse_limit_params(diversion_t, sipdiversion)) == FALSE)
|
|
return FALSE;
|
|
|
|
/* Parse diversion-counter parameter */
|
|
} else if (strncasecmp(diversion_t, DIVERSION_COUNTER, 7) == 0) {
|
|
diversion_t += 7;
|
|
if ((diversion_t = sippmh_parse_counter_params(diversion_t, sipdiversion)) == FALSE)
|
|
return FALSE;
|
|
|
|
/* Parse diversion-privacy parameter */
|
|
} else if (strncasecmp(diversion_t, DIVERSION_PRIVACY, 7) == 0) {
|
|
diversion_t += 7;
|
|
if ((diversion_t = parse_generic_param(diversion_t, &(sipdiversion->privacy))) == NULL)
|
|
return FALSE;
|
|
|
|
/* Parse diversion-screen parameter */
|
|
} else if (strncasecmp(diversion_t, DIVERSION_SCREEN, 6) == 0) {
|
|
diversion_t += 6;
|
|
if ((diversion_t = parse_generic_param(diversion_t, &(sipdiversion->screen))) == NULL)
|
|
return FALSE;
|
|
}
|
|
|
|
SKIP_LWS(diversion_t);
|
|
if (*diversion_t == SEMI_COLON) {
|
|
/* More parameters follow */
|
|
*diversion_t++ = 0;
|
|
SKIP_LWS(diversion_t);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
sipDiversion_t *
|
|
sippmh_parse_diversion (const char *diversion, char *diversionhead)
|
|
{
|
|
char *param_ptr;
|
|
sipDiversion_t *sipdiversion;
|
|
sipLocation_t *sipLoc;
|
|
char *diversion_t, *start_ptr;
|
|
|
|
/* CC-Diversion = "CC-Diversion"
|
|
* *(diversion-params [comment])
|
|
* diversion-params = diversion-addr | diversion-reason |
|
|
* diversion-counter| diversion-limit
|
|
* diversion-addr = (name-addr | addr-spec)*(";"addr-params)
|
|
* diversion-reason = "reason"= "unknown" | "user-busy" | "no-answer" |
|
|
* "unconditional" | "deflection"| "follow-me" |
|
|
* "out-of-service" | " time-of-day" | "unavailable" |
|
|
* "do-not-disturb"
|
|
* diversion-counter = "counter" = 2*DIGIT
|
|
* diversion-limit = "limit" = 2*DIGIT
|
|
*/
|
|
|
|
/*
|
|
* Diversion = "Diversion" ":" 1# (name-addr *( ";" diversion_params ))
|
|
* diversion-params = diversion-reason | diversion-counter |
|
|
* diversion-limit | diversion-privacy |
|
|
* diversion-screen | diversion-extension
|
|
* diversion-reason = "reason" "="
|
|
* ( "unknown" | "user-busy" | "no-answer" |
|
|
* "unavailable" | "unconditional" |
|
|
* "time-of-day" | "do-not-disturb" |
|
|
* "deflection" | "follow-me" |
|
|
* "out-of-service" | "away" |
|
|
* token | quoted-string )
|
|
* diversion-counter = "counter" "=" 1*2DIGIT
|
|
* diversion-limit = "limit" "=" 1*2DIGIT
|
|
* diversion-privacy = "privacy" "=" ( "full" | "name" |
|
|
* "uri" | "off" | token | quoted-string )
|
|
* diversion-screen = "screen" "=" ( "yes" | "no" | token |
|
|
* quoted-string )
|
|
* diversion-extension = token ["=" (token | quoted-string)]
|
|
*/
|
|
|
|
sipdiversion = (sipDiversion_t *) cpr_calloc(1, sizeof(sipDiversion_t));
|
|
if (sipdiversion == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
diversion_t = cpr_strdup(diversion);
|
|
|
|
if (diversion_t == NULL) {
|
|
sippmh_free_diversion(sipdiversion);
|
|
return NULL;
|
|
}
|
|
|
|
start_ptr = diversion_t;
|
|
|
|
do {
|
|
|
|
param_ptr = NULL;
|
|
sipLoc = sippmh_parse_nameaddr_or_addrspec(diversion_t, start_ptr, FALSE, FALSE,
|
|
¶m_ptr);
|
|
if (sipLoc == NULL) {
|
|
cpr_free(start_ptr);
|
|
sippmh_free_diversion(sipdiversion);
|
|
sipdiversion = NULL;
|
|
break;
|
|
}
|
|
|
|
sipdiversion->locations = sipLoc;
|
|
|
|
if (param_ptr == NULL || *param_ptr == 0) {
|
|
/* No params */
|
|
break;
|
|
}
|
|
diversion_t = param_ptr;
|
|
if (*diversion_t == SEMI_COLON) {
|
|
/* Parsing Diversion Headers */
|
|
*diversion_t++ = 0;
|
|
|
|
if ((strncasecmp(diversionhead, SIP_HEADER_DIVERSION,
|
|
sizeof(SIP_HEADER_DIVERSION)) == 0) ||
|
|
(strncasecmp(diversionhead, SIP_HEADER_CC_DIVERSION,
|
|
sizeof(SIP_HEADER_CC_DIVERSION)) == 0)) {
|
|
|
|
if (sippmh_parse_diversion_params(diversion_t, sipdiversion) == FALSE) {
|
|
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\nsippmh_parse_diversion: syntax error in Diversion header");
|
|
}
|
|
parse_errno = PARSE_ERR_SYNTAX;
|
|
sippmh_free_diversion(sipdiversion);
|
|
sipdiversion = NULL;
|
|
break;
|
|
}
|
|
}
|
|
/* Must reach here after parsing all parameters */
|
|
break; /* break out of original do loop */
|
|
} else {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\nsippmh_parse_diversion: syntax error missing "
|
|
"semicolon in Diversion header");
|
|
}
|
|
parse_errno = PARSE_ERR_SYNTAX;
|
|
sippmh_free_diversion(sipdiversion);
|
|
sipdiversion = NULL;
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
return sipdiversion;
|
|
}
|
|
|
|
void
|
|
sippmh_free_diversion (sipDiversion_t * ccr)
|
|
{
|
|
if (ccr) {
|
|
if (ccr->locations) {
|
|
sippmh_free_location(ccr->locations);
|
|
}
|
|
cpr_free(ccr);
|
|
}
|
|
}
|
|
|
|
|
|
sipContact_t *
|
|
sippmh_parse_contact (const char *input_contact)
|
|
{
|
|
int j, k;
|
|
char *contact = NULL, *start_ptr = NULL;
|
|
char *more_ptr;
|
|
sipContact_t *sipContact;
|
|
sipContactParams_t params;
|
|
|
|
/*
|
|
* Contact = ( "Contact" | "m" ) ":"
|
|
* ("*" | (1# (( name-addr | addr-spec )
|
|
* [ *( ";" contact-params ) ] [ comment ] )))
|
|
* name-addr = [ display-name ] "<" addr-spec ">"
|
|
* addr-spec = SIP-URL | URI
|
|
* display-name = *token | quoted-string
|
|
*
|
|
* contact-params = "q" "=" qvalue
|
|
* | "action" "=" "proxy" | "redirect"
|
|
* | "expires" "=" delta-seconds | <"> SIP-date <">
|
|
* | extension-attribute
|
|
*
|
|
*/
|
|
parse_errno = 0;
|
|
contact = cpr_strdup(input_contact);
|
|
if (contact == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
sipContact = (sipContact_t *) cpr_calloc(1, sizeof(sipContact_t));
|
|
if (sipContact == NULL) {
|
|
cpr_free(contact);
|
|
return NULL;
|
|
}
|
|
|
|
memset(¶ms, 0, sizeof(sipContactParams_t));
|
|
|
|
start_ptr = contact;
|
|
|
|
do {
|
|
sipLocation_t *sipLoc;
|
|
|
|
more_ptr = NULL;
|
|
sipLoc = sippmh_parse_nameaddr_or_addrspec(contact, start_ptr, FALSE,
|
|
FALSE, &more_ptr);
|
|
if (sipLoc == NULL) {
|
|
if ((more_ptr != NULL) && (*more_ptr == COMMA)) {
|
|
/* Another location follows */
|
|
contact = more_ptr;
|
|
*contact++ = 0;
|
|
SKIP_LWS(contact);
|
|
if (sipContact->num_locations == SIP_MAX_LOCATIONS) {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\nsippmh_parse_contact: Too many location headers"
|
|
" in Contact header");
|
|
}
|
|
/* go with what we have */
|
|
break;
|
|
}
|
|
continue;
|
|
} else {
|
|
if (sipContact->num_locations == 0) {
|
|
// Free start_ptr only if we fail the first location
|
|
// For the others, it will be freed when sipContact is
|
|
// freed
|
|
cpr_free(start_ptr);
|
|
}
|
|
sippmh_free_contact(sipContact);
|
|
sipContact = NULL;
|
|
break;
|
|
}
|
|
}
|
|
if (sipContact->num_locations) {
|
|
sipLoc->loc_start = NULL;
|
|
}
|
|
if (more_ptr == NULL || *more_ptr == 0) {
|
|
/* No params, means no qval and assume lowest priority */
|
|
sipContact->locations[sipContact->num_locations++] = sipLoc;
|
|
break;
|
|
}
|
|
contact = more_ptr;
|
|
/*
|
|
* At this point, either contact is pointing to
|
|
* 1) ';' - Contact params
|
|
* 2) ',' - Beginning of next location
|
|
* 3) End of string
|
|
* 4) Any other character is syntax error
|
|
*/
|
|
|
|
j = sipContact->num_locations; /* By default insert at the end */
|
|
params.qval = "";
|
|
|
|
if (*contact == SEMI_COLON) {
|
|
/* Now parse the Contact params */
|
|
*contact++ = 0;
|
|
params.flags = 0;
|
|
contact = sippmh_parse_contact_params(contact, ¶ms);
|
|
/*
|
|
* Now, do an insertion sort on this list (qval is the key).
|
|
* Higher values of q come first.
|
|
*/
|
|
for (j = 0; j < sipContact->num_locations; ++j) {
|
|
if (strcmp(params.qval, sipContact->params[j].qval) > 0) {
|
|
for (k = sipContact->num_locations; k > j; --k) {
|
|
sipContact->locations[k] = sipContact->locations[k - 1];
|
|
sipContact->params[k] = sipContact->params[k - 1];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/* At this point j points to the entry to use */
|
|
}
|
|
|
|
sipContact->params[j] = params;
|
|
sipContact->locations[j] = sipLoc;
|
|
sipContact->num_locations++;
|
|
|
|
if (contact && *contact == COMMA) {
|
|
/* Another location follows */
|
|
*contact++ = 0;
|
|
SKIP_LWS(contact);
|
|
if (sipContact->num_locations == SIP_MAX_LOCATIONS) {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\nsippmh_parse_contact: Too many location headers"
|
|
" in Contact header");
|
|
}
|
|
/* go with what we have */
|
|
break;
|
|
}
|
|
} else {
|
|
if (contact && *contact) {
|
|
parse_errno = PARSE_ERR_SYNTAX;
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], "sippmh_parse_contact",
|
|
contact);
|
|
sippmh_free_contact(sipContact);
|
|
sipContact = NULL;
|
|
}
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
if (sipContact) {
|
|
sipContact->new_flag = TRUE;
|
|
}
|
|
return sipContact;
|
|
}
|
|
|
|
|
|
void
|
|
sippmh_free_contact (sipContact_t *sc)
|
|
{
|
|
int i;
|
|
|
|
if (sc) {
|
|
for (i = 0; i < sc->num_locations; ++i) {
|
|
if (sc->locations[i]->loc_start) {
|
|
/* Free the entire header value that we duplicated */
|
|
cpr_free(sc->locations[i]->loc_start);
|
|
}
|
|
/* Free the genUrl_t struct in the sipLocation_t */
|
|
sippmh_genurl_free(sc->locations[i]->genUrl);
|
|
/* Free the sipLocation_t struct */
|
|
cpr_free(sc->locations[i]);
|
|
}
|
|
|
|
/* Free the sipContact_t struct */
|
|
cpr_free(sc);
|
|
}
|
|
}
|
|
|
|
static boolean
|
|
parse_via_params (char *str, sipVia_t *sipVia)
|
|
{
|
|
char **ptr;
|
|
|
|
/* str is currently pointing to beginning of parameters */
|
|
|
|
while (1) {
|
|
/* To be friendly, skip leading spaces */
|
|
SKIP_LWS(str);
|
|
if (strncasecmp(str, VIA_HIDDEN, 6) == 0) {
|
|
str += 6;
|
|
sipVia->flags |= VIA_IS_HIDDEN;
|
|
ptr = NULL;
|
|
} else if (strncasecmp(str, VIA_TTL, 3) == 0) {
|
|
str += 3;
|
|
ptr = &(sipVia->ttl);
|
|
} else if (strncasecmp(str, VIA_MADDR, 5) == 0) {
|
|
str += 5;
|
|
ptr = &(sipVia->maddr);
|
|
} else if (strncasecmp(str, VIA_RECEIVED, 8) == 0) {
|
|
str += 8;
|
|
ptr = &(sipVia->recd_host);
|
|
} else if (strncasecmp(str, VIA_BRANCH, 6) == 0) {
|
|
str += 6;
|
|
ptr = &(sipVia->branch_param);
|
|
} else {
|
|
ptr = NULL;
|
|
}
|
|
if (ptr) {
|
|
if (*ptr) {
|
|
/* ptr already points to something
|
|
* -- duplicate param found */
|
|
return FALSE;
|
|
}
|
|
SKIP_LWS(str); /* Skip spaces till equal sign */
|
|
if (*str == EQUAL_SIGN) {
|
|
str++;
|
|
SKIP_LWS(str);
|
|
*ptr = str;
|
|
}
|
|
}
|
|
str = strpbrk(str, ";,");
|
|
if (str == NULL) {
|
|
if (ptr && *ptr) {
|
|
trim_right(*ptr);
|
|
}
|
|
return TRUE;
|
|
}
|
|
if (*str == COMMA) {
|
|
*str++ = 0;
|
|
SKIP_LWS(str);
|
|
sipVia->more_via = str;
|
|
if (ptr && *ptr) {
|
|
trim_right(*ptr);
|
|
}
|
|
return TRUE;
|
|
}
|
|
*str++ = 0; /* Zero ';' and advance pointer */
|
|
if (ptr && *ptr) {
|
|
trim_right(*ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
sipVia_t *
|
|
sippmh_parse_via (const char *input_via)
|
|
{
|
|
static const char fname[] = "sippmh_parse_via";
|
|
char *via;
|
|
char *separator;
|
|
sipVia_t *sipVia;
|
|
char *endptr;
|
|
char *port_num_str;
|
|
uint16_t port_num;
|
|
|
|
/*
|
|
* Via = ( "Via" | "v") ":" 1#( sent-protocol sent-by
|
|
* *( ";" via-params ) [ comment ] )
|
|
* via-params = via-hidden | via-ttl | via-maddr
|
|
* | via-received | via-branch
|
|
* via-hidden = "hidden"
|
|
* via-ttl = "ttl" "=" ttl
|
|
* via-maddr = "maddr" "=" maddr
|
|
* via-received = "received" "=" host
|
|
* via-branch = "branch" "=" token
|
|
* sent-protocol = protocol-name "/" protocol-version "/" transport
|
|
* protocol-name = "SIP" | token
|
|
* protocol-version = token
|
|
* transport = "UDP" | "TCP" | "TLS" | token
|
|
* sent-by = ( host [ ":" port ] ) | ( concealed-host )
|
|
* concealed-host = token
|
|
* ttl = 1*3DIGIT ; 0 to 255
|
|
*/
|
|
|
|
parse_errno = PARSE_ERR_SYNTAX;
|
|
if (input_via == NULL) {
|
|
return NULL;
|
|
}
|
|
sipVia = NULL;
|
|
via = NULL;
|
|
SKIP_LWS(input_via);
|
|
do {
|
|
if (strncasecmp(input_via, "SIP", 3)) {
|
|
/* Unknown protocol-name */
|
|
break;
|
|
}
|
|
input_via += 3;
|
|
|
|
SKIP_LWS(input_via);
|
|
if (*input_via != '/') {
|
|
break;
|
|
}
|
|
input_via++; /* Skip '/' */
|
|
|
|
SKIP_LWS(input_via);
|
|
/* input_via should now be pointing to version */
|
|
|
|
/* Duplicate the string and work with it */
|
|
via = cpr_strdup(input_via);
|
|
if (via == NULL) {
|
|
parse_errno = 0;
|
|
return NULL;
|
|
}
|
|
/* Allocate sipVia_t struct */
|
|
sipVia = (sipVia_t *) cpr_calloc(1, sizeof(sipVia_t));
|
|
if (sipVia == NULL) {
|
|
parse_errno = 0;
|
|
cpr_free(via);
|
|
return NULL;
|
|
}
|
|
|
|
/* version also points to the start of memory allocated */
|
|
sipVia->version = via;
|
|
if (strncmp(via, "2.0", 3)) {
|
|
break;
|
|
}
|
|
via += 3; /* Go past version */
|
|
|
|
SKIP_LWS(via);
|
|
if (*via != '/') {
|
|
break;
|
|
}
|
|
*via++ = 0; /* Terminate version string and go past '/' */
|
|
|
|
SKIP_LWS(via); /* Point to transport */
|
|
sipVia->transport = via;
|
|
if (strncasecmp(via, "UDP", 3) && strncasecmp(via, "TCP", 3) &&
|
|
strncasecmp(via, "TLS", 3)) {
|
|
/* Invalid transport */
|
|
break;
|
|
}
|
|
via += 3; /* Go past the transport string */
|
|
*via++ = 0; /* Terminate transport string and advance pointer */
|
|
|
|
|
|
/* Skip blanks */
|
|
SKIP_LWS(via);
|
|
if (*via == 0) {
|
|
parse_errno = PARSE_ERR_UNEXPECTED_EOS;
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname);
|
|
break;
|
|
}
|
|
|
|
parse_errno = 0;
|
|
|
|
} while (0);
|
|
|
|
if (parse_errno) {
|
|
if (parse_errno == PARSE_ERR_SYNTAX) {
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno], fname, via ? via : input_via);
|
|
}
|
|
sippmh_free_via(sipVia);
|
|
return NULL;
|
|
}
|
|
|
|
port_num_str = SIP_WELL_KNOWN_PORT_STR;
|
|
sipVia->host = via;
|
|
/*
|
|
* Either we can have a ':' followed by port
|
|
* or start of parameters signalled by ';'
|
|
* or end of this Via indicated by ','
|
|
*/
|
|
if (*via == '[') {
|
|
/* IPv6 address */
|
|
sipVia->host = via;
|
|
sipVia->is_ipv6 = TRUE;
|
|
separator = strpbrk(via, "]");
|
|
|
|
if (separator && *separator == ']') {
|
|
separator++;
|
|
}
|
|
} else {
|
|
|
|
separator = via;
|
|
}
|
|
|
|
if (separator) {
|
|
separator = strpbrk(separator, ":;,");
|
|
}
|
|
if (separator) {
|
|
if (*separator == COLON) {
|
|
/* Port number follows */
|
|
*separator++ = 0;
|
|
port_num_str = separator;
|
|
separator = strpbrk(separator, ";,");
|
|
}
|
|
if (separator) {
|
|
if (*separator == SEMI_COLON) {
|
|
*separator++ = 0;
|
|
/* Parameters follow */
|
|
if (parse_via_params(separator, sipVia) == FALSE) {
|
|
CCSIP_ERR_DEBUG
|
|
buginf("%s: Duplicate params in Via\n", fname);
|
|
sippmh_free_via(sipVia);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
*separator++ = 0;
|
|
SKIP_LWS(separator);
|
|
sipVia->more_via = separator;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Validate the host portion */
|
|
if ((sipSPI_validate_ip_addr_name(sipVia->host) == FALSE)) {
|
|
CCSIP_ERR_DEBUG buginf("\n%s: Invalid host in Via", fname);
|
|
sippmh_free_via(sipVia);
|
|
return NULL;
|
|
}
|
|
|
|
/* Fix host portion */
|
|
trim_right(sipVia->host);
|
|
/* Fix IPv6 host */
|
|
trim_ipv6_host(sipVia->host);
|
|
|
|
/* validate port portion */
|
|
port_num = (uint16_t) strtol(port_num_str, &endptr, 10);
|
|
|
|
/*
|
|
* There may be trailing white space in the port portion. So, ignore them.
|
|
*/
|
|
SKIP_LWS(endptr);
|
|
if (*endptr || port_num == 0) {
|
|
sippmh_free_via(sipVia);
|
|
CCSIP_ERR_DEBUG buginf("\n%s: Invalid port number in Via", fname);
|
|
return NULL;
|
|
}
|
|
sipVia->remote_port = port_num;
|
|
sipVia->recd_host = sipVia->host;
|
|
return sipVia;
|
|
}
|
|
|
|
void
|
|
sippmh_free_via (sipVia_t * sipVia)
|
|
{
|
|
if (sipVia) {
|
|
cpr_free(sipVia->version);
|
|
cpr_free(sipVia);
|
|
}
|
|
}
|
|
|
|
|
|
sipCseq_t *
|
|
sippmh_parse_cseq (const char *cseq)
|
|
{
|
|
char *lasts = NULL;
|
|
/*
|
|
* CSeq = "CSeq" ":" 1*DIGIT Method
|
|
*/
|
|
sipCseq_t *sipCseq = (sipCseq_t *) cpr_calloc(1, sizeof(sipCseq_t));
|
|
|
|
if (!sipCseq) {
|
|
return (NULL);
|
|
}
|
|
|
|
/* if ',' is present then more than 1 cseq are present */
|
|
if (strchr(cseq, ',')) {
|
|
cpr_free(sipCseq);
|
|
return (NULL);
|
|
}
|
|
|
|
if (cseq) {
|
|
char *mycseq = cpr_strdup(cseq);
|
|
|
|
sipCseq->method = sipMethodInvalid;
|
|
|
|
if (mycseq) {
|
|
char *this_token = PL_strtok_r(mycseq, " ", &lasts);
|
|
|
|
if (this_token) {
|
|
sipCseq->number = strtoul(this_token, NULL, 10);
|
|
|
|
/* make sure the CSeq value is not > 2^^31 */
|
|
if (sipCseq->number >= TWO_POWER_31) {
|
|
cpr_free(sipCseq);
|
|
cpr_free(mycseq);
|
|
return (NULL);
|
|
}
|
|
|
|
this_token = PL_strtok_r(NULL, " ", &lasts);
|
|
if (this_token) {
|
|
sipCseq->method = sippmh_get_method_code(this_token);
|
|
}
|
|
|
|
}
|
|
if (!this_token) {
|
|
cpr_free(sipCseq);
|
|
cpr_free(mycseq);
|
|
return (NULL);
|
|
}
|
|
cpr_free(mycseq);
|
|
} else {
|
|
cpr_free(sipCseq);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return (sipCseq);
|
|
}
|
|
|
|
sipRet_t
|
|
sippmh_add_cseq (sipMessage_t *msg, const char *method, uint32_t seq_no)
|
|
{
|
|
sipRet_t retval = STATUS_FAILURE;
|
|
|
|
if (msg && method) {
|
|
char cseq[32];
|
|
|
|
sprintf(&cseq[0], "%lu %s", (unsigned long) seq_no, method);
|
|
retval = sippmh_add_text_header(msg, SIP_HEADER_CSEQ,
|
|
(const char *)&cseq[0]);
|
|
}
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* The Valid SIP URL MUST contain host field.
|
|
* This is not true for TEL URLs. Only user is mandatory.
|
|
* This check is sufficient for "From" & "To" headers
|
|
* from a GW standpoint. The ReqLine MUST have a "user"
|
|
* in the URL as the GW supports multiple users.
|
|
*/
|
|
boolean
|
|
sippmh_valid_url (genUrl_t *genUrl)
|
|
{
|
|
boolean retval = FALSE;
|
|
|
|
if (!genUrl) {
|
|
return retval;
|
|
}
|
|
if (genUrl->schema == URL_TYPE_SIP) {
|
|
if (genUrl->u.sipUrl->host && genUrl->u.sipUrl->host[0])
|
|
retval = TRUE;
|
|
} else if (genUrl->schema == URL_TYPE_TEL) {
|
|
if (genUrl->u.telUrl->user)
|
|
retval = TRUE;
|
|
}
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* The Input string is the comma separated cached Record-Route
|
|
* header string. This function duplicates the input string
|
|
* and generates a set of pointers which are pointing to a
|
|
* name-addr format string. These pointers are pointing within
|
|
* the duplicated cached header string.
|
|
*
|
|
* Loc 0 Loc 1 Loc 2
|
|
* Loc_start -> name-addr1, name-addr2, name-addr3
|
|
*
|
|
* Loc_start points to the duplicated input string and needs
|
|
* to be freed.
|
|
* Loc_start is preserved so that the string
|
|
* can be freed, when required. Thus this pointer needs to be
|
|
* stored only once.
|
|
*/
|
|
sipRecordRoute_t *
|
|
sippmh_parse_record_route (const char *input_record_route)
|
|
{
|
|
char *record_route = NULL, *start_ptr = NULL;
|
|
char *more_ptr;
|
|
sipRecordRoute_t *sipRecordRoute;
|
|
char *rr_other_param = NULL;
|
|
|
|
/*
|
|
* Record-Route = "Record-Route" ":" (name-addr *(";" rr-param))
|
|
* name-addr = [ display-name ] "<" addr-spec ">"
|
|
* addr-spec = SIP-URL | URI
|
|
* display-name = *token | quoted-string
|
|
* rr-param = generic-param
|
|
* generic-param = token [ = ( token | host | quoted-string ) ]
|
|
*/
|
|
|
|
record_route = cpr_strdup(input_record_route);
|
|
if (record_route == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
sipRecordRoute = (sipRecordRoute_t *)
|
|
cpr_calloc(1, sizeof(sipRecordRoute_t));
|
|
|
|
if (sipRecordRoute == NULL) {
|
|
cpr_free(record_route);
|
|
return NULL;
|
|
}
|
|
|
|
start_ptr = record_route;
|
|
|
|
do {
|
|
sipLocation_t *sipLoc;
|
|
|
|
more_ptr = NULL;
|
|
parse_errno = 0;
|
|
sipLoc = sippmh_parse_nameaddr_or_addrspec(record_route, start_ptr,
|
|
FALSE, TRUE, &more_ptr);
|
|
if (sipLoc == NULL) {
|
|
if (sipRecordRoute->locations == 0) {
|
|
// Free record_route only if we fail the first location
|
|
// For the others, it will be freed when sipRecordRoute is
|
|
// freed
|
|
cpr_free(record_route);
|
|
}
|
|
sippmh_free_record_route(sipRecordRoute);
|
|
sipRecordRoute = NULL;
|
|
break;
|
|
}
|
|
|
|
/* Loc_start points to the duplicated input string &
|
|
* is stored in the zeroth location of location[].
|
|
* This value needs to be freed only once
|
|
*/
|
|
if (sipRecordRoute->num_locations) {
|
|
sipLoc->loc_start = NULL;
|
|
}
|
|
|
|
sipRecordRoute->locations[sipRecordRoute->num_locations++] = sipLoc;
|
|
|
|
if (more_ptr == NULL || *more_ptr == 0) {
|
|
/* No params, means no qval and assume lowest priority */
|
|
break;
|
|
}
|
|
|
|
record_route = more_ptr;
|
|
|
|
/*
|
|
* At this point, either record_route is pointing to
|
|
* 1) ',' - Beginning of next location
|
|
* 3) End of string
|
|
* 4) Any other character is syntax error
|
|
*/
|
|
|
|
/*
|
|
* This following while loop eats up all the rr_params.
|
|
* Note that these 'other_params' are not the same as the ones handled
|
|
* by parseURLParams. That routines parses other params INSIDE the < > brackets.
|
|
* The following code would handle unknown params outside the brackets.
|
|
*/
|
|
while (record_route && *record_route == SEMI_COLON) {
|
|
record_route++;
|
|
record_route = parse_other_param(record_route, &rr_other_param);
|
|
/*
|
|
* Record route extensions are not supported. Throw away
|
|
* any that were just parsed.
|
|
*/
|
|
if (rr_other_param != NULL) {
|
|
cpr_free(rr_other_param);
|
|
}
|
|
}
|
|
|
|
|
|
if (record_route && *record_route == COMMA) {
|
|
/* Another location follows */
|
|
*record_route++ = 0;
|
|
SKIP_LWS(record_route);
|
|
if (sipRecordRoute->num_locations == SIP_MAX_LOCATIONS) {
|
|
sippmh_free_record_route(sipRecordRoute);
|
|
sipRecordRoute = NULL;
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\nsippmh_parse_record_route: Too many location "
|
|
"headers in Record-Route header");
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
/* Flag error if not "end of header" &
|
|
* any other character other than COMMA
|
|
*/
|
|
if (record_route && *record_route) {
|
|
parse_errno = PARSE_ERR_SYNTAX;
|
|
CCSIP_DEBUG_ERROR(parse_errors[parse_errno],
|
|
"sippmh_parse_record_route", record_route);
|
|
sippmh_free_record_route(sipRecordRoute);
|
|
sipRecordRoute = NULL;
|
|
}
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
if (sipRecordRoute) {
|
|
sipRecordRoute->new_flag = TRUE;
|
|
}
|
|
return sipRecordRoute;
|
|
}
|
|
|
|
/*
|
|
* This function is a placeholder for future expansion.
|
|
* If deep copy of the record route is needed from one ccb to another
|
|
* this function can be used.
|
|
*/
|
|
sipRecordRoute_t *
|
|
sippmh_copy_record_route (sipRecordRoute_t *rr)
|
|
{
|
|
//TBD
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
sippmh_free_record_route (sipRecordRoute_t *rr)
|
|
{
|
|
int i;
|
|
|
|
if (rr) {
|
|
for (i = 0; i < rr->num_locations; ++i) {
|
|
if (rr->locations[i]->loc_start) {
|
|
/* Free the entire header value that we duplicated */
|
|
cpr_free(rr->locations[i]->loc_start);
|
|
}
|
|
/* Free the genUrl_t struct in the sipLocation_t */
|
|
sippmh_genurl_free(rr->locations[i]->genUrl);
|
|
/* Free the sipLocation_t in Record-Route struct */
|
|
cpr_free(rr->locations[i]);
|
|
}
|
|
/* Free the sipContact_t struct */
|
|
cpr_free(rr);
|
|
}
|
|
}
|
|
|
|
cc_content_disposition_t *
|
|
sippmh_parse_content_disposition (const char *input_content_disp)
|
|
{
|
|
char *content_disp = NULL;
|
|
char *content_disp_start = NULL;
|
|
cc_content_disposition_t *sipContentDisp = NULL;
|
|
char *separator = NULL;
|
|
char *more_ptr = NULL;
|
|
|
|
if (input_content_disp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
content_disp_start = content_disp = cpr_strdup(input_content_disp);
|
|
if (content_disp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Content-Disposition = "Content-Disposition" ":"
|
|
* disposition-type*(";" disposition-param)
|
|
* disposition-type = "render" "session" "Icon" "alert" "precondition"
|
|
* disp-extension-token
|
|
* disposition-param = "handling" "="
|
|
* ("optional"|"required" other-handling)
|
|
* generic-param
|
|
* other-handling = token
|
|
* disp-extension-token = token
|
|
*/
|
|
|
|
sipContentDisp = (cc_content_disposition_t *)
|
|
cpr_calloc(1, sizeof(cc_content_disposition_t));
|
|
if (sipContentDisp == NULL) {
|
|
cpr_free(content_disp);
|
|
return sipContentDisp;
|
|
}
|
|
|
|
/* Set the field to protocol define defaults
|
|
* - Session & required handling
|
|
*/
|
|
sipContentDisp->disposition = cc_disposition_session;
|
|
sipContentDisp->required_handling = TRUE;
|
|
|
|
/* first token should be disposition-type - so look for
|
|
* a token delimiter either a space or semicolon
|
|
*/
|
|
SKIP_LWS(content_disp);
|
|
separator = strpbrk(content_disp, " ;");
|
|
if (separator) {
|
|
if (*separator == ';') {
|
|
*separator = 0;
|
|
more_ptr = separator + 1;
|
|
} else {
|
|
*separator = 0;
|
|
more_ptr = NULL;
|
|
}
|
|
} else {
|
|
more_ptr = NULL;
|
|
}
|
|
|
|
/* Parse disposition-type */
|
|
if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_SESSION, 7) == 0) {
|
|
sipContentDisp->disposition = cc_disposition_session;
|
|
} else if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_PRECONDITION, 12) == 0) {
|
|
sipContentDisp->disposition = cc_disposition_precondition;
|
|
} else if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_ICON, 4) == 0) {
|
|
sipContentDisp->disposition = cc_dispostion_icon;
|
|
} else if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_ALERT, 5) == 0) {
|
|
sipContentDisp->disposition = cc_disposition_alert;
|
|
} else if (strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_RENDER, 6) == 0) {
|
|
sipContentDisp->disposition = cc_disposition_render;
|
|
} else {
|
|
sipContentDisp->disposition = cc_disposition_unknown;
|
|
}
|
|
|
|
if (more_ptr) {
|
|
/* parse disposition-param */
|
|
SKIP_LWS(more_ptr);
|
|
if (strncasecmp(more_ptr, "handling", 8) == 0) {
|
|
more_ptr += 8;
|
|
SKIP_LWS(more_ptr);
|
|
if (*more_ptr == '=') {
|
|
more_ptr++;
|
|
SKIP_LWS(more_ptr);
|
|
if (strncasecmp(more_ptr, "optional", 8) == 0) {
|
|
sipContentDisp->required_handling = FALSE;
|
|
} else if (strncasecmp(more_ptr, "required", 8) == 0) {
|
|
sipContentDisp->required_handling = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
/* Keyword "handling" is not found
|
|
* default values are already populated
|
|
*/
|
|
}
|
|
}
|
|
|
|
cpr_free(content_disp_start);
|
|
return (sipContentDisp);
|
|
}
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Parses headers in Refer-To body in a char array
|
|
*
|
|
**************************************************************/
|
|
static uint16_t
|
|
sippmh_parse_referto_headers (char *refto_line, char **header_arr)
|
|
{
|
|
uint16_t headNum = 0;
|
|
char *tmpHead = NULL;
|
|
char *lasts = NULL;
|
|
|
|
if (refto_line == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
tmpHead = PL_strtok_r(refto_line, "?&", &lasts);
|
|
while (tmpHead != NULL) {
|
|
header_arr[headNum++] = tmpHead;
|
|
tmpHead = PL_strtok_r(NULL, "?&", &lasts);
|
|
}
|
|
return headNum;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Parser routine for headers other than sip url. Parses Replaces
|
|
* header, copies Accept-Contact and Proxy-Authorization headers
|
|
* and skips other headers.
|
|
*
|
|
* Input parameters : Array containing headers in Refer-To body,
|
|
* count of number of headers in Refer-To body
|
|
* & pointer to Refer-To structure for parsed
|
|
* values to be returned to calling function
|
|
*
|
|
* Returns : Zero if success, non-zero otherwise
|
|
*
|
|
*******************************************************************/
|
|
static int
|
|
sippmh_parse_other_headers (char **headerArr, uint16_t tokCount,
|
|
sipReferTo_t *refer_to)
|
|
{
|
|
uint16_t count = 0;
|
|
char *dup_header, *mhead;
|
|
|
|
dup_header = mhead = NULL;
|
|
|
|
for (count = 1; count < tokCount; count++) {
|
|
dup_header = cpr_strdup(headerArr[count]);
|
|
if (dup_header == NULL) {
|
|
return (PARSE_ERR_NO_MEMORY);
|
|
}
|
|
mhead = strchr(dup_header, '=');
|
|
if (mhead == NULL) {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_SYNTAX);
|
|
} else {
|
|
*mhead = '\0';
|
|
}
|
|
if (cpr_strcasecmp(dup_header, SIP_HEADER_REPLACES) == 0) {
|
|
|
|
char *unescRepl_str = NULL;
|
|
|
|
/*
|
|
* copy the Replaces header so that it can be
|
|
* echoed back in the triggered INVITE. Also
|
|
* unescape any escpaed characters.
|
|
*/
|
|
headerArr[count] = strstr(headerArr[count], "=");
|
|
if (headerArr[count] == NULL) {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_SYNTAX);
|
|
}
|
|
*(headerArr[count])++ = 0;
|
|
SKIP_LWS(headerArr[count]);
|
|
|
|
/* if header body is NULL, return syntax error */
|
|
if (*headerArr[count] == '\0') {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_SYNTAX);
|
|
}
|
|
|
|
unescRepl_str = (char *) cpr_malloc(strlen(headerArr[count]) + 1);
|
|
if (unescRepl_str == NULL) {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_NO_MEMORY);
|
|
}
|
|
|
|
sippmh_convertEscCharToChar((const char *) headerArr[count],
|
|
(uint8_t) strlen(headerArr[count]), unescRepl_str);
|
|
|
|
refer_to->sip_replaces_hdr = unescRepl_str;
|
|
|
|
} else if (cpr_strcasecmp(dup_header, SIP_HEADER_ACCEPT_CONTACT) == 0 ||
|
|
cpr_strcasecmp(dup_header, SIP_C_HEADER_ACCEPT_CONTACT) == 0) {
|
|
char *unescAccCont = NULL;
|
|
|
|
/*
|
|
* copy unescaped version of this header so
|
|
* that it can be echoed back in triggered INVITE
|
|
*/
|
|
headerArr[count] = strstr(headerArr[count], "=");
|
|
if (headerArr[count] == NULL) {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_SYNTAX);
|
|
}
|
|
*(headerArr[count])++ = 0;
|
|
SKIP_LWS(headerArr[count]);
|
|
|
|
/* if header body is NULL, return syntax error */
|
|
if (*headerArr[count] == '\0') {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_SYNTAX);
|
|
}
|
|
|
|
unescAccCont = (char *) cpr_malloc(strlen(headerArr[count]) + 1);
|
|
|
|
if (unescAccCont == NULL) {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_NO_MEMORY);
|
|
}
|
|
|
|
sippmh_convertEscCharToChar((const char *) headerArr[count],
|
|
(uint8_t) strlen(headerArr[count]),
|
|
unescAccCont);
|
|
|
|
refer_to->sip_acc_cont = unescAccCont;
|
|
|
|
} else if (cpr_strcasecmp(dup_header, SIP_HEADER_PROXY_AUTH) == 0) {
|
|
|
|
char *unescPrAuth = NULL;
|
|
|
|
/*
|
|
* copy this header to be echoed back in triggered INVITE
|
|
* unescape any escaped chars before storing
|
|
*/
|
|
|
|
headerArr[count] = strstr(headerArr[count], "=");
|
|
if (headerArr[count] == NULL) {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_SYNTAX);
|
|
}
|
|
*(headerArr[count])++ = 0;
|
|
SKIP_LWS(headerArr[count]);
|
|
|
|
/* if header body is NULL, return syntax error */
|
|
if (*headerArr[count] == '\0') {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_SYNTAX);
|
|
}
|
|
|
|
unescPrAuth = (char *) cpr_malloc(strlen(headerArr[count]) + 1);
|
|
if (unescPrAuth == NULL) {
|
|
cpr_free(dup_header);
|
|
return (PARSE_ERR_NO_MEMORY);
|
|
}
|
|
|
|
sippmh_convertEscCharToChar((const char *) headerArr[count],
|
|
(uint8_t) strlen(headerArr[count]),
|
|
unescPrAuth);
|
|
|
|
refer_to->sip_proxy_auth = unescPrAuth;
|
|
}
|
|
cpr_free(dup_header);
|
|
}
|
|
|
|
return 0; /* SUCCESS */
|
|
}
|
|
|
|
/**************************************************************
|
|
* Parser routine for Refer-To header. Refer-To may contain
|
|
* other headers like:
|
|
* a) Replaces
|
|
* b) Accept-Contact
|
|
* c) Proxy-Authorization
|
|
* will skip headers, we do not understand; parse Replaces;
|
|
* and copy Accept-Contact and Proxy-Authorization headers
|
|
* to be echoed back in the triggered INVITE.
|
|
*
|
|
* Input parameters : Refer-To string to be parsed.
|
|
* Returns : Pointer to Refer-To structure with parsed values
|
|
* if successful, NULL otherwise.
|
|
*
|
|
**************************************************************/
|
|
sipReferTo_t *
|
|
sippmh_parse_refer_to (char *input_refer_to)
|
|
{
|
|
char *refer_to_headers[MAX_REFER_TO_HEADER_CONTENTS];
|
|
char *left_bracket, *right_bracket, *msg_body, *refer_to_val;
|
|
sipReferTo_t *sipReferTo = NULL;
|
|
genUrl_t *refUrl = NULL;
|
|
uint16_t tokens = 0;
|
|
int ref_header_count = 0;
|
|
|
|
left_bracket = right_bracket = msg_body = refer_to_val = NULL;
|
|
|
|
if (input_refer_to == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Refer-To = ("Refer-To" | "r") ":" URL
|
|
*/
|
|
|
|
/* make a copy of refer-to to work with */
|
|
refer_to_val = cpr_strdup(input_refer_to);
|
|
if (refer_to_val == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
sipReferTo = (sipReferTo_t *) cpr_calloc(1, sizeof(sipReferTo_t));
|
|
if (sipReferTo == NULL) {
|
|
cpr_free(refer_to_val);
|
|
return NULL;
|
|
}
|
|
sipReferTo->ref_to_start = refer_to_val;
|
|
|
|
/* Initialize */
|
|
for (ref_header_count = 0;
|
|
ref_header_count < MAX_REFER_TO_HEADER_CONTENTS;
|
|
ref_header_count++) {
|
|
refer_to_headers[ref_header_count] = NULL;
|
|
}
|
|
|
|
/* first get rid of opening and closing braces, if there */
|
|
left_bracket = strpbrk(refer_to_val, ",<");
|
|
if (left_bracket) {
|
|
|
|
/* This is a name-addr form */
|
|
left_bracket++;
|
|
right_bracket = strchr(left_bracket, '>');
|
|
if (right_bracket) {
|
|
*right_bracket++ = 0;
|
|
}
|
|
msg_body = left_bracket;
|
|
} else {
|
|
|
|
/* This is addr-spec format */
|
|
msg_body = refer_to_val;
|
|
SKIP_LWS(msg_body);
|
|
}
|
|
|
|
/* parse headers separated by ? or & in an array of char pointers */
|
|
tokens = sippmh_parse_referto_headers(msg_body, refer_to_headers);
|
|
|
|
if (tokens == 0) {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\nEmpty Refer-To header\n");
|
|
}
|
|
sippmh_free_refer_to(sipReferTo);
|
|
return NULL;
|
|
}
|
|
|
|
/* First parse url portion, this should be the first element
|
|
* of the headers array
|
|
*/
|
|
refUrl = sippmh_parse_url(refer_to_headers[0], FALSE);
|
|
if (refUrl == NULL) {
|
|
sippmh_free_refer_to(sipReferTo);
|
|
return NULL;
|
|
}
|
|
sipReferTo->targetUrl = refUrl;
|
|
|
|
/* next parse rest of the headers, skip ones we don't understand */
|
|
parse_errno = sippmh_parse_other_headers(refer_to_headers, tokens,
|
|
sipReferTo);
|
|
|
|
if (parse_errno != 0) {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\nError while parsing other Refer-To header\n");
|
|
}
|
|
sippmh_free_refer_to(sipReferTo);
|
|
return NULL;
|
|
}
|
|
|
|
return (sipReferTo);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************
|
|
*
|
|
* Parser routine for Replaces header. Replaces header has
|
|
* call-id, to and from tags and signature scheme as parameters.
|
|
* The parser parses and stores call-id and tags in the Replaces
|
|
* structure and copies the signature scheme to be echoed back
|
|
* in the triggered INVITE for attended transfer.
|
|
*
|
|
* Input parameters : Replaces string to be parsed,
|
|
* boolean flag indicating whether the input
|
|
* string shall be duplicated or not.
|
|
*
|
|
* Returns : pointer to Replaces structure with parsed values
|
|
* if successful, NULL otherwise.
|
|
*
|
|
*****************************************************************/
|
|
sipReplaces_t *
|
|
sippmh_parse_replaces (char *input_repl, boolean dup_flag)
|
|
{
|
|
|
|
char *repl_str, *this_tok, *tagVal, *sign, *callid;
|
|
sipReplaces_t *repcs = NULL;
|
|
char *lasts = NULL;
|
|
|
|
repl_str = this_tok = tagVal = sign = NULL;
|
|
|
|
if (input_repl == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Replaces = "Replaces" ":" 1#replaces-values
|
|
* replaces-values = callid *( ";" replaces-param )
|
|
* callid = token [ "@" token ]
|
|
* replaces-param = to-tag | from-tag | rep-signature
|
|
* | extension-param
|
|
* to-tag = "to-tag=" UUID
|
|
* from-tag = "from-tag=" UUID
|
|
* rep-signature = signature-scheme *( ";" sig-scheme-params)
|
|
* signature-scheme = "scheme" "=" token
|
|
* sig-scheme-params = token "=" ( token | quoted string )
|
|
*/
|
|
|
|
repcs = (sipReplaces_t *) cpr_calloc(1, sizeof(sipReplaces_t));
|
|
if (repcs == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (dup_flag) {
|
|
repl_str = cpr_strdup(input_repl);
|
|
if (!repl_str) {
|
|
cpr_free(repcs);
|
|
return NULL;
|
|
}
|
|
repcs->str_start = repl_str;
|
|
} else {
|
|
repl_str = input_repl;
|
|
}
|
|
|
|
|
|
/* if Replaces string has signature scheme parameter,
|
|
* make a copy of the input string for parsing entire signature
|
|
* parameter
|
|
*/
|
|
|
|
if ((sign = strstr(repl_str, SIGNATURE_SCHEME)) != NULL) {
|
|
|
|
/*
|
|
* if to or from tag follows signature params, remove
|
|
* them from this string
|
|
*/
|
|
char *sign_str, *tag_str;
|
|
|
|
sign_str = tag_str = NULL;
|
|
|
|
sign_str = cpr_strdup(sign);
|
|
if (sign_str == NULL) {
|
|
sippmh_free_replaces(repcs);
|
|
return NULL;
|
|
}
|
|
if ((tag_str = strstr(sign_str, ";to-tag")) ||
|
|
(tag_str = strstr(sign_str, ";from-tag"))) {
|
|
*tag_str = '\0';
|
|
} else {
|
|
/* terminate input string at signature param */
|
|
*sign = '\0';
|
|
}
|
|
(repcs->signature_scheme) = sign_str;
|
|
}
|
|
|
|
|
|
this_tok = PL_strtok_r(input_repl, ";", &lasts);
|
|
while (this_tok != NULL) {
|
|
if (strncasecmp(this_tok, TO_TAG, 6) == 0) {
|
|
if (repcs->toTag != NULL) {
|
|
sippmh_free_replaces(repcs);
|
|
return NULL; /* ERROR - more than 1 TO tag */
|
|
} else {
|
|
tagVal = strchr(this_tok, '=');
|
|
if (tagVal) {
|
|
tagVal++;
|
|
SKIP_LWS(tagVal);
|
|
repcs->toTag = tagVal;
|
|
} else {
|
|
sippmh_free_replaces(repcs);
|
|
return NULL; /* ERROR */
|
|
}
|
|
}
|
|
} else if (strncasecmp(this_tok, FROM_TAG, 8) == 0) {
|
|
if (repcs->fromTag != NULL) {
|
|
sippmh_free_replaces(repcs);
|
|
return NULL; /* ERROR - more than 1 FROM tag */
|
|
} else {
|
|
tagVal = strchr(this_tok, '=');
|
|
if (tagVal) {
|
|
tagVal++;
|
|
SKIP_LWS(tagVal);
|
|
repcs->fromTag = tagVal;
|
|
} else {
|
|
sippmh_free_replaces(repcs);
|
|
return NULL; /* ERROR */
|
|
}
|
|
}
|
|
} else if (strncasecmp(this_tok, SIP_HEADER_REPLACES,
|
|
sizeof(SIP_HEADER_REPLACES) - 1) == 0) {
|
|
/* only other allowable field should be callid */
|
|
callid = strchr(this_tok, '=');
|
|
if (callid) {
|
|
char *sp_token;
|
|
|
|
callid++;
|
|
SKIP_LWS(callid);
|
|
repcs->callid = callid;
|
|
/* remove trailing spaces */
|
|
sp_token = strchr(repcs->callid, ' ');
|
|
if (sp_token == NULL) {
|
|
sp_token = strchr(repcs->callid, '\t');
|
|
}
|
|
if (sp_token) {
|
|
*sp_token = 0;
|
|
}
|
|
} else {
|
|
sippmh_free_replaces(repcs);
|
|
return NULL; /* ERROR */
|
|
}
|
|
} else {
|
|
sippmh_free_replaces(repcs);
|
|
return NULL;
|
|
}
|
|
this_tok = PL_strtok_r(NULL, ";", &lasts);
|
|
}
|
|
/* check for errors */
|
|
if ((repcs->callid == NULL) ||
|
|
(repcs->toTag == NULL) ||
|
|
(repcs->fromTag == NULL)) {
|
|
sippmh_free_replaces(repcs);
|
|
return NULL;
|
|
}
|
|
|
|
return repcs; /* SUCCESS */
|
|
}
|
|
|
|
|
|
/*
|
|
* Converts a hex character (0-9, a-f, or A-F) into its integer
|
|
* equivalent value.
|
|
* ex.) '9' --> 9
|
|
* 'a' --> 10
|
|
* 'F' --> 15
|
|
*/
|
|
static int
|
|
sippmh_htoi (const char inputChar)
|
|
{
|
|
char inputValue[2];
|
|
long strtol_result;
|
|
char *strtol_end;
|
|
|
|
inputValue[0] = inputChar;
|
|
inputValue[1] = '\0';
|
|
|
|
errno = 0;
|
|
strtol_result = strtol(inputValue, &strtol_end, 16);
|
|
|
|
if (errno || inputValue == strtol_end) {
|
|
return 0;
|
|
} else {
|
|
return (int) strtol_result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function(similar to memchr()) works with both strings and array of characters.
|
|
* Returns the pointer to the matched character, if any; NULL otherwise. To include memchr.c
|
|
* in the phone library takes 1K of extra size and hence wrote this.
|
|
*/
|
|
static void *
|
|
char_lookup (const void *src, unsigned char c, int n)
|
|
{
|
|
const unsigned char *mem = (unsigned char *) src;
|
|
|
|
while (n--) {
|
|
if (*mem == c) {
|
|
return ((void *) mem);
|
|
}
|
|
mem++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* List of characters that can exist in user part of URL/URI without escaping */
|
|
#define userUnResCharListSize 17
|
|
static char userUnResCharList[userUnResCharListSize] =
|
|
{
|
|
SEMI_COLON, EQUAL_SIGN, TILDA, STAR, UNDERSCORE, PLUS,
|
|
SINGLE_QUOTE, LEFT_PARENTHESIS, RIGHT_PARENTHESIS, DOLLAR_SIGN,
|
|
FORWARD_SLASH, DOT, DASH, EXCLAMATION, AMPERSAND, COMMA, QUESTION_MARK
|
|
};
|
|
|
|
|
|
/*
|
|
* This utility function converts selected characters in a string to escaped characters,
|
|
* per RFC2396. Escape format = %hexhex (hexhex - hex value of ASCII character)
|
|
* ex. 'abc@123' gets converted to "abc%40123" if '@' exists in escapable character list
|
|
*
|
|
* inptStr = ptr to string of chars
|
|
* inputStrLen = num of characters that you want to convert
|
|
* excludeCharTbl = lookup table/string of characters that need not be escaped
|
|
* excludeCharTblSize = number of characters in the table/string that need not be escaped
|
|
* null_terminate = TRUE if '\0' is required to be added at end
|
|
* FALSE if not required.
|
|
* outputStr = ptr to pre-allocated memory for storing the
|
|
* converted string
|
|
* outputStrSize = the size limit of the outputStr buffer
|
|
*
|
|
* returns the new size of the output string
|
|
* Converted string is stored in outputStr
|
|
* (contents = converted str)
|
|
*/
|
|
static size_t
|
|
sippmh_escapeChars_util (const char *inputStr, size_t inputStrLen,
|
|
char *excludeCharTbl, size_t excludeCharTblSize,
|
|
char *outputStr, size_t outputStrSize,
|
|
boolean null_terminate)
|
|
{
|
|
size_t char_cnt = 0;
|
|
char *nextOutputChar;
|
|
int additional_byte = null_terminate ? 1 : 0;
|
|
size_t copy_count = 0;
|
|
|
|
nextOutputChar = outputStr;
|
|
|
|
while (char_cnt++ < inputStrLen) {
|
|
/* Check for characters that doesn't need to be escaped */
|
|
if (((*inputStr) >= 'A' && (*inputStr) <= 'Z') ||
|
|
((*inputStr) >= 'a' && (*inputStr) <= 'z') ||
|
|
((*inputStr) >= '0' && (*inputStr) <= '9') ||
|
|
(char_lookup(excludeCharTbl, (unsigned char) *inputStr,
|
|
excludeCharTblSize) != NULL)) {
|
|
if (outputStrSize < (copy_count + 1 + additional_byte)) {
|
|
break;
|
|
}
|
|
*nextOutputChar++ = *inputStr++;
|
|
copy_count++;
|
|
} else { // Characters that need escaping
|
|
if (outputStrSize < (copy_count + 3 + additional_byte)) {
|
|
break;
|
|
}
|
|
/* Copy %hh to output string */
|
|
sprintf(nextOutputChar, "%c%x", '%', *inputStr++);
|
|
nextOutputChar = nextOutputChar + 3;
|
|
copy_count += 3;
|
|
}
|
|
|
|
}
|
|
|
|
if (null_terminate) {
|
|
copy_count++;
|
|
*nextOutputChar = '\0';
|
|
}
|
|
return (copy_count);
|
|
}
|
|
|
|
/*
|
|
* This function converts selected characters in a string to escaped characters,
|
|
* per RFC2396. Escape format = %hexhex (hexhex - hex value of ASCII character)
|
|
* ex. 'abc@123' gets converted to "abc%40123" if '@' exists in escapable character list
|
|
*
|
|
* inptStr = ptr to string of chars
|
|
* inputStrLen = num of characters that you want to convert
|
|
* outputStr = ptr to pre-allocated memory for storing the
|
|
* converted string
|
|
* outputStrSize = the size limit of the outputStr buffer
|
|
* null_terminate = TRUE if '\0' is required to be added at end
|
|
* FALSE if not required.
|
|
* returns the new size of the output string
|
|
*
|
|
* Converted string is stored in outputStr
|
|
* (contents = converted str)
|
|
*/
|
|
size_t
|
|
sippmh_convertURLCharToEscChar (const char *inputStr, size_t inputStrLen,
|
|
char *outputStr, size_t outputStrSize,
|
|
boolean null_terminate)
|
|
{
|
|
return (sippmh_escapeChars_util(inputStr, inputStrLen,
|
|
userUnResCharList, userUnResCharListSize,
|
|
outputStr, outputStrSize, null_terminate));
|
|
}
|
|
/*
|
|
* This function converts any '\' or '"' character in a quoted string to escaped characters,
|
|
* per RFC3261. Escape format = %hexhex (hexhex - hex value of ASCII character)
|
|
* ex. 'abc\\' gets converted to "abc%5c%5c" if '\' exists in escapable character list
|
|
*
|
|
* inptStr = ptr to string of chars
|
|
* inputStrLen = num of characters that you want to convert
|
|
* outputStr = ptr to pre-allocated memory for storing the
|
|
* converted string
|
|
* outputStrSize = the size limit of the outputStr buffer
|
|
* null_terminate = TRUE if '\0' is required to be added at end
|
|
* FALSE if not required.
|
|
* returns the new size of the output string
|
|
*
|
|
* Converted string is stored in outputStr
|
|
* (contents = converted str)
|
|
*/
|
|
size_t
|
|
sippmh_converQuotedStrToEscStr(const char *inputStr, size_t inputStrLen,
|
|
char *outputStr, size_t outputStrSize,
|
|
boolean null_terminate)
|
|
{
|
|
size_t char_cnt = 0;
|
|
char *nextOutputChar;
|
|
int additional_byte = null_terminate ? 1 : 0;
|
|
size_t copy_count = 0;
|
|
|
|
nextOutputChar = outputStr;
|
|
|
|
while (char_cnt++ < inputStrLen) {
|
|
/* Check for characters that need to be escaped */
|
|
if( ((*inputStr) == ESCAPE_CHAR) || ((*inputStr) == DOUBLE_QUOTE))
|
|
{
|
|
// Characters that need escaping
|
|
if (outputStrSize < (copy_count + 3 + additional_byte)) {
|
|
break;
|
|
}
|
|
/* Copy %hh to output string */
|
|
sprintf(nextOutputChar, "%c%02x", '%', *inputStr++);
|
|
nextOutputChar = nextOutputChar + 3;
|
|
copy_count += 3;
|
|
}
|
|
else {
|
|
if (outputStrSize < (copy_count + 1 + additional_byte)) {
|
|
break;
|
|
}
|
|
*nextOutputChar++ = *inputStr++;
|
|
copy_count++;
|
|
}
|
|
|
|
}
|
|
|
|
if (null_terminate) {
|
|
copy_count++;
|
|
*nextOutputChar = '\0';
|
|
}
|
|
|
|
return (copy_count);
|
|
}
|
|
|
|
/*
|
|
* This function converts all Escaped characters in a string
|
|
* to their non-escaped format according to RFC2396.
|
|
* Escape format = %hh (%<hex equivalent of ASCII char>)
|
|
* ex. 'abc%40123' gets converted to "abc@123"
|
|
*
|
|
* inptStr = ptr to string of chars
|
|
* inputStrLen = num of characters that you want to convert
|
|
* outputStr = ptr to pre-allocated memory for storing the
|
|
* converted string
|
|
*
|
|
* Converted string is stored in outputStr
|
|
* (contents = converted str)
|
|
* (contents = '\0' if no input string)
|
|
*/
|
|
void
|
|
sippmh_convertEscCharToChar (const char *inputStr, size_t inputStrLen,
|
|
char *outputStr)
|
|
{
|
|
size_t char_cnt = 0;
|
|
char *nextOutputChar;
|
|
int value;
|
|
boolean esc_char_found = FALSE;
|
|
|
|
*outputStr = '\0';
|
|
nextOutputChar = outputStr;
|
|
|
|
while (char_cnt < inputStrLen) {
|
|
if (*inputStr == PERCENT) {
|
|
/* Skip ESC syntax */
|
|
inputStr++;
|
|
char_cnt++;
|
|
esc_char_found = TRUE;
|
|
}
|
|
|
|
/* Copy character to output str */
|
|
if (esc_char_found) {
|
|
|
|
/* Convert 1st and 2nd hex chars to int */
|
|
value = sippmh_htoi(*inputStr) * 16;
|
|
inputStr++;
|
|
char_cnt++;
|
|
value += sippmh_htoi(*inputStr);
|
|
sprintf(nextOutputChar, "%c", toascii(value));
|
|
|
|
inputStr++;
|
|
char_cnt++;
|
|
esc_char_found = FALSE;
|
|
|
|
} else {
|
|
*nextOutputChar = *inputStr;
|
|
inputStr++;
|
|
char_cnt++;
|
|
}
|
|
|
|
nextOutputChar++;
|
|
}
|
|
|
|
*nextOutputChar = '\0';
|
|
}
|
|
|
|
|
|
void
|
|
sippmh_free_replaces (sipReplaces_t *repl)
|
|
{
|
|
|
|
if (repl == NULL) {
|
|
return;
|
|
}
|
|
if (repl->str_start) {
|
|
cpr_free(repl->str_start);
|
|
}
|
|
if (repl->signature_scheme) {
|
|
cpr_free(repl->signature_scheme);
|
|
}
|
|
cpr_free(repl);
|
|
}
|
|
|
|
|
|
void
|
|
sippmh_free_refer_to (sipReferTo_t *ref_to)
|
|
{
|
|
|
|
if (ref_to == NULL) {
|
|
return;
|
|
}
|
|
if (ref_to->ref_to_start) {
|
|
cpr_free(ref_to->ref_to_start);
|
|
}
|
|
if (ref_to->sip_replaces_hdr) {
|
|
cpr_free(ref_to->sip_replaces_hdr);
|
|
}
|
|
if (ref_to->sip_acc_cont) {
|
|
cpr_free(ref_to->sip_acc_cont);
|
|
}
|
|
if (ref_to->sip_proxy_auth) {
|
|
cpr_free(ref_to->sip_proxy_auth);
|
|
}
|
|
if (ref_to->targetUrl) {
|
|
sippmh_genurl_free(ref_to->targetUrl);
|
|
}
|
|
cpr_free(ref_to);
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
sippmh_free_remote_party_id (sipRemotePartyId_t *remote_party_id)
|
|
{
|
|
if (remote_party_id) {
|
|
if (remote_party_id->loc) {
|
|
sippmh_free_location(remote_party_id->loc);
|
|
}
|
|
cpr_free(remote_party_id);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
sippmh_free_remote_party_id_info (sipRemotePartyIdInfo_t *rpid_info)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (rpid_info) {
|
|
if (rpid_info->num_rpid > 0) {
|
|
for (i = 0; i < rpid_info->num_rpid; i++) {
|
|
sippmh_free_remote_party_id(rpid_info->rpid[i]);
|
|
rpid_info->rpid[i] = NULL;
|
|
}
|
|
}
|
|
cpr_free(rpid_info);
|
|
}
|
|
}
|
|
|
|
void
|
|
sippmh_free_diversion_info (sipDiversionInfo_t *div_info)
|
|
{
|
|
if (div_info) {
|
|
if (div_info->orig_called_name) {
|
|
strlib_free(div_info->orig_called_name);
|
|
}
|
|
if (div_info->orig_called_number) {
|
|
strlib_free(div_info->orig_called_number);
|
|
}
|
|
if (div_info->last_redirect_name) {
|
|
strlib_free(div_info->last_redirect_name);
|
|
}
|
|
if (div_info->last_redirect_number) {
|
|
strlib_free(div_info->last_redirect_number);
|
|
}
|
|
cpr_free(div_info);
|
|
}
|
|
|
|
}
|
|
|
|
static boolean
|
|
sippmh_parse_remote_party_id_params (char *params,
|
|
sipRemotePartyId_t *remote_party_id)
|
|
{
|
|
boolean params_good;
|
|
int param_len;
|
|
char *param_name;
|
|
|
|
if ((params == NULL) || (remote_party_id == NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
while (1) {
|
|
params_good = FALSE;
|
|
|
|
while (*params == ';') {
|
|
params++;
|
|
}
|
|
|
|
param_name = params;
|
|
SKIP_SIP_TOKEN(params);
|
|
param_len = params - param_name;
|
|
if (param_len == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* Parse screen parameter */
|
|
/* If there are multiple screen parameters, "no" takes precedence */
|
|
if ((param_len == RPID_SCREEN_LEN) &&
|
|
(strncasecmp(param_name, RPID_SCREEN, RPID_SCREEN_LEN) == 0) &&
|
|
(!remote_party_id->screen ||
|
|
cpr_strcasecmp(remote_party_id->screen, "no"))) {
|
|
params = parse_generic_param(params, &remote_party_id->screen);
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
params_good = TRUE;
|
|
}
|
|
|
|
/* Parse party-type parameter */
|
|
} else if ((param_len == RPID_PARTY_TYPE_LEN) &&
|
|
(strncasecmp(param_name, RPID_PARTY_TYPE,
|
|
RPID_PARTY_TYPE_LEN) == 0)) {
|
|
params = parse_generic_param(params, &remote_party_id->party_type);
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
params_good = TRUE;
|
|
}
|
|
|
|
/* Parse id-type parameter */
|
|
} else if ((param_len == RPID_ID_TYPE_LEN) &&
|
|
(strncasecmp(param_name, RPID_ID_TYPE,
|
|
RPID_ID_TYPE_LEN) == 0)) {
|
|
params = parse_generic_param(params, &remote_party_id->id_type);
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
params_good = TRUE;
|
|
}
|
|
|
|
/* Parse privacy parameter */
|
|
} else if ((param_len == RPID_PRIVACY_LEN) &&
|
|
(strncasecmp(param_name, RPID_PRIVACY,
|
|
RPID_PRIVACY_LEN) == 0)) {
|
|
params = parse_generic_param(params, &remote_party_id->privacy);
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
params_good = TRUE;
|
|
}
|
|
|
|
/* Parse np parameter */
|
|
} else if ((param_len == RPID_NP_LEN) &&
|
|
(strncasecmp(param_name, RPID_NP, RPID_NP_LEN) == 0)) {
|
|
params = parse_generic_param(params, &remote_party_id->np);
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
params_good = TRUE;
|
|
}
|
|
}
|
|
|
|
SKIP_LWS(params);
|
|
if (*params == SEMI_COLON) {
|
|
/* More parameters follow */
|
|
*params++ = '\0';
|
|
SKIP_LWS(params);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return params_good;
|
|
}
|
|
|
|
sipRemotePartyId_t *
|
|
sippmh_parse_remote_party_id (const char *input_remote_party_id)
|
|
{
|
|
/* static const char fname[] = "sippmh_parse_remote_party_id"; */
|
|
char *more_ptr;
|
|
sipRemotePartyId_t *remote_party_id;
|
|
char *remote_party_id_str;
|
|
|
|
/*
|
|
* Remote-Party-ID = "Remote-Party-ID" ":" [display-name]
|
|
* "<" addr-spec ">" *(";" rpi-token)
|
|
* rpi-token = rpi-screen | rpi-pty-type |
|
|
* rpi-id-type | rpi-privacy | rpi-np |
|
|
* other-rpi-token
|
|
* rpi-screen = "screen" "=" ("no" | "yes")
|
|
* rpi-pty-type = "party" "=" ("calling" | "called" | token)
|
|
* rpi-id-type = "id-type" "=" ("subscriber" | "user" | "alias" |
|
|
* "return" | "term" | token)
|
|
* rpi-privacy = "privacy" = 1#(
|
|
* ("full" | "name" | "uri" | "off" | token)
|
|
* ["-" ("network" | token) ] )
|
|
* rpi-np = "np" "=" ("ordinary" | "residential" | "business" |
|
|
* "priority" | "hotel" | "failure" | "hospital" |
|
|
* "prison" | "police" | "test" | "payphone" |
|
|
* "coin" | "payphone-public" | "payphone-private" |
|
|
* "coinless" | "restrict" | "coin-restrict" |
|
|
* "coinless-restrict" | "reserved" | "operator" |
|
|
* "trans-freephone" | "isdn-res" | "isdn-bus" |
|
|
* "unknown" | "emergency" | token )
|
|
* other-rpi-token = ["-"] token ["=" (token | quoted-string)]
|
|
*/
|
|
|
|
remote_party_id = (sipRemotePartyId_t *)
|
|
cpr_calloc(1, sizeof(sipRemotePartyId_t));
|
|
if (remote_party_id == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Duplicate the string and work with it -
|
|
* This duplicate string is stored in the location structure
|
|
* pointed to by remote_party_id->loc and freed when the that
|
|
* structure is freed. Note that this pointer is *not* dup'ed
|
|
* in the parse_nameaddr_or_addrspec structure
|
|
*/
|
|
remote_party_id_str = cpr_strdup(input_remote_party_id);
|
|
|
|
if (remote_party_id_str == NULL) {
|
|
sippmh_free_remote_party_id(remote_party_id);
|
|
remote_party_id = NULL;
|
|
more_ptr = NULL;
|
|
} else {
|
|
remote_party_id->loc =
|
|
sippmh_parse_nameaddr_or_addrspec(remote_party_id_str,
|
|
remote_party_id_str, FALSE,
|
|
FALSE, &more_ptr);
|
|
|
|
if (remote_party_id->loc == NULL) {
|
|
cpr_free(remote_party_id_str);
|
|
sippmh_free_remote_party_id(remote_party_id);
|
|
remote_party_id = NULL;
|
|
more_ptr = NULL;
|
|
}
|
|
}
|
|
|
|
if (more_ptr == NULL || *more_ptr == 0) {
|
|
/* No params. Use defaults of:
|
|
* party=calling, screen=no, id-type=subscriber,
|
|
* privacy=off, np=0 (ordinary)
|
|
*/
|
|
} else {
|
|
(void) sippmh_parse_remote_party_id_params(more_ptr, remote_party_id);
|
|
}
|
|
|
|
return remote_party_id;
|
|
}
|
|
|
|
|
|
char *
|
|
sippmh_generate_authorization (sip_author_t *sip_author)
|
|
{
|
|
char *buffer;
|
|
|
|
/*
|
|
* This routine takes a sip_author_t struct and generates an Authorization
|
|
* header or a Proxy-Authorization header. Since this routine generates
|
|
* a header for Authorization or Proxy-Authorization, the header will
|
|
* start with "Digest" or "Basic". The user should use sstrncat to
|
|
* add "Proxy Authorization: " or "Authorization: " depending which is
|
|
* needed.
|
|
*/
|
|
if (!sip_author) {
|
|
return NULL;
|
|
}
|
|
buffer = (char *) cpr_malloc(MAX_SIP_HEADER_LENGTH);
|
|
if (!buffer) {
|
|
return NULL;
|
|
}
|
|
|
|
if (sip_author->scheme == SIP_DIGEST) {
|
|
snprintf(buffer, MAX_SIP_HEADER_LENGTH,
|
|
"%s %s=\"%s\",%s=\"%s\",%s=\"%s\",%s=\"%s\",%s=\"%s\"",
|
|
AUTHENTICATION_DIGEST,
|
|
AUTHENTICATION_USERNAME, sip_author->d_username,
|
|
AUTHENTICATION_REALM, sip_author->realm,
|
|
AUTHENTICATION_URI, sip_author->unparsed_uri,
|
|
AUTHENTICATION_RESPONSE, sip_author->response,
|
|
AUTHENTICATION_NONCE, sip_author->nonce);
|
|
|
|
/*
|
|
* The header must have username, realm, uri, response, and nonce.
|
|
* All other parameters are optional, so check if they exist and
|
|
* add them on to the buffer if they do.
|
|
*/
|
|
|
|
if (sip_author->opaque) {
|
|
char *buffer2;
|
|
|
|
buffer2 = (char *) cpr_malloc(MAX_URI_LENGTH);
|
|
if (!buffer2) {
|
|
cpr_free(buffer);
|
|
return NULL;
|
|
}
|
|
snprintf(buffer2, MAX_URI_LENGTH, ",%s=\"%s\"",
|
|
AUTHENTICATION_OPAQUE, sip_author->opaque);
|
|
sstrncat(buffer, buffer2, MAX_SIP_HEADER_LENGTH - strlen(buffer));
|
|
cpr_free(buffer2);
|
|
}
|
|
if (sip_author->cnonce) {
|
|
char *buffer3;
|
|
|
|
buffer3 = (char *) cpr_malloc(MAX_URI_LENGTH);
|
|
if (!buffer3) {
|
|
cpr_free(buffer);
|
|
return NULL;
|
|
}
|
|
snprintf(buffer3, MAX_URI_LENGTH,
|
|
",cnonce=\"%s\"", sip_author->cnonce);
|
|
sstrncat(buffer, buffer3, MAX_SIP_HEADER_LENGTH - strlen(buffer));
|
|
cpr_free(buffer3);
|
|
}
|
|
if (sip_author->qop) {
|
|
char *buffer4;
|
|
|
|
buffer4 = (char *) cpr_malloc(MAX_URI_LENGTH);
|
|
if (!buffer4) {
|
|
cpr_free(buffer);
|
|
return NULL;
|
|
}
|
|
snprintf(buffer4, MAX_URI_LENGTH, ",qop=%s", sip_author->qop);
|
|
sstrncat(buffer, buffer4, MAX_SIP_HEADER_LENGTH - strlen(buffer));
|
|
cpr_free(buffer4);
|
|
}
|
|
if (sip_author->nc_count) {
|
|
char *buffer5;
|
|
|
|
buffer5 = (char *) cpr_malloc(MAX_URI_LENGTH);
|
|
if (!buffer5) {
|
|
cpr_free(buffer);
|
|
return NULL;
|
|
}
|
|
snprintf(buffer5, MAX_URI_LENGTH, ",nc=%s", sip_author->nc_count);
|
|
sstrncat(buffer, buffer5, MAX_SIP_HEADER_LENGTH - strlen(buffer));
|
|
cpr_free(buffer5);
|
|
}
|
|
if (sip_author->algorithm) {
|
|
char *buffer6;
|
|
|
|
buffer6 = (char *) cpr_malloc(MAX_URI_LENGTH);
|
|
if (!buffer6) {
|
|
cpr_free(buffer);
|
|
return NULL;
|
|
}
|
|
snprintf(buffer6, MAX_URI_LENGTH,
|
|
",%s=%s", AUTHENTICATION_ALGORITHM, sip_author->algorithm);
|
|
sstrncat(buffer, buffer6, MAX_SIP_HEADER_LENGTH - strlen(buffer));
|
|
cpr_free(buffer6);
|
|
}
|
|
} else {
|
|
sprintf(buffer, "%s %s", AUTHENTICATION_BASIC, sip_author->user_pass);
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
void
|
|
sippmh_free_authen (sip_authen_t *sip_authen)
|
|
{
|
|
if (sip_authen) {
|
|
cpr_free(sip_authen->str_start);
|
|
cpr_free(sip_authen);
|
|
}
|
|
}
|
|
|
|
static boolean
|
|
sippmh_validate_authenticate (sip_authen_t *sip_authen)
|
|
{
|
|
if (sip_authen) {
|
|
if ((sip_authen->realm != NULL) && (sip_authen->nonce != NULL)) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* routine used to parse WWW-Authenticate and Proxy-Authenticate headers */
|
|
|
|
sip_authen_t *
|
|
sippmh_parse_authenticate (const char *input_char)
|
|
{
|
|
sip_authen_t *sip_authen;
|
|
char *input;
|
|
boolean good_params;
|
|
boolean qop_found = FALSE;
|
|
char *trash;
|
|
|
|
/*
|
|
* Basic:
|
|
* "WWW-Authenticate" ":" 1#challenge
|
|
* challenge = "Basic" realm
|
|
*******************************************************
|
|
* Digest:
|
|
* "Digest" digest-challenge
|
|
* digest-challenge = 1#( realm | [ domain ] | nonce |
|
|
* [ opaque ] |[ stale ] | [ algorithm ] |
|
|
* [ qop-options ] | [auth-param] )
|
|
*
|
|
* domain = "domain" "=" <"> URI ( 1*SP URI ) <">
|
|
* URI = absoluteURI | abs_path
|
|
* opaque = "opaque" "=" quoted-string
|
|
* stale = "stale" "=" ( "true" | "false" )
|
|
* algorithm = "algorithm" "=" ( "MD5" | "MD5-sess" |
|
|
* token )
|
|
* qop-options = "qop" "=" <"> 1#qop-value <">
|
|
* qop-value = "auth" | "auth-int" | token
|
|
*/
|
|
|
|
if (!input_char) {
|
|
return NULL;
|
|
}
|
|
|
|
input = cpr_strdup(input_char);
|
|
if (!input) {
|
|
return NULL;
|
|
}
|
|
|
|
sip_authen = (sip_authen_t *) cpr_calloc(1, sizeof(sip_authen_t));
|
|
if (!sip_authen) {
|
|
cpr_free(input);
|
|
return NULL;
|
|
}
|
|
|
|
sip_authen->str_start = input;
|
|
SKIP_WHITE_SPACE(input);
|
|
|
|
/* now pointing at Basic, pgp or Digest */
|
|
|
|
if (strncasecmp(input, AUTHENTICATION_BASIC, 5) == 0) {
|
|
sip_authen->scheme = SIP_BASIC;
|
|
input += 5;
|
|
*input = 0;
|
|
input++;
|
|
SKIP_WHITE_SPACE(input);
|
|
if (strncasecmp(input, AUTHENTICATION_REALM, 5) == 0) {
|
|
input += 5;
|
|
SKIP_WHITE_SPACE(input);
|
|
if (*input == '=') {
|
|
input++;
|
|
} else {
|
|
sippmh_free_authen(sip_authen);
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\n%s", get_debug_string(DEBUG_PMH_INCORRECT_SYNTAX));
|
|
}
|
|
return NULL;
|
|
}
|
|
SKIP_WHITE_SPACE(input);
|
|
sip_authen->realm = input;
|
|
return sip_authen;
|
|
}
|
|
sip_authen->user_pass = input; /* encoded base 64 */
|
|
return sip_authen;
|
|
|
|
} else if (strncasecmp(input, AUTHENTICATION_DIGEST, 6) == 0) {
|
|
sip_authen->scheme = SIP_DIGEST;
|
|
input += 6;
|
|
*input = 0;
|
|
input++;
|
|
SKIP_WHITE_SPACE(input);
|
|
} else {
|
|
sippmh_free_authen(sip_authen);
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\n%s", get_debug_string(DEBUG_PMH_INVALID_SCHEME));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* pointing at Digest-challenge */
|
|
|
|
/* if algorithm is not in string set it to md5 */
|
|
|
|
sip_authen->algorithm = "md5";
|
|
|
|
/* loop through string until all fields have been handled */
|
|
|
|
while (input) {
|
|
char **ptr = NULL;
|
|
|
|
if (strncasecmp(input, AUTHENTICATION_DOMAIN, 6) == 0) {
|
|
input += 6;
|
|
ptr = &(sip_authen->unparsed_domain);
|
|
} else if (strncasecmp(input, AUTHENTICATION_ALGORITHM, 9) == 0) {
|
|
input += 9;
|
|
ptr = &(sip_authen->algorithm);
|
|
} else if (strncasecmp(input, AUTHENTICATION_OPAQUE, 6) == 0) {
|
|
input += 6;
|
|
ptr = &(sip_authen->opaque);
|
|
} else if (strncasecmp(input, "stale", 5) == 0) {
|
|
input += 5;
|
|
ptr = &(sip_authen->stale);
|
|
} else if (strncasecmp(input, AUTHENTICATION_REALM, 5) == 0) {
|
|
input += 5;
|
|
ptr = &(sip_authen->realm);
|
|
} else if (strncasecmp(input, AUTHENTICATION_NONCE, 5) == 0) {
|
|
input += 5;
|
|
ptr = &(sip_authen->nonce);
|
|
} else if (strncasecmp(input, "qop", 3) == 0) {
|
|
input += 3;
|
|
ptr = &(sip_authen->qop);
|
|
qop_found = TRUE;
|
|
} else {
|
|
/*
|
|
* Ignore unrecognized parameters.
|
|
*/
|
|
ptr = &trash;
|
|
input = strchr(input, EQUAL_SIGN);
|
|
if (input == NULL) {
|
|
sippmh_free_authen(sip_authen);
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\n%s", get_debug_string(DEBUG_PMH_INVALID_FIELD_VALUE));
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
SKIP_WHITE_SPACE(input);
|
|
if (*input != EQUAL_SIGN) {
|
|
sippmh_free_authen(sip_authen);
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\n%s", get_debug_string(DEBUG_PMH_INCORRECT_SYNTAX));
|
|
}
|
|
return NULL;
|
|
}
|
|
input++; /* skip the equal sign */
|
|
SKIP_WHITE_SPACE(input);
|
|
/* input is now pointing at the value of the parameter */
|
|
*ptr = input;
|
|
if (*input == DOUBLE_QUOTE) {
|
|
input++;
|
|
*ptr = input; /*sam do not want the quotes in the string */
|
|
input = strchr(input, DOUBLE_QUOTE);
|
|
if (input == NULL) {
|
|
sippmh_free_authen(sip_authen);
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\n%s", get_debug_string(DEBUG_PMH_INCORRECT_SYNTAX));
|
|
}
|
|
return NULL;
|
|
} else {
|
|
*input++ = '\0'; /*sam string needs to be null terminated */
|
|
}
|
|
/*
|
|
* Check the qop. If qop is included it has to be either
|
|
* auth, auth-int or both. If both are included then we will
|
|
* just drop the second one.
|
|
*/
|
|
if (qop_found == TRUE) {
|
|
char *qop_input = NULL;
|
|
|
|
if (sip_authen->qop) {
|
|
qop_input = strchr(sip_authen->qop, COMMA);
|
|
}
|
|
if (qop_input != NULL) {
|
|
*qop_input = '\0';
|
|
}
|
|
|
|
/*
|
|
* Make sure that the qop is either auth or auth-int
|
|
*/
|
|
if ((strncasecmp(sip_authen->qop, "auth", 4) != 0) &&
|
|
(strncasecmp(sip_authen->qop, "auth-int", 8) != 0)) {
|
|
sip_authen->qop = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
input = strchr(input, COMMA);
|
|
if (!input) {
|
|
good_params = sippmh_validate_authenticate(sip_authen);
|
|
if (good_params) {
|
|
return sip_authen;
|
|
} else {
|
|
sippmh_free_authen(sip_authen);
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("\n%s", get_debug_string(DEBUG_PMH_NOT_ENOUGH_PARAMETERS));
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
*input++ = 0;
|
|
SKIP_WHITE_SPACE(input);
|
|
}
|
|
|
|
sippmh_free_authen(sip_authen);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_parse_displaystr
|
|
*
|
|
* Parameters: Display string to be parsed
|
|
*
|
|
* Description:Removes leading <sip: and every character
|
|
* after : in string
|
|
*
|
|
* Returns: Pointer to parsed string
|
|
*
|
|
*/
|
|
|
|
string_t
|
|
sippmh_parse_displaystr (string_t displaystr)
|
|
{
|
|
char temp_displaystr[CC_MAX_DIALSTRING_LEN];
|
|
char *temp_ptr;
|
|
char *temp;
|
|
|
|
sstrncpy(temp_displaystr, displaystr, CC_MAX_DIALSTRING_LEN);
|
|
temp_ptr = (char *) temp_displaystr;
|
|
|
|
temp = strcasestr(temp_ptr, "sip:");
|
|
if (temp) {
|
|
temp_ptr = temp + 4;
|
|
}
|
|
temp = strchr(temp_ptr, ':');
|
|
if (temp) {
|
|
*temp = 0;
|
|
}
|
|
temp = strchr(temp_ptr, '?');
|
|
if (temp) {
|
|
*temp = 0;
|
|
}
|
|
temp = strchr(temp_ptr, ';');
|
|
if (temp) {
|
|
*temp = 0;
|
|
}
|
|
temp = strchr(temp_ptr, '>');
|
|
if (temp) {
|
|
*temp = 0;
|
|
}
|
|
displaystr = strlib_update(displaystr, temp_ptr);
|
|
return (displaystr);
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_process_via_header
|
|
*
|
|
* Parameters: Received Sip Message
|
|
* IP Address the Message was received from
|
|
*
|
|
* Description: Appends received=<ip_address> to the first Via
|
|
* field if the ip address we received this message
|
|
* from is not the same as the ip address in the via
|
|
* field or if it contains a domain name
|
|
*
|
|
* Returns: None
|
|
*
|
|
*/
|
|
void
|
|
sippmh_process_via_header (sipMessage_t *sip_message,
|
|
cpr_ip_addr_t *source_ip_address)
|
|
{
|
|
char dotted_ip[MAX_IPADDR_STR_LEN];
|
|
char *hdr_start;
|
|
char *new_buf;
|
|
int new_buf_len;
|
|
char *offset;
|
|
long old_header_offset;
|
|
sipVia_t *via;
|
|
cpr_ip_addr_t tmp_ip_addr;
|
|
|
|
CPR_IP_ADDR_INIT(tmp_ip_addr);
|
|
|
|
if (sip_message && sippmh_is_request(sip_message)) {
|
|
via = sippmh_parse_via(sip_message->hdr_cache[VIA].val_start);
|
|
if (via == NULL) {
|
|
/* error message already displayed in parse function */
|
|
/* mark the message as being incomplete so a 400 will get sent */
|
|
sip_message->is_complete = 0;
|
|
return;
|
|
}
|
|
|
|
util_ntohl(&tmp_ip_addr, source_ip_address);
|
|
ipaddr2dotted(dotted_ip, &tmp_ip_addr);
|
|
if (strcmp(via->host, dotted_ip) && (via->recd_host == NULL)) {
|
|
hdr_start = sip_message->hdr_cache[VIA].hdr_start;
|
|
|
|
/*
|
|
* +3 accounts for the ; and the = before and after the received
|
|
* and the terminating NULL
|
|
*/
|
|
new_buf_len = strlen(hdr_start) + sizeof(VIA_RECEIVED) + strlen(dotted_ip) + 3;
|
|
new_buf = (char *) cpr_malloc(new_buf_len);
|
|
/*
|
|
* If we cannot allocate memory, we will just leave
|
|
* the VIA alone and hope things work out for the best
|
|
*/
|
|
if (new_buf != NULL) {
|
|
offset = strchr(hdr_start, ',');
|
|
old_header_offset = sip_message->hdr_cache[VIA].val_start -
|
|
hdr_start;
|
|
sip_message->hdr_cache[VIA].hdr_start = new_buf;
|
|
sip_message->hdr_cache[VIA].val_start = new_buf +
|
|
old_header_offset;
|
|
|
|
if (offset) {
|
|
snprintf(new_buf, new_buf_len, "%.*s;%s=%s%s", (int) (offset - hdr_start), hdr_start, VIA_RECEIVED, dotted_ip, offset);
|
|
} else {
|
|
snprintf(new_buf, new_buf_len, "%s;%s=%s", hdr_start, VIA_RECEIVED, dotted_ip);
|
|
}
|
|
|
|
cpr_free(hdr_start);
|
|
}
|
|
}
|
|
sippmh_free_via(via);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: sippmh_parse_user
|
|
*
|
|
* Parameters: user portion of URL to parse
|
|
*
|
|
* Description: This function will remove any parameters
|
|
* which appear in the user portion of the url.
|
|
* (i.e. 15726;oct3=128@10.10.10.10 will return 15726)
|
|
* The SIP Phone does not care about the extra parameters and
|
|
* they need to be removed so that the user portion will
|
|
* match the phone provisioning and display correctly.
|
|
*
|
|
* Returns: A Copy of the input string with the parameters removed
|
|
* if things go well. NULL if we encounter an error.
|
|
*/
|
|
char *
|
|
sippmh_parse_user (char *url_main)
|
|
{
|
|
char *user = NULL;
|
|
size_t size = 0;
|
|
char *lasts = NULL;
|
|
|
|
if (url_main == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* If the first char is ';', give up. That
|
|
* breaks our assumption.
|
|
*/
|
|
if (*url_main == SEMI_COLON) {
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Create a new string and copy the url_main
|
|
* string into it. (We can't change the original
|
|
* string because it may be needed for creating
|
|
* SIP response messages.)
|
|
*/
|
|
size = strlen(url_main) + 1;
|
|
user = (char *) cpr_malloc(size);
|
|
/*
|
|
* If the malloc fails, return the original URL.
|
|
*/
|
|
if (user == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
sstrncpy(user, url_main, size);
|
|
|
|
/*
|
|
* Search for ";" and null terminate the string there.
|
|
* An assumption has been made that any parameters will come
|
|
* after the number. i.e.: "15726;param=paramval"
|
|
*/
|
|
(void) PL_strtok_r(user, ";", &lasts);
|
|
return (user);
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_parse_message_summary
|
|
*
|
|
* Parameters: The incoming SIP message and a container to be
|
|
* filled with the parsed message body.
|
|
*
|
|
* Description: This function will parse the incoming MWI NTFY SIP message
|
|
* and fill the passed in structure with the
|
|
* contents of the parsed message.Basic Error checking is done on
|
|
* the passed in message.
|
|
*
|
|
* Returns: SIP_ERROR on error.SIP_OK otherwise.
|
|
*/
|
|
int32_t
|
|
sippmh_parse_message_summary(sipMessage_t *pSipMessage, sipMessageSummary_t *mesgSummary)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
char *p = NULL;
|
|
char *val = NULL;
|
|
char temp[MAX_SIP_URL_LENGTH];
|
|
boolean token_found = FALSE;
|
|
boolean hp_found = FALSE;
|
|
long strtol_result;
|
|
char *strtol_end;
|
|
|
|
p = strstr(pSipMessage->mesg_body[0].msgBody, "Messages-Waiting");
|
|
|
|
if (!p) {
|
|
p = strstr(pSipMessage->mesg_body[0].msgBody, "Message-Waiting");
|
|
}
|
|
|
|
if (p) {
|
|
memset(temp, '\0', MAX_SIP_URL_LENGTH);
|
|
|
|
trim_right(p);
|
|
|
|
val = strstr(p, ":");
|
|
if (val) {
|
|
val++;
|
|
|
|
i = j = 0;
|
|
while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
|
|
j++;
|
|
}
|
|
while (val[j] != '\0' && val[j] != '\n' && val[j] != '\r') {
|
|
// return error if obviously wrong
|
|
if (!isalpha(val[j])) {
|
|
return SIP_ERROR;
|
|
}
|
|
temp[i] = val[j];
|
|
j++;
|
|
if (++i >= MAX_SIP_URL_LENGTH) {
|
|
return SIP_ERROR;
|
|
}
|
|
}
|
|
temp[i] = '\0';
|
|
} else {
|
|
return SIP_ERROR;
|
|
}
|
|
} else {
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
if (cpr_strcasecmp(temp, "no") == 0) {
|
|
mesgSummary->mesg_waiting_on = FALSE;
|
|
} else if (cpr_strcasecmp(temp, "yes") == 0) {
|
|
mesgSummary->mesg_waiting_on = TRUE;
|
|
} else {
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
/*
|
|
p = strstr(pSipMessage->mesg_body[0].msgBody, "Message-Account");
|
|
|
|
if (p) {
|
|
trim_right(p);
|
|
|
|
val = strstr(p, ":");
|
|
if (val) {
|
|
val++;
|
|
|
|
i = j = 0;
|
|
while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
|
|
j++;
|
|
}
|
|
|
|
while (val[j] != '\0' && val[j] != '\n' && val[j] != '\r') {
|
|
mesgSummary->message_account[i] = val[j];
|
|
j++;
|
|
if (++i >= MAX_SIP_URL_LENGTH) {
|
|
return SIP_ERROR;
|
|
}
|
|
}
|
|
mesgSummary->message_account[i] = '\0';
|
|
} else {
|
|
return SIP_ERROR;
|
|
}
|
|
}
|
|
*/
|
|
|
|
p = strstr(pSipMessage->mesg_body[0].msgBody, "Voice-Message");
|
|
|
|
if (p) {
|
|
mesgSummary->type = mwiVoiceType;
|
|
memset(temp, '\0', MAX_SIP_URL_LENGTH);
|
|
trim_right(p);
|
|
i = j = 0;
|
|
val = strstr(p, ":");
|
|
if (val) {
|
|
val++;
|
|
|
|
while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
|
|
j++;
|
|
}
|
|
|
|
while (val[j] != '\0' && val[j] != '\n' && val[j] != '\r' && !isspace(val[j]) && val[j] != '(') {
|
|
if (!isdigit(val[j])) {
|
|
if (val[j] == '/') {
|
|
temp[i] = '\0';
|
|
|
|
errno = 0;
|
|
strtol_result = strtol(temp, &strtol_end, 10);
|
|
|
|
if (errno || temp == strtol_end || strtol_result > INT_MAX) {
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
mesgSummary->newCount = (int) strtol_result;
|
|
token_found = TRUE;
|
|
i = 0;
|
|
} else {
|
|
return SIP_ERROR;
|
|
}
|
|
} else {
|
|
temp[i] = val[j];
|
|
if (++i >= MAX_SIP_URL_LENGTH) {
|
|
return SIP_ERROR;
|
|
}
|
|
}
|
|
j++;
|
|
}
|
|
temp[i] = '\0';
|
|
|
|
if (token_found) {
|
|
errno = 0;
|
|
strtol_result = strtol(temp, &strtol_end, 10);
|
|
|
|
if (errno || temp == strtol_end || strtol_result > INT_MAX) {
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
mesgSummary->oldCount = (int) strtol_result;
|
|
}
|
|
|
|
temp[i = 0] = '\0';
|
|
while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
|
|
j++;
|
|
}
|
|
|
|
if (val[j] == '(') {
|
|
j++;
|
|
while (val[j] != '\0' && val[j] != '\n' && isspace(val[j])) {
|
|
j++;
|
|
}
|
|
|
|
while (val[j] != '\0' && val[j] != '\n' && val[j] != '\r' && !isspace(val[j]) && val[j] != ')') {
|
|
if (!isdigit(val[j])) {
|
|
if (val[j] == '/') {
|
|
temp[i] = '\0';
|
|
|
|
errno = 0;
|
|
strtol_result = strtol(temp, &strtol_end, 10);
|
|
|
|
if (errno || temp == strtol_end || strtol_result > INT_MAX) {
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
mesgSummary->hpNewCount = (int) strtol_result;
|
|
hp_found = TRUE;
|
|
i = 0;
|
|
} else {
|
|
return SIP_ERROR;
|
|
}
|
|
} else {
|
|
temp[i] = val[j];
|
|
if (++i >= MAX_SIP_URL_LENGTH) {
|
|
return SIP_ERROR;
|
|
}
|
|
}
|
|
j++;
|
|
}
|
|
temp[i] = '\0';
|
|
if (hp_found) {
|
|
errno = 0;
|
|
strtol_result = strtol(temp, &strtol_end, 10);
|
|
|
|
if (errno || temp == strtol_end || strtol_result > INT_MAX) {
|
|
return SIP_ERROR;
|
|
}
|
|
|
|
mesgSummary->hpOldCount = (int) strtol_result;
|
|
}
|
|
}
|
|
if (!hp_found) {
|
|
mesgSummary->hpNewCount = mesgSummary->hpOldCount = -1;
|
|
}
|
|
/*make sure the urgent message counts don't exceed the total message counts*/
|
|
if ((mesgSummary->hpNewCount > mesgSummary->newCount) ||
|
|
(mesgSummary->hpOldCount > mesgSummary->oldCount)) {
|
|
return SIP_ERROR;
|
|
}
|
|
if (!token_found) {
|
|
return SIP_ERROR;
|
|
}
|
|
} else {
|
|
return SIP_ERROR;
|
|
}
|
|
}
|
|
|
|
return SIP_OK;
|
|
}
|
|
/*
|
|
* This function compares two strings that may contain escaped characters
|
|
* in either of them and determines if they are equivalent, ignoring the
|
|
* case if ignore_case is TRUE.
|
|
* s1 = ptr to the first string of chars
|
|
* s2 = ptr to the second string of chars
|
|
* return: integer value of 0 if they are equivalent, <> 0 otherwise.
|
|
*/
|
|
int
|
|
sippmh_cmpURLStrings (const char *s1, const char *s2, boolean ignore_case)
|
|
{
|
|
const unsigned char *us1 = (const unsigned char *) s1;
|
|
const unsigned char *us2 = (const unsigned char *) s2;
|
|
int value1, value2;
|
|
int esc_char_incr_in_s1 = 0; // # of chars to skip in s1 if the given char is escaped
|
|
int esc_char_incr_in_s2 = 0; // # of chars to skip in s2 if the given char is escaped
|
|
|
|
if ((!s1 && s2) || (s1 && !s2)) /* no match if only one ptr is NULL */
|
|
return (int) (s1 - s2); /* if one of these is NULL it will be the
|
|
* lesser of the two values and therefore
|
|
* we'll get the proper sign in the int */
|
|
|
|
if (s1 == s2) { /* match if both ptrs the same (e.g. NULL) */
|
|
return 0;
|
|
}
|
|
|
|
value1 = 0;
|
|
value2 = 0;
|
|
while (*us1 != '\0') {
|
|
if (*us1 == PERCENT) {
|
|
esc_char_incr_in_s1 = 2;
|
|
|
|
/* Convert 1st and 2nd hex chars to int */
|
|
value1 = sippmh_htoi(*(us1 + 1)) * 16;
|
|
value1 += sippmh_htoi(*(us1 + 2));
|
|
} else {
|
|
value1 = *us1;
|
|
}
|
|
|
|
if (*us2 == PERCENT) {
|
|
esc_char_incr_in_s2 = 2;
|
|
|
|
/* Convert 1st and 2nd hex chars to int */
|
|
value2 = sippmh_htoi(*(us2 + 1)) * 16;
|
|
value2 += sippmh_htoi(*(us2 + 2));
|
|
} else {
|
|
value2 = *us2;
|
|
}
|
|
|
|
if ((ignore_case && (toupper(value1) == toupper(value2))) ||
|
|
(!ignore_case && (value1 == value2))) {
|
|
us1 += (1 + esc_char_incr_in_s1);
|
|
us2 += (1 + esc_char_incr_in_s2);
|
|
esc_char_incr_in_s1 = 0;
|
|
esc_char_incr_in_s2 = 0;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ignore_case) {
|
|
return (toupper(value1) - toupper(value2));
|
|
} else {
|
|
return (value1 - value2);
|
|
}
|
|
}
|
|
|
|
int
|
|
sippmh_add_call_info (sipMessage_t *sip_message_p, cc_call_info_t *call_info_p)
|
|
{
|
|
if (sip_message_p && call_info_p) {
|
|
char *call_info_hdr_p = ccsip_encode_call_info_hdr(call_info_p, NULL);
|
|
|
|
if (call_info_hdr_p != NULL) {
|
|
(void) sippmh_add_text_header(sip_message_p, SIP_HEADER_CALL_INFO,
|
|
(const char *) call_info_hdr_p);
|
|
cpr_free(call_info_hdr_p);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_parse_supported_require
|
|
*
|
|
* Parameters: contents of the Supported or Require header
|
|
*
|
|
* punsupported_tokens: if the caller wants to know the
|
|
* unsupported option tokens, it will pass a non-null pointer.
|
|
* If punsupporte_tokens is NULL, then the caller does not
|
|
* want to know the unsupported options.
|
|
*
|
|
* It is caller's responsibility to free the contents of this
|
|
* pointer.
|
|
*
|
|
* Description: This function will parse for the various options tags
|
|
* in the supported and require headers
|
|
*
|
|
* Returns: A bit map containing the values in the header
|
|
*
|
|
* Format of the line is as follows:
|
|
* Supported: replaces, join, sec-agree, whatever ...
|
|
* Require: replaces, join, sec-agree, whatever ...
|
|
*/
|
|
uint32_t
|
|
sippmh_parse_supported_require (const char *header, char **punsupported_tokens)
|
|
{
|
|
const char *fname = "sippmh_parse_supported_require";
|
|
uint32_t tags = 0;
|
|
char *temp_header;
|
|
char *token;
|
|
const char *delim = ", \r\n\t";
|
|
int unsupported_tokens_size = 0;
|
|
char *bad_token = NULL;
|
|
int size;
|
|
char *lasts = NULL;
|
|
|
|
if (header == NULL) {
|
|
return (tags);
|
|
}
|
|
|
|
if (punsupported_tokens != NULL) {
|
|
*punsupported_tokens = NULL; //assume everything will go right
|
|
}
|
|
|
|
//need to keep own buffer since PL_strtok_r is destructive
|
|
size = strlen(header) + 1;
|
|
temp_header = (char *) cpr_malloc(size);
|
|
if (temp_header == NULL) {
|
|
CCSIP_DEBUG_ERROR("%s: malloc failed for strlen(header)=%d\n", fname,
|
|
strlen(header));
|
|
return tags;
|
|
}
|
|
sstrncpy(temp_header, header, size);
|
|
|
|
token = PL_strtok_r(temp_header, delim, &lasts);
|
|
while (token != NULL) {
|
|
bad_token = NULL;
|
|
|
|
if (strcmp(token, REQ_SUPP_PARAM_REPLACES) == 0) {
|
|
tags |= replaces_tag;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_100REL) == 0) {
|
|
tags |= rel_tag;
|
|
bad_token = token;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_EARLY_SESSION) == 0) {
|
|
tags |= early_session_tag;
|
|
bad_token = token;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_JOIN) == 0) {
|
|
tags |= join_tag;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_PATH) == 0) {
|
|
tags |= path_tag;
|
|
bad_token = token;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_PRECONDITION) == 0) {
|
|
tags |= precondition_tag;
|
|
bad_token = token;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_PREF) == 0) {
|
|
tags |= pref_tag;
|
|
bad_token = token;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_PRIVACY) == 0) {
|
|
tags |= privacy_tag;
|
|
bad_token = token;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_SEC_AGREE) == 0) {
|
|
tags |= sec_agree_tag;
|
|
bad_token = token;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_TIMER) == 0) {
|
|
tags |= timer_tag;
|
|
bad_token = token;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_NOREFERSUB) == 0) {
|
|
tags |= norefersub_tag;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_CISCO_CALLINFO) == 0) {
|
|
tags |= cisco_callinfo_tag;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_CISCO_SERVICE_CONTROL) == 0) {
|
|
tags |= cisco_service_control_tag;
|
|
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_SDP_ANAT) == 0) {
|
|
tags |= sdp_anat_tag;
|
|
}
|
|
else if (strcmp(token, REQ_SUPP_PARAM_EXTENED_REFER) == 0) {
|
|
tags |= extended_refer_tag;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_CISCO_SERVICEURI) == 0) {
|
|
tags |= cisco_serviceuri_tag;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_CISCO_ESCAPECODES) == 0) {
|
|
tags |= cisco_escapecodes_tag;
|
|
} else if (strcmp(token, REQ_SUPP_PARAM_CISCO_SRTP_FALLBACK) == 0) {
|
|
tags |= cisco_srtp_fallback_tag;
|
|
}
|
|
else {
|
|
//This is not necessarily an error. Other end may support something we
|
|
//don't. Is only an error if it shows up in Require.
|
|
tags |= (uint32_t) unrecognized_tag;
|
|
bad_token = token;
|
|
}
|
|
|
|
if (bad_token != NULL) {
|
|
CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Invalid tag in Require/Supported %s\n",
|
|
DEB_F_PREFIX_ARGS(SIP_TAG, fname), bad_token);
|
|
|
|
//allocate memory for unsupported options if necessary
|
|
if (punsupported_tokens) {
|
|
if (!*punsupported_tokens) {
|
|
unsupported_tokens_size = strlen(header) + 1;
|
|
*punsupported_tokens = (char *) cpr_malloc(unsupported_tokens_size);
|
|
if (*punsupported_tokens) {
|
|
memset(*punsupported_tokens, 0, unsupported_tokens_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
//caller wants to know what came in Require and we dont support
|
|
if (punsupported_tokens && *punsupported_tokens) {
|
|
//include token into illegal_tokens
|
|
if (strlen(*punsupported_tokens) > 0) {
|
|
sstrncat(*punsupported_tokens, ",", unsupported_tokens_size - strlen(*punsupported_tokens)); //add a ","
|
|
}
|
|
sstrncat(*punsupported_tokens, bad_token, unsupported_tokens_size - strlen(*punsupported_tokens));
|
|
}
|
|
bad_token = NULL;
|
|
}
|
|
|
|
//get next token
|
|
token = PL_strtok_r(NULL, delim, &lasts);
|
|
}
|
|
cpr_free(temp_header);
|
|
return (tags);
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_parse_allow_header
|
|
*
|
|
* Parameters: contents of the Allow header
|
|
*
|
|
* Description: This function will parse for the various methods
|
|
* supported in the allow header
|
|
*
|
|
* Returns: A bit map containing the values in the header
|
|
*
|
|
* Format of the line is as follows:
|
|
* Allow: ACK, BYE, CANCEL, ....
|
|
*/
|
|
uint16_t
|
|
sippmh_parse_allow_header (const char *header)
|
|
{
|
|
uint16_t tags = 0;
|
|
|
|
if (header == NULL) {
|
|
return (tags);
|
|
}
|
|
if (strstr(header, SIP_METHOD_ACK)) {
|
|
tags |= ALLOW_ACK;
|
|
}
|
|
if (strstr(header, SIP_METHOD_BYE)) {
|
|
tags |= ALLOW_BYE;
|
|
}
|
|
if (strstr(header, SIP_METHOD_CANCEL)) {
|
|
tags |= ALLOW_CANCEL;
|
|
}
|
|
if (strstr(header, SIP_METHOD_INFO)) {
|
|
tags |= ALLOW_INFO;
|
|
}
|
|
if (strstr(header, SIP_METHOD_INVITE)) {
|
|
tags |= ALLOW_INVITE;
|
|
}
|
|
if (strstr(header, SIP_METHOD_MESSAGE)) {
|
|
tags |= ALLOW_MESSAGE;
|
|
}
|
|
if (strstr(header, SIP_METHOD_NOTIFY)) {
|
|
tags |= ALLOW_NOTIFY;
|
|
}
|
|
if (strstr(header, SIP_METHOD_OPTIONS)) {
|
|
tags |= ALLOW_OPTIONS;
|
|
}
|
|
if (strstr(header, SIP_METHOD_PRACK)) {
|
|
tags |= ALLOW_PRACK;
|
|
}
|
|
if (strstr(header, SIP_METHOD_PUBLISH)) {
|
|
tags |= ALLOW_PUBLISH;
|
|
}
|
|
if (strstr(header, SIP_METHOD_REFER)) {
|
|
tags |= ALLOW_REFER;
|
|
}
|
|
if (strstr(header, SIP_METHOD_REGISTER)) {
|
|
tags |= ALLOW_REGISTER;
|
|
}
|
|
if (strstr(header, SIP_METHOD_SUBSCRIBE)) {
|
|
tags |= ALLOW_SUBSCRIBE;
|
|
}
|
|
if (strstr(header, SIP_METHOD_UPDATE)) {
|
|
tags |= ALLOW_UPDATE;
|
|
}
|
|
return (tags);
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_parse_accept_header
|
|
*
|
|
* Parameters: contents of the Accept header
|
|
*
|
|
* Description: This function will parse for the various capabilities
|
|
* signalled in the Accept header
|
|
*
|
|
* Returns: A bit map containing the values in the header
|
|
*
|
|
* Format of the line is as follows:
|
|
* Accept: application/sdp, nultipart/mixed, multipart/alternative
|
|
*/
|
|
uint16_t
|
|
sippmh_parse_accept_header (const char *header)
|
|
{
|
|
uint16_t tags = 0;
|
|
|
|
if (header == NULL) {
|
|
return (tags);
|
|
}
|
|
if (strstr(header, SIP_CONTENT_TYPE_SDP)) {
|
|
tags |= (0x01 << SIP_CONTENT_TYPE_SDP_VALUE);
|
|
}
|
|
if (strstr(header, SIP_CONTENT_TYPE_MULTIPART_MIXED)) {
|
|
tags |= (0x01 << SIP_CONTENT_TYPE_MULTIPART_MIXED_VALUE);
|
|
}
|
|
if (strstr(header, SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE)) {
|
|
tags |= (0x01 << SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE_VALUE);
|
|
}
|
|
return tags;
|
|
}
|
|
|
|
/*
|
|
* Function: ccsip_process_network_message
|
|
*
|
|
* Parameters: sipmsg, pointer to pointer of the buffer
|
|
* bytes used, display message.
|
|
*
|
|
* Description: Currently only used for tcp packets received to do the
|
|
* framing. Ported from Propel.
|
|
* Returns: A bit map containing the values in the header
|
|
*/
|
|
ccsipRet_e
|
|
ccsip_process_network_message (sipMessage_t **sipmsg_p,
|
|
char **buf,
|
|
unsigned long *nbytes_used,
|
|
char **display_msg)
|
|
{
|
|
static const char fname[] = "ccsip_process_network_message";
|
|
sipMessage_t *sip_msg = NULL;
|
|
int local_nbytes = *nbytes_used;
|
|
uint32_t bytes_used;
|
|
char *local_buf_ptr;
|
|
|
|
sip_msg = sippmh_message_create();
|
|
if (sip_msg == NULL) {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("%s: Error in creating SIP Msg\n", fname);
|
|
}
|
|
*sipmsg_p = NULL;
|
|
return SIP_MSG_CREATE_ERR;
|
|
}
|
|
|
|
/* Init local variables */
|
|
bytes_used = local_nbytes;
|
|
local_buf_ptr = *buf;
|
|
|
|
if (sippmh_process_network_message(sip_msg, local_buf_ptr, &bytes_used)
|
|
== STATUS_FAILURE) {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("%s: process_network_message failed.\n", fname);
|
|
}
|
|
sippmh_message_free(sip_msg);
|
|
*sipmsg_p = NULL;
|
|
return SIP_MSG_PARSE_ERR;
|
|
}
|
|
|
|
if (sippmh_is_message_complete(sip_msg)) {
|
|
if (display_msg) {
|
|
*display_msg = (char *) cpr_malloc(bytes_used + 1);
|
|
if (*display_msg == NULL) {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("%s: malloc of display msg failed.\n", fname);
|
|
}
|
|
sippmh_message_free(sip_msg);
|
|
*sipmsg_p = NULL;
|
|
return SIP_MSG_PARSE_ERR;
|
|
}
|
|
sstrncpy(*display_msg, local_buf_ptr, bytes_used + 1);
|
|
}
|
|
local_nbytes -= bytes_used;
|
|
local_buf_ptr += bytes_used;
|
|
|
|
/* Update information */
|
|
*sipmsg_p = sip_msg;
|
|
*nbytes_used = local_nbytes;
|
|
*buf = local_buf_ptr;
|
|
|
|
return SIP_SUCCESS;
|
|
} else {
|
|
CCSIP_ERR_DEBUG {
|
|
buginf("%s: process_network_msg: not complete\n", fname);
|
|
}
|
|
sippmh_message_free(sip_msg);
|
|
*sipmsg_p = NULL;
|
|
|
|
return SIP_MSG_INCOMPLETE_ERR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_parse_service_control_body
|
|
*
|
|
* Parameters: contents of the service control notify body and its length
|
|
*
|
|
* Description: This function will parse for the reset/restart
|
|
* instructions and parameters in this body
|
|
*
|
|
* Returns: 0 if body parsed correctly
|
|
* Parsed values are added to passed structure
|
|
*
|
|
* Format of the body is as follows:
|
|
* action = [reset | restart | check-version | call-preservation | apply-config ]
|
|
* RegisterCallId= {<callid from register msg>}
|
|
* ConfigVersionStamp = {79829A69-9489-4C8E-8143-90A9C22DFAD7}
|
|
* DialplanVersionStamp = {79829A69-9489-4C8E-8143-90A9C22DFAD7}
|
|
* SoftkeyVersionStamp = {79829A69-9489-4C8E-8143-90A9C22DFAD7}
|
|
* CUCMResult=[no_change | config_applied | reregister_needed]
|
|
* FirmwareLoadId = {SIP70.8-4-0-28S}
|
|
* LoadServer={10.81.15.24}
|
|
* LogServer={<ipv4 address or ipv6 address or fqdn> <port>} // This is used for ppid
|
|
* UpgradeTime=[now | later]
|
|
* PPID=[enabled | disabled]
|
|
*/
|
|
|
|
sipServiceControl_t *
|
|
sippmh_parse_service_control_body (char *msgBody, int msgLength)
|
|
{
|
|
pmhRstream_t *rs = NULL;
|
|
char *line = NULL, *value = NULL;
|
|
boolean body_read = FALSE;
|
|
sipServiceControl_t *scp = NULL;
|
|
|
|
if (msgLength==0) {
|
|
return (NULL);
|
|
}
|
|
|
|
if ((rs = pmhutils_rstream_create(msgBody, msgLength))
|
|
== NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
scp = (sipServiceControl_t *)
|
|
cpr_calloc(1, sizeof(sipServiceControl_t));
|
|
if (!scp) {
|
|
cpr_free(rs);
|
|
return NULL;
|
|
}
|
|
|
|
while (!body_read) {
|
|
line = pmhutils_rstream_read_line(rs);
|
|
if (line) {
|
|
value = strchr(line, '=');
|
|
if (value) {
|
|
value++;
|
|
while (*value == ' ') {
|
|
value++;
|
|
}
|
|
}
|
|
if (strlen(line) == 0) {
|
|
|
|
} else if (!strncasecmp(line, "action", sizeof("action") - 1)) {
|
|
if (value == NULL) {
|
|
scp->action = SERVICE_CONTROL_ACTION_INVALID;
|
|
} else if (!strncasecmp(value, "reset", sizeof("reset") - 1)) {
|
|
scp->action = SERVICE_CONTROL_ACTION_RESET;
|
|
} else if (!strncasecmp(value, "restart",
|
|
sizeof("restart") - 1)) {
|
|
scp->action = SERVICE_CONTROL_ACTION_RESTART;
|
|
} else if (!strncasecmp(value, "check-version",
|
|
sizeof("check-version") - 1)) {
|
|
scp->action = SERVICE_CONTROL_ACTION_CHECK_VERSION;
|
|
} else if (!strncasecmp(value, "call-preservation",
|
|
sizeof("call-preservation") - 1)) {
|
|
scp->action = SERVICE_CONTROL_ACTION_CALL_PRESERVATION;
|
|
} else if (!strncasecmp(value, "apply-config",
|
|
sizeof("apply-config") - 1)) {
|
|
scp->action = SERVICE_CONTROL_ACTION_APPLY_CONFIG;
|
|
|
|
} else {
|
|
scp->action = SERVICE_CONTROL_ACTION_INVALID;
|
|
}
|
|
} else if (!strncasecmp(line, "RegisterCallId",
|
|
sizeof("RegisterCallId") - 1)) {
|
|
if (scp->registerCallID == NULL) {
|
|
if (value == NULL) {
|
|
scp->registerCallID = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->registerCallID = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->registerCallID)
|
|
memcpy(scp->registerCallID, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (!strncasecmp(line, "ConfigVersionStamp",
|
|
sizeof("ConfigVersionStamp") - 1)) {
|
|
if (scp->configVersionStamp == NULL) {
|
|
if (value == NULL) {
|
|
scp->configVersionStamp = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->configVersionStamp = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->configVersionStamp)
|
|
memcpy(scp->configVersionStamp, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (!strncasecmp(line, "DialplanVersionStamp",
|
|
sizeof("DialplanVersionStamp") - 1)) {
|
|
if (scp->dialplanVersionStamp == NULL) {
|
|
if (value == NULL) {
|
|
scp->dialplanVersionStamp = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->dialplanVersionStamp = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->dialplanVersionStamp)
|
|
memcpy(scp->dialplanVersionStamp, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
} else if (!strncasecmp(line, "SoftkeyVersionStamp",
|
|
sizeof("SoftkeyVersionStamp") - 1)) {
|
|
if (scp->softkeyVersionStamp == NULL) {
|
|
if (value == NULL) {
|
|
scp->softkeyVersionStamp = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->softkeyVersionStamp = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->softkeyVersionStamp)
|
|
memcpy(scp->softkeyVersionStamp, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
} else if (!strncasecmp(line, "FeatureControlVersionStamp",
|
|
sizeof("FeatureControlVersionStamp") - 1)) {
|
|
if (scp->fcpVersionStamp == NULL) {
|
|
if (value == NULL) {
|
|
scp->fcpVersionStamp = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->fcpVersionStamp = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->fcpVersionStamp)
|
|
memcpy(scp->fcpVersionStamp, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
} else if (!strncasecmp(line, "CUCMResult",
|
|
sizeof("CUCMResult") - 1)) {
|
|
if (value == NULL) {
|
|
scp->cucm_result = NULL;
|
|
} else {
|
|
int len =0;
|
|
if ((strncasecmp(value, "no_change",
|
|
len = strlen("no_change")) == 0) ||
|
|
(strncasecmp(value, "config_applied",
|
|
len = strlen("config_applied")) == 0) ||
|
|
(strncasecmp(value, "reregister_needed",
|
|
len = strlen("reregister_needed")) == 0)) {
|
|
scp->cucm_result = (char *) cpr_calloc(1,len + 1);
|
|
if (scp->cucm_result) {
|
|
sstrncpy(scp->cucm_result, value, len);
|
|
scp->cucm_result[len] = '\0';
|
|
}
|
|
}
|
|
}
|
|
} else if (!strncasecmp(line, "LoadId",
|
|
sizeof("LoadId") - 1)) {
|
|
if (scp->firmwareLoadId == NULL) {
|
|
if (value == NULL) {
|
|
scp->firmwareLoadId = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->firmwareLoadId = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->firmwareLoadId)
|
|
memcpy(scp->firmwareLoadId, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
} else if (!strncasecmp(line, "InactiveLoadId",
|
|
sizeof("InactiveLoadId") - 1)) {
|
|
if (scp->firmwareInactiveLoadId == NULL) {
|
|
if (value == NULL) {
|
|
scp->firmwareInactiveLoadId = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->firmwareInactiveLoadId = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->firmwareInactiveLoadId)
|
|
memcpy(scp->firmwareInactiveLoadId, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
} else if (!strncasecmp(line, "LoadServer",
|
|
sizeof("LoadServer") - 1)) {
|
|
if (scp->loadServer == NULL) {
|
|
if (value == NULL) {
|
|
scp->loadServer = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->loadServer = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->loadServer)
|
|
memcpy(scp->loadServer, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
} else if (!strncasecmp(line, "LogServer",
|
|
sizeof("LogServer") - 1)) {
|
|
if (scp->logServer == NULL) {
|
|
if (value == NULL) {
|
|
scp->logServer = NULL;
|
|
} else if (value[0]) {
|
|
int len = strlen(value);
|
|
|
|
/* make sure curly braces are present */
|
|
if ((*value == '{') && (*(value + len - 1) == '}')) {
|
|
scp->logServer = (char *)
|
|
cpr_calloc(1, len + 1);
|
|
/* ignore the beginning and ending curly brace */
|
|
if (scp->logServer)
|
|
memcpy(scp->logServer, value + 1,
|
|
(len - 2));
|
|
}
|
|
}
|
|
}
|
|
} else if (!strncasecmp(line, "PPID", sizeof("PPID") - 1)) {
|
|
if (value == NULL) {
|
|
scp->ppid = FALSE;
|
|
} else if (!strncasecmp(value, "enabled", sizeof("enabled") - 1)) {
|
|
scp->ppid = TRUE;
|
|
} else if (!strncasecmp(value, "disabled",
|
|
sizeof("disabled") - 1)) {
|
|
scp->ppid = FALSE;
|
|
} else {
|
|
scp->ppid = FALSE;
|
|
}
|
|
|
|
}
|
|
cpr_free(line);
|
|
|
|
} else {
|
|
body_read = TRUE;
|
|
}
|
|
}
|
|
// Free the created stream structure
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
return (scp);
|
|
}
|
|
|
|
void
|
|
sippmh_free_service_control_info (sipServiceControl_t *scp)
|
|
{
|
|
if (!scp) {
|
|
return;
|
|
}
|
|
|
|
if (scp->configVersionStamp)
|
|
cpr_free(scp->configVersionStamp);
|
|
if (scp->dialplanVersionStamp)
|
|
cpr_free(scp->dialplanVersionStamp);
|
|
if (scp->registerCallID)
|
|
cpr_free(scp->registerCallID);
|
|
if (scp->softkeyVersionStamp)
|
|
cpr_free(scp->softkeyVersionStamp);
|
|
if (scp->fcpVersionStamp)
|
|
cpr_free(scp->fcpVersionStamp);
|
|
if (scp->cucm_result)
|
|
cpr_free(scp->cucm_result);
|
|
if (scp->loadServer)
|
|
cpr_free(scp->loadServer);
|
|
if (scp->firmwareLoadId)
|
|
cpr_free(scp->firmwareLoadId);
|
|
if (scp->logServer)
|
|
cpr_free(scp->logServer);
|
|
|
|
cpr_free(scp);
|
|
}
|
|
|
|
int32_t
|
|
sippmh_parse_max_forwards (const char *max_fwd_hdr)
|
|
{
|
|
int32_t maxFwd;
|
|
|
|
if (max_fwd_hdr) {
|
|
if (isdigit(*max_fwd_hdr)) {
|
|
maxFwd = strtol(max_fwd_hdr, NULL, 10);
|
|
if ((maxFwd >= 0) && (maxFwd <= 255)) {
|
|
return maxFwd;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_parse_url_from_hdr
|
|
*
|
|
* Parameters: String that needs to be parsed
|
|
*
|
|
* Description: This function will strip out the tags and
|
|
* other characters in the header to retreive
|
|
* a string that could be used as the request uri.
|
|
* Note that the input string needs to be well formed.
|
|
*
|
|
* Returns: A string containing the request uri
|
|
*
|
|
*/
|
|
string_t
|
|
sippmh_get_url_from_hdr (char *input_str)
|
|
{
|
|
char *left_bracket, *right_bracket = NULL;
|
|
|
|
/*
|
|
This function is only intended for properly
|
|
formed headers.
|
|
*/
|
|
left_bracket = strpbrk(input_str, ",<");
|
|
if (left_bracket) {
|
|
left_bracket++;
|
|
right_bracket = strchr(left_bracket, '>');
|
|
if (right_bracket) {
|
|
*right_bracket = '\0';
|
|
}
|
|
input_str = left_bracket;
|
|
}
|
|
return (string_t)input_str;
|
|
}
|
|
/*
|
|
* Function: sippmh_parse_join_header
|
|
*
|
|
* Parameters: pointer to string of the join header contents
|
|
*
|
|
* Returns: Parsed structure if parsed correctly, NULL otherwise
|
|
*
|
|
* Format of the header is as follows:
|
|
* Join: abcd;from-tag=efgh;to-tag=ijkl
|
|
*/
|
|
sipJoinInfo_t *
|
|
sippmh_parse_join_header (const char *header)
|
|
{
|
|
sipJoinInfo_t *join = NULL;
|
|
char *semi = NULL;
|
|
unsigned int param_len;
|
|
char *params, *param_name, *param_value, *save_params;
|
|
|
|
if (!header) {
|
|
return NULL;
|
|
}
|
|
|
|
join = (sipJoinInfo_t *) cpr_calloc(1, sizeof(sipJoinInfo_t));
|
|
if (!join) {
|
|
return NULL;
|
|
}
|
|
|
|
// Read the call-id, if present
|
|
semi = strchr(header, SEMI_COLON);
|
|
if (semi) {
|
|
join->call_id = (char *) cpr_calloc(1, semi-header + 1);
|
|
if (join->call_id == NULL) {
|
|
sippmh_free_join_info(join);
|
|
return NULL;
|
|
}
|
|
sstrncpy(join->call_id, header, semi-header);
|
|
} else {
|
|
// call-id is the only parameter
|
|
join->call_id = cpr_strdup(header);
|
|
if (join->call_id == NULL) {
|
|
sippmh_free_join_info(join);
|
|
return NULL;
|
|
}
|
|
return (join);
|
|
}
|
|
|
|
params = cpr_strdup(semi);
|
|
if (!params) {
|
|
sippmh_free_join_info(join);
|
|
return NULL;
|
|
}
|
|
save_params = params;
|
|
while (1) {
|
|
while (*params == ';') {
|
|
params++;
|
|
}
|
|
param_name = params;
|
|
SKIP_SIP_TOKEN(params);
|
|
param_len = params - param_name;
|
|
if (param_len == 0) {
|
|
sippmh_free_join_info(join);
|
|
cpr_free(save_params);
|
|
return NULL;
|
|
}
|
|
|
|
/* Parse from-tag parameter */
|
|
if ((param_len == sizeof(SIP_HEADER_JOIN_FROM_TAG) - 1) &&
|
|
(strncasecmp(param_name, SIP_HEADER_JOIN_FROM_TAG,
|
|
sizeof(SIP_HEADER_JOIN_FROM_TAG) - 1) == 0) &&
|
|
(join->from_tag == NULL)) {
|
|
params = parse_generic_param(params, ¶m_value);
|
|
if (params == NULL) {
|
|
sippmh_free_join_info(join);
|
|
cpr_free(save_params);
|
|
return NULL;
|
|
} else {
|
|
join->from_tag = (char *) cpr_calloc(1, params-param_value + 1);
|
|
if (join->from_tag) {
|
|
sstrncpy(join->from_tag, param_value, params-param_value + 1);
|
|
}
|
|
SKIP_LWS(params);
|
|
if (*params == SEMI_COLON) {
|
|
*params++ = '\0';
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/* Parse to-tag parameter */
|
|
} else if ((param_len == sizeof(SIP_HEADER_JOIN_TO_TAG) - 1) &&
|
|
(strncasecmp(param_name, SIP_HEADER_JOIN_TO_TAG,
|
|
sizeof(SIP_HEADER_JOIN_TO_TAG) - 1) == 0) &&
|
|
(join->to_tag == NULL)) {
|
|
params = parse_generic_param(params, ¶m_value);
|
|
if (params == NULL) {
|
|
sippmh_free_join_info(join);
|
|
cpr_free(save_params);
|
|
return NULL;
|
|
} else {
|
|
join->to_tag = (char *) cpr_calloc(1, params-param_value + 1);
|
|
if (join->to_tag) {
|
|
sstrncpy(join->to_tag, param_value, params-param_value + 1);
|
|
}
|
|
if (*params == SEMI_COLON) {
|
|
*params++ = '\0';
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// Skip over unexpected parameter
|
|
SKIP_SIP_TOKEN(params);
|
|
}
|
|
|
|
SKIP_LWS(params);
|
|
}
|
|
cpr_free(save_params);
|
|
return (join);
|
|
}
|
|
|
|
void
|
|
sippmh_free_join_info (sipJoinInfo_t *join)
|
|
{
|
|
|
|
if (!join) {
|
|
return;
|
|
}
|
|
if (join->call_id)
|
|
cpr_free(join->call_id);
|
|
if (join->from_tag)
|
|
cpr_free(join->from_tag);
|
|
if (join->to_tag)
|
|
cpr_free(join->to_tag);
|
|
cpr_free(join);
|
|
}
|
|
|
|
sipRet_t
|
|
sippmh_add_join_header (sipMessage_t *message, sipJoinInfo_t *join)
|
|
{
|
|
char joinhdr[MAX_SIP_HEADER_LENGTH+1];
|
|
int left;
|
|
|
|
// Write out the header in a buffer
|
|
if (!message) {
|
|
return STATUS_FAILURE;
|
|
}
|
|
|
|
snprintf(joinhdr, MAX_SIP_HEADER_LENGTH, "%s", join->call_id);
|
|
left = (uint16_t) MAX_SIP_HEADER_LENGTH - strlen(join->call_id);
|
|
if (join->from_tag && left > 0) {
|
|
sstrncat(joinhdr, ";from-tag=", left);
|
|
left -= sizeof(";from-tag=") - 1;
|
|
sstrncat(joinhdr, join->from_tag, left);
|
|
left -= strlen(join->from_tag);
|
|
}
|
|
if (join->to_tag && left > 0) {
|
|
sstrncat(joinhdr, ";to-tag=", left);
|
|
left -= sizeof(";to-tag=") - 1;
|
|
sstrncat(joinhdr, join->to_tag, left);
|
|
}
|
|
return (sippmh_add_text_header(message, SIP_HEADER_JOIN, joinhdr));
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_parse_subscription_state
|
|
*
|
|
* Parameters: contents of the Subscription-State header
|
|
*
|
|
* Description: This function will parse for the state, expiry
|
|
* time, and a reason code in the header
|
|
*
|
|
* Returns: -1 if error, 0 if successfully parsed.
|
|
*
|
|
* Format of the line is as follows:
|
|
* Subscription-State: active;expires=7200;reason=whatever;retry-after=50
|
|
*/
|
|
int
|
|
sippmh_parse_subscription_state(sipSubscriptionStateInfo_t *subsStateInfo,
|
|
const char *subs_state)
|
|
//TODO: how can subs_state be a const char [] when it can be modified below?
|
|
{
|
|
char *ptr;
|
|
char temp[TEMP_PARSE_BUFFER_SIZE];
|
|
uint8_t i;
|
|
|
|
if (!subs_state) {
|
|
return (-1);
|
|
}
|
|
|
|
if (!strncasecmp(subs_state, SIP_SUBSCRIPTION_STATE_ACTIVE,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_ACTIVE) - 1)) {
|
|
subsStateInfo->state = SUBSCRIPTION_STATE_ACTIVE;
|
|
} else if (!strncasecmp(subs_state, SIP_SUBSCRIPTION_STATE_PENDING,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_PENDING) - 1)) {
|
|
subsStateInfo->state = SUBSCRIPTION_STATE_PENDING;
|
|
} else if (!strncasecmp(subs_state, SIP_SUBSCRIPTION_STATE_TERMINATED,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_PENDING) - 1)) {
|
|
subsStateInfo->state = SUBSCRIPTION_STATE_TERMINATED;
|
|
}
|
|
|
|
ptr = strchr(subs_state, ';');
|
|
if (!ptr) {
|
|
return (0);
|
|
}
|
|
SKIP_LWS(ptr);
|
|
|
|
// Look for expires tag
|
|
ptr = strstr(subs_state, SIP_SUBSCRIPTION_STATE_EXPIRES);
|
|
if (ptr) {
|
|
ptr = ptr + sizeof(SIP_SUBSCRIPTION_STATE_EXPIRES); // Go over the '='
|
|
SKIP_LWS(ptr);
|
|
if (*ptr) {
|
|
boolean is_int = FALSE;
|
|
|
|
memset(temp, 0, sizeof(temp));
|
|
i = 0;
|
|
while (isdigit(*ptr) && (i < sizeof(temp)-1)) {
|
|
temp[i++] = *ptr;
|
|
ptr++;
|
|
is_int = TRUE;
|
|
}
|
|
if (is_int == TRUE) {
|
|
subsStateInfo->expires = strtoul(temp, NULL, 10);
|
|
}
|
|
}
|
|
}
|
|
// Look for reason tag
|
|
ptr = strstr(subs_state, SIP_SUBSCRIPTION_STATE_REASON);
|
|
if (ptr) {
|
|
ptr = ptr + sizeof(SIP_SUBSCRIPTION_STATE_REASON);
|
|
SKIP_LWS(ptr);
|
|
if (*ptr) {
|
|
if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_DEACTIVATED,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_REASON_DEACTIVATED) - 1)) {
|
|
subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_DEACTIVATED;
|
|
} else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_PROBATION,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_REASON_PROBATION) - 1)) {
|
|
subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_PROBATION;
|
|
} else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_REJECTED,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_REASON_REJECTED) - 1)) {
|
|
subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_REJECTED;
|
|
} else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_TIMEOUT,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_REASON_TIMEOUT) - 1)) {
|
|
subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_TIMEOUT;
|
|
} else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_GIVEUP,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_REASON_GIVEUP) - 1)) {
|
|
subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_GIVEUP;
|
|
} else if (!strncasecmp(ptr, SIP_SUBSCRIPTION_STATE_REASON_NORESOURCE,
|
|
sizeof(SIP_SUBSCRIPTION_STATE_REASON_NORESOURCE) - 1)) {
|
|
subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_NORESOURCE;
|
|
} else {
|
|
subsStateInfo->reason = SUBSCRIPTION_STATE_REASON_INVALID;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Look for retry-after tag
|
|
ptr = strstr(subs_state, SIP_SUBSCRIPTION_STATE_RETRY_AFTER);
|
|
if (ptr) {
|
|
ptr = ptr + sizeof(SIP_SUBSCRIPTION_STATE_RETRY_AFTER); // Go over the '='
|
|
SKIP_LWS(ptr);
|
|
if (*ptr) {
|
|
boolean is_int = FALSE;
|
|
|
|
memset(temp, 0, sizeof(temp));
|
|
i = 0;
|
|
while (isdigit(*ptr) && (i < sizeof(temp)-1)) {
|
|
temp[i++] = *ptr;
|
|
ptr++;
|
|
is_int = TRUE;
|
|
}
|
|
if (is_int == TRUE) {
|
|
*ptr = '\0';
|
|
subsStateInfo->retry_after = strtoul(temp, NULL, 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Function: sippmh_add_subscription_state
|
|
*
|
|
* Parameters: contents of the Subscription-State header
|
|
*
|
|
* Description: This function will add the subscription state header
|
|
*
|
|
* Returns: -1 if error, 0 if successfully added
|
|
*
|
|
* Format of the line is as follows:
|
|
* Subscription-State: active; expires=7200; reason=whatever
|
|
*/
|
|
int
|
|
sippmh_add_subscription_state(sipMessage_t *msg,
|
|
sipSubscriptionStateInfo_t *subsStateInfo)
|
|
{
|
|
char subs_state[MAX_SUB_STATE_HEADER_SIZE];
|
|
|
|
if (!msg) {
|
|
return -1;
|
|
}
|
|
|
|
if ((subsStateInfo->state == SUBSCRIPTION_STATE_ACTIVE) && (subsStateInfo->expires == 0)) {
|
|
snprintf(&subs_state[0], MAX_SUB_STATE_HEADER_SIZE, "%s", SIP_SUBSCRIPTION_STATE_ACTIVE);
|
|
} else if (subsStateInfo->state == SUBSCRIPTION_STATE_ACTIVE) {
|
|
snprintf(&subs_state[0], MAX_SUB_STATE_HEADER_SIZE, "%s; expires=%d",
|
|
SIP_SUBSCRIPTION_STATE_ACTIVE,
|
|
subsStateInfo->expires);
|
|
} else if (subsStateInfo->state == SUBSCRIPTION_STATE_PENDING) {
|
|
snprintf(&subs_state[0], MAX_SUB_STATE_HEADER_SIZE, "%s; expires=%d",
|
|
SIP_SUBSCRIPTION_STATE_PENDING,
|
|
subsStateInfo->expires);
|
|
} else if (subsStateInfo->state == SUBSCRIPTION_STATE_TERMINATED) {
|
|
snprintf(&subs_state[0], MAX_SUB_STATE_HEADER_SIZE, "%s; reason=timeout",
|
|
SIP_SUBSCRIPTION_STATE_TERMINATED);
|
|
}
|
|
|
|
(void) sippmh_add_text_header(msg, SIP_HEADER_SUBSCRIPTION_STATE,
|
|
(const char *)&subs_state[0]);
|
|
return 0;
|
|
}
|
|
|
|
boolean
|
|
sippmh_parse_kpml_event_id_params (char *params, char **call_id,
|
|
char **from_tag, char **to_tag)
|
|
{
|
|
boolean params_good;
|
|
int param_len;
|
|
char *param_name;
|
|
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
while (1) {
|
|
params_good = FALSE;
|
|
|
|
while (*params == ';') {
|
|
params++;
|
|
}
|
|
|
|
param_name = params;
|
|
SKIP_SIP_TOKEN(params);
|
|
param_len = params - param_name;
|
|
if (param_len == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* Parse call-id parameter */
|
|
if ((param_len == KPML_ID_CALLID_LEN) &&
|
|
(strncasecmp(param_name, KPML_ID_CALLID, KPML_ID_CALLID_LEN) == 0)) {
|
|
params = parse_generic_param(params, call_id);
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
params_good = TRUE;
|
|
}
|
|
|
|
/* Parse from-tag parameter */
|
|
} else if ((param_len == KPML_ID_FROM_TAG_LEN) &&
|
|
(strncasecmp(param_name, KPML_ID_FROM_TAG,
|
|
KPML_ID_FROM_TAG_LEN) == 0)) {
|
|
params = parse_generic_param(params, from_tag);
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
params_good = TRUE;
|
|
}
|
|
|
|
/* Parse to-tag parameter */
|
|
} else if ((param_len == KPML_ID_TO_TAG_LEN) &&
|
|
(strncasecmp(param_name, KPML_ID_TO_TAG,
|
|
KPML_ID_TO_TAG_LEN) == 0)) {
|
|
params = parse_generic_param(params, to_tag);
|
|
if (params == NULL) {
|
|
return FALSE;
|
|
} else {
|
|
params_good = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
SKIP_LWS(params);
|
|
if (*params == SEMI_COLON) {
|
|
/* More parameters follow */
|
|
*params++ = '\0';
|
|
SKIP_LWS(params);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return params_good;
|
|
}
|