Commit a5e65b39 authored by Tien-Thinh Nguyen's avatar Tien-Thinh Nguyen

Use a simple Mime parser instead of multipartparser

parent 824102f8
......@@ -12,7 +12,7 @@
#include "N1N2MessageCollectionDocumentApi.h"
#include "Helpers.h"
#include "multipartparser.hpp"
#include "mime_parser.hpp"
#include "logger.hpp"
namespace oai {
......@@ -45,71 +45,42 @@ void N1N2MessageCollectionDocumentApi::n1_n2_message_transfer_handler(const Pist
Logger::amf_server().debug("Received a N1N2MessageTrasfer request with ue_ctx_id(%s) ",ueContextId.c_str());
// Getting the body param
std::size_t found = request.body().find("Content-Type");
std::string boundary_str = request.body().substr(2, found - 4);
Logger::amf_server().debug("Boundary: %s", boundary_str.c_str());
//step 1. use multipartparser to decode the request
multipartparser_callbacks_init(&g_callbacks);
g_callbacks.on_body_begin = &on_body_begin;
g_callbacks.on_part_begin = &on_part_begin;
g_callbacks.on_header_field = &on_header_field;
g_callbacks.on_header_value = &on_header_value;
g_callbacks.on_headers_complete = &on_headers_complete;
g_callbacks.on_data = &on_data;
g_callbacks.on_part_end = &on_part_end;
g_callbacks.on_body_end = &on_body_end;
multipartparser parser = {};
init_globals();
multipartparser_init(&parser, reinterpret_cast<const char*>(boundary_str.c_str()));
unsigned int str_len = request.body().length();
unsigned char *data = (unsigned char*) malloc(str_len + 1);
memset(data, 0, str_len + 1);
memcpy((void*) data, (void*) request.body().c_str(), str_len);
//if ((multipartparser_execute(&parser, &g_callbacks, request.body().c_str(), strlen(request.body().c_str())) != strlen(request.body().c_str())) or (!g_body_begin_called)){
if ((multipartparser_execute(&parser, &g_callbacks,
reinterpret_cast<const char*>(data), str_len)
!= strlen(request.body().c_str())) or (!g_body_begin_called)) {
Logger::amf_server().warn("The received message can not be parsed properly!");
//response.send(Pistache::Http::Code::Bad_Request, "");
//return;
}
//simple parser
mime_parser sp = { };
sp.parse(request.body());
std::vector<mime_part> parts = { };
sp.get_mime_parts(parts);
uint8_t size = parts.size();
Logger::amf_server().debug("Number of MIME parts %d", size);
Logger::amf_server().debug("Number of g_parts %d", g_parts.size());
//at least 2 parts for Json data and N1 (+ N2)
if (g_parts.size() < 2){
response.send(Pistache::Http::Code::Bad_Request, "");
if (size < 2) {
response.send(Pistache::Http::Code::Bad_Request);
return;
}
part p0 = g_parts.front(); g_parts.pop_front();
Logger::amf_server().debug("Request body, part 1: \n%s", p0.body.c_str());
part p1 = g_parts.front(); g_parts.pop_front();
Logger::amf_server().debug("Request body, part 2: \n %s",p1.body.c_str());
part p2; bool is_ngap = false;
if (g_parts.size() > 0) {
p2 = g_parts.front(); g_parts.pop_front();
Logger::amf_server().debug("Request body, part 1: \n%s", parts[0].body.c_str());
Logger::amf_server().debug("Request body, part 2: \n %s",parts[1].body.c_str());
bool is_ngap = false;
if (size > 2) {
is_ngap = true;
Logger::amf_server().debug("Request body, part 3: \n %s",p2.body.c_str());
Logger::amf_server().debug("Request body, part 3: \n %s",parts[2].body.c_str());
}
N1N2MessageTransferReqData n1N2MessageTransferReqData = {};
try {
//from_json(nlohmann::json::parse(p0.body.c_str()), n1N2MessageTransferReqData);
nlohmann::json::parse(p0.body.c_str()).get_to(n1N2MessageTransferReqData);
nlohmann::json::parse(parts[0].body.c_str()).get_to(n1N2MessageTransferReqData);
if(!is_ngap)
this->n1_n2_message_transfer(ueContextId, n1N2MessageTransferReqData, p1.body, response);
this->n1_n2_message_transfer(ueContextId, n1N2MessageTransferReqData, parts[1].body, response);
else
this->n1_n2_message_transfer(ueContextId, n1N2MessageTransferReqData, p1.body, p2.body, response);
this->n1_n2_message_transfer(ueContextId, n1N2MessageTransferReqData, parts[1].body, parts[2].body, response);
} catch (nlohmann::detail::exception &e) {
//send a 400 error
Logger::amf_server().error("response 400 error");
response.send(Pistache::Http::Code::Bad_Request, e.what());
//response.send(Pistache::Http::Code::Bad_Request, "error");
return;
} catch (std::exception &e) {
//send a 500 error
......@@ -117,7 +88,6 @@ void N1N2MessageCollectionDocumentApi::n1_n2_message_transfer_handler(const Pist
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
return;
}
}
void N1N2MessageCollectionDocumentApi::n1_n2_message_collection_document_api_default_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
......
......@@ -11,8 +11,8 @@ add_library (AMF_UTILS STATIC
${CMAKE_CURRENT_SOURCE_DIR}/hex_string_convert.cpp
${CMAKE_CURRENT_SOURCE_DIR}/http_multi_parser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/if.cpp
${CMAKE_CURRENT_SOURCE_DIR}/multipartparser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread_sched.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mime_parser.cpp
)
#include "multipartparser.hpp"
#include "mime_parser.hpp"
#include <string>
#include <iostream>
using namespace std;
extern "C"{
#include <string.h>
}
bool multipart_parser(string input, string &jsonData, string &n1sm, string &n2sm){
std::size_t found = input.find("Content-Type");
std::string boundary_str = input.substr(2, found - 4);
//step 1. use multipartparser to decode the input
multipartparser_callbacks_init(&g_callbacks);
g_callbacks.on_body_begin = &on_body_begin;
g_callbacks.on_part_begin = &on_part_begin;
g_callbacks.on_header_field = &on_header_field;
g_callbacks.on_header_value = &on_header_value;
g_callbacks.on_headers_complete = &on_headers_complete;
g_callbacks.on_data = &on_data;
g_callbacks.on_part_end = &on_part_end;
g_callbacks.on_body_end = &on_body_end;
multipartparser parser = {};
init_globals();
multipartparser_init(&parser, reinterpret_cast<const char*>(boundary_str.c_str()));
unsigned int str_len = input.length();
unsigned char *data = (unsigned char*) malloc(str_len + 1);
memset(data, 0, str_len + 1);
memcpy((void*) data, (void*) input.c_str(), str_len);
if ((multipartparser_execute(&parser, &g_callbacks,
reinterpret_cast<const char*>(data), str_len)
!= strlen(input.c_str())) or (!g_body_begin_called)) {
//return;
std::cout<<"multipartparser_execute"<<std::endl;
}
//at least 2 parts for Json data and N1 (+ N2)
if (g_parts.size() < 2){
return false;
}
part p0 = g_parts.front(); g_parts.pop_front(); jsonData = p0.body;
part p1 = g_parts.front(); g_parts.pop_front(); n1sm = p1.body;
part p2; bool is_ngap = false;
if (g_parts.size() > 0) {
p2 = g_parts.front(); g_parts.pop_front();
is_ngap = true;
}
if(is_ngap) n2sm = p2.body;
else n2sm = "null";
return true;
bool multipart_parser(std::string input, std::string &jsonData, std::string &n1sm, std::string &n2sm) {
//simple parser
mime_parser sp = { };
sp.parse(input);
std::vector<mime_part> parts = { };
sp.get_mime_parts(parts);
uint8_t size = parts.size();
//at least 2 parts for Json data and N1 (+ N2)
if (size < 2) {
return false;
}
jsonData = parts[0].body;
n1sm = parts[1].body;
bool is_ngap = false;
if (size > 2) {
n2sm = parts[2].body;
} else {
n2sm = "null";
}
return true;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "mime_parser.hpp"
#include "logger.hpp"
#include "conversions.hpp"
bool mime_parser::parse(const std::string &str) {
std::string CRLF = "\r\n";
Logger::amf_app().debug("Parsing the message with Simple Parser");
//find boundary
std::size_t content_type_pos = str.find("Content-Type"); //first part
if ((content_type_pos <= 4) or (content_type_pos == std::string::npos))
return false;
std::string boundary_str = str.substr(2, content_type_pos - 4); // 2 for -- and 2 for CRLF
Logger::amf_app().debug("Boundary: %s", boundary_str.c_str());
std::string boundary_full = "--" + boundary_str + CRLF;
std::string last_boundary = "--" + boundary_str + "--" + CRLF;
std::size_t crlf_pos = str.find(CRLF, content_type_pos);
std::size_t boundary_pos = str.find(boundary_full);
std::size_t boundary_last_post = str.find(last_boundary);
while (boundary_pos < boundary_last_post) {
mime_part p = { };
content_type_pos = str.find("Content-Type", boundary_pos);
crlf_pos = str.find(CRLF, content_type_pos);
if ((content_type_pos == std::string::npos)
or (crlf_pos == std::string::npos))
break;
p.content_type = str.substr(content_type_pos + 14,
crlf_pos - (content_type_pos + 14));
Logger::amf_app().debug("Content Type: %s", p.content_type.c_str());
crlf_pos = str.find(CRLF + CRLF, content_type_pos); //beginning of content
boundary_pos = str.find(boundary_full, crlf_pos);
if (boundary_pos == std::string::npos) {
boundary_pos = str.find(last_boundary, crlf_pos);
}
if (boundary_pos > 0) {
p.body = str.substr(crlf_pos + 4, boundary_pos - 2 - (crlf_pos + 4));
Logger::amf_app().debug("Body: %s", p.body.c_str());
mime_parts.push_back(p);
}
}
return true;
}
void mime_parser::get_mime_parts(std::vector<mime_part> &parts) const {
for (auto it : mime_parts) {
parts.push_back(it);
}
}
//---------------------------------------------------------------------------------------------
unsigned char* mime_parser::format_string_as_hex(const std::string &str) {
unsigned int str_len = str.length();
char *data = (char*) malloc(str_len + 1);
memset(data, 0, str_len + 1);
memcpy((void*) data, (void*) str.c_str(), str_len);
unsigned char *data_hex = (uint8_t*) malloc(str_len / 2 + 1);
conv::ascii_to_hex(data_hex, (const char*) data);
Logger::amf_app().debug("[Format string as Hex] Input string (%d bytes): %s ",
str_len, str.c_str());
Logger::amf_app().debug("Data (formatted):");
#if DEBUG_IS_ON
for (int i = 0; i < str_len / 2; i++)
printf(" %02x ", data_hex[i]);
printf("\n");
#endif
//free memory
//free_wrapper((void**) &data);
free(data);
data = NULL;
return data_hex;
}
//------------------------------------------------------------------------------
void mime_parser::create_multipart_related_content(std::string &body,
const std::string &json_part,
const std::string boundary,
const std::string &n1_message,
const std::string &n2_message) {
//TODO: provide Content-Ids as function parameters
//format string as hex
unsigned char *n1_msg_hex = format_string_as_hex(n1_message);
unsigned char *n2_msg_hex = format_string_as_hex(n2_message);
std::string CRLF = "\r\n";
body.append("--" + boundary + CRLF);
body.append("Content-Type: application/json" + CRLF);
body.append(CRLF);
body.append(json_part + CRLF);
body.append("--" + boundary + CRLF);
body.append(
"Content-Type: application/vnd.3gpp.5gnas" + CRLF + "Content-Id: n1SmMsg"
+ CRLF);
body.append(CRLF);
body.append(std::string((char*) n1_msg_hex, n1_message.length() / 2) + CRLF);
body.append("--" + boundary + CRLF);
body.append(
"Content-Type: application/vnd.3gpp.ngap" + CRLF + "Content-Id: n2msg"
+ CRLF);
body.append(CRLF);
body.append(std::string((char*) n2_msg_hex, n2_message.length() / 2) + CRLF);
body.append("--" + boundary + "--" + CRLF);
}
//------------------------------------------------------------------------------
void mime_parser::create_multipart_related_content(
std::string &body, const std::string &json_part, const std::string boundary,
const std::string &message, const multipart_related_content_part_e content_type) {
//TODO: provide Content-Id as function parameters
//format string as hex
unsigned char *msg_hex = format_string_as_hex(message);
std::string CRLF = "\r\n";
body.append("--" + boundary + CRLF);
body.append("Content-Type: application/json" + CRLF);
body.append(CRLF);
body.append(json_part + CRLF);
body.append("--" + boundary + CRLF);
if (content_type == multipart_related_content_part_e::NAS) { //NAS
body.append(
"Content-Type: application/vnd.3gpp.5gnas" + CRLF
+ "Content-Id: n1SmMsg" + CRLF);
} else if (content_type == multipart_related_content_part_e::NGAP) { //NGAP
body.append(
"Content-Type: application/vnd.3gpp.ngap" + CRLF + "Content-Id: n2msg"
+ CRLF);
}
body.append(CRLF);
body.append(std::string((char*) msg_hex, message.length() / 2) + CRLF);
body.append("--" + boundary + "--" + CRLF);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file mime_parser.hpp
\brief
\author
\company Eurecom
\email:
*/
#ifndef FILE_MIME_PARSER_HPP_SEEN
#define FILE_MIME_PARSER_HPP_SEEN
# include <string>
#include <map>
#include <vector>
enum class multipart_related_content_part_e {
JSON = 0,
NAS = 1,
NGAP = 2
};
static const std::vector<std::string> multipart_related_content_part_e2str = {
"JSON", "NAS", "NGAP" };
typedef struct mime_part {
std::string content_type;
std::string body;
} mime_part;
class mime_parser {
public:
/*
* Parse the input string into different Mime parts
* @param [const std::string &] str: input string
* @return void
*/
bool parse(const std::string &str);
/*
* Get vector of Mime parts
* @param [std::vector<mime_part> &] parts: store vector of Mime parts
* @return void
*/
void get_mime_parts(std::vector<mime_part> &parts) const;
/*
* Represent a string as hex
* @param [const std::string&] str: input string
* @return String represents string in hex format
*/
unsigned char* format_string_as_hex(const std::string &str);
/*
* Create HTTP body content for multipart/related message
* @param [std::string] body: Body of the created message
* @param [std::string] json_part: Json part of multipart/related msg
* @param [std::string] boundary: Boundary of multipart/related msg
* @param [std::string] n1_message: N1 (NAS) part
* @param [std::string] n2_message: N2 (NGAP) part
* @return void
*/
void create_multipart_related_content(std::string &body,
const std::string &json_part,
const std::string boundary,
const std::string &n1_message,
const std::string &n2_message);
/*
* Create HTTP body content for multipart/related message
* @param [std::string] body: Body of the created message
* @param [std::string] json_part: Json part of multipart/related msg
* @param [std::string] boundary: Boundary of multipart/related msg
* @param [std::string] message: N1 (NAS) or N2 (NGAP) part
* @param [uint8_t] content_type: 1 for NAS content, else NGAP content
* @return void
*/
void create_multipart_related_content(
std::string &body, const std::string &json_part,
const std::string boundary, const std::string &message,
const multipart_related_content_part_e content_type);
private:
std::vector<mime_part> mime_parts;
};
#endif /* FILE_MIME_PARSER_HPP_SEEN */
/*
* https://github.com/iafonov/multipart-parser-c
*/
#include "multipartparser.hpp"
#include <string.h>
#define CR '\r'
#define LF '\n'
#define SP ' '
#define HT '\t'
#define HYPHEN '-'
#define CALLBACK_NOTIFY(NAME) \
if (callbacks->on_##NAME != NULL) { \
if (callbacks->on_##NAME(parser) != 0) \
goto error; \
}
#define CALLBACK_DATA(NAME, P, S) \
if (callbacks->on_##NAME != NULL) { \
if (callbacks->on_##NAME(parser, P, S) != 0) \
goto error; \
}
enum state {
s_preamble,
s_preamble_hy_hy,
s_first_boundary,
s_header_field_start,
s_header_field,
s_header_value_start,
s_header_value,
s_header_value_cr,
s_headers_done,
s_data,
s_data_cr,
s_data_cr_lf,
s_data_cr_lf_hy,
s_data_boundary_start,
s_data_boundary,
s_data_boundary_done,
s_data_boundary_done_cr_lf,
s_data_boundary_done_hy_hy,
s_epilogue,
};
/* Header field name as defined by rfc 2616. Also lowercases them.
* field-name = token
* token = 1*<any CHAR except CTLs or tspecials>
* CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
* tspecials = "(" | ")" | "<" | ">" | "@"
* | "," | ";" | ":" | "\" | DQUOTE
* | "/" | "[" | "]" | "?" | "="
* | "{" | "}" | SP | HT
* DQUOTE = <US-ASCII double-quote mark (34)>
* SP = <US-ASCII SP, space (32)>
* HT = <US-ASCII HT, horizontal-tab (9)>
*/
static const char header_field_chars[256] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
0, 0, 0, 0, 0, 0, 0, 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
0, 0, 0, 0, 0, 0, 0, 0,
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
0, 0, 0, 0, 0, 0, 0, 0,
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
0, 0, 0, 0, 0, 0, 0, 0,
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
0, '!', 0, '#', '$', '%', '&', '\'',
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
0, 0, '*', '+', 0, '-', '.', 0,
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
'0', '1', '2', '3', '4', '5', '6', '7',
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
'8', '9', 0, 0, 0, 0, 0, 0,
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
'X', 'Y', 'Z', 0, 0, 0, '^', '_',
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
'x', 'y', 'z', 0, '|', 0, '~', 0
};
void multipartparser_init(multipartparser* parser, const char* boundary)
{
memset(parser, 0, sizeof(*parser));
//strncpy(parser->boundary, boundary, sizeof(parser->boundary));
strcpy(parser->boundary, boundary);
parser->boundary_length = strlen(parser->boundary);
parser->state = s_preamble;
}
void multipartparser_callbacks_init(multipartparser_callbacks* callbacks)
{
memset(callbacks, 0, sizeof(*callbacks));
}
size_t multipartparser_execute(multipartparser* parser,
multipartparser_callbacks* callbacks,
const char* data,
size_t size)
{
const char* mark;
const char* p;
unsigned char c;
for (p = data; p < data + size; ++p) {
c = *p;
reexecute:
switch (parser->state) {
case s_preamble:
if (c == HYPHEN)
parser->state = s_preamble_hy_hy;
// else ignore everything before first boundary
break;
case s_preamble_hy_hy:
if (c == HYPHEN)
parser->state = s_first_boundary;
else
parser->state = s_preamble;
break;
case s_first_boundary:
if (parser->index == parser->boundary_length) {
if (c != CR)
goto error;
parser->index++;
break;
}
if (parser->index == parser->boundary_length + 1) {
if (c != LF)
goto error;
CALLBACK_NOTIFY(body_begin);
CALLBACK_NOTIFY(part_begin);
parser->index = 0;
parser->state = s_header_field_start;
break;
}
if (c == parser->boundary[parser->index]) {
parser->index++;
break;
}
goto error;
case s_header_field_start:
if (c == CR) {
parser->state = s_headers_done;
break;
}
parser->state = s_header_field;
// fallthrough;
case s_header_field:
mark = p;
while (p != data + size) {
c = *p;
if (header_field_chars[c] == 0)
break;
++p;
}
if (p > mark) {
CALLBACK_DATA(header_field, mark, p - mark);
}
if (p == data + size) {
break;
}
if (c == ':') {
parser->state = s_header_value_start;
break;
}
goto error;
case s_header_value_start:
if (c == SP || c == HT) {
break;
}
parser->state = s_header_value;
// fallthrough;
case s_header_value:
mark = p;
while (p != data + size) {
c = *p;
if (c == CR) {
parser->state = s_header_value_cr;
break;
}
++p;
}
if (p > mark) {
CALLBACK_DATA(header_value, mark, p - mark);
}
break;
case s_header_value_cr:
if (c == LF) {
parser->state = s_header_field_start;
break;
}
goto error;
case s_headers_done:
if (c == LF) {
CALLBACK_NOTIFY(headers_complete);
parser->state = s_data;
break;
}
goto error;
case s_data:
mark = p;
while (p != data + size) {
c = *p;
if (c == CR) {
parser->state = s_data_cr;
break;
}
++p;
}
if (p > mark) {
CALLBACK_DATA(data, mark, p - mark);
}
break;
case s_data_cr:
if (c == LF) {
parser->state = s_data_cr_lf;
break;
}
CALLBACK_DATA(data, "\r", 1);
parser->state = s_data;
goto reexecute;
case s_data_cr_lf:
if (c == HYPHEN) {
parser->state = s_data_cr_lf_hy;
break;
}
CALLBACK_DATA(data, "\r\n", 2);
parser->state = s_data;
goto reexecute;
case s_data_cr_lf_hy:
if (c == HYPHEN) {
parser->state = s_data_boundary_start;
break;
}
CALLBACK_DATA(data, "\r\n-", 3);
parser->state = s_data;
goto reexecute;
case s_data_boundary_start:
parser->index = 0;
parser->state = s_data_boundary;
// fallthrough;
case s_data_boundary:
if (parser->index == parser->boundary_length) {
parser->index = 0;
parser->state = s_data_boundary_done;
goto reexecute;
}
if (c == parser->boundary[parser->index]) {
parser->index++;
break;
}
CALLBACK_DATA(data, parser->boundary, parser->index);
parser->state = s_data;
goto reexecute;
case s_data_boundary_done:
if (c == CR) {
parser->state = s_data_boundary_done_cr_lf;
break;
}
if (c == HYPHEN) {
parser->state = s_data_boundary_done_hy_hy;
break;
}
goto error;
case s_data_boundary_done_cr_lf:
if (c == LF) {
CALLBACK_NOTIFY(part_end);
CALLBACK_NOTIFY(part_begin);
parser->state = s_header_field_start;
break;
}
goto error;
case s_data_boundary_done_hy_hy:
if (c == HYPHEN) {
CALLBACK_NOTIFY(part_end);
CALLBACK_NOTIFY(body_end);
parser->state = s_epilogue;
break;
}
goto error;
case s_epilogue:
// Must be ignored according to rfc 1341.
break;
}
}
return size;
error:
return p - data;
}
/*
* https://github.com/iafonov/multipart-parser-c
*/
#ifndef MULTIPARTPARSER_H
#define MULTIPARTPARSER_H
#include <map>
#include <string>
#include <list>
extern "C" {
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
}
typedef struct multipartparser multipartparser;
typedef struct multipartparser_callbacks multipartparser_callbacks;
typedef int (*multipart_cb) (multipartparser*);
typedef int (*multipart_data_cb) (multipartparser*, const char* data, size_t size);
struct multipartparser {
/** PRIVATE **/
char boundary[70];
int boundary_length;
int index;
uint16_t state;
/** PUBLIC **/
void* data;
};
struct multipartparser_callbacks {
multipart_cb on_body_begin;
multipart_cb on_part_begin;
multipart_data_cb on_header_field;
multipart_data_cb on_header_value;
multipart_cb on_headers_complete;
multipart_data_cb on_data;
multipart_cb on_part_end;
multipart_cb on_body_end;
};
void multipartparser_init(multipartparser* parser, const char* boundary);
void multipartparser_callbacks_init(multipartparser_callbacks* callbacks);
size_t multipartparser_execute(multipartparser* parser,
multipartparser_callbacks* callbacks,
const char* data,
size_t size);
#define BOUNDARY "----Boundary"
typedef struct part {
std::map<std::string,std::string> headers;
std::string body;
} part;
static multipartparser_callbacks g_callbacks;
static bool g_body_begin_called;
static std::string g_header_name;
static std::string g_header_value;
static std::list<part> g_parts;
static bool g_body_end_called;
static void init_globals()
{
g_body_begin_called = false;
g_header_name.clear();
g_header_value.clear();
g_parts.clear();
g_body_end_called = false;
}
static int on_body_begin(multipartparser* /*parser*/)
{
g_body_begin_called = true;
return 0;
}
static int on_part_begin(multipartparser* /*parser*/)
{
g_parts.push_back(part());
return 0;
}
static void on_header_done()
{
g_parts.back().headers[g_header_name] = g_header_value;
g_header_name.clear();
g_header_value.clear();
}
static int on_header_field(multipartparser* /*parser*/, const char* data, size_t size)
{
if (g_header_value.size() > 0)
on_header_done();
g_header_name.append(data, size);
return 0;
}
static int on_header_value(multipartparser* /*parser*/, const char* data, size_t size)
{
g_header_value.append(data, size);
return 0;
}
static int on_headers_complete(multipartparser* /*parser*/)
{
if (g_header_value.size() > 0)
on_header_done();
return 0;
}
static int on_data(multipartparser* /*parser*/, const char* data, size_t size)
{
std::string str;
//g_parts.back().body.append(data, size);
for(int i = 0;i < size; i++)
{
//printf("%02x ",data[i]);
str.push_back(data[i]);
}
g_parts.back().body.append(str);
return 0;
}
static int on_part_end(multipartparser* /*parser*/)
{
return 0;
}
static int on_body_end(multipartparser* /*parser*/)
{
g_body_end_called = true;
return 0;
}
#endif // MULTIPARTPARSER_H
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment