/* The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-) Copyright (C) 2001-2020 Aymeric MOIZARD amoizard@antisip.com This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "parser.h" /* adds the from header to message. */ /* INPUT : char *hvalue | value of header. */ /* OUTPUT: osip_message_t *sip | structure to save results. */ /* returns -1 on error. */ int osip_message_set_from(osip_message_t *sip, const char *hvalue) { int i; if (hvalue == NULL || hvalue[0] == '\0') return OSIP_SUCCESS; if (sip->from != NULL) return OSIP_SYNTAXERROR; i = osip_from_init(&(sip->from)); if (i != 0) return i; sip->message_property = 2; i = osip_from_parse(sip->from, hvalue); if (i != 0) { osip_from_free(sip->from); sip->from = NULL; return i; } return OSIP_SUCCESS; } #ifndef MINISIZE /* returns the from header. */ /* INPUT : osip_message_t *sip | sip message. */ /* returns null on error. */ osip_from_t *osip_message_get_from(const osip_message_t *sip) { return sip->from; } #endif int osip_from_init(osip_from_t **from) { *from = (osip_from_t *) osip_malloc(sizeof(osip_from_t)); if (*from == NULL) return OSIP_NOMEM; (*from)->displayname = NULL; (*from)->url = NULL; osip_list_init(&(*from)->gen_params); return OSIP_SUCCESS; } /* deallocates a osip_from_t structure. */ /* INPUT : osip_from_t *from | from. */ void osip_from_free(osip_from_t *from) { if (from == NULL) return; if (from->url != NULL) { osip_uri_free(from->url); } osip_free(from->displayname); osip_generic_param_freelist(&from->gen_params); osip_free(from); } /* parses the string as a from header. */ /* INPUT : const char *string | pointer to a from string.*/ /* OUTPUT: osip_from_t *from | structure to save results. */ /* returns -1 on error. */ int osip_from_parse(osip_from_t *from, const char *hvalue) { const char *displayname = NULL; const char *url = NULL; const char *url_end = NULL; const char *gen_params; const char *ptr; int i; if (from == NULL || hvalue == NULL) return OSIP_BADPARAMETER; /* How to parse: we'll place the pointers: displayname => beginning of displayname url => beginning of url url_end => end of url gen_params => beginning of params examples: jack ;tag=34erZ ^ ^ ^ ^ sip:amoizard@antisip.com;tag=34erZ ^ ^^ */ /* search for first char */ ptr = hvalue; while (ptr[0] != '\0') { if (ptr[0] == ' ') { ptr++; continue; } if (ptr[0] == '"') { displayname = ptr; break; } break; } if (ptr[0] == '\0') return OSIP_SUCCESS; /* empty header allowed? */ if (displayname != NULL) { /* displayname IS A quoted-string (not a '*token') */ const char *second = NULL; /* search for quotes */ second = __osip_quote_find(displayname + 1); if (second == NULL) return OSIP_SYNTAXERROR; /* missing quote */ if (second - displayname + 2 >= 2) { from->displayname = (char *) osip_malloc(second - displayname + 2); if (from->displayname == NULL) return OSIP_NOMEM; osip_strncpy(from->displayname, displayname, second - displayname + 1); /* osip_clrspace(from->displayname); */ /*should we do that? */ /* special case: "" */ } /* else displayname is empty? */ ptr = second + 1; while (ptr[0] != '\0') { if (ptr[0] == ' ') { ptr++; continue; } if (ptr[0] == '<') { url = ptr; break; } break; } if (url == NULL) return OSIP_SYNTAXERROR; /* '<' MUST exist */ if (ptr[1] == '\0') return OSIP_SYNTAXERROR; /* '<' MUST contain something */ } else { /* 1*(alphanum / "-" / "." / "!" / "%" / "*" / "_" / "+" / "`" / "'" / "~" ) */ /* search for URL -> continue until non allowed element is found */ const char *beg = ptr; while (ptr[0] != '\0') { if (ptr[0] == ' ') { ptr++; continue; } if (ptr[0] >= 'a' && ptr[0] <= 'z') { ptr++; continue; } if (ptr[0] >= 'A' && ptr[0] <= 'Z') { ptr++; continue; } if (ptr[0] >= '0' && ptr[0] <= '9') { ptr++; continue; } if (ptr[0] == '-' || ptr[0] == '.' || ptr[0] == '!' || ptr[0] == '%' || ptr[0] == '*' || ptr[0] == '_' || ptr[0] == '+' || ptr[0] == '`' || ptr[0] == '\'' || ptr[0] == '~') { ptr++; continue; } url = ptr; break; } if (ptr[0] == '\0' || url == NULL) return OSIP_SYNTAXERROR; /* not special char found? broken header? */ if (ptr[0] == '<') { /* "<" found for URI */ if (ptr[1] == '\0') return OSIP_SYNTAXERROR; /* '<' MUST contain something */ if (url - beg > 0) { from->displayname = (char *) osip_malloc(url - beg + 1); if (from->displayname == NULL) return OSIP_NOMEM; osip_clrncpy(from->displayname, hvalue, url - beg); } } else if (ptr[0] == ':') { /* this was a scheme for a URL? */ url = beg; } else { /* this is some non URL header? */ url = beg; } } /* define url_end and gen_params for name-addr */ if (url[0] == '<') { url++; ptr = url; /* first occurence of ">" is the end of url */ url_end = strchr(ptr, '>'); if (url_end == NULL) return OSIP_SYNTAXERROR; url_end--; gen_params = strchr(url_end, ';'); } /* define url_end and gen_params for addr-spec */ if (url_end == NULL) { /* rfc3261 // 20.10 Contact: Even if the "display-name" is empty, the "name-addr" form MUST be used if the "addr-spec" contains a comma, semicolon, or question mark. There may or may not be LWS between the display-name and the "<". Conclusion: there is no semicolon (in username) before the semicolon generic param delimiter... */ gen_params = strchr(url, ';'); if (gen_params != NULL) url_end = gen_params - 1; else url_end = url + strlen(url); } if (gen_params != NULL) { /* now we are sure a param exist */ i = __osip_generic_param_parseall(&from->gen_params, gen_params); if (i != 0) { return i; } } /* set the url */ { char *tmp; if (url_end - url + 2 < 7) return OSIP_SYNTAXERROR; i = osip_uri_init(&(from->url)); if (i != 0) return i; tmp = (char *) osip_malloc(url_end - url + 2); if (tmp == NULL) return OSIP_NOMEM; osip_strncpy(tmp, url, url_end - url + 1); i = osip_uri_parse(from->url, tmp); osip_free(tmp); if (i != 0) return i; } return OSIP_SUCCESS; } /* returns the from header as a string. */ /* INPUT : osip_from_t *from | from header. */ /* returns -1 on error. */ int osip_from_to_str(const osip_from_t *from, char **dest) { char *url; char *buf; int i; size_t len; *dest = NULL; if ((from == NULL) || (from->url == NULL)) return OSIP_BADPARAMETER; i = osip_uri_to_str(from->url, &url); if (i != 0) return i; if (from->displayname == NULL) len = strlen(url) + 5; else len = strlen(url) + strlen(from->displayname) + 5; buf = (char *) osip_malloc(len); if (buf == NULL) { osip_free(url); return OSIP_NOMEM; } if (from->displayname != NULL) sprintf(buf, "%s <%s>", from->displayname, url); else /* from rfc2543bis-04: for authentication related issue! "The To and From header fields always include the < and > delimiters even if the display-name is empty." */ sprintf(buf, "<%s>", url); osip_free(url); { size_t plen; char *tmp; osip_list_iterator_t it; osip_generic_param_t *u_param = (osip_generic_param_t *) osip_list_get_first(&from->gen_params, &it); while (u_param != OSIP_SUCCESS) { if (u_param->gvalue == NULL) plen = strlen(u_param->gname) + 2; else plen = strlen(u_param->gname) + strlen(u_param->gvalue) + 3; len = len + plen; buf = (char *) osip_realloc(buf, len); tmp = buf; tmp = tmp + strlen(tmp); if (u_param->gvalue == NULL) snprintf(tmp, len - (tmp - buf), ";%s", u_param->gname); else snprintf(tmp, len - (tmp - buf), ";%s=%s", u_param->gname, u_param->gvalue); u_param = (osip_generic_param_t *) osip_list_get_next(&it); } } *dest = buf; return OSIP_SUCCESS; } char *osip_from_get_displayname(osip_from_t *from) { if (from == NULL) return NULL; return from->displayname; } void osip_from_set_displayname(osip_from_t *from, char *displayname) { from->displayname = displayname; } osip_uri_t *osip_from_get_url(osip_from_t *from) { if (from == NULL) return NULL; return from->url; } void osip_from_set_url(osip_from_t *from, osip_uri_t *url) { from->url = url; } int osip_from_param_get(osip_from_t *from, int pos, osip_generic_param_t **fparam) { *fparam = NULL; if (from == NULL) return OSIP_BADPARAMETER; if (osip_list_size(&from->gen_params) <= pos) return OSIP_UNDEFINED_ERROR; /* does not exist */ *fparam = (osip_generic_param_t *) osip_list_get(&from->gen_params, pos); return pos; } int osip_from_clone(const osip_from_t *from, osip_from_t **dest) { int i; osip_from_t *fr; *dest = NULL; if (from == NULL) return OSIP_BADPARAMETER; i = osip_from_init(&fr); if (i != 0) /* allocation failed */ return i; if (from->displayname != NULL) { fr->displayname = osip_strdup(from->displayname); if (fr->displayname == NULL) { osip_from_free(fr); return OSIP_NOMEM; } } if (from->url != NULL) { i = osip_uri_clone(from->url, &(fr->url)); if (i != 0) { osip_from_free(fr); return i; } } i = osip_list_clone(&from->gen_params, &fr->gen_params, (int (*)(void *, void **)) & osip_generic_param_clone); if (i != 0) { osip_from_free(fr); return i; } *dest = fr; return OSIP_SUCCESS; } int osip_from_compare(osip_from_t *from1, osip_from_t *from2) { char *tag1; char *tag2; if (from1 == NULL || from2 == NULL) return OSIP_BADPARAMETER; if (from1->url == NULL || from2->url == NULL) return OSIP_BADPARAMETER; /* we could have a sip or sips url, but if string!=NULL, host part will be NULL. */ if (from1->url->host == NULL && from2->url->host == NULL) { if (from1->url->string == NULL || from2->url->string == NULL) return OSIP_UNDEFINED_ERROR; if (0 == strcmp(from1->url->string, from2->url->string)) return OSIP_SUCCESS; } if (from1->url->host == NULL || from2->url->host == NULL) return OSIP_UNDEFINED_ERROR; /* compare url including tag */ if (0 != strcmp(from1->url->host, from2->url->host)) return OSIP_UNDEFINED_ERROR; if (from1->url->username != NULL && from2->url->username != NULL) if (0 != strcmp(from1->url->username, from2->url->username)) return OSIP_UNDEFINED_ERROR; tag1 = NULL; tag2 = NULL; { osip_list_iterator_t it; osip_generic_param_t *u_param = (osip_generic_param_t *) osip_list_get_first(&from1->gen_params, &it); while (u_param != OSIP_SUCCESS) { if (0 == strncmp(u_param->gname, "tag", 3)) { tag1 = u_param->gvalue; break; } u_param = (osip_generic_param_t *) osip_list_get_next(&it); } } { osip_list_iterator_t it; osip_generic_param_t *u_param = (osip_generic_param_t *) osip_list_get_first(&from2->gen_params, &it); while (u_param != OSIP_SUCCESS) { if (0 == strncmp(u_param->gname, "tag", 3)) { tag2 = u_param->gvalue; break; } u_param = (osip_generic_param_t *) osip_list_get_next(&it); } } /* sounds like a BUG! if tag2 exists and tag1 does not, then it will return OSIP_SUCCESS; in the first request, (INVITE) the To field does not contain any tag. The response contains one! and the response must match the request.... */ /* so we test the tags only when both exist! */ if (tag1 != NULL && tag2 != NULL) if (0 != strcmp(tag1, tag2)) return OSIP_UNDEFINED_ERROR; /* We could return a special case, when */ /* only one tag exists?? */ return OSIP_SUCCESS; /* return code changed to 0 from release 0.6.1 */ } int __osip_generic_param_parseall(osip_list_t *gen_params, const char *params) { int i; char *pname; char *pvalue; const char *comma; const char *equal; const char *startquote; const char *endquote; /* find '=' wich is the separator for one param */ /* find ';' wich is the separator for multiple params */ equal = next_separator(params + 1, '=', ';'); comma = strchr(params + 1, ';'); /* If comma points after value start quote, move it to after end quote */ if (equal != NULL) { const char *tmp; startquote = NULL; for (tmp = equal + 1; tmp[0] == ' '; tmp++) { } if (tmp[0] == '"') startquote = tmp; if (startquote != NULL && comma > startquote) { comma = NULL; endquote = __osip_quote_find(startquote + 1); if (endquote) comma = strchr(endquote, ';'); } } while (comma != NULL) { if (equal == NULL) { equal = comma; pvalue = NULL; } else { const char *tmp; /* check for NULL param with an '=' character */ tmp = equal + 1; tmp += strspn(tmp, "\t "); pvalue = NULL; if (*tmp != ',' && *tmp != '\0') { if (comma - equal < 2) return OSIP_SYNTAXERROR; pvalue = (char *) osip_malloc(comma - equal); if (pvalue == NULL) return OSIP_NOMEM; osip_strncpy(pvalue, equal + 1, comma - equal - 1); } } if (equal - params < 2) { osip_free(pvalue); return OSIP_SYNTAXERROR; } pname = (char *) osip_malloc(equal - params); if (pname == NULL) { osip_free(pvalue); return OSIP_NOMEM; } osip_strncpy(pname, params + 1, equal - params - 1); i = osip_generic_param_add(gen_params, pname, pvalue); if (i != OSIP_SUCCESS) { osip_free(pname); osip_free(pvalue); return OSIP_NOMEM; } params = comma; equal = next_separator(params + 1, '=', ';'); comma = strchr(params + 1, ';'); /* If comma points after value start quote, move it to after end quote */ if (equal != NULL) { const char *tmp; startquote = NULL; for (tmp = equal + 1; tmp[0] == ' '; tmp++) { } if (tmp[0] == '"') startquote = tmp; if (startquote != NULL && comma > startquote) { comma = NULL; endquote = __osip_quote_find(startquote + 1); if (endquote) comma = strchr(endquote, ';'); } } } /* this is the last header (comma==NULL) */ comma = params + strlen(params); if (equal == NULL) { equal = comma; /* at the end */ pvalue = NULL; if (equal - params < 2) { osip_free(pvalue); return OSIP_SUCCESS; /* empty comma? */ } } else { const char *tmp; /* check for NULL param with an '=' character */ tmp = equal + 1; tmp += strspn(tmp, "\t "); pvalue = NULL; if (*tmp != ',' && *tmp != '\0') { if (comma - equal < 2) return OSIP_SYNTAXERROR; pvalue = (char *) osip_malloc(comma - equal); if (pvalue == NULL) return OSIP_NOMEM; osip_strncpy(pvalue, equal + 1, comma - equal - 1); } } if (equal - params < 2) { osip_free(pvalue); return OSIP_SYNTAXERROR; } pname = (char *) osip_malloc(equal - params); if (pname == NULL) { osip_free(pvalue); return OSIP_NOMEM; } osip_strncpy(pname, params + 1, equal - params - 1); i = osip_generic_param_add(gen_params, pname, pvalue); if (i != OSIP_SUCCESS) { osip_free(pname); osip_free(pvalue); return OSIP_NOMEM; } return OSIP_SUCCESS; } void osip_generic_param_set_value(osip_generic_param_t *fparam, char *value) { fparam->gvalue = value; } char *osip_generic_param_get_name(const osip_generic_param_t *fparam) { if (fparam == NULL) return NULL; return fparam->gname; } void osip_generic_param_set_name(osip_generic_param_t *fparam, char *name) { fparam->gname = name; } char *osip_generic_param_get_value(const osip_generic_param_t *fparam) { if (fparam == NULL) return NULL; if (fparam->gname == NULL) return NULL; /* name is mandatory */ return fparam->gvalue; } int osip_from_tag_match(osip_from_t *from1, osip_from_t *from2) { osip_generic_param_t *tag_from1; osip_generic_param_t *tag_from2; if (from1 == NULL || from2 == NULL) return OSIP_BADPARAMETER; osip_from_param_get_byname(from1, "tag", &tag_from1); osip_from_param_get_byname(from2, "tag", &tag_from2); if (tag_from1 == NULL && tag_from2 == NULL) return OSIP_SUCCESS; if ((tag_from1 != NULL && tag_from2 == NULL) || (tag_from1 == NULL && tag_from2 != NULL)) return OSIP_UNDEFINED_ERROR; if (tag_from1->gvalue == NULL || tag_from2->gvalue == NULL) return OSIP_UNDEFINED_ERROR; if (0 != strcmp(tag_from1->gvalue, tag_from2->gvalue)) return OSIP_UNDEFINED_ERROR; return OSIP_SUCCESS; }