stanley-king 9 лет назад
Родитель
Сommit
d18145bd3a
3 измененных файлов с 659 добавлено и 208 удалено
  1. 305 208
      fcgi.c
  2. 306 0
      multipart_parser.c
  3. 48 0
      multipart_parser.h

+ 305 - 208
fcgi.c

@@ -1,208 +1,305 @@
-/*
-  +----------------------------------------------------------------------+
-  | PHP Version 5                                                        |
-  +----------------------------------------------------------------------+
-  | Copyright (c) 1997-2015 The PHP Group                                |
-  +----------------------------------------------------------------------+
-  | This source file is subject to version 3.01 of the PHP license,      |
-  | that is bundled with this package in the file LICENSE, and is        |
-  | available through the world-wide-web at the following url:           |
-  | http://www.php.net/license/3_01.txt                                  |
-  | If you did not receive a copy of the PHP license and are unable to   |
-  | obtain it through the world-wide-web, please send a note to          |
-  | license@php.net so we can mail you a copy immediately.               |
-  +----------------------------------------------------------------------+
-  | Author:                                                              |
-  +----------------------------------------------------------------------+
-*/
-
-/* $Id$ */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "php.h"
-#include "php_ini.h"
-#include "ext/standard/info.h"
-#include "php_fcgi.h"
-#include <fcgi_stdio.h>
-
-static int le_fcgi;
-
-const zend_function_entry fcgi_functions[] = {
-	PHP_FE(confirm_fcgi_compiled,	NULL)		/* For testing, remove later. */
-	PHP_FE(fcgi_init, NULL)
-	PHP_FE(fcgi_fini, NULL)	
-	PHP_FE(fcgi_accept,	NULL)
-	PHP_FE(fcgi_finish,	NULL)	
-	PHP_FE(fcgi_getparam, NULL)
-	PHP_FE(fcgi_echo, NULL)
-	PHP_FE(fcgi_getcontent, NULL)
-	PHP_FE_END	/* Must be the last line in fcgi_functions[] */
-};
-
-zend_module_entry fcgi_module_entry = {
-#if ZEND_MODULE_API_NO >= 20010901
-	STANDARD_MODULE_HEADER,
-#endif
-	"fcgi",
-	fcgi_functions,
-	PHP_MINIT(fcgi),
-	PHP_MSHUTDOWN(fcgi),
-	PHP_RINIT(fcgi),		/* Replace with NULL if there's nothing to do at request start */
-	PHP_RSHUTDOWN(fcgi),	/* Replace with NULL if there's nothing to do at request end */
-	PHP_MINFO(fcgi),
-#if ZEND_MODULE_API_NO >= 20010901
-	PHP_FCGI_VERSION,
-#endif
-	STANDARD_MODULE_PROPERTIES
-};
-/* }}} */
-
-#ifdef COMPILE_DL_FCGI
-ZEND_GET_MODULE(fcgi)
-#endif
-
-PHP_MINIT_FUNCTION(fcgi)
-{
-	/* If you have INI entries, uncomment these lines 
-	REGISTER_INI_ENTRIES();
-	*/
-	return SUCCESS;
-}
-
-PHP_MSHUTDOWN_FUNCTION(fcgi)
-{
-	/* uncomment this line if you have INI entries
-	UNREGISTER_INI_ENTRIES();
-	*/
-	return SUCCESS;
-}
-
-PHP_RINIT_FUNCTION(fcgi)
-{
-	return SUCCESS;
-}
-
-PHP_RSHUTDOWN_FUNCTION(fcgi)
-{
-	return SUCCESS;
-}
-
-PHP_MINFO_FUNCTION(fcgi)
-{
-	php_info_print_table_start();
-	php_info_print_table_header(2, "fcgi support", "enabled");
-	php_info_print_table_end();
-}
-
-PHP_FUNCTION(confirm_fcgi_compiled)
-{
-	char *arg = NULL;
-	int arg_len, len;
-	char *strg;
-
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
-		return;
-	}
-
-	len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "fcgi", arg);
-	RETURN_STRINGL(strg, len, 0);
-}
-
-PHP_FUNCTION(fcgi_init)
-{
-}
-
-PHP_FUNCTION(fcgi_fini)
-{
-}
-
-static FCGX_Stream* stInstream;
-static FCGX_Stream* stOutstream;
-static FCGX_Stream* stErrstream;
-static FCGX_ParamArray stParams;
-
-PHP_FUNCTION(fcgi_accept)
-{
-	int ret = FCGX_Accept(&stInstream,&stOutstream,&stErrstream,&stParams);
-
-	if(stInstream == NULL) {
-		RETVAL_LONG(-1);
-	}
-	if(stOutstream == NULL) {
-		RETVAL_LONG(-1);
-	}
-	if(stErrstream == NULL) {
-		RETVAL_LONG(-1);
-	}
-
-	RETVAL_LONG(ret);
-}
-
-PHP_FUNCTION(fcgi_getparam)
-{
-	char *arg = NULL;	 
-	int arg_len;	 
-	 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {	 
-		return;
-	}
-
-	const char* ret = FCGX_GetParam(arg, stParams);
-	ret = (ret == NULL) ? "" : ret;
-
-	char* result = estrdup(ret);
-	RETURN_STRINGL(result,strlen(result),0);
-}
-
-static int content_length()
-{
-	const char* pbuf = FCGX_GetParam("CONTENT_LENGTH", stParams);
-	if(pbuf)
-	{
-	    if(strlen(pbuf) == 0)
-	        return 0;
-	    else
-	        return atoi(pbuf);
-	} else {
-	    return 0;
-	}
-}
-
-PHP_FUNCTION(fcgi_getcontent)
-{
-	int con_len = content_length();
-	if(con_len) {
-		char* pCon = (char*)malloc(con_len + 1);
-		con_len = FCGX_GetStr(pCon, con_len, stInstream);
-		pCon[con_len] = '\0';	
-
-		char* result = estrdup(pCon);
-		RETURN_STRINGL(result,strlen(result),0);
-	} else {
-		char* result = estrdup("");
-		RETURN_STRINGL(result,strlen(result),0);
-	}
-}
-
-PHP_FUNCTION(fcgi_echo)
-{
-	char *arg = NULL;	 
-	int arg_len;	 
-	 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {	 
-		return;
-	}
-
-	if(arg != NULL) {
-		FCGX_PutS(arg,stOutstream);
-	}
-}
-
-PHP_FUNCTION(fcgi_finish)
-{
-	FCGX_Finish();
-}
-
+/*
+  +----------------------------------------------------------------------+
+  | PHP Version 5                                                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 1997-2015 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author:                                                              |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_fcgi.h"
+#include <fcgi_stdio.h>
+#include "multipart_parser.h"
+#include "multipart_parser.c"
+
+static int le_fcgi;
+
+const zend_function_entry fcgi_functions[] = {
+	PHP_FE(confirm_fcgi_compiled,	NULL)		/* For testing, remove later. */
+	PHP_FE(fcgi_init, NULL)
+	PHP_FE(fcgi_fini, NULL)	
+	PHP_FE(fcgi_accept,	NULL)
+	PHP_FE(fcgi_finish,	NULL)	
+	PHP_FE(fcgi_getparam, NULL)
+	PHP_FE(fcgi_echo, NULL)
+	PHP_FE(fcgi_getcontent, NULL)
+	PHP_FE_END	/* Must be the last line in fcgi_functions[] */
+};
+
+zend_module_entry fcgi_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+	STANDARD_MODULE_HEADER,
+#endif
+	"fcgi",
+	fcgi_functions,
+	PHP_MINIT(fcgi),
+	PHP_MSHUTDOWN(fcgi),
+	PHP_RINIT(fcgi),		/* Replace with NULL if there's nothing to do at request start */
+	PHP_RSHUTDOWN(fcgi),	/* Replace with NULL if there's nothing to do at request end */
+	PHP_MINFO(fcgi),
+#if ZEND_MODULE_API_NO >= 20010901
+	PHP_FCGI_VERSION,
+#endif
+	STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_FCGI
+ZEND_GET_MODULE(fcgi)
+#endif
+
+PHP_MINIT_FUNCTION(fcgi)
+{
+	/* If you have INI entries, uncomment these lines 
+	REGISTER_INI_ENTRIES();
+	*/
+	return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(fcgi)
+{
+	/* uncomment this line if you have INI entries
+	UNREGISTER_INI_ENTRIES();
+	*/
+	return SUCCESS;
+}
+
+PHP_RINIT_FUNCTION(fcgi)
+{
+	return SUCCESS;
+}
+
+PHP_RSHUTDOWN_FUNCTION(fcgi)
+{
+	return SUCCESS;
+}
+
+PHP_MINFO_FUNCTION(fcgi)
+{
+	php_info_print_table_start();
+	php_info_print_table_header(2, "fcgi support", "enabled");
+	php_info_print_table_end();
+}
+
+PHP_FUNCTION(confirm_fcgi_compiled)
+{
+	char *arg = NULL;
+	int arg_len, len;
+	char *strg;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
+		return;
+	}
+
+	len = sprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "fcgi", arg);
+	RETURN_STRINGL(strg, len, 0);
+}
+
+PHP_FUNCTION(fcgi_init)
+{
+}
+
+PHP_FUNCTION(fcgi_fini)
+{
+}
+
+static FCGX_Stream* stInstream;
+static FCGX_Stream* stOutstream;
+static FCGX_Stream* stErrstream;
+static FCGX_ParamArray stParams;
+
+PHP_FUNCTION(fcgi_accept)
+{
+	int ret = FCGX_Accept(&stInstream,&stOutstream,&stErrstream,&stParams);
+
+	if(stInstream == NULL) {
+		RETVAL_LONG(-1);
+	}
+	if(stOutstream == NULL) {
+		RETVAL_LONG(-1);
+	}
+	if(stErrstream == NULL) {
+		RETVAL_LONG(-1);
+	}
+
+	RETVAL_LONG(ret);
+}
+
+PHP_FUNCTION(fcgi_getparam)
+{
+	char *arg = NULL;	 
+	int arg_len;	 
+	 
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {	 
+		return;
+	}
+
+	const char* ret = FCGX_GetParam(arg, stParams);
+	ret = (ret == NULL) ? "" : ret;
+
+	char* result = estrdup(ret);
+	RETURN_STRINGL(result,strlen(result),0);
+}
+
+static int content_length()
+{
+	const char* pbuf = FCGX_GetParam("CONTENT_LENGTH", stParams);
+	if(pbuf)
+	{
+	    if(strlen(pbuf) == 0)
+	        return 0;
+	    else
+	        return atoi(pbuf);
+	} else {
+	    return 0;
+	}
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// rfc1867 callback
+static char* g_pcontent = NULL;
+int read_header_name(multipart_parser* p, const char *at, size_t length)
+{
+	// printf("%.*s: ", length, at);
+	return 0;
+}
+
+// rfc1867 callback
+int read_header_value(multipart_parser* p, const char *at, size_t length)
+{
+	// printf("%.*s\n", length, at);
+
+	if (NULL == g_pcontent) return 0;
+
+	char* pbegin = strstr(at, "\"");
+	if (!pbegin) return 0;
+
+	++pbegin;
+	const char* pend = strstr(pbegin, "\"");
+
+	if (!pend) return 0;
+	const int len = pend - pbegin;
+
+	strncat(g_pcontent, pbegin, len);
+	strcat(g_pcontent, "=");
+	return 0;
+}
+
+// rfc1867 callback
+int read_part_data(multipart_parser* p, const char *at, size_t length)
+{
+   // printf("%.*s\n", length, at);
+   if (NULL == g_pcontent) return 0;
+
+   strncat(g_pcontent, at, length);
+   strcat(g_pcontent, "&");
+   return 0;
+}
+
+PHP_FUNCTION(fcgi_getcontent)
+{
+	int con_len = content_length();
+	if(con_len) {
+		char* pCon = (char*)malloc(con_len + 1);
+		con_len = FCGX_GetStr(pCon, con_len, stInstream);
+		pCon[con_len] = '\0';
+
+		// post webform boundary proccess
+		const char* pbuf = FCGX_GetParam("CONTENT_TYPE", stParams);
+		if(pbuf)
+		{
+			const char* boundary_begin = strstr(pbuf, "boundary");
+			if (boundary_begin)
+			{
+				boundary_begin = boundary_begin + strlen("boundary");
+				boundary_begin = strstr(boundary_begin, "=");
+				if (boundary_begin)
+				{
+					++boundary_begin;
+					const int boundary_len = strlen(boundary_begin) + 3;
+					char* boundary_value = (char*)malloc(boundary_len);
+					if (boundary_value)
+					{
+						memset(boundary_value, 0, boundary_len);
+						strcat(boundary_value, "--");
+						strcat(boundary_value, boundary_begin);
+
+						g_pcontent = (char*)malloc(con_len + 1);
+						memset(g_pcontent, 0, con_len + 1);
+
+						multipart_parser_settings callbacks;
+						memset(&callbacks, 0, sizeof(multipart_parser_settings));
+
+						callbacks.on_header_field = read_header_name;
+						callbacks.on_header_value = read_header_value;
+						callbacks.on_part_data = read_part_data;
+					
+						multipart_parser* parser = multipart_parser_init(boundary_value, &callbacks);
+						if (parser) {
+							multipart_parser_execute(parser, pCon, con_len);
+							multipart_parser_free(parser);
+						}
+
+						if(g_pcontent != NULL)
+						{
+							 int len = strlen(g_pcontent);
+							 if(len > 0 && g_pcontent[len - 1] == '&') {
+							 	g_pcontent[len - 1] = '\0';
+							 }
+						}
+
+						char* result = estrdup(g_pcontent);
+						free(g_pcontent);
+						g_pcontent = NULL;
+						RETURN_STRINGL(result, strlen(result),0);
+					}					
+				}
+			}
+		}
+
+		// normal proccess
+		char* result = estrdup(pCon);
+		RETURN_STRINGL(result,strlen(result),0);
+	} else {
+		char* result = estrdup("");
+		RETURN_STRINGL(result,strlen(result),0);
+	}
+}
+
+PHP_FUNCTION(fcgi_echo)
+{
+	char *arg = NULL;	 
+	int arg_len;	 
+	 
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {	 
+		return;
+	}
+
+	if(arg != NULL) {
+		FCGX_PutS(arg,stOutstream);
+	}
+}
+
+PHP_FUNCTION(fcgi_finish)
+{
+	FCGX_Finish();
+}
+

+ 306 - 0
multipart_parser.c

@@ -0,0 +1,306 @@
+/* Based on node-formidable by Felix Geisendörfer 
+ * Igor Afonov - afonov@gmail.com - 2012
+ * MIT License - http://www.opensource.org/licenses/mit-license.php
+ */
+
+#include "multipart_parser.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+static void multipart_log(const char * format, ...)
+{
+#ifdef DEBUG_MULTIPART
+    va_list args;
+    va_start(args, format);
+
+    fprintf(stderr, "[HTTP_MULTIPART_PARSER] %s:%d: ", __FILE__, __LINE__);
+    vfprintf(stderr, format, args);
+    fprintf(stderr, "\n");
+#endif
+}
+
+#define NOTIFY_CB(FOR)                                                 \
+do {                                                                   \
+  if (p->settings->on_##FOR) {                                         \
+    if (p->settings->on_##FOR(p) != 0) {                               \
+      return i;                                                        \
+    }                                                                  \
+  }                                                                    \
+} while (0)
+
+#define EMIT_DATA_CB(FOR, ptr, len)                                    \
+do {                                                                   \
+  if (p->settings->on_##FOR) {                                         \
+    if (p->settings->on_##FOR(p, ptr, len) != 0) {                     \
+      return i;                                                        \
+    }                                                                  \
+  }                                                                    \
+} while (0)
+
+
+#define LF 10
+#define CR 13
+
+struct multipart_parser {
+  void * data;
+
+  size_t index;
+  size_t boundary_length;
+
+  unsigned char state;
+
+  const multipart_parser_settings* settings;
+
+  char* lookbehind;
+  char multipart_boundary[1];
+};
+
+enum state {
+  s_uninitialized = 1,
+  s_start,
+  s_start_boundary,
+  s_header_field_start,
+  s_header_field,
+  s_headers_almost_done,
+  s_header_value_start,
+  s_header_value,
+  s_header_value_almost_done,
+  s_part_data_start,
+  s_part_data,
+  s_part_data_almost_boundary,
+  s_part_data_boundary,
+  s_part_data_almost_end,
+  s_part_data_end,
+  s_part_data_final_hyphen,
+  s_end
+};
+
+multipart_parser* multipart_parser_init
+    (const char *boundary, const multipart_parser_settings* settings) {
+
+  multipart_parser* p = malloc(sizeof(multipart_parser) +
+                               strlen(boundary) +
+                               strlen(boundary) + 9);
+
+  strcpy(p->multipart_boundary, boundary);
+  p->boundary_length = strlen(boundary);
+  
+  p->lookbehind = (p->multipart_boundary + p->boundary_length + 1);
+
+  p->index = 0;
+  p->state = s_start;
+  p->settings = settings;
+
+  return p;
+}
+
+void multipart_parser_free(multipart_parser* p) {
+  free(p);
+}
+
+void multipart_parser_set_data(multipart_parser *p, void *data) {
+    p->data = data;
+}
+
+void *multipart_parser_get_data(multipart_parser *p) {
+    return p->data;
+}
+
+size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len) {
+  size_t i = 0;
+  size_t mark = 0;
+  char c, cl;
+  int is_last = 0;
+
+  while(i < len) {
+    c = buf[i];
+    is_last = (i == (len - 1));
+    switch (p->state) {
+      case s_start:
+        multipart_log("s_start");
+        p->index = 0;
+        p->state = s_start_boundary;
+
+      /* fallthrough */
+      case s_start_boundary:
+        multipart_log("s_start_boundary");
+        if (p->index == p->boundary_length) {
+          if (c != CR) {
+            return i;
+          }
+          p->index++;
+          break;
+        } else if (p->index == (p->boundary_length + 1)) {
+          if (c != LF) {
+            return i;
+          }
+          p->index = 0;
+          NOTIFY_CB(part_data_begin);
+          p->state = s_header_field_start;
+          break;
+        }
+        if (c != p->multipart_boundary[p->index]) {
+          return i;
+        }
+        p->index++;
+        break;
+
+      case s_header_field_start:
+        multipart_log("s_header_field_start");
+        mark = i;
+        p->state = s_header_field;
+
+      /* fallthrough */
+      case s_header_field:
+        multipart_log("s_header_field");
+        if (c == CR) {
+          p->state = s_headers_almost_done;
+          break;
+        }
+
+        if (c == ':') {
+          EMIT_DATA_CB(header_field, buf + mark, i - mark);
+          p->state = s_header_value_start;
+          break;
+        }
+
+        cl = tolower(c);
+        if ((c != '-') && (cl < 'a' || cl > 'z')) {
+          multipart_log("invalid character in header name");
+          return i;
+        }
+        if (is_last)
+            EMIT_DATA_CB(header_field, buf + mark, (i - mark) + 1);
+        break;
+
+      case s_headers_almost_done:
+        multipart_log("s_headers_almost_done");
+        if (c != LF) {
+          return i;
+        }
+
+        p->state = s_part_data_start;
+        break;
+
+      case s_header_value_start:
+        multipart_log("s_header_value_start");
+        if (c == ' ') {
+          break;
+        }
+
+        mark = i;
+        p->state = s_header_value;
+
+      /* fallthrough */
+      case s_header_value:
+        multipart_log("s_header_value");
+        if (c == CR) {
+          EMIT_DATA_CB(header_value, buf + mark, i - mark);
+          p->state = s_header_value_almost_done;
+          break;
+        }
+        if (is_last)
+            EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1);
+        break;
+
+      case s_header_value_almost_done:
+        multipart_log("s_header_value_almost_done");
+        if (c != LF) {
+          return i;
+        }
+        p->state = s_header_field_start;
+        break;
+
+      case s_part_data_start:
+        multipart_log("s_part_data_start");
+        NOTIFY_CB(headers_complete);
+        mark = i;
+        p->state = s_part_data;
+
+      /* fallthrough */
+      case s_part_data:
+        multipart_log("s_part_data");
+        if (c == CR) {
+            EMIT_DATA_CB(part_data, buf + mark, i - mark);
+            mark = i;
+            p->state = s_part_data_almost_boundary;
+            p->lookbehind[0] = CR;
+            break;
+        }
+        if (is_last)
+            EMIT_DATA_CB(part_data, buf + mark, (i - mark) + 1);
+        break;
+
+      case s_part_data_almost_boundary:
+        multipart_log("s_part_data_almost_boundary");
+        if (c == LF) {
+            p->state = s_part_data_boundary;
+            p->lookbehind[1] = LF;
+            p->index = 0;
+            break;
+        }
+        EMIT_DATA_CB(part_data, p->lookbehind, 1);
+        p->state = s_part_data;
+        mark = i --;
+        break;
+
+      case s_part_data_boundary:
+        multipart_log("s_part_data_boundary");
+        if (p->multipart_boundary[p->index] != c) {
+          EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index);
+          p->state = s_part_data;
+          mark = i --;
+          break;
+        }
+        p->lookbehind[2 + p->index] = c;
+        if ((++ p->index) == p->boundary_length) {
+            NOTIFY_CB(part_data_end);
+            p->state = s_part_data_almost_end;
+        }
+        break;
+
+      case s_part_data_almost_end:
+        multipart_log("s_part_data_almost_end");
+        if (c == '-') {
+            p->state = s_part_data_final_hyphen;
+            break;
+        }
+        if (c == CR) {
+            p->state = s_part_data_end;
+            break;
+        }
+        return i;
+   
+      case s_part_data_final_hyphen:
+        multipart_log("s_part_data_final_hyphen");
+        if (c == '-') {
+            NOTIFY_CB(body_end);
+            p->state = s_end;
+            break;
+        }
+        return i;
+
+      case s_part_data_end:
+        multipart_log("s_part_data_end");
+        if (c == LF) {
+            p->state = s_header_field_start;
+            NOTIFY_CB(part_data_begin);
+            break;
+        }
+        return i;
+
+      case s_end:
+        multipart_log("s_end: %02X", (int) c);
+        break;
+
+      default:
+        multipart_log("Multipart parser unrecoverable error");
+        return 0;
+    }
+    ++ i;
+  }
+
+  return len;
+}

