/* 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 #include #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 ; 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" * "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 (%) * 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 '); 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= 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= {} * 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={ } // 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; }