/* 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 #include #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; }