+ 48 - 0
multipart_parser.h

@@ -0,0 +1,48 @@
+/* Based on node-formidable by Felix Geisendörfer 
+ * Igor Afonov - afonov@gmail.com - 2012
+ * MIT License - http://www.opensource.org/licenses/mit-license.php
+ */
+#ifndef _multipart_parser_h
+#define _multipart_parser_h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+
+typedef struct multipart_parser multipart_parser;
+typedef struct multipart_parser_settings multipart_parser_settings;
+typedef struct multipart_parser_state multipart_parser_state;
+
+typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length);
+typedef int (*multipart_notify_cb) (multipart_parser*);
+
+struct multipart_parser_settings {
+  multipart_data_cb on_header_field;
+  multipart_data_cb on_header_value;
+  multipart_data_cb on_part_data;
+
+  multipart_notify_cb on_part_data_begin;
+  multipart_notify_cb on_headers_complete;
+  multipart_notify_cb on_part_data_end;
+  multipart_notify_cb on_body_end;
+};
+
+multipart_parser* multipart_parser_init
+    (const char *boundary, const multipart_parser_settings* settings);
+
+void multipart_parser_free(multipart_parser* p);
+
+size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len);
+
+void multipart_parser_set_data(multipart_parser* p, void* data);
+void * multipart_parser_get_data(multipart_parser* p);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif