/* 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 #include "parser.h" static int osip_body_parse_header(osip_body_t *body, const char *start_of_osip_body_header, const char **next_body); int osip_body_init(osip_body_t **body) { *body = (osip_body_t *) osip_malloc(sizeof(osip_body_t)); if (*body == NULL) return OSIP_NOMEM; (*body)->body = NULL; (*body)->content_type = NULL; (*body)->length = 0; (*body)->headers = (osip_list_t *) osip_malloc(sizeof(osip_list_t)); if ((*body)->headers == NULL) { osip_free(*body); *body = NULL; return OSIP_NOMEM; } osip_list_init((*body)->headers); return OSIP_SUCCESS; } int osip_message_set_body(osip_message_t *sip, const char *buf, size_t length) { osip_body_t *body; int i; i = osip_body_init(&body); if (i != 0) return i; i = osip_body_parse(body, buf, length); if (i != 0) { osip_body_free(body); return i; } sip->message_property = 2; osip_list_add(&sip->bodies, body, -1); return OSIP_SUCCESS; } int osip_body_clone(const osip_body_t *body, osip_body_t **dest) { int i; osip_body_t *copy; if (body == NULL || body->length <= 0) return OSIP_BADPARAMETER; i = osip_body_init(©); if (i != 0) return i; copy->body = (char *) osip_malloc(body->length + 2); if (copy->body == NULL) { osip_body_free(copy); return OSIP_NOMEM; } copy->length = body->length; memcpy(copy->body, body->body, body->length); copy->body[body->length] = '\0'; if (body->content_type != NULL) { i = osip_content_type_clone(body->content_type, &(copy->content_type)); if (i != 0) { osip_body_free(copy); return i; } } i = osip_list_clone(body->headers, copy->headers, (int (*)(void *, void **)) & osip_header_clone); if (i != 0) { osip_body_free(copy); return i; } *dest = copy; return OSIP_SUCCESS; } #ifndef MINISIZE /* returns the pos of body header. */ /* INPUT : int pos | pos of body header. */ /* INPUT : osip_message_t *sip | sip message. */ /* OUTPUT: osip_body_t *body | structure to save results. */ /* returns -1 on error. */ int osip_message_get_body(const osip_message_t *sip, int pos, osip_body_t **dest) { osip_body_t *body; *dest = NULL; if (osip_list_size(&sip->bodies) <= pos) return OSIP_UNDEFINED_ERROR; /* does not exist */ body = (osip_body_t *) osip_list_get(&sip->bodies, pos); *dest = body; return pos; } #endif int osip_body_set_contenttype(osip_body_t *body, const char *hvalue) { int i; if (body == NULL) return OSIP_BADPARAMETER; if (hvalue == NULL) return OSIP_BADPARAMETER; i = osip_content_type_init(&(body->content_type)); if (i != 0) return i; i = osip_content_type_parse(body->content_type, hvalue); if (i != 0) { osip_content_type_free(body->content_type); body->content_type = NULL; return i; } return OSIP_SUCCESS; } int osip_body_set_header(osip_body_t *body, const char *hname, const char *hvalue) { osip_header_t *h; int i; if (body == NULL) return OSIP_BADPARAMETER; if (hname == NULL) return OSIP_BADPARAMETER; if (hvalue == NULL) return OSIP_BADPARAMETER; i = osip_header_init(&h); if (i != 0) return i; h->hname = osip_strdup(hname); if (h->hname == NULL) { osip_header_free(h); return OSIP_NOMEM; } h->hvalue = osip_strdup(hvalue); if (h->hvalue == NULL) { osip_header_free(h); return OSIP_NOMEM; } osip_list_add(body->headers, h, -1); return OSIP_SUCCESS; } /* fill the body of message. */ /* INPUT : char *buf | pointer to the start of body. */ /* OUTPUT: osip_message_t *sip | structure to save results. */ /* returns -1 on error. */ int osip_message_set_body_mime(osip_message_t *sip, const char *buf, size_t length) { osip_body_t *body; int i; if (sip == NULL) return OSIP_BADPARAMETER; i = osip_body_init(&body); if (i != 0) return i; i = osip_body_parse_mime(body, buf, length); if (i != 0) { osip_body_free(body); return i; } sip->message_property = 2; osip_list_add(&sip->bodies, body, -1); return OSIP_SUCCESS; } static int osip_body_parse_header(osip_body_t *body, const char *start_of_osip_body_header, const char **next_body) { const char *start_of_line; const char *end_of_line; const char *colon_index; char *hname; char *hvalue; int i; *next_body = NULL; start_of_line = start_of_osip_body_header; for (;;) { i = __osip_find_next_crlf(start_of_line, &end_of_line); if (i == -2) { } else if (i != 0) return i; /* error case: no end of body found */ /* find the header name */ colon_index = strchr(start_of_line, ':'); if (colon_index == NULL) return OSIP_SYNTAXERROR; /* this is also an error case */ if (colon_index - start_of_line + 1 < 2) return OSIP_SYNTAXERROR; hname = (char *) osip_malloc(colon_index - start_of_line + 1); if (hname == NULL) return OSIP_NOMEM; osip_clrncpy(hname, start_of_line, colon_index - start_of_line); if ((end_of_line - 2) - colon_index < 2) { osip_free(hname); return OSIP_SYNTAXERROR; } hvalue = (char *) osip_malloc((end_of_line - 2) - colon_index); if (hvalue == NULL) { osip_free(hname); return OSIP_NOMEM; } osip_clrncpy(hvalue, colon_index + 1, (end_of_line - 2) - colon_index - 1); /* really store the header in the sip structure */ if (osip_strncasecmp(hname, "content-type", 12) == 0) i = osip_body_set_contenttype(body, hvalue); else i = osip_body_set_header(body, hname, hvalue); osip_free(hname); osip_free(hvalue); if (i != 0) return i; if (strncmp(end_of_line, OSIP_CRLF, 2) == 0 || strncmp(end_of_line, "\n", 1) == 0 || strncmp(end_of_line, "\r", 1) == 0) { *next_body = end_of_line; return OSIP_SUCCESS; } start_of_line = end_of_line; } } int osip_body_parse(osip_body_t *body, const char *start_of_body, size_t length) { if (body == NULL) return OSIP_BADPARAMETER; if (start_of_body == NULL) return OSIP_BADPARAMETER; if (body->headers == NULL) return OSIP_BADPARAMETER; body->body = (char *) osip_malloc(length + 1); if (body->body == NULL) return OSIP_NOMEM; memcpy(body->body, start_of_body, length); body->body[length] = '\0'; body->length = length; return OSIP_SUCCESS; } int osip_body_parse_mime(osip_body_t *body, const char *start_of_body, size_t length) { const char *end_of_osip_body_header; const char *start_of_osip_body_header; int i; if (body == NULL) return OSIP_BADPARAMETER; if (start_of_body == NULL) return OSIP_BADPARAMETER; if (body->headers == NULL) return OSIP_BADPARAMETER; start_of_osip_body_header = start_of_body; i = osip_body_parse_header(body, start_of_osip_body_header, &end_of_osip_body_header); if (i != 0) return i; start_of_osip_body_header = end_of_osip_body_header; /* set the real start of body */ if (strncmp(start_of_osip_body_header, OSIP_CRLF, 2) == 0) start_of_osip_body_header = start_of_osip_body_header + 2; else { if ((strncmp(start_of_osip_body_header, "\n", 1) == 0) || (strncmp(start_of_osip_body_header, "\r", 1) == 0)) start_of_osip_body_header = start_of_osip_body_header + 1; else return OSIP_SYNTAXERROR; /* message does not end with CRLFCRLF, CRCR or LFLF */ } end_of_osip_body_header = start_of_body + length; if (end_of_osip_body_header - start_of_osip_body_header <= 0) return OSIP_SYNTAXERROR; body->body = (char *) osip_malloc(end_of_osip_body_header - start_of_osip_body_header + 1); if (body->body == NULL) return OSIP_NOMEM; memcpy(body->body, start_of_osip_body_header, end_of_osip_body_header - start_of_osip_body_header); body->length = end_of_osip_body_header - start_of_osip_body_header; body->body[body->length] = '\0'; return OSIP_SUCCESS; } /* returns the body as a string. */ /* INPUT : osip_body_t *body | body. */ /* returns null on error. */ int osip_body_to_str(const osip_body_t *body, char **dest, size_t *str_length) { char *tmp_body; char *tmp; char *ptr; int i; size_t length; if (dest) *dest = NULL; if (str_length) *str_length = 0; if (body == NULL) return OSIP_BADPARAMETER; if (body->body == NULL) return OSIP_BADPARAMETER; if (body->headers == NULL) return OSIP_BADPARAMETER; if (body->length <= 0) return OSIP_BADPARAMETER; length = 15 + body->length + (osip_list_size(body->headers) * 40); tmp_body = (char *) osip_malloc(length); if (tmp_body == NULL) return OSIP_NOMEM; ptr = tmp_body; /* save the initial address of the string */ if (body->content_type != NULL) { tmp_body = osip_strn_append(tmp_body, "content-type: ", 14); i = osip_content_type_to_str(body->content_type, &tmp); if (i != 0) { osip_free(ptr); return i; } if (length < tmp_body - ptr + strlen(tmp) + 4) { size_t len; len = tmp_body - ptr; length = length + strlen(tmp) + 4; ptr = osip_realloc(ptr, length); tmp_body = ptr + len; } tmp_body = osip_str_append(tmp_body, tmp); osip_free(tmp); tmp_body = osip_strn_append(tmp_body, OSIP_CRLF, 2); } { osip_list_iterator_t it; osip_header_t *header = (osip_header_t *) osip_list_get_first(body->headers, &it); while (header != OSIP_SUCCESS) { i = osip_header_to_str(header, &tmp); if (i != 0) { osip_free(ptr); return i; } if (length < tmp_body - ptr + strlen(tmp) + 4) { size_t len; len = tmp_body - ptr; length = length + strlen(tmp) + 4; ptr = osip_realloc(ptr, length); tmp_body = ptr + len; } tmp_body = osip_str_append(tmp_body, tmp); osip_free(tmp); tmp_body = osip_strn_append(tmp_body, OSIP_CRLF, 2); header = (osip_header_t *) osip_list_get_next(&it); } } if ((osip_list_size(body->headers) > 0) || (body->content_type != NULL)) { if (length < (size_t)(tmp_body - ptr + 3)) { size_t len; len = tmp_body - ptr; length = length + 3 + body->length; /* add body->length, to avoid calling realloc often */ ptr = osip_realloc(ptr, length); tmp_body = ptr + len; } tmp_body = osip_strn_append(tmp_body, OSIP_CRLF, 2); } if (length < tmp_body - ptr + body->length + 4) { size_t len; len = tmp_body - ptr; length = length + body->length + 4; ptr = osip_realloc(ptr, length); tmp_body = ptr + len; } memcpy(tmp_body, body->body, body->length); tmp_body = tmp_body + body->length; /* end of this body */ if (str_length != NULL) *str_length = tmp_body - ptr; *dest = ptr; return OSIP_SUCCESS; } /* deallocates a body structure. */ /* INPUT : osip_body_t *body | body. */ void osip_body_free(osip_body_t *body) { if (body == NULL) return; osip_free(body->body); if (body->content_type != NULL) { osip_content_type_free(body->content_type); } osip_list_special_free(body->headers, (void (*)(void *)) & osip_header_free); osip_free(body->headers); osip_free(body); }