|
|
|
@ -6,6 +6,36 @@ |
|
|
|
|
|
|
|
|
|
#define BOUNDARY_SIZE 100 |
|
|
|
|
|
|
|
|
|
typedef enum { |
|
|
|
|
STATE_SEARCH_BOUNDARY = 0, // state: searching multipart boundary
|
|
|
|
|
STATE_SEARCH_HEADER, // state: search multipart file header
|
|
|
|
|
STATE_SEARCH_HEADER_END, // state: search the end of the file header
|
|
|
|
|
STATE_UPLOAD_FILE, // state: read file content
|
|
|
|
|
STATE_ERROR, // state: error (stop processing)
|
|
|
|
|
} MultipartState; |
|
|
|
|
|
|
|
|
|
struct _MultipartCtx { |
|
|
|
|
MultipartCallback callBack; // callback for multipart events
|
|
|
|
|
int position; // current file position
|
|
|
|
|
int startTime; // timestamp when connection was initiated
|
|
|
|
|
int recvPosition; // receive position (how many bytes was processed from the HTTP post)
|
|
|
|
|
char * boundaryBuffer; // buffer used for boundary detection
|
|
|
|
|
int boundaryBufferPtr; // pointer in the boundary buffer
|
|
|
|
|
MultipartState state; // multipart processing state
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// this method is responsible for creating the multipart context
|
|
|
|
|
MultipartCtx * ICACHE_FLASH_ATTR multipartCreateContext(MultipartCallback callback) |
|
|
|
|
{ |
|
|
|
|
MultipartCtx * ctx = (MultipartCtx *)os_malloc(sizeof(MultipartCtx)); |
|
|
|
|
ctx->callBack = callback; |
|
|
|
|
ctx->position = ctx->startTime = ctx->recvPosition = ctx->boundaryBufferPtr = 0; |
|
|
|
|
ctx->boundaryBuffer = NULL; |
|
|
|
|
ctx->state = STATE_SEARCH_BOUNDARY; |
|
|
|
|
return ctx; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// for allocating buffer for multipart upload
|
|
|
|
|
void ICACHE_FLASH_ATTR multipartAllocBoundaryBuffer(MultipartCtx * context) |
|
|
|
|
{ |
|
|
|
|
if( context->boundaryBuffer == NULL ) |
|
|
|
@ -13,6 +43,7 @@ void ICACHE_FLASH_ATTR multipartAllocBoundaryBuffer(MultipartCtx * context) |
|
|
|
|
context->boundaryBufferPtr = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// for freeing multipart buffer
|
|
|
|
|
void ICACHE_FLASH_ATTR multipartFreeBoundaryBuffer(MultipartCtx * context) |
|
|
|
|
{ |
|
|
|
|
if( context->boundaryBuffer != NULL ) |
|
|
|
@ -22,11 +53,66 @@ void ICACHE_FLASH_ATTR multipartFreeBoundaryBuffer(MultipartCtx * context) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int ICACHE_FLASH_ATTR multipartProcessBoundaryBuffer(MultipartCtx * context, char * boundary, char * buff, int len, int last) |
|
|
|
|
// for destroying the context
|
|
|
|
|
void ICACHE_FLASH_ATTR multipartDestroyContext(MultipartCtx * context) |
|
|
|
|
{ |
|
|
|
|
multipartFreeBoundaryBuffer(context); |
|
|
|
|
os_free(context); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// this is because of os_memmem is missing
|
|
|
|
|
void * mp_memmem(const void *l, size_t l_len, const void *s, size_t s_len) |
|
|
|
|
{ |
|
|
|
|
register char *cur, *last; |
|
|
|
|
const char *cl = (const char *)l; |
|
|
|
|
const char *cs = (const char *)s; |
|
|
|
|
|
|
|
|
|
/* we need something to compare */ |
|
|
|
|
if (l_len == 0 || s_len == 0) |
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
/* "s" must be smaller or equal to "l" */ |
|
|
|
|
if (l_len < s_len) |
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
/* special case where s_len == 1 */ |
|
|
|
|
if (s_len == 1) |
|
|
|
|
return memchr(l, (int)*cs, l_len); |
|
|
|
|
|
|
|
|
|
/* the last position where its possible to find "s" in "l" */ |
|
|
|
|
last = (char *)cl + l_len - s_len; |
|
|
|
|
|
|
|
|
|
for (cur = (char *)cl; cur <= last; cur++) |
|
|
|
|
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) |
|
|
|
|
return cur; |
|
|
|
|
|
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// this method is for processing data coming from the HTTP post request
|
|
|
|
|
// context: the multipart context
|
|
|
|
|
// boundary: a string which indicates boundary
|
|
|
|
|
// data: the received data
|
|
|
|
|
// len: the received data length (can't be bigger than BOUNDARY_SIZE)
|
|
|
|
|
// last: last packet indicator
|
|
|
|
|
//
|
|
|
|
|
// Detecting a boundary is not easy. One has to take care of boundaries which are splitted in 2 packets
|
|
|
|
|
// [Packet 1, 5 bytes of the boundary][Packet 2, remaining 10 bytes of the boundary];
|
|
|
|
|
//
|
|
|
|
|
// Algorythm:
|
|
|
|
|
// - create a buffer which size is 3*BOUNDARY_SIZE
|
|
|
|
|
// - put data into the buffer as long as the buffer size is smaller than 2*BOUNDARY_SIZE
|
|
|
|
|
// - search boundary in the received buffer, if found: boundary reached -> process data before boundary -> process boundary
|
|
|
|
|
// - if not found -> process the first BOUNDARY_SIZE amount of bytes from the buffer
|
|
|
|
|
// - remove processed data from the buffer
|
|
|
|
|
// this algorythm guarantees that no boundary loss will happen
|
|
|
|
|
|
|
|
|
|
int ICACHE_FLASH_ATTR multipartProcessData(MultipartCtx * context, char * boundary, char * data, int len, int last) |
|
|
|
|
{ |
|
|
|
|
if( len != 0 ) |
|
|
|
|
if( len != 0 ) // add data to the boundary buffer
|
|
|
|
|
{ |
|
|
|
|
os_memcpy(context->boundaryBuffer + context->boundaryBufferPtr, buff, len); |
|
|
|
|
os_memcpy(context->boundaryBuffer + context->boundaryBufferPtr, data, len); |
|
|
|
|
|
|
|
|
|
context->boundaryBufferPtr += len; |
|
|
|
|
context->boundaryBuffer[context->boundaryBufferPtr] = 0; |
|
|
|
@ -34,22 +120,22 @@ int ICACHE_FLASH_ATTR multipartProcessBoundaryBuffer(MultipartCtx * context, cha |
|
|
|
|
|
|
|
|
|
while( context->boundaryBufferPtr > 0 ) |
|
|
|
|
{ |
|
|
|
|
if( ! last && context->boundaryBufferPtr <= 2 * BOUNDARY_SIZE ) |
|
|
|
|
if( ! last && context->boundaryBufferPtr <= 2 * BOUNDARY_SIZE ) // return if buffer is too small and not the last packet is processed
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
int dataSize = BOUNDARY_SIZE; |
|
|
|
|
|
|
|
|
|
char * loc = os_strstr( context->boundaryBuffer, boundary ); |
|
|
|
|
if( loc != NULL ) |
|
|
|
|
char * boundaryLoc = mp_memmem( context->boundaryBuffer, context->boundaryBufferPtr, boundary, os_strlen(boundary) ); |
|
|
|
|
if( boundaryLoc != NULL ) |
|
|
|
|
{ |
|
|
|
|
int pos = loc - context->boundaryBuffer; |
|
|
|
|
if( pos > BOUNDARY_SIZE ) |
|
|
|
|
loc = NULL; |
|
|
|
|
int pos = boundaryLoc - context->boundaryBuffer; |
|
|
|
|
if( pos > BOUNDARY_SIZE ) // process in the next call
|
|
|
|
|
boundaryLoc = NULL; |
|
|
|
|
else |
|
|
|
|
dataSize = pos; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if( dataSize != 0 ) |
|
|
|
|
if( dataSize != 0 ) // data to process
|
|
|
|
|
{ |
|
|
|
|
switch( context->state ) |
|
|
|
|
{ |
|
|
|
@ -59,11 +145,12 @@ int ICACHE_FLASH_ATTR multipartProcessBoundaryBuffer(MultipartCtx * context, cha |
|
|
|
|
char * chr = os_strchr( context->boundaryBuffer, '\n' ); |
|
|
|
|
if( chr != NULL ) |
|
|
|
|
{ |
|
|
|
|
// chop datasize to contain only one line
|
|
|
|
|
int pos = chr - context->boundaryBuffer + 1; |
|
|
|
|
if( pos < dataSize ) |
|
|
|
|
if( pos < dataSize ) // if chop smaller than the dataSize, delete the boundary
|
|
|
|
|
{ |
|
|
|
|
dataSize = pos; |
|
|
|
|
loc = NULL; // this is not yet the boundary
|
|
|
|
|
boundaryLoc = NULL; // process boundary next time
|
|
|
|
|
} |
|
|
|
|
if( context->state == STATE_SEARCH_HEADER_END ) |
|
|
|
|
{ |
|
|
|
@ -81,24 +168,24 @@ int ICACHE_FLASH_ATTR multipartProcessBoundaryBuffer(MultipartCtx * context, cha |
|
|
|
|
int pos = fnam - context->boundaryBuffer + 9; |
|
|
|
|
if( pos < dataSize ) |
|
|
|
|
{ |
|
|
|
|
while(context->boundaryBuffer[pos] == ' ') pos++; |
|
|
|
|
if( context->boundaryBuffer[pos] == '"' ) |
|
|
|
|
while(context->boundaryBuffer[pos] == ' ') pos++; // skip spaces
|
|
|
|
|
if( context->boundaryBuffer[pos] == '"' ) // quote start
|
|
|
|
|
{ |
|
|
|
|
pos++; |
|
|
|
|
int start = pos; |
|
|
|
|
while( pos < context->boundaryBufferPtr ) |
|
|
|
|
{ |
|
|
|
|
if( context->boundaryBuffer[pos] == '"' ) |
|
|
|
|
if( context->boundaryBuffer[pos] == '"' ) // quote end
|
|
|
|
|
break; |
|
|
|
|
pos++; |
|
|
|
|
} |
|
|
|
|
if( pos < context->boundaryBufferPtr ) |
|
|
|
|
{ |
|
|
|
|
context->boundaryBuffer[pos] = 0; |
|
|
|
|
context->boundaryBuffer[pos] = 0; // terminating zero for the file name
|
|
|
|
|
os_printf("Uploading file: %s\n", context->boundaryBuffer + start); |
|
|
|
|
if( context->callBack( FILE_START, context->boundaryBuffer + start, pos - start, 0 ) ) |
|
|
|
|
return 1; |
|
|
|
|
context->boundaryBuffer[pos] = '"'; |
|
|
|
|
if( context->callBack( FILE_START, context->boundaryBuffer + start, pos - start, 0 ) ) // FILE_START callback
|
|
|
|
|
return 1; // if an error happened
|
|
|
|
|
context->boundaryBuffer[pos] = '"'; // restore the original quote
|
|
|
|
|
context->state = STATE_SEARCH_HEADER_END; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -112,7 +199,7 @@ int ICACHE_FLASH_ATTR multipartProcessBoundaryBuffer(MultipartCtx * context, cha |
|
|
|
|
{ |
|
|
|
|
char c = context->boundaryBuffer[dataSize]; |
|
|
|
|
context->boundaryBuffer[dataSize] = 0; // add terminating zero (for easier handling)
|
|
|
|
|
if( context->callBack( FILE_DATA, context->boundaryBuffer, dataSize, context->position ) ) |
|
|
|
|
if( context->callBack( FILE_DATA, context->boundaryBuffer, dataSize, context->position ) ) // FILE_DATA callback
|
|
|
|
|
return 1; |
|
|
|
|
context->boundaryBuffer[dataSize] = c; |
|
|
|
|
context->position += dataSize; |
|
|
|
@ -123,25 +210,27 @@ int ICACHE_FLASH_ATTR multipartProcessBoundaryBuffer(MultipartCtx * context, cha |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if( loc != NULL ) |
|
|
|
|
if( boundaryLoc != NULL ) // boundary found?
|
|
|
|
|
{ |
|
|
|
|
dataSize += os_strlen(boundary); |
|
|
|
|
dataSize += os_strlen(boundary); // jump over the boundary
|
|
|
|
|
if( context->state == STATE_UPLOAD_FILE ) |
|
|
|
|
{ |
|
|
|
|
if( context->callBack( FILE_DONE, NULL, 0, context->position ) ) |
|
|
|
|
return 1; |
|
|
|
|
if( context->callBack( FILE_DONE, NULL, 0, context->position ) ) // file done callback
|
|
|
|
|
return 1; // if an error happened
|
|
|
|
|
os_printf("File upload done\n"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
context->state = STATE_SEARCH_HEADER; |
|
|
|
|
context->state = STATE_SEARCH_HEADER; // search the next header
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// move the buffer back with dataSize
|
|
|
|
|
context->boundaryBufferPtr -= dataSize; |
|
|
|
|
os_memcpy(context->boundaryBuffer, context->boundaryBuffer + dataSize, context->boundaryBufferPtr); |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// for processing multipart requests
|
|
|
|
|
int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * connData ) |
|
|
|
|
{ |
|
|
|
|
if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
|
|
|
|
@ -157,7 +246,7 @@ int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * c |
|
|
|
|
|
|
|
|
|
if( connData->startTime != context->startTime ) |
|
|
|
|
{ |
|
|
|
|
// reinitialize, as this is a different request
|
|
|
|
|
// reinitialize, as this is a new request
|
|
|
|
|
context->position = 0; |
|
|
|
|
context->recvPosition = 0; |
|
|
|
|
context->startTime = connData->startTime; |
|
|
|
@ -174,7 +263,7 @@ int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * c |
|
|
|
|
int len = post->buffLen - feed; |
|
|
|
|
if( len > BOUNDARY_SIZE ) |
|
|
|
|
len = BOUNDARY_SIZE; |
|
|
|
|
if( multipartProcessBoundaryBuffer(context, post->multipartBoundary, post->buff + feed, len, 0) ) |
|
|
|
|
if( multipartProcessData(context, post->multipartBoundary, post->buff + feed, len, 0) ) |
|
|
|
|
{ |
|
|
|
|
context->state = STATE_ERROR; |
|
|
|
|
break; |
|
|
|
@ -189,7 +278,8 @@ int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * c |
|
|
|
|
|
|
|
|
|
if( context->state != STATE_ERROR ) |
|
|
|
|
{ |
|
|
|
|
if( multipartProcessBoundaryBuffer(context, post->multipartBoundary, NULL, 0, 1) ) |
|
|
|
|
// this is the last package, process the remaining data
|
|
|
|
|
if( multipartProcessData(context, post->multipartBoundary, NULL, 0, 1) ) |
|
|
|
|
context->state = STATE_ERROR; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|