1643 lines
54 KiB
C
1643 lines
54 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* Functions that parse and create HTTP/1.1-like messages(RFC 2068). Basically
|
|
* code that converts from network(ie text) form to a usable structure
|
|
* and vice-versa.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
|
|
#include "plstr.h"
|
|
#include "cpr_types.h"
|
|
#include "cpr_stdio.h"
|
|
#include "cpr_stdlib.h"
|
|
#include "cpr_string.h"
|
|
#include "httpish.h"
|
|
#include "ccsip_protocol.h"
|
|
#include "phone_debug.h"
|
|
#include "ccsip_core.h"
|
|
|
|
//#define HTTPISH_DEBUG if (1)
|
|
#define MSG_DELIMIT_SIZE 80
|
|
#define TMP_BODY_BUF_SIZE 200
|
|
#define CMPC_HEADER_SIZE 256
|
|
|
|
extern sip_header_t sip_cached_headers[];
|
|
httpishMsg_t *
|
|
httpish_msg_create (void)
|
|
{
|
|
int i;
|
|
httpishMsg_t *msg;
|
|
|
|
msg = (httpishMsg_t *) cpr_calloc(1, sizeof(httpishMsg_t));
|
|
if (!msg) {
|
|
return NULL;
|
|
}
|
|
|
|
msg->headers = (queuetype *) cpr_calloc(1, sizeof(queuetype));
|
|
|
|
if (!msg->headers) {
|
|
cpr_free(msg);
|
|
return NULL;
|
|
}
|
|
|
|
msg->retain_flag = FALSE;
|
|
msg->mesg_line = NULL;
|
|
msg->content_length = 0;
|
|
msg->is_complete = FALSE;
|
|
msg->headers_read = FALSE;
|
|
|
|
for (i = 0; i < HTTPISH_MAX_BODY_PARTS; i++) {
|
|
msg->mesg_body[i].msgContentType = NULL;
|
|
msg->mesg_body[i].msgBody = NULL;
|
|
msg->mesg_body[i].msgLength = 0;
|
|
msg->mesg_body[i].msgContentId = NULL;
|
|
msg->mesg_body[i].msgContentEnc = SIP_CONTENT_ENCODING_IDENTITY_VALUE;
|
|
msg->mesg_body[i].msgContentDisp = SIP_CONTENT_DISPOSITION_SESSION_VALUE;
|
|
msg->mesg_body[i].msgRequiredHandling = TRUE;
|
|
msg->mesg_body[i].msgContentTypeValue = SIP_CONTENT_TYPE_UNKNOWN_VALUE;
|
|
}
|
|
|
|
msg->num_body_parts = 0;
|
|
msg->raw_body = NULL;
|
|
|
|
queue_init(msg->headers, 0);
|
|
|
|
return msg;
|
|
}
|
|
|
|
int
|
|
httpish_strncasecmp(const char *s1, const char* s2, size_t len)
|
|
{
|
|
/*This routine is an enhanced version of strncasecmp().
|
|
*It ensures that the two strings being compared for size "len"
|
|
*don't have trailing characters beyond "len" chars.
|
|
*The trailing whitespaces beyond "len" chars is ignored.
|
|
*/
|
|
const unsigned char *us1 = (const unsigned char *) s1;
|
|
const unsigned char *us2 = (const unsigned char *) s2;
|
|
|
|
/* No match if only one ptr is NULL */
|
|
if ((!s1 && s2) || (s1 && !s2))
|
|
return ((int) (s1 - s2));
|
|
|
|
if ((len == 0) || (s1 == s2))
|
|
return 0;
|
|
|
|
while (len-- > 0 && toupper(*us1) == toupper(*us2)) {
|
|
if (len == 0 || *us1 == '\0' || *us2 == '\0')
|
|
break;
|
|
us1++;
|
|
us2++;
|
|
}
|
|
|
|
if (len == 0 && toupper(*us1) == toupper(*us2)) {
|
|
//all "len" chars are compared, need to look for trailing
|
|
//chars beyond "len" string size. Ignore white spaces.
|
|
while (*(++us1) != '\0') {
|
|
if (*us1 != ' ' && *us1 != '\t') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (*(++us2) != '\0') {
|
|
if (*us2 != ' ' && *us2 != '\t') {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return (toupper(*us1) - toupper(*us2));
|
|
}
|
|
|
|
void
|
|
httpish_msg_free (httpishMsg_t *msg)
|
|
{
|
|
int i;
|
|
|
|
if ((!msg) || (msg->retain_flag == TRUE)) {
|
|
return;
|
|
}
|
|
|
|
UTILFREE(msg->mesg_line);
|
|
|
|
// Free all body parts
|
|
for (i = 0; i < HTTPISH_MAX_BODY_PARTS; i++) {
|
|
UTILFREE(msg->mesg_body[i].msgContentType);
|
|
UTILFREE(msg->mesg_body[i].msgBody);
|
|
UTILFREE(msg->mesg_body[i].msgContentId);
|
|
}
|
|
UTILFREE(msg->raw_body);
|
|
|
|
if (msg->headers) {
|
|
httpish_header *this_header;
|
|
|
|
this_header = (httpish_header *) dequeue(msg->headers);
|
|
while (this_header != NULL) {
|
|
UTILFREE(this_header->header);
|
|
UTILFREE(this_header);
|
|
this_header = (httpish_header *) dequeue(msg->headers);
|
|
}
|
|
}
|
|
|
|
UTILFREE(msg->headers);
|
|
msg->headers = NULL;
|
|
|
|
/* Free the header cache */
|
|
for (i = 0; i < HTTPISH_HEADER_CACHE_SIZE; ++i) {
|
|
if (msg->hdr_cache[i].hdr_start) {
|
|
cpr_free(msg->hdr_cache[i].hdr_start);
|
|
}
|
|
}
|
|
|
|
/* Free the httpishMsg_t struct itself */
|
|
cpr_free(msg);
|
|
}
|
|
|
|
boolean
|
|
httpish_msg_is_request (httpishMsg_t *msg,
|
|
const char *schema,
|
|
int schema_len)
|
|
{
|
|
char *loc;
|
|
|
|
loc = msg->mesg_line;
|
|
|
|
if (!msg->is_complete || !msg->mesg_line) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* There might be a couple of leading spaces. Not allowed,
|
|
* but still be friendly
|
|
*/
|
|
while ((*loc == ' ') && (*loc != '\0')) {
|
|
loc++;
|
|
}
|
|
|
|
if (strncmp(loc, schema, schema_len)) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
boolean
|
|
httpish_msg_is_complete (httpishMsg_t *msg)
|
|
{
|
|
return msg->is_complete;
|
|
}
|
|
|
|
|
|
hStatus_t
|
|
httpish_msg_add_reqline (httpishMsg_t *msg,
|
|
const char *method,
|
|
const char *url,
|
|
const char *version)
|
|
{
|
|
|
|
uint32_t linesize = 0;
|
|
|
|
if (!msg || !method || !url || !version) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
if (msg->mesg_line) {
|
|
cpr_free(msg->mesg_line);
|
|
}
|
|
|
|
linesize = strlen(method) + 1 + strlen(url) + 1 + strlen(version) + 1;
|
|
|
|
msg->mesg_line = (char *) cpr_malloc(linesize * sizeof(char));
|
|
if (!msg->mesg_line) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
snprintf(msg->mesg_line, linesize, "%s %s %s", method, url, version);
|
|
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
hStatus_t
|
|
httpish_msg_add_respline (httpishMsg_t *msg,
|
|
const char *version,
|
|
uint16_t status_code,
|
|
const char *reason_phrase)
|
|
{
|
|
uint32_t linesize = 0;
|
|
|
|
if (!msg || !reason_phrase || !version ||
|
|
(status_code < HTTPISH_MIN_STATUS_CODE)) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
if (msg->mesg_line) {
|
|
cpr_free(msg->mesg_line);
|
|
}
|
|
|
|
/* Assumes status codes are max 6 characters long */
|
|
linesize = strlen(version) + 1 + 6 + 1 + strlen(reason_phrase) + 1;
|
|
|
|
msg->mesg_line = (char *) cpr_malloc(linesize * sizeof(char));
|
|
if (!msg->mesg_line) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
snprintf(msg->mesg_line, linesize, "%s %d %s",
|
|
version, status_code, reason_phrase);
|
|
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
httpishReqLine_t *
|
|
httpish_msg_get_reqline (httpishMsg_t *msg)
|
|
{
|
|
char *this_token;
|
|
char *msgline;
|
|
httpishReqLine_t *hreq = NULL;
|
|
char *strtok_state;
|
|
|
|
if (!msg || !msg->mesg_line || !(msgline = cpr_strdup(msg->mesg_line))) {
|
|
return NULL;
|
|
}
|
|
|
|
hreq = (httpishReqLine_t *) cpr_malloc(sizeof(httpishReqLine_t));
|
|
if (!hreq) {
|
|
cpr_free(msgline);
|
|
return NULL;
|
|
}
|
|
|
|
this_token = PL_strtok_r(msgline, " ", &strtok_state);
|
|
|
|
if (!this_token) {
|
|
cpr_free(hreq);
|
|
cpr_free(msgline);
|
|
return NULL;
|
|
}
|
|
|
|
hreq->method = cpr_strdup(this_token);
|
|
|
|
this_token = PL_strtok_r(NULL, " ", &strtok_state);
|
|
|
|
if (!this_token) {
|
|
cpr_free(hreq->method);
|
|
cpr_free(hreq);
|
|
cpr_free(msgline);
|
|
return NULL;
|
|
}
|
|
|
|
hreq->url = cpr_strdup(this_token);
|
|
|
|
this_token = PL_strtok_r(NULL, " ", &strtok_state);
|
|
|
|
if (!this_token) {
|
|
cpr_free(hreq->method);
|
|
cpr_free(hreq->url);
|
|
cpr_free(hreq);
|
|
cpr_free(msgline);
|
|
return NULL;
|
|
}
|
|
|
|
hreq->version = cpr_strdup(this_token);
|
|
cpr_free(msgline);
|
|
return hreq;
|
|
}
|
|
|
|
|
|
httpishRespLine_t *
|
|
httpish_msg_get_respline (httpishMsg_t *msg)
|
|
{
|
|
char *this_token;
|
|
char *msgline;
|
|
httpishRespLine_t *hrsp = NULL;
|
|
char *strtok_state;
|
|
unsigned long strtoul_result;
|
|
char *strtoul_end;
|
|
|
|
if (!msg || !msg->mesg_line) {
|
|
return NULL;
|
|
}
|
|
|
|
msgline = cpr_strdup(msg->mesg_line);
|
|
if (!msgline) {
|
|
return NULL;
|
|
}
|
|
|
|
hrsp = (httpishRespLine_t *) cpr_malloc(sizeof(httpishRespLine_t));
|
|
|
|
if (!hrsp) {
|
|
cpr_free(msgline);
|
|
return NULL;
|
|
}
|
|
|
|
this_token = PL_strtok_r(msgline, " ", &strtok_state);
|
|
|
|
if (!this_token) {
|
|
cpr_free(hrsp);
|
|
cpr_free(msgline);
|
|
return NULL;
|
|
}
|
|
|
|
hrsp->version = cpr_strdup(this_token);
|
|
|
|
this_token = PL_strtok_r(NULL, " ", &strtok_state);
|
|
|
|
if (!this_token) {
|
|
cpr_free(hrsp->version);
|
|
cpr_free(hrsp);
|
|
cpr_free(msgline);
|
|
return NULL;
|
|
}
|
|
|
|
errno = 0;
|
|
strtoul_result = strtoul(this_token, &strtoul_end, 10);
|
|
|
|
if (errno || this_token == strtoul_end || strtoul_result > USHRT_MAX) {
|
|
cpr_free(hrsp->version);
|
|
cpr_free(hrsp);
|
|
cpr_free(msgline);
|
|
return NULL;
|
|
}
|
|
|
|
hrsp->status_code = (uint16_t) strtoul_result;
|
|
|
|
this_token = PL_strtok_r(NULL, " ", &strtok_state);
|
|
|
|
/* reason phrase is optional */
|
|
if (this_token) {
|
|
hrsp->reason_phrase = cpr_strdup(this_token);
|
|
} else {
|
|
hrsp->reason_phrase = NULL;
|
|
}
|
|
|
|
cpr_free(msgline);
|
|
return (hrsp);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
httpish_msg_free_reqline (httpishReqLine_t *rqline)
|
|
{
|
|
if (!rqline) {
|
|
return;
|
|
}
|
|
|
|
UTILFREE(rqline->method);
|
|
UTILFREE(rqline->url);
|
|
UTILFREE(rqline->version);
|
|
}
|
|
|
|
|
|
void
|
|
httpish_msg_free_respline (httpishRespLine_t *rspline)
|
|
{
|
|
if (!rspline) {
|
|
return;
|
|
}
|
|
|
|
UTILFREE(rspline->reason_phrase);
|
|
UTILFREE(rspline->version);
|
|
}
|
|
|
|
hStatus_t
|
|
httpish_msg_add_text_header (httpishMsg_t *msg,
|
|
const char *hname,
|
|
const char *hval)
|
|
{
|
|
uint32_t linesize = 0;
|
|
httpish_header *this_header = NULL;
|
|
char *header_line = NULL;
|
|
|
|
if (!msg || !hname || !hval) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
linesize = strlen(hname) + 2 + strlen(hval) + 1;
|
|
|
|
header_line = (char *) cpr_malloc(linesize * sizeof(char));
|
|
if (!header_line)
|
|
return HSTATUS_FAILURE;
|
|
|
|
this_header = (httpish_header *) cpr_malloc(sizeof(httpish_header));
|
|
if (!this_header) {
|
|
cpr_free(header_line);
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
snprintf(header_line, linesize, "%s: %s", hname, hval);
|
|
|
|
this_header->header = header_line;
|
|
this_header->next = NULL;
|
|
|
|
enqueue(msg->headers, (void *) this_header);
|
|
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
hStatus_t
|
|
httpish_msg_add_int_header (httpishMsg_t *msg,
|
|
const char *hname,
|
|
int32_t hval)
|
|
{
|
|
uint32_t linesize = 0;
|
|
char *header_line = NULL;
|
|
httpish_header *this_header = NULL;
|
|
|
|
if (!msg || !hname) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
/* Assumes the int is less than 10 characters */
|
|
linesize = strlen(hname) + 2 + 10 + 1;
|
|
|
|
header_line = (char *) cpr_malloc(linesize * sizeof(char));
|
|
if (!header_line) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
this_header = (httpish_header *) cpr_malloc(sizeof(httpish_header));
|
|
if (!this_header) {
|
|
cpr_free(header_line);
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
snprintf(header_line, linesize, "%s: %d", hname, hval);
|
|
|
|
this_header->header = header_line;
|
|
this_header->next = NULL;
|
|
|
|
enqueue(msg->headers, (void *) this_header);
|
|
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
const char *
|
|
httpish_msg_get_cached_header_val (httpishMsg_t *msg,
|
|
int cache_index)
|
|
{
|
|
return msg->hdr_cache[cache_index].val_start;
|
|
}
|
|
|
|
int
|
|
compact_hdr_cmp (char *this_line,
|
|
const char *c_hname)
|
|
{
|
|
char cmpct_hdr[CMPC_HEADER_SIZE];
|
|
|
|
if (c_hname) {
|
|
sstrncpy(cmpct_hdr, c_hname, CMPC_HEADER_SIZE);
|
|
return cpr_strcasecmp(this_line, cmpct_hdr);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
httpish_header_name_val (char *sipHeaderName, char *this_line)
|
|
{
|
|
unsigned int x = 0;
|
|
boolean nameFound = FALSE;
|
|
|
|
if (!sipHeaderName || !this_line) {
|
|
return (SIP_ERROR);
|
|
}
|
|
|
|
sipHeaderName[0] = '\0';
|
|
|
|
/* Remove the leading white spaces eg: ......From: or .....From....: */
|
|
while ((*this_line==' ' || *this_line=='\t') ) {
|
|
this_line++;
|
|
}
|
|
|
|
/* Copy the allowed characters for header field name */
|
|
while ((*this_line > 32) && (*this_line < 127) && (x < HTTPISH_HEADER_NAME_SIZE)) {
|
|
if (*this_line == ':') {
|
|
nameFound = TRUE;
|
|
sipHeaderName[x] = '\0';
|
|
break;
|
|
}
|
|
sipHeaderName[x] = *this_line;
|
|
this_line++;
|
|
x++;
|
|
}
|
|
|
|
/* Remove trailing white spaces */
|
|
if (nameFound == FALSE && x < HTTPISH_HEADER_NAME_SIZE) {
|
|
while ((*this_line == ' ' || *this_line=='\t') ){
|
|
this_line++;
|
|
if (*this_line == ':') {
|
|
nameFound = TRUE;
|
|
sipHeaderName[x] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
sipHeaderName[HTTPISH_HEADER_NAME_SIZE-1] = '\0';
|
|
|
|
if (nameFound) {
|
|
return (SIP_OK);
|
|
} else {
|
|
return (SIP_ERROR);
|
|
}
|
|
}
|
|
|
|
boolean
|
|
httpish_msg_header_present (httpishMsg_t *msg,
|
|
const char *hname)
|
|
{
|
|
nexthelper *p;
|
|
char *this_line = NULL;
|
|
|
|
/*
|
|
* To allow case-insensitive compact headers, we need to compare 2
|
|
* characters before we can decide, what the header name is.
|
|
* e.g. Call-ID and Content-Type both start with C.
|
|
* For now assume there is no white space between header name and ":"
|
|
*/
|
|
|
|
if (!msg || !hname || (msg->headers->count == 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
p = (nexthelper *) msg->headers->qhead;
|
|
while (p) {
|
|
this_line = ((httpish_header *)p)->header;
|
|
if (this_line) {
|
|
/* Remove leading spaces */
|
|
while ((*this_line == ' ') && (*this_line != '\0'))
|
|
this_line++;
|
|
if ((strlen(this_line) >= strlen(hname)) &&
|
|
(cpr_strncasecmp(this_line, hname, strlen(hname))) == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
p = p->next;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
const char *
|
|
httpish_msg_get_header_val (httpishMsg_t *msg,
|
|
const char *hname,
|
|
const char *c_hname)
|
|
{
|
|
static const char fname[] = "httpish_msg_get_header_val";
|
|
nexthelper *p;
|
|
char *this_line = NULL;
|
|
char headerName[HTTPISH_HEADER_NAME_SIZE];
|
|
|
|
headerName[0] = '\0';
|
|
|
|
/*
|
|
* To allow case-insensitive compact headers, we need to compare 2
|
|
* characters before we can decide, what the header name is.
|
|
* e.g. Call-ID and Content-Type both start with C.
|
|
* For now assume there is no white space between header name and ":"
|
|
*/
|
|
|
|
if (!msg || !hname || (msg->headers->count == 0)) {
|
|
return NULL;
|
|
}
|
|
|
|
p = (nexthelper *) msg->headers->qhead;
|
|
while (p) {
|
|
this_line = ((httpish_header *)p)->header;
|
|
|
|
if (httpish_header_name_val(headerName, this_line)) {
|
|
CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Invalid Header Passed %s\n", DEB_F_PREFIX_ARGS(HTTPISH, fname), this_line);
|
|
return (NULL);
|
|
}
|
|
|
|
if (this_line) {
|
|
if ((cpr_strcasecmp(headerName, hname) == 0 ||
|
|
compact_hdr_cmp(headerName, c_hname) == 0)) {
|
|
this_line = strchr(this_line, ':');
|
|
if (this_line) {
|
|
this_line++;
|
|
/* Remove leading spaces */
|
|
while ((*this_line == ' ') && (*this_line != '\0'))
|
|
this_line++;
|
|
if (*this_line == '\0')
|
|
return (NULL);
|
|
else
|
|
return ((const char *) this_line);
|
|
}
|
|
}
|
|
}
|
|
p = p->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int32_t
|
|
httpish_msg_get_content_length (httpishMsg_t *msg)
|
|
{
|
|
return msg->content_length;
|
|
}
|
|
|
|
static boolean
|
|
httpish_msg_to_wstream (pmhWstream_t *ws,
|
|
httpishMsg_t *msg)
|
|
{
|
|
nexthelper *p;
|
|
char tmp_body_buf[TMP_BODY_BUF_SIZE];
|
|
int buf_len, total_length = 0, i, boundary_size;
|
|
|
|
if (!pmhutils_wstream_write_line(ws, msg->mesg_line)) {
|
|
return (FALSE);
|
|
}
|
|
|
|
p = (nexthelper *) msg->headers->qhead;
|
|
while (p) {
|
|
if (!pmhutils_wstream_write_line(ws,
|
|
(char *) (((httpish_header *)p)->header))) {
|
|
return (FALSE);
|
|
}
|
|
p = p->next;
|
|
}
|
|
if (msg->num_body_parts > 0) {
|
|
if (msg->num_body_parts > 1) {
|
|
// Write out the special Content-Type header and the
|
|
// Mime-Version header and the aggregate Content-Length header
|
|
// followed by the unique boundary
|
|
buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE,
|
|
"%s: multipart/mixed; boundary=%s\r\n",
|
|
HTTPISH_HEADER_CONTENT_TYPE, uniqueBoundary);
|
|
|
|
if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
|
|
return (FALSE);
|
|
}
|
|
|
|
buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: 1.0\r\n",
|
|
HTTPISH_HEADER_MIME_VERSION);
|
|
if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
|
|
return (FALSE);
|
|
}
|
|
|
|
// Traverse the list and calculate the total size of the
|
|
// body
|
|
boundary_size = strlen("\r\n--\r\n") + strlen(uniqueBoundary);
|
|
for (i = 0; i < msg->num_body_parts; i++) {
|
|
total_length += boundary_size;
|
|
total_length += msg->mesg_body[i].msgLength;
|
|
total_length += sizeof(HTTPISH_HEADER_CONTENT_TYPE) + 1;
|
|
switch (msg->mesg_body[i].msgContentTypeValue) {
|
|
default:
|
|
case SIP_CONTENT_TYPE_UNKNOWN_VALUE:
|
|
total_length += strlen(msg->mesg_body[i].msgContentType) + 2;
|
|
break;
|
|
case SIP_CONTENT_TYPE_SDP_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_SDP) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_SIPFRAG_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_SIPFRAG) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_DIALOG_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_DIALOG) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_KPML_REQUEST_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_KPML_REQUEST) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_KPML_RESPONSE_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_KPML_RESPONSE) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_REMOTECC_REQUEST) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_REMOTECC_RESPONSE) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_CTI_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_CTI) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_CMXML_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_CMXML) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_TEXT_PLAIN) + 1;
|
|
break;
|
|
case SIP_CONTENT_TYPE_PRESENCE_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_TYPE_PRESENCE) + 1;
|
|
break;
|
|
}
|
|
// Now estimate size of Content-Disposition header
|
|
total_length += sizeof(SIP_HEADER_CONTENT_DISP) + 1;
|
|
switch (msg->mesg_body[i].msgContentDisp) {
|
|
case SIP_CONTENT_DISPOSITION_RENDER_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_DISPOSITION_RENDER) - 1;
|
|
break;
|
|
case SIP_CONTENT_DISPOSITION_SESSION_VALUE:
|
|
default:
|
|
total_length += sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1;
|
|
break;
|
|
case SIP_CONTENT_DISPOSITION_ICON_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_DISPOSITION_ICON) - 1;
|
|
break;
|
|
case SIP_CONTENT_DISPOSITION_ALERT_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_DISPOSITION_ALERT) - 1;
|
|
break;
|
|
case SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE:
|
|
total_length += sizeof(SIP_CONTENT_DISPOSITION_PRECONDITION) - 1;
|
|
break;
|
|
}
|
|
// Now the handling attribute
|
|
total_length += sizeof(";handling=") - 1;
|
|
if (msg->mesg_body[i].msgRequiredHandling) {
|
|
total_length += sizeof("required") + 1;
|
|
} else {
|
|
total_length += sizeof("optional") + 1;
|
|
}
|
|
// Now the content id
|
|
if (msg->mesg_body[i].msgContentId) {
|
|
total_length += sizeof(HTTPISH_HEADER_CONTENT_ID) + 1 +
|
|
strlen(msg->mesg_body[i].msgContentId) + 2;
|
|
|
|
}
|
|
}
|
|
// Now account for the closing boundary which is 2+boundary_size
|
|
total_length += 2 + boundary_size + 2;
|
|
|
|
// Now write it out
|
|
buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: %d\r\n",
|
|
HTTPISH_HEADER_CONTENT_LENGTH, total_length);
|
|
|
|
if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
|
|
return (FALSE);
|
|
}
|
|
|
|
buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s\r\n", uniqueBoundary);
|
|
|
|
if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
|
|
return (FALSE);
|
|
}
|
|
|
|
} else {
|
|
// Write out Content-Length for the first body
|
|
total_length = msg->mesg_body[0].msgLength;
|
|
buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: %d\r\n",
|
|
HTTPISH_HEADER_CONTENT_LENGTH, total_length);
|
|
|
|
if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
for (i = 0; i < msg->num_body_parts; i++) {
|
|
if (i > 0) {
|
|
// If there is another body to come, write the unique boundary
|
|
buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s\r\n", uniqueBoundary);
|
|
if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "Content-Type: %s\r\n",
|
|
msg->mesg_body[i].msgContentType);
|
|
sstrncat(tmp_body_buf, "Content-Disposition: ",
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
|
|
switch (msg->mesg_body[i].msgContentDisp) {
|
|
case SIP_CONTENT_DISPOSITION_RENDER_VALUE:
|
|
sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_RENDER,
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
break;
|
|
case SIP_CONTENT_DISPOSITION_SESSION_VALUE:
|
|
default:
|
|
sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_SESSION,
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
break;
|
|
case SIP_CONTENT_DISPOSITION_ICON_VALUE:
|
|
sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_ICON,
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
break;
|
|
case SIP_CONTENT_DISPOSITION_ALERT_VALUE:
|
|
sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_ALERT,
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
break;
|
|
case SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE:
|
|
sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_PRECONDITION,
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
break;
|
|
}
|
|
if (msg->mesg_body[i].msgRequiredHandling) {
|
|
sstrncat(tmp_body_buf, ";handling=required\r\n",
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
} else {
|
|
sstrncat(tmp_body_buf, ";handling=optional\r\n",
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
}
|
|
if (msg->mesg_body[i].msgContentId) {
|
|
sstrncat(tmp_body_buf, "Content-Id: ",
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
sstrncat(tmp_body_buf, msg->mesg_body[i].msgContentId,
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
sstrncat(tmp_body_buf, "\r\n",
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
}
|
|
sstrncat(tmp_body_buf, "\r\n",
|
|
sizeof(tmp_body_buf) - strlen(tmp_body_buf));
|
|
buf_len = strlen(tmp_body_buf);
|
|
if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
|
|
return (FALSE);
|
|
}
|
|
|
|
// Now write the body
|
|
if (!pmhutils_wstream_write_bytes(ws, msg->mesg_body[i].msgBody,
|
|
msg->mesg_body[i].msgLength)) {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
// After writing out the last body part, write out the last unique
|
|
// boundary line
|
|
if (msg->num_body_parts > 1) {
|
|
snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s--\r\n", uniqueBoundary);
|
|
buf_len = strlen(tmp_body_buf);
|
|
if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
} else {
|
|
if (!pmhutils_wstream_write_byte(ws, '\r')) {
|
|
return (FALSE);
|
|
}
|
|
if (!pmhutils_wstream_write_byte(ws, '\n')) {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
hStatus_t
|
|
httpish_msg_write (httpishMsg_t *msg,
|
|
char *buf,
|
|
uint32_t *nbytes)
|
|
{
|
|
pmhWstream_t *ws = NULL;
|
|
|
|
ws = pmhutils_wstream_create_with_buf(buf, *nbytes);
|
|
if (!ws) {
|
|
return (HSTATUS_FAILURE);
|
|
}
|
|
|
|
if (!httpish_msg_to_wstream(ws, msg)) {
|
|
pmhutils_wstream_delete(ws, FALSE);
|
|
cpr_free(ws);
|
|
return (HSTATUS_FAILURE);
|
|
}
|
|
|
|
*nbytes = pmhutils_wstream_get_length(ws);
|
|
pmhutils_wstream_delete(ws, FALSE);
|
|
cpr_free(ws);
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
int
|
|
httpish_cache_header_val (httpishMsg_t *hmsg,
|
|
char *this_line)
|
|
{
|
|
static const char fname[] = "httpish_cache_header_val";
|
|
char *hdr_start;
|
|
httpish_cache_t *hdr_cache;
|
|
int i;
|
|
char headerName[HTTPISH_HEADER_NAME_SIZE];
|
|
|
|
headerName[0] = '\0';
|
|
|
|
hdr_cache = hmsg->hdr_cache;
|
|
hdr_start = this_line;
|
|
|
|
if (httpish_header_name_val(headerName, this_line)) {
|
|
CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Invalid Header %s\n", DEB_F_PREFIX_ARGS(HTTPISH, fname), this_line);
|
|
return (SIP_ERROR);
|
|
}
|
|
|
|
for (i = 0; i < HTTPISH_HEADER_CACHE_SIZE; ++i) {
|
|
sip_header_t *tmp = sip_cached_headers + i;
|
|
|
|
if (cpr_strcasecmp(headerName, tmp->hname) == 0 ||
|
|
compact_hdr_cmp(headerName, tmp->c_hname) == 0) {
|
|
this_line = strchr(this_line, ':');
|
|
if (this_line) {
|
|
this_line++; /* Skip the ':' */
|
|
|
|
/* Remove leading spaces */
|
|
while (*this_line == ' ' || *this_line == '\t') {
|
|
this_line++;
|
|
}
|
|
if (*this_line) {
|
|
if (hdr_cache[i].hdr_start) {
|
|
int org_len, offset;
|
|
int size;
|
|
char *newbuf;
|
|
|
|
/* Multiple instances of a header, concatenate the
|
|
* header values with a ','
|
|
*/
|
|
org_len = strlen(hdr_cache[i].hdr_start);
|
|
offset = hdr_cache[i].val_start - hdr_cache[i].hdr_start;
|
|
size = org_len + 2 + strlen(this_line);
|
|
newbuf = (char *) cpr_realloc(hdr_cache[i].hdr_start,
|
|
size);
|
|
if (newbuf == NULL) {
|
|
cpr_free(hdr_cache[i].hdr_start);
|
|
hdr_cache[i].hdr_start = NULL;
|
|
break;
|
|
}
|
|
hdr_cache[i].hdr_start = newbuf;
|
|
hdr_cache[i].val_start = hdr_cache[i].hdr_start + offset;
|
|
hdr_cache[i].hdr_start[org_len] = ',';
|
|
sstrncpy(hdr_cache[i].hdr_start + org_len + 1, this_line,
|
|
size - org_len - 1);
|
|
cpr_free(hdr_start);
|
|
} else {
|
|
hdr_cache[i].hdr_start = hdr_start;
|
|
hdr_cache[i].val_start = this_line;
|
|
}
|
|
} else { // this line is blank
|
|
cpr_free(hdr_start);
|
|
}
|
|
} else { // this line does not have a ':'
|
|
cpr_free(hdr_start);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Return -1 for invalid/empty content-length value
|
|
* else return the content length
|
|
*/
|
|
int32_t
|
|
get_content_length (httpishMsg_t *hmsg)
|
|
{
|
|
int i;
|
|
const char *hdr_val;
|
|
long strtol_result;
|
|
char *strtol_end;
|
|
|
|
hdr_val = httpish_msg_get_cached_header_val(hmsg, CONTENT_LENGTH);
|
|
if (hdr_val == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; hdr_val[i]; ++i) {
|
|
if (!isdigit((int) hdr_val[i])) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* If the string was empty then the content length is still invalid */
|
|
if (!i) {
|
|
return -1;
|
|
}
|
|
|
|
errno = 0;
|
|
strtol_result = strtol(hdr_val, &strtol_end, 10);
|
|
|
|
if (errno || hdr_val == strtol_end || strtol_result > INT_MAX) {
|
|
return -1;
|
|
} else {
|
|
return (int) strtol_result;
|
|
}
|
|
}
|
|
|
|
uint8_t
|
|
get_content_type_value (const char *content_type)
|
|
{
|
|
if (!content_type) {
|
|
return SIP_CONTENT_TYPE_UNKNOWN_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SDP,
|
|
sizeof(SIP_CONTENT_TYPE_SDP) - 1)) {
|
|
return SIP_CONTENT_TYPE_SDP_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SIPFRAG,
|
|
sizeof(SIP_CONTENT_TYPE_SIPFRAG) - 1)) {
|
|
return SIP_CONTENT_TYPE_SIPFRAG_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_TEXT_PLAIN,
|
|
sizeof(SIP_CONTENT_TYPE_TEXT_PLAIN) - 1)) {
|
|
return SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SIP,
|
|
sizeof(SIP_CONTENT_TYPE_SIP) - 1)) {
|
|
return SIP_CONTENT_TYPE_SIP_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MWI,
|
|
sizeof(SIP_CONTENT_TYPE_MWI) - 1)) {
|
|
return SIP_CONTENT_TYPE_MWI_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MULTIPART_MIXED,
|
|
sizeof(SIP_CONTENT_TYPE_MULTIPART_MIXED) - 1)) {
|
|
return SIP_CONTENT_TYPE_MULTIPART_MIXED_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE,
|
|
sizeof(SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE) - 1)) {
|
|
return SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE_VALUE;
|
|
}
|
|
else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_DIALOG,
|
|
sizeof(SIP_CONTENT_TYPE_DIALOG) - 1)) {
|
|
return SIP_CONTENT_TYPE_DIALOG_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_KPML_REQUEST,
|
|
sizeof(SIP_CONTENT_TYPE_KPML_REQUEST) - 1)) {
|
|
return SIP_CONTENT_TYPE_KPML_REQUEST_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_KPML_RESPONSE,
|
|
sizeof(SIP_CONTENT_TYPE_KPML_RESPONSE) - 1)) {
|
|
return SIP_CONTENT_TYPE_KPML_RESPONSE_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_REMOTECC_REQUEST,
|
|
sizeof(SIP_CONTENT_TYPE_REMOTECC_REQUEST) - 1)) {
|
|
return SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CONFIGAPP,
|
|
sizeof(SIP_CONTENT_TYPE_CONFIGAPP) - 1)) {
|
|
return SIP_CONTENT_TYPE_CONFIGAPP_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_REMOTECC_RESPONSE,
|
|
sizeof(SIP_CONTENT_TYPE_REMOTECC_RESPONSE) - 1)) {
|
|
return SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_PRESENCE,
|
|
sizeof(SIP_CONTENT_TYPE_PRESENCE) - 1)) {
|
|
return SIP_CONTENT_TYPE_PRESENCE_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CMXML,
|
|
sizeof(SIP_CONTENT_TYPE_CMXML) - 1)) {
|
|
return SIP_CONTENT_TYPE_CMXML_VALUE;
|
|
} else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CTI,
|
|
sizeof(SIP_CONTENT_TYPE_CTI) - 1)) {
|
|
return SIP_CONTENT_TYPE_CTI_VALUE;
|
|
}
|
|
return SIP_CONTENT_TYPE_UNKNOWN_VALUE;
|
|
}
|
|
|
|
/******************************************************************
|
|
* msg_process_one_body
|
|
* This function will process one of the body parts of the whole body
|
|
* Headers not understood will be ignored
|
|
* - msg_start should be pointing at the first header of the body part
|
|
* - msg_end at its last character
|
|
******************************************************************/
|
|
int
|
|
msg_process_one_body (httpishMsg_t *hmsg,
|
|
char *msg_start,
|
|
char *msg_end,
|
|
int current_body_part)
|
|
{
|
|
static const char fname[] = "msg_process_one_body";
|
|
pmhRstream_t *rstream = NULL;
|
|
char *content_type = NULL, *content_disp = NULL;
|
|
char *line = NULL, *body = NULL;
|
|
char *content_enc = NULL;
|
|
char *content_id = NULL; int nbytes = 0;
|
|
boolean body_read = FALSE;
|
|
|
|
// Adjust msg_start to point to the first valid character
|
|
while (*msg_start == '\r' || *msg_start == '\n') {
|
|
msg_start++;
|
|
}
|
|
|
|
// Convert this chunk of data into a more parse-able format
|
|
rstream = pmhutils_rstream_create(msg_start, (uint32_t)(msg_end - msg_start));
|
|
|
|
while (!body_read) {
|
|
if (rstream) {
|
|
line = pmhutils_rstream_read_line(rstream);
|
|
}
|
|
|
|
if (line) {
|
|
// Look for the kind of line it is
|
|
if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_TYPE,
|
|
sizeof(HTTPISH_HEADER_CONTENT_TYPE) - 1)) {
|
|
// Its Content-Type, read in the value
|
|
// XXX what if the line looks like "Content-Type-Garbage: some-value"?
|
|
content_type = line + sizeof(HTTPISH_HEADER_CONTENT_TYPE);
|
|
// XXX what if the line looks like "Content-Type : some-value"?
|
|
while (*content_type == ' ') {
|
|
content_type++;
|
|
}
|
|
hmsg->mesg_body[current_body_part].msgContentTypeValue = get_content_type_value(content_type);
|
|
nbytes = strlen(content_type) + 1;
|
|
hmsg->mesg_body[current_body_part].msgContentType =
|
|
(char *) cpr_malloc((nbytes)*sizeof(char));
|
|
if (hmsg->mesg_body[current_body_part].msgContentType == NULL) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
|
|
} else {
|
|
memcpy(hmsg->mesg_body[current_body_part].msgContentType,
|
|
content_type, nbytes);
|
|
}
|
|
} else if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_ID,
|
|
sizeof(HTTPISH_HEADER_CONTENT_ID) - 1)) {
|
|
//Its Content-Id, read the value
|
|
content_id = line + sizeof(HTTPISH_HEADER_CONTENT_ID);
|
|
while(*content_id == ' ') {
|
|
content_id++;
|
|
}
|
|
nbytes = strlen(content_id) + 1;
|
|
hmsg->mesg_body[current_body_part].msgContentId =
|
|
(char *) cpr_malloc((nbytes)*sizeof(char));
|
|
if (hmsg->mesg_body[current_body_part].msgContentId == NULL) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
|
|
}
|
|
memcpy(hmsg->mesg_body[current_body_part].msgContentId,
|
|
content_id, nbytes);
|
|
} else if (!cpr_strncasecmp(line, SIP_HEADER_CONTENT_DISP,
|
|
sizeof(SIP_HEADER_CONTENT_DISP) - 1)) {
|
|
// Its Content-Disposition, read in the value
|
|
content_disp = line + sizeof(SIP_HEADER_CONTENT_DISP);
|
|
while (*content_disp == ' ') {
|
|
content_disp++;
|
|
}
|
|
if (!cpr_strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_SESSION,
|
|
sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1)) {
|
|
hmsg->mesg_body[current_body_part].msgContentDisp =
|
|
SIP_CONTENT_DISPOSITION_SESSION_VALUE;
|
|
content_disp += sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1;
|
|
} else {
|
|
hmsg->mesg_body[current_body_part].msgContentDisp =
|
|
SIP_CONTENT_DISPOSITION_UNKNOWN_VALUE;
|
|
content_disp = strchr(line, ';');
|
|
}
|
|
if (content_disp && *content_disp == ';') {
|
|
content_disp++;
|
|
if (!cpr_strncasecmp(content_disp, "handling", 8)) {
|
|
content_disp += 9;
|
|
if (!cpr_strncasecmp(content_disp, "required", 8)) {
|
|
hmsg->mesg_body[current_body_part].msgRequiredHandling = TRUE;
|
|
} else if (!cpr_strncasecmp(content_disp, "optional", 8)) {
|
|
hmsg->mesg_body[current_body_part].msgRequiredHandling = FALSE;
|
|
}
|
|
}
|
|
}
|
|
} else if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_ENCODING,
|
|
sizeof(HTTPISH_HEADER_CONTENT_ENCODING) - 1)) {
|
|
content_enc = line + sizeof(HTTPISH_HEADER_CONTENT_ENCODING);
|
|
while (*content_enc == ' ') {
|
|
content_enc++;
|
|
}
|
|
if (!cpr_strcasecmp(content_enc, SIP_CONTENT_ENCODING_IDENTITY)) {
|
|
hmsg->mesg_body[current_body_part].msgContentEnc =
|
|
SIP_CONTENT_ENCODING_IDENTITY_VALUE;
|
|
} else {
|
|
hmsg->mesg_body[current_body_part].msgContentEnc =
|
|
SIP_CONTENT_ENCODING_UNKNOWN_VALUE;
|
|
}
|
|
} else if (!(*line)) {
|
|
body_read = TRUE;
|
|
// The rest of the bytes are the body
|
|
hmsg->mesg_body[current_body_part].msgLength =
|
|
rstream->nbytes - rstream->bytes_read;
|
|
body = pmhutils_rstream_read_bytes(rstream, rstream->nbytes - rstream->bytes_read);
|
|
if (body) {
|
|
hmsg->mesg_body[current_body_part].msgBody = body;
|
|
}
|
|
} else {
|
|
// Unhandled header type
|
|
CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unrecognized header in body\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
|
|
}
|
|
// Free the read line
|
|
cpr_free(line);
|
|
line = NULL;
|
|
} else {
|
|
body_read = TRUE;
|
|
}
|
|
}
|
|
// Free the created stream structure
|
|
pmhutils_rstream_delete(rstream, FALSE);
|
|
cpr_free(rstream);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************
|
|
* msg_process_multiple_bodies
|
|
* This function will process multiple body parts of the whole body
|
|
* boundary should point to the delimiter string, raw body points to
|
|
* the beginning of the whole body structure in the message
|
|
*
|
|
* The algorithm here is to find the beginning and the end of each
|
|
* body part and send it off for further header and body extraction
|
|
******************************************************************/
|
|
int
|
|
msg_process_multiple_bodies (httpishMsg_t *hmsg,
|
|
char *boundary,
|
|
char *raw_body)
|
|
{
|
|
char msg_delimit[MSG_DELIMIT_SIZE];
|
|
int i, body_part = 0;
|
|
boolean end_of_proc = FALSE;
|
|
char *msg_start, *msg_end;
|
|
|
|
// First copy the boundary in an array
|
|
msg_delimit[0] = '-';
|
|
msg_delimit[1] = '-';
|
|
for (i = 0; boundary[i] != '\r' && boundary[i] != '\n' &&
|
|
boundary[i] != ';' && boundary[i] != '\0'; i++) {
|
|
if (i + 2 >= MSG_DELIMIT_SIZE) {
|
|
return body_part;
|
|
}
|
|
msg_delimit[i + 2] = boundary[i];
|
|
}
|
|
msg_delimit[i + 2] = '\0';
|
|
|
|
// Loop through the body segments
|
|
while (!end_of_proc && body_part < HTTPISH_MAX_BODY_PARTS) {
|
|
msg_start = strstr(raw_body, msg_delimit);
|
|
if (msg_start) {
|
|
msg_start += strlen(msg_delimit) + 1;
|
|
msg_end = strstr(msg_start, msg_delimit);
|
|
if (msg_end) {
|
|
(void) msg_process_one_body(hmsg, msg_start, msg_end - 1,
|
|
body_part);
|
|
} else {
|
|
// No boundary found for message end, return error
|
|
end_of_proc = TRUE;
|
|
continue;
|
|
}
|
|
} else {
|
|
// No boundary found for message start, return error
|
|
end_of_proc = TRUE;
|
|
continue;
|
|
}
|
|
raw_body = msg_end;
|
|
// Check if this is the last boundary
|
|
if (*(raw_body + strlen(msg_delimit)) == '-') {
|
|
end_of_proc = TRUE;
|
|
}
|
|
body_part++;
|
|
}
|
|
return body_part;
|
|
}
|
|
|
|
hStatus_t
|
|
httpish_msg_process_network_msg (httpishMsg_t *hmsg,
|
|
char *nmsg,
|
|
uint32_t *nbytes)
|
|
{
|
|
static const char fname[] = "httpish_msg_process_network_msg";
|
|
pmhRstream_t *rs = NULL;
|
|
int32_t bytes_remaining, delta;
|
|
char *mline;
|
|
hStatus_t retval;
|
|
char *raw_body = NULL;
|
|
const char *content_type = NULL;
|
|
const char *content_id = NULL;
|
|
int32_t contentid_len;
|
|
int32_t contenttype_len;
|
|
|
|
if (!hmsg || !nmsg || (*nbytes <= 0)) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
if (hmsg->is_complete == TRUE) {
|
|
*nbytes = 0;
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
if ((rs = pmhutils_rstream_create(nmsg, *nbytes)) == NULL)
|
|
return HSTATUS_FAILURE;
|
|
|
|
/* Try to read message line */
|
|
while (!hmsg->mesg_line) {
|
|
mline = pmhutils_rstream_read_line(rs);
|
|
if (!mline) {
|
|
*nbytes = rs->bytes_read;
|
|
if (rs->eof == TRUE) {
|
|
retval = HSTATUS_SUCCESS;
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Msg line read failure due to RS->EOF\n", fname);
|
|
} else {
|
|
retval = HSTATUS_FAILURE;
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Msg line read failure\n", fname);
|
|
}
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
return retval;
|
|
}
|
|
if (!(*mline)) {
|
|
cpr_free(mline);
|
|
} else {
|
|
hmsg->mesg_line = mline;
|
|
}
|
|
}
|
|
|
|
/* There is a message line. We could have a header or a message body */
|
|
while (!hmsg->headers_read) {
|
|
char *this_header;
|
|
|
|
this_header = pmhutils_rstream_read_line(rs);
|
|
if (!this_header) {
|
|
*nbytes = rs->bytes_read;
|
|
if (rs->eof == TRUE) {
|
|
retval = HSTATUS_SUCCESS;
|
|
CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Header line read failure due to RS->EOF\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
|
|
} else {
|
|
retval = HSTATUS_FAILURE;
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Header line read failure\n", fname);
|
|
}
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
return retval;
|
|
}
|
|
if (!(*this_header)) {
|
|
cpr_free(this_header);
|
|
hmsg->headers_read = TRUE;
|
|
} else {
|
|
httpish_header *h;
|
|
|
|
if (httpish_cache_header_val(hmsg, this_header) == -1) {
|
|
/*
|
|
* For a non-cacheable header or error in caching routine,
|
|
* use the header linked list.
|
|
*/
|
|
h = (httpish_header *) cpr_malloc(sizeof(httpish_header));
|
|
if (!h) {
|
|
*nbytes = rs->bytes_read;
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
cpr_free(this_header);
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
h->next = NULL;
|
|
h->header = this_header;
|
|
enqueue(hmsg->headers, (void *)h);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Calculate the bytes remaining in the read stream */
|
|
bytes_remaining = rs->nbytes - rs->bytes_read;
|
|
|
|
/* Now get the content length header value */
|
|
hmsg->content_length = get_content_length(hmsg);
|
|
if (hmsg->content_length == -1) {
|
|
/* Bad or missing content-length header
|
|
* For UDP, assume remaining msg is message body.
|
|
* For TCP, message is not complete without content-length header
|
|
* but we will ignore this possibility for now (assume content-length will always be there)
|
|
* since we don't know if we have a complete message or a fragmented one
|
|
*/
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Content-Length header not received\n", fname);
|
|
hmsg->content_length = bytes_remaining;
|
|
}
|
|
|
|
delta = bytes_remaining - hmsg->content_length;
|
|
if (delta) {
|
|
CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX "Content Length %d, Bytes Remaining %d.\n", DEB_F_PREFIX_ARGS(HTTPISH, fname),
|
|
hmsg->content_length, bytes_remaining);
|
|
}
|
|
if (delta < 0) {
|
|
/* We have fewer bytes than specified by Content-Length header */
|
|
hmsg->content_length = bytes_remaining;
|
|
hmsg->is_complete = FALSE;
|
|
CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Partial body received\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
|
|
} else {
|
|
hmsg->is_complete = TRUE;
|
|
}
|
|
|
|
if (hmsg->content_length > 0) {
|
|
raw_body = pmhutils_rstream_read_bytes(rs, hmsg->content_length);
|
|
if (!raw_body) {
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
}
|
|
|
|
// If message is not complete we should not parse the message any more
|
|
if (!hmsg->is_complete) {
|
|
*nbytes = rs->bytes_read;
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
UTILFREE(raw_body);
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Figure out whether more parsing of the received body is necessary
|
|
content_type = httpish_msg_get_cached_header_val(hmsg, CONTENT_TYPE);
|
|
if (content_type && (hmsg->content_length > 0)) {
|
|
if (!cpr_strncasecmp(content_type, "multipart/mixed", 15)) {
|
|
|
|
char *boundary = NULL;
|
|
int num_bodies = 0;
|
|
|
|
// find the boundary tag
|
|
boundary = strchr(content_type, '=');
|
|
if (boundary) {
|
|
boundary++;
|
|
if (raw_body) {
|
|
num_bodies = msg_process_multiple_bodies(hmsg, boundary, raw_body);
|
|
}
|
|
|
|
if (num_bodies == 0) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in decoding multipart messages\n", fname);
|
|
} else {
|
|
hmsg->num_body_parts = (uint8_t) num_bodies;
|
|
}
|
|
} else {
|
|
// No boundary specified!
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in decoding multipart messages: No body delimiter\n", fname);
|
|
}
|
|
hmsg->raw_body = raw_body;
|
|
|
|
} else {
|
|
// All of the body is of a single type
|
|
// hmsg->mesg_body[0].msgBody = raw_body;
|
|
hmsg->mesg_body[0].msgBody = (char *)
|
|
cpr_malloc(hmsg->content_length + 1);
|
|
if (hmsg->mesg_body[0].msgBody == NULL) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
cpr_free(raw_body);
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
if (raw_body) {
|
|
memcpy(hmsg->mesg_body[0].msgBody, raw_body,
|
|
hmsg->content_length + 1);
|
|
}
|
|
|
|
content_id = httpish_msg_get_header_val(hmsg, HTTPISH_HEADER_CONTENT_ID, NULL);
|
|
if (content_id) {
|
|
contentid_len = strlen(content_id) + 1;
|
|
hmsg->mesg_body[0].msgContentId = (char *)
|
|
cpr_malloc(contentid_len * sizeof(char));
|
|
if (hmsg->mesg_body[0].msgContentId == NULL) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
cpr_free(raw_body);
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
memcpy(hmsg->mesg_body[0].msgContentId,
|
|
content_id, contentid_len);
|
|
}
|
|
|
|
hmsg->mesg_body[0].msgContentTypeValue = get_content_type_value(content_type);
|
|
contenttype_len = strlen(content_type) + 1;
|
|
hmsg->mesg_body[0].msgContentType = (char *)
|
|
cpr_malloc(contenttype_len * sizeof(char));
|
|
if (hmsg->mesg_body[0].msgContentType == NULL) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
cpr_free(raw_body);
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
memcpy(hmsg->mesg_body[0].msgContentType,
|
|
content_type, contenttype_len);
|
|
|
|
hmsg->mesg_body[0].msgContentDisp =
|
|
SIP_CONTENT_DISPOSITION_SESSION_VALUE;
|
|
hmsg->mesg_body[0].msgRequiredHandling = TRUE;
|
|
hmsg->mesg_body[0].msgLength = hmsg->content_length;
|
|
hmsg->num_body_parts = 1;
|
|
hmsg->raw_body = raw_body;
|
|
}
|
|
} else if (hmsg->content_length > 0) {
|
|
// No content-type specified but there is a body present. Treat this
|
|
// as an error
|
|
hmsg->is_complete = FALSE;
|
|
hmsg->content_length = -1;
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Body found without content-type\n", fname);
|
|
UTILFREE(raw_body);
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
*nbytes = rs->bytes_read;
|
|
pmhutils_rstream_delete(rs, FALSE);
|
|
cpr_free(rs);
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
hStatus_t
|
|
httpish_msg_add_body (httpishMsg_t *msg,
|
|
char *body,
|
|
uint32_t nbytes,
|
|
const char *content_type,
|
|
uint8_t msg_disposition,
|
|
boolean required,
|
|
char *content_id)
|
|
{
|
|
static const char fname[] = "httpish_msg_add_body";
|
|
uint8_t current_body_part;
|
|
uint32_t contenttype_len;
|
|
|
|
if (!msg || !body || (nbytes == 0)) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
if (msg->num_body_parts == HTTPISH_MAX_BODY_PARTS) {
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
// Add body at the next available index
|
|
current_body_part = msg->num_body_parts;
|
|
|
|
contenttype_len = strlen(content_type) + 1;
|
|
msg->mesg_body[current_body_part].msgContentType = (char *)
|
|
cpr_malloc(contenttype_len * sizeof(char));
|
|
if (msg->mesg_body[current_body_part].msgContentType == NULL) {
|
|
CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
|
|
return HSTATUS_FAILURE;
|
|
}
|
|
|
|
msg->mesg_body[current_body_part].msgBody = body;
|
|
memcpy(msg->mesg_body[current_body_part].msgContentType,
|
|
content_type, contenttype_len);
|
|
msg->mesg_body[current_body_part].msgContentTypeValue = get_content_type_value(content_type);
|
|
msg->mesg_body[current_body_part].msgContentDisp = msg_disposition;
|
|
msg->mesg_body[current_body_part].msgRequiredHandling = required;
|
|
msg->mesg_body[current_body_part].msgLength = nbytes;
|
|
msg->mesg_body[current_body_part].msgContentId = content_id;
|
|
|
|
msg->num_body_parts++;
|
|
|
|
return HSTATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
httpishStatusCodeClass_t
|
|
httpish_msg_get_code_class (uint16_t statusCode)
|
|
{
|
|
httpishStatusCodeClass_t retval;
|
|
int fc = statusCode / 100;
|
|
|
|
switch (fc) {
|
|
case 1:
|
|
retval = codeClass1xx;
|
|
break;
|
|
case 2:
|
|
retval = codeClass2xx;
|
|
break;
|
|
case 3:
|
|
retval = codeClass3xx;
|
|
break;
|
|
case 4:
|
|
retval = codeClass4xx;
|
|
break;
|
|
case 5:
|
|
retval = codeClass5xx;
|
|
break;
|
|
case 6:
|
|
retval = codeClass6xx;
|
|
break;
|
|
default:
|
|
retval = codeClassInvalid;
|
|
break;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
uint16_t
|
|
httpish_msg_get_num_particular_headers (httpishMsg_t *msg,
|
|
const char *hname,
|
|
const char *c_hname,
|
|
char *header_val[],
|
|
uint16_t max_headers)
|
|
{
|
|
nexthelper *p;
|
|
char *this_line = NULL;
|
|
uint16_t found = 0;
|
|
|
|
if (!msg || !hname) {
|
|
return 0;
|
|
}
|
|
|
|
p = (nexthelper *) msg->headers->qhead;
|
|
|
|
while (p && found < max_headers) {
|
|
this_line = ((httpish_header *)p)->header;
|
|
if (this_line) {
|
|
/* Remove leading spaces */
|
|
while ((*this_line == ' ') && (*this_line != '\0'))
|
|
this_line++;
|
|
if ((strlen(this_line) > strlen(hname) + 1) &&
|
|
(cpr_strncasecmp(this_line, hname, strlen(hname)) == 0 ||
|
|
compact_hdr_cmp(this_line, c_hname) == 0)) {
|
|
this_line = strchr(this_line, ':');
|
|
if (this_line) {
|
|
this_line++;
|
|
/* Remove leading spaces */
|
|
while ((*this_line == ' ') && (*this_line != '\0'))
|
|
this_line++;
|
|
if (*this_line == '\0') {
|
|
p = p->next;
|
|
continue;
|
|
} else {
|
|
header_val[found] = this_line;
|
|
found++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = p->next;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
|