/** Parse SIP version. * * Parse a SIP version string. Update the * pointer at @a ss to first non-LWS character after the version string. * * @param ss string to be parsed [IN/OUT] * @param ver value result for version [OUT] * * @retval 0 when successful, * @retval -1 upon an error. */ int sip_version_d(char **ss, char const **ver) { char *s = *ss; char const *result; size_t const version_size = sizeof(sip_version_2_0) - 1; if (su_casenmatch(s, sip_version_2_0, version_size) && !IS_TOKEN(s[version_size])) { result = sip_version_2_0; s += version_size; } else { /* Version consists of two tokens, separated by / */ size_t l1 = 0, l2 = 0, n; result = s; l1 = span_token(s); for (n = l1; IS_LWS(s[n]); n++) {} if (s[n] == '/') { for (n++; IS_LWS(s[n]); n++) {} l2 = span_token(s + n); n += l2; } if (l1 == 0 || l2 == 0) return -1; /* If there is extra ws between tokens, compact version */ if (n > l1 + 1 + l2) { s[l1] = '/'; memmove(s + l1 + 1, s + n - l2, l2); s[l1 + 1 + l2] = 0; /* Compare again with compacted version */ if (su_casematch(s, sip_version_2_0)) result = sip_version_2_0; } s += n; } while (IS_WS(*s)) *s++ = '\0'; *ss = s; if (ver) *ver = result; return 0; }
static bool parse_next(char **data, char **key, char **value) { /* @UNSAFE */ char *p, *dest; p = *data; while (IS_LWS(*p)) p++; /* get key */ *key = p; while (*p != '\0' && *p != '=' && *p != ',') p++; if (*p != '=') { *data = p; return FALSE; } *value = p+1; /* skip trailing whitespace in key */ while (p > *data && IS_LWS(p[-1])) p--; *p = '\0'; /* get value */ p = *value; while (IS_LWS(*p)) p++; if (*p != '"') { while (*p != '\0' && *p != ',') p++; *data = p+1; while (IS_LWS(p[-1])) p--; *p = '\0'; } else { /* quoted string */ *value = dest = ++p; while (*p != '\0' && *p != '"') { if (*p == '\\' && p[1] != '\0') p++; *dest++ = *p++; } *data = *p == '"' ? p+1 : p; *dest = '\0'; } return TRUE; }
/**Parse a HTTP method name. * * The function @c http_method_d() parses a HTTP method, and returns a code * corresponding to the method. It stores the address of the first non-LWS * character after method name in @c *ss. * * @param ss pointer to pointer to string to be parsed * @param nname pointer to value-result parameter formethod name * * @note * If there is no whitespace after method name, the value in @a *nname * may not be NUL-terminated. The calling function @b must NUL terminate * the value by setting the @a **ss to NUL after first examining its value. * * @return The function @c http_method_d returns the method code if method * was identified, 0 (@c http_method_unknown) if method is not known, or @c -1 * (@c http_method_invalid) if an error occurred. * * If the value-result argument @a nname is not @c NULL, http_method_d() * stores a pointer to the method name to it. */ http_method_t http_method_d(char **ss, char const **nname) { char *s = *ss, c = *s; char const *name; int code = http_method_unknown; size_t n = 0; #define MATCH(s, m) (su_casenmatch(s, m, n = sizeof(m) - 1)) if (c >= 'a' && c <= 'z') c += 'A' - 'a'; switch (c) { case 'C': if (MATCH(s, "CONNECT")) code = http_method_connect; break; case 'D': if (MATCH(s, "DELETE")) code = http_method_delete; break; case 'G': if (MATCH(s, "GET")) code = http_method_get; break; case 'H': if (MATCH(s, "HEAD")) code = http_method_head; break; case 'O': if (MATCH(s, "OPTIONS")) code = http_method_options; break; case 'P': if (MATCH(s, "POST")) code = http_method_post; else if (MATCH(s, "PUT")) code = http_method_put; break; case 'T': if (MATCH(s, "TRACE")) code = http_method_trace; break; } #undef MATCH if (!code || IS_NON_WS(s[n])) { /* Unknown method */ code = http_method_unknown; name = s; for (n = 0; IS_UNRESERVED(s[n]); n++) ; if (s[n]) { if (!IS_LWS(s[n])) return http_method_invalid; if (nname) s[n++] = '\0'; } } else { name = methods[code]; } while (IS_LWS(s[n])) n++; *ss = (s + n); if (nname) *nname = name; return (http_method_t)code; }
/* Scan a cookie parameter */ static issize_t set_cookie_scanner(char *s) { char *rest; #define LOOKING_AT(s, what) \ (su_casenmatch((s), what, strlen(what)) && (rest = s + strlen(what))) /* Special cases from Netscape spec */ if (LOOKING_AT(s, "expires=")) { msg_time_t value; msg_date_d((char const **)&rest, &value); } else if (LOOKING_AT(s, "path=/")) { for (;;) { rest += span_unreserved(rest); if (*rest != '/') break; rest++; } } else { return msg_attribute_value_scanner(s); } #undef LOOKING_AT if (IS_LWS(*rest)) { *rest++ = '\0'; skip_lws(&rest); } return rest - s; }
issize_t sip_refer_to_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen) { issize_t retval; sip_refer_to_t *r = (sip_refer_to_t *)h; retval = sip_name_addr_d(home, &s, &r->r_display, r->r_url, &r->r_params, NULL); if (retval < 0) return retval; if (*s == '?' && !r->r_display && !r->r_url->url_headers) { /* Missing <> around URL */ *s++ = '\0'; r->r_url->url_headers = s; s += strcspn(s, " \t;,"); if (IS_LWS(*s)) *s++ = '\0', skip_lws(&s); if (*s) return -1; r->r_display = s; /* Put empty string in display so that we encode using <> */ } else if (*s) return -1; return retval; }
/* Scan a cookie parameter */ static issize_t cookie_scanner(char *s) { char *p = s; size_t tlen; skip_token(&s); if (s == p) /* invalid parameter name */ return -1; tlen = s - p; if (IS_LWS(*s)) { *s++ = '\0'; skip_lws(&s); } if (*s == '=') { char *v; s++; skip_lws(&s); v = s; /* get value */ if (*s == '"') { size_t qlen = span_quoted(s); if (!qlen) return -1; s += qlen; } else { s += strcspn(s, ",;" LWS); if (s == v) return -1; } if (p + tlen + 1 != v) { memmove(p + tlen + 1, v, s - v); p[tlen] = '='; p[tlen + 1 + (s - v)] = '\0'; } } if (IS_LWS(*s)) { *s++ = '\0'; skip_lws(&s); } return s - p; }
issize_t sip_rack_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen) { sip_rack_t *ra = h->sh_rack; ra->ra_response = strtoul(s, &s, 10); if (IS_LWS(*s)) { skip_lws(&s); ra->ra_cseq = strtoul(s, &s, 10); if (IS_LWS(*s)) { skip_lws(&s); if ((ra->ra_method = sip_method_d(&s, &ra->ra_method_name)) >= 0) { return 0; } } } return -1; }
issize_t sip_priority_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen) { sip_priority_t *priority = (sip_priority_t *)h; if (msg_token_d(&s, &priority->g_string) < 0) return -1; if (*s && !IS_LWS(*s)) /* Something extra after priority token? */ return -1; return 0; }
issize_t sip_timestamp_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen) { sip_timestamp_t *ts = (sip_timestamp_t*)h; ts->ts_stamp = s; s += span_digit(s); if (s == ts->ts_stamp) return -1; if (*s == '.') { s += span_digit(s + 1) + 1; } if (IS_LWS(*s)) { *s = '\0'; s += span_lws(s + 1) + 1; ts->ts_delay = s; s += span_digit(s); if (*s == '.') { s += span_digit(s + 1) + 1; } } if (!*s || IS_LWS(*s)) *s++ = '\0'; else return -1; return 0; }
/** Decode a string containg header field. * * The header object is initialized with the contents of the string. The * string is modified when parsing. The home is used to allocate extra * memory required when parsing, e.g., for parameter list or when there * string contains multiple header fields. * * @deprecated * Use msg_header_make() or header-specific make functions, e.g., * sip_via_make(). * * @retval 0 when successful * @retval -1 upon an error. */ issize_t sip_header_field_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen) { if (h && s && s[slen] == '\0') { size_t n = span_lws(s); s += n; slen -= n; for (n = slen; n >= 1 && IS_LWS(s[n - 1]); n--) ; s[n] = '\0'; assert(SIP_HDR_TEST(h)); return h->sh_class->hc_parse(home, h, s, slen); } else return -1; }
/** Parse SIP <word "@" word> construct used in @CallID. */ char *sip_word_at_word_d(char **ss) { char *rv = *ss, *s0 = *ss; skip_word(ss); if (s0 == *ss) return NULL; if (**ss == '@') { (*ss)++; s0 = *ss; skip_word(ss); if (s0 == *ss) return NULL; } if (IS_LWS(**ss)) (*ss)++; skip_lws(ss); return rv; }
/** Decode transport */ issize_t sip_transport_d(char **ss, char const **ttransport) { char const *transport; char *pn, *pv, *pt; size_t pn_len, pv_len, pt_len; char *s = *ss; #define TRANSPORT_MATCH(t) \ (su_casenmatch(s + 7, t + 7, (sizeof t) - 8) && \ (!s[sizeof(t) - 1] || IS_LWS(s[sizeof(t) - 1])) \ && (transport = t, s += sizeof(t) - 1)) if (!su_casenmatch(s, "SIP/2.0", 7) || (!TRANSPORT_MATCH(sip_transport_udp) && !TRANSPORT_MATCH(sip_transport_tcp) && !TRANSPORT_MATCH(sip_transport_sctp) && !TRANSPORT_MATCH(sip_transport_ws) && !TRANSPORT_MATCH(sip_transport_wss) && !TRANSPORT_MATCH(sip_transport_tls))) { /* Protocol name */ transport = pn = s; skip_token(&s); pn_len = s - pn; skip_lws(&s); if (pn_len == 0 || *s++ != '/') return -1; skip_lws(&s); /* Protocol version */ pv = s; skip_token(&s); pv_len = s - pv; skip_lws(&s); if (pv_len == 0 || *s++ != '/') return -1; skip_lws(&s); /* Transport protocol */ pt = s; skip_token(&s); pt_len = s - pt; if (pt_len == 0) return -1; /* Remove whitespace between protocol name and version */ if (pn + pn_len + 1 != pv) { pn[pn_len] = '/'; pv = memmove(pn + pn_len + 1, pv, pv_len); } /* Remove whitespace between protocol version and transport */ if (pv + pv_len + 1 != pt) { pv[pv_len] = '/'; pt = memmove(pv + pv_len + 1, pt, pt_len); pt[pt_len] = '\0'; /* extra whitespace? */ if (su_casematch(transport, sip_transport_udp)) transport = sip_transport_udp; else if (su_casematch(transport, sip_transport_tcp)) transport = sip_transport_tcp; else if (su_casematch(transport, sip_transport_sctp)) transport = sip_transport_sctp; else if (su_casematch(transport, sip_transport_ws)) transport = sip_transport_ws; else if (su_casematch(transport, sip_transport_wss)) transport = sip_transport_wss; else if (su_casematch(transport, sip_transport_tls)) transport = sip_transport_tls; } } if (IS_LWS(*s)) { *s++ = '\0'; skip_lws(&s); } *ss = s; *ttransport = transport; return 0; }
/**Parse a SIP method name. * * Parse a SIP method name and return a code corresponding to the method. * The address of the first non-LWS character after method name is stored in * @a *ss. * * @param ss pointer to pointer to string to be parsed * @param return_name value-result parameter for method name * * @note * If there is no whitespace after method name, the value in @a *return_name * may not be NUL-terminated. The calling function @b must NUL terminate * the value by setting the @a **ss to NUL after first examining its value. * * @return The method code if method * was identified, 0 (sip_method_unknown()) if method is not known, or @c -1 * (sip_method_invalid()) if an error occurred. * * If the value-result argument @a return_name is not @c NULL, * a pointer to the method name is stored to it. */ sip_method_t sip_method_d(char **ss, char const **return_name) { char *s = *ss, c = *s; char const *name; int code = sip_method_unknown; size_t n = 0; #define MATCH(s, m) (strncmp(s, m, n = sizeof(m) - 1) == 0) switch (c) { case 'A': if (MATCH(s, "ACK")) code = sip_method_ack; break; case 'B': if (MATCH(s, "BYE")) code = sip_method_bye; break; case 'C': if (MATCH(s, "CANCEL")) code = sip_method_cancel; break; case 'I': if (MATCH(s, "INVITE")) code = sip_method_invite; else if (MATCH(s, "INFO")) code = sip_method_info; break; case 'M': if (MATCH(s, "MESSAGE")) code = sip_method_message; break; case 'N': if (MATCH(s, "NOTIFY")) code = sip_method_notify; break; case 'O': if (MATCH(s, "OPTIONS")) code = sip_method_options; break; case 'P': if (MATCH(s, "PRACK")) code = sip_method_prack; else if (MATCH(s, "PUBLISH")) code = sip_method_publish; break; case 'R': if (MATCH(s, "REGISTER")) code = sip_method_register; else if (MATCH(s, "REFER")) code = sip_method_refer; break; case 'S': if (MATCH(s, "SUBSCRIBE")) code = sip_method_subscribe; break; case 'U': if (MATCH(s, "UPDATE")) code = sip_method_update; break; } #undef MATCH if (IS_NON_WS(s[n])) /* Unknown method */ code = sip_method_unknown; if (code == sip_method_unknown) { name = s; for (n = 0; IS_UNRESERVED(s[n]); n++) ; if (s[n]) { if (!IS_LWS(s[n])) return sip_method_invalid; if (return_name) s[n++] = '\0'; } } else { name = sip_method_names[code]; } while (IS_LWS(s[n])) n++; *ss = (s + n); if (return_name) *return_name = name; return (sip_method_t)code; }