diff --git a/src/app-layer-htp-file.c b/src/app-layer-htp-file.c index 9a17033a72..5e580907d5 100644 --- a/src/app-layer-htp-file.c +++ b/src/app-layer-htp-file.c @@ -62,6 +62,128 @@ #include "util-memcmp.h" +/** + * \brief Open the file with "filename" and pass the first chunk + * of data if any. + * + * \param f flow to store the file in + * \param filename name of the file + * \param filename_len length of the name + * \param data data chunk (if any) + * \param data_len length of the data portion + * + * \retval 0 ok + * \retval -1 error + */ +int HTPFileOpen(Flow *f, uint8_t *filename, uint16_t filename_len, + uint8_t *data, uint32_t data_len) +{ + int retval = 0; + + if (f == NULL) { + return -1; + } + + SCMutexLock(&f->files_m); + { + if (f->files == NULL) { + f->files = FlowFileContainerAlloc(); + if (f->files == NULL) { + retval = -1; + goto end; + } + } + + if (FlowFileOpenFile(f->files, filename, filename_len, + data, data_len) == NULL) + { + retval = -1; + } + } + +end: + SCMutexUnlock(&f->files_m); + return retval; +} + +/** + * \brief Store a chunk of data in the flow + * + * \param f flow to store the file in + * \param data data chunk (if any) + * \param data_len length of the data portion + * + * \retval 0 ok + * \retval -1 error + */ +int HTPFileStoreChunk(Flow *f, uint8_t *data, uint32_t data_len) { + SCEnter(); + + int retval = 0; + + if (f == NULL) { + SCReturnInt(-1); + } + + SCMutexLock(&f->files_m); + { + if (f->files == NULL) { + SCLogDebug("no files in flow"); + retval = -1; + goto end; + } + + if (FlowFileAppendData(f->files, data, data_len) == -1) + { + SCLogDebug("appending data failed"); + retval = -1; + } + } + +end: + SCMutexUnlock(&f->files_m); + SCReturnInt(retval); +} + +/** + * \brief Close the file in the flow + * + * \param f flow to store in + * \param data data chunk if any + * \param data_len length of the data portion + * \param flags flags to indicate events + * + * Currently on the FLOW_FILE_TRUNCATED flag is implemented, indicating + * that the file isn't complete but we're stopping storing it. + * + * \retval 0 ok + * \retval -1 error + */ +int HTPFileClose(Flow *f, uint8_t *data, uint32_t data_len, uint8_t flags) { + int retval = 0; + + if (f == NULL) { + return -1; + } + + SCMutexLock(&f->files_m); + { + if (f->files == NULL) { + retval = -1; + goto end; + } + + if (FlowFileCloseFile(f->files, data, data_len, flags) == -1) + { + retval = -1; + } + } + +end: + SCMutexUnlock(&f->files_m); + return retval; +} + #ifdef UNITTESTS static int HTPFileParserTest01(void) { int result = 0; @@ -456,7 +578,11 @@ static int HTPFileParserTest04(void) { if (tx->request_method == NULL || memcmp(bstr_tocstr(tx->request_method), "POST", 4) != 0) { - printf("expected method POST, got %s \n", bstr_tocstr(tx->request_method)); + printf("expected method POST, got %s: ", bstr_tocstr(tx->request_method)); + goto end; + } + + if (f.files == NULL || f.files->tail == NULL || f.files->tail->state != FLOWFILE_STATE_CLOSED) { goto end; } diff --git a/src/app-layer-htp-file.h b/src/app-layer-htp-file.h index a752057cd7..300ec96de3 100644 --- a/src/app-layer-htp-file.h +++ b/src/app-layer-htp-file.h @@ -25,6 +25,10 @@ #ifndef __APP_LAYER_HTP_FILE_H__ #define __APP_LAYER_HTP_FILE_H__ +int HTPFileOpen(Flow *, uint8_t *, uint16_t, uint8_t *, uint32_t); +int HTPFileStoreChunk(Flow *, uint8_t *, uint32_t); +int HTPFileClose(Flow *, uint8_t *, uint32_t, uint8_t); + void HTPFileParserRegisterTests(void); #endif /* __APP_LAYER_HTP_FILE_H__ */ diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index 68a0d5ab54..ea31e16890 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -24,6 +24,7 @@ /** * \file * + * \author Victor Julien * \author Gurvinder Singh * \author Pablo Rincon * \author Brian Rectanus @@ -780,84 +781,6 @@ static int HTTPParseContentTypeHeader(uint8_t *name, size_t name_len, SCReturnInt(0); } -static int HTTPStoreFileNameType(Flow *f, uint8_t *filename, size_t filename_len, - uint8_t *filetype, size_t filetype_len) -{ - if (filename == NULL) { - SCReturnInt(-1); - } - - int i = 0; - FlowFileContainer *ffc = NULL; - - SCMutexLock(&f->files_m); - { - /* We have to add/update the file */ - SCLogDebug("Adding file entry to flow"); - - if (f->files == NULL) { - ffc = FlowFileContainerAlloc(); - if (ffc == NULL) { - goto end; - } - f->files = ffc; - } else { - /* TODO: else append chunk, update things...*/ - ffc = f->files; - } - - FlowFile *cur_file = FlowFileContainerRetrieve(ffc, ALPROTO_HTTP, filename, - filename_len); - SCLogDebug("cur_file %p", cur_file); - - if (cur_file == NULL) { - cur_file = FlowFileAlloc(); - if (cur_file == NULL) { - goto end; - } - - cur_file->name = SCMalloc(filename_len); - if (cur_file->name == NULL) { - /** \todo remove cur_file */ - goto end; - } - memcpy(cur_file->name, filename, filename_len); - cur_file->name_len = filename_len; - - /* find the ext portion */ - for (i = filename_len - 2; i >= 0 && filename[i] != '.'; i--); - SCLogDebug("i %d", i); - if (filename[i] == '.') { - cur_file->ext = &cur_file->name[++i]; - cur_file->ext_len = filename_len - i; -#if 0 - printf("EXT START: \n"); - PrintRawDataFp(stdout, cur_file->ext, cur_file->ext_len); - printf("EXT END: \n"); -#endif - } - - if (filetype != NULL) { - cur_file->proto_type = SCMalloc(filetype_len); - if (cur_file->proto_type == NULL) { - goto end; - } - - memcpy(cur_file->proto_type, filetype, filetype_len); - cur_file->proto_type_len = filetype_len; - } - - cur_file->alproto = ALPROTO_HTTP; - SCLogDebug("Added"); - FlowFileContainerAdd(ffc, cur_file); - /* cur_file->ext = get extension */ - } - } -end: - SCMutexUnlock(&f->files_m); - SCReturnInt(0); -} - #define C_D_HDR "content-disposition:" #define C_D_HDR_LEN 20 #define C_T_HDR "content-type:" @@ -1037,6 +960,7 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d) uint8_t *filedata = chunks_buffer; uint32_t filedata_len = 0; + uint8_t flags = 0; if (header_start < form_end) { filedata_len = header_start - filedata; @@ -1046,12 +970,18 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d) filedata_len = form_end - filedata; } else if (htud->flags & HTP_BODY_COMPLETE) { filedata_len = chunks_buffer_len; + flags = FLOW_FILE_TRUNCATED; } printf("FILEDATA (final chunk) START: \n"); PrintRawDataFp(stdout, filedata, filedata_len); printf("FILEDATA (final chunk) END: \n"); + if (HTPFileClose(hstate->f, filedata, filedata_len, flags) == -1) + { + goto end; + } + htud->flags &=~ HTP_FILENAME_SET; /* fall through */ @@ -1066,6 +996,10 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d) PrintRawDataFp(stdout, filedata, filedata_len); printf("FILEDATA (part) END: \n"); + if (HTPFileStoreChunk(hstate->f, filedata, filedata_len) == -1) { + goto end; + } + htud->body_parsed += filedata_len; } else { SCLogDebug("chunk too small to already process in part"); @@ -1135,16 +1069,17 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d) } /* while (header_len > 0) */ if (filename != NULL) { + uint8_t *filedata = NULL; + uint32_t filedata_len = 0; + SCLogDebug("we have a filename"); - HTTPStoreFileNameType(hstate->f, filename, filename_len, - filetype, filetype_len); htud->flags |= HTP_FILENAME_SET; /* everything until the final boundary is the file */ if (form_end != NULL) { - uint8_t *filedata = header_end + 4; - uint32_t filedata_len = form_end - (header_end + 4 + 2); + filedata = header_end + 4; + filedata_len = form_end - (header_end + 4 + 2); SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len); //#if 0 @@ -1159,6 +1094,11 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d) SCLogDebug("offset %u", offset); htud->body_parsed = offset; } + + if (HTPFileOpen(hstate->f, filename, filename_len, + filedata, filedata_len) == -1) { + goto end; + } } filename = NULL; diff --git a/src/detect-fileext.c b/src/detect-fileext.c index ef319d59e0..df62d9bd3f 100644 --- a/src/detect-fileext.c +++ b/src/detect-fileext.c @@ -44,6 +44,7 @@ #include "util-unittest-helper.h" #include "util-spm-bm.h" #include "util-print.h" +#include "util-memcmp.h" #include "app-layer.h" @@ -118,23 +119,29 @@ int DetectFileextMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, DetectFileextData *fileext = (DetectFileextData *)m->ctx; SCMutexLock(&f->files_m); - if (f->files != NULL && f->files->cnt > 0) { - FlowFile *file = f->files->start; + if (f->files != NULL) { + FlowFile *file = f->files->head; for (; file != NULL; file = file->next) { - if (file->ext != NULL) { - //PrintRawDataFp(stdout, file->ext, file->ext_len); - - if (BoyerMooreNocase(fileext->ext, fileext->len, file->ext, - file->ext_len, fileext->bm_ctx->bmGs, - fileext->bm_ctx->bmBc) != NULL) - { - ret = 1; - SCLogDebug("File ext found"); - - /* Stop searching */ - break; - } + if (file->state == FLOWFILE_STATE_NONE) + continue; + + if (file->name == NULL) + continue; + + if (file->name_len <= fileext->len) + continue; + + int offset = file->name_len - fileext->len; + + if (file->name[offset - 1] == '.' && + SCMemcmp(file->name + offset, fileext->ext, fileext->len) == 0) + { + ret = 1; + SCLogDebug("File ext found"); + + /* Stop searching */ + break; } } } diff --git a/src/detect-filename.c b/src/detect-filename.c index b976660710..c653506b6d 100644 --- a/src/detect-filename.c +++ b/src/detect-filename.c @@ -117,12 +117,18 @@ int DetectFilenameMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, DetectFilenameData *filename = m->ctx; SCMutexLock(&f->files_m); - if (f->files != NULL && f->files->cnt > 0) { - FlowFile *file = f->files->start; + if (f->files != NULL) { + FlowFile *file = f->files->head; for (; file != NULL; file = file->next) { - if (file != NULL && file->name != NULL && - BoyerMooreNocase(filename->name, filename->len, file->name, - file->name_len, filename->bm_ctx->bmGs, filename->bm_ctx->bmBc) != NULL) + if (file->state == FLOWFILE_STATE_NONE) + continue; + + if (file->name == NULL) + continue; + + if (BoyerMooreNocase(filename->name, filename->len, file->name, + file->name_len, filename->bm_ctx->bmGs, + filename->bm_ctx->bmBc) != NULL) { ret = 1; SCLogDebug("File %s found", file->name); diff --git a/src/flow-file.c b/src/flow-file.c index 35592c7586..3f4ba517cf 100644 --- a/src/flow-file.c +++ b/src/flow-file.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2010 Open Information Security Foundation +/* Copyright (C) 2007-2011 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -18,6 +18,7 @@ /** * \file * + * \author Victor Julien * \author Pablo Rincon * */ @@ -30,6 +31,10 @@ #include "util-hash.h" #include "util-debug.h" #include "util-memcmp.h" +#include "util-print.h" + +/* prototypes */ +static void FlowFileFree(FlowFile *); /** * \brief allocate a FlowFileContainer @@ -43,9 +48,8 @@ FlowFileContainer *FlowFileContainerAlloc(void) { SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem"); return NULL; } - memset(new, 0, sizeof(new)); - new->start = new->end = NULL; - new->cnt = 0; + memset(new, 0, sizeof(FlowFileContainer)); + new->head = new->tail = NULL; return new; } @@ -58,15 +62,13 @@ void FlowFileContainerRecycle(FlowFileContainer *ffc) { if (ffc == NULL) return; - FlowFile *cur = ffc->start; + FlowFile *cur = ffc->head; FlowFile *next = NULL; - for (;cur != NULL && ffc->cnt > 0; cur = next) { + for (;cur != NULL; cur = next) { next = cur->next; FlowFileFree(cur); - ffc->cnt--; } - ffc->start = ffc->end = NULL; - ffc->cnt = 0; + ffc->head = ffc->tail = NULL; } /** @@ -78,53 +80,116 @@ void FlowFileContainerFree(FlowFileContainer *ffc) { if (ffc == NULL) return; - FlowFile *ptr = ffc->start; + FlowFile *ptr = ffc->head; FlowFile *next = NULL; - for (;ptr != NULL && ffc->cnt > 0; ptr = next) { + for (;ptr != NULL; ptr = next) { next = ptr->next; FlowFileFree(ptr); - ffc->cnt--; } - ffc->start = ffc->end = NULL; - ffc->cnt = 0; + ffc->head = ffc->tail = NULL; SCFree(ffc); } -FlowFileChunk *FlowFileChunkAlloc(void) { - FlowFileChunk *new = SCMalloc(sizeof(FlowFileChunk)); +/** + * \internal + * + * \brief allocate a FlowFileData chunk and set it up + * + * \param data data chunk to store in the FlowFileData + * \param data_len lenght of the data + * + * \retval new FlowFileData object + */ +static FlowFileData *FlowFileDataAlloc(uint8_t *data, uint32_t data_len) { + FlowFileData *new = SCMalloc(sizeof(FlowFileData)); if (new == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem"); return NULL; } - memset(new, 0, sizeof(new)); + memset(new, 0, sizeof(FlowFileData)); + + new->data = SCMalloc(data_len); + if (new->data == NULL) { + SCFree(new); + return NULL; + } + + new->len = data_len; + memcpy(new->data, data, data_len); + new->next = NULL; return new; } -void FlowFileChunkFree(FlowFileChunk *ffc) { - if (ffc == NULL) +/** + * \internal + * + * \brief free a FlowFileData object + * + * \param ffd the flow file data object to free + */ +static void FlowFileDataFree(FlowFileData *ffd) { + if (ffd == NULL) return; - //TODO: To implement - SCFree(ffc); + if (ffd->data != NULL) { + SCFree(ffd->data); + } + + SCFree(ffd); } -FlowFile *FlowFileAlloc(void) { +static int FlowFileAppendFlowFileData(FlowFileContainer *ffc, FlowFileData *ffd) { + SCEnter(); + + if (ffc == NULL) { + SCReturnInt(-1); + } + + FlowFile *ff = ffc->tail; + if (ff == NULL) { + SCReturnInt(-1); + } + + if (ff->chunks_tail == NULL) { + ff->chunks_head = ffd; + ff->chunks_tail = ffd; + } else { + ff->chunks_tail->next = ffd; + ff->chunks_tail = ffd; + } + + SCReturnInt(0); +} + +/** + * \brief Alloc a new FlowFile + * + * \param name character array containing the name (not a string) + * \param name_len length in bytes of the name + * + * \retval new FlowFile object or NULL on error + */ +static FlowFile *FlowFileAlloc(uint8_t *name, uint16_t name_len) { FlowFile *new = SCMalloc(sizeof(FlowFile)); if (new == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem"); return NULL; } - memset(new, 0, sizeof(new)); + memset(new, 0, sizeof(FlowFile)); + + new->name = SCMalloc(name_len); + if (new->name == NULL) { + SCFree(new); + return NULL; + } + + new->name_len = name_len; + memcpy(new->name, name, name_len); - new->state = FLOWFILE_STATE_EMPTY; - new->name = NULL; - new->ext = NULL; - new->next = NULL; return new; } -void FlowFileFree(FlowFile *ff) { +static void FlowFileFree(FlowFile *ff) { if (ff == NULL) return; @@ -134,37 +199,145 @@ void FlowFileFree(FlowFile *ff) { } void FlowFileContainerAdd(FlowFileContainer *ffc, FlowFile *ff) { - if (ffc->start == NULL) { - ffc->start = ffc->end = ff; + if (ffc->head == NULL) { + ffc->head = ffc->tail = ff; } else { - ffc->end->next = ff; - ffc->end = ff; + ffc->tail->next = ff; + ffc->tail = ff; + } +} + +/** + * \brief Open a new FlowFile + * + * \param ffc flow container + * \param name filename character array + * \param name_len filename len + * \param data initial data + * \param data_len initial data len + * + * \retval ff flowfile object + * + * \note filename is not a string, so it's not nul terminated. + */ +FlowFile *FlowFileOpenFile(FlowFileContainer *ffc, uint8_t *name, + uint16_t name_len, uint8_t *data, uint32_t data_len) +{ + SCEnter(); + + PrintRawDataFp(stdout, name, name_len); + + FlowFile *ff = FlowFileAlloc(name, name_len); + if (ff == NULL) { + SCReturnPtr(NULL, "FlowFile"); + } + + ff->state = FLOWFILE_STATE_OPENED; + SCLogDebug("flowfile state transitioned to FLOWFILE_STATE_OPENED"); + + if (data != NULL) { + PrintRawDataFp(stdout, data, data_len); + + FlowFileData *ffd = FlowFileDataAlloc(data, data_len); + if (ffd == NULL) { + FlowFileFree(ff); + SCReturnPtr(NULL, "FlowFile"); + } + + /* append the data */ + if (FlowFileAppendFlowFileData(ffc, ffd) < 0) { + FlowFileFree(ff); + FlowFileDataFree(ffd); + SCReturnPtr(NULL, "FlowFile"); + } } - ffc->cnt += 1; + + FlowFileContainerAdd(ffc, ff); + + SCReturnPtr(ff, "FlowFile"); } -FlowFile *FlowFileContainerRetrieve(FlowFileContainer *ffc, uint16_t alproto, - uint8_t *name, uint16_t name_len) //, uint8_t *type, uint16_t type_len) +/** + * \brief Close a FlowFile + * + * \param ffc the container + * \param data final data if any + * \param data_len data len if any + * \param flags flags + * + * \retval 0 ok + * \retval -1 error + */ +int FlowFileCloseFile(FlowFileContainer *ffc, uint8_t *data, + uint32_t data_len, uint8_t flags) { - FlowFile *ptr = ffc->start; - - if (ffc->cnt > 0) { - while (ptr != NULL) { - if (ptr->alproto == alproto && - name_len == ptr->name_len && - SCMemcmp(ptr->name, name, name_len) == 0) - { - return ptr; - } - - ptr = ptr->next; + SCEnter(); + + if (ffc == NULL || ffc->tail == NULL) { + SCReturnInt(-1); + } + + if (ffc->tail->state != FLOWFILE_STATE_OPENED) { + SCReturnInt(-1); + } + + if (data != NULL) { + PrintRawDataFp(stdout, data, data_len); + + FlowFileData *ffd = FlowFileDataAlloc(data, data_len); + if (ffd == NULL) { + SCReturnInt(-1); + } + + /* append the data */ + if (FlowFileAppendFlowFileData(ffc, ffd) < 0) { + FlowFileDataFree(ffd); + SCReturnInt(-1); } } - return NULL; + if (flags & FLOW_FILE_TRUNCATED) { + ffc->tail->state = FLOWFILE_STATE_TRUNCATED; + SCLogDebug("flowfile state transitioned to FLOWFILE_STATE_TRUNCATED"); + } else { + ffc->tail->state = FLOWFILE_STATE_CLOSED; + SCLogDebug("flowfile state transitioned to FLOWFILE_STATE_CLOSED"); + } + + SCReturnInt(0); } -FlowFile *FlowFileAppendChunk(FlowFile *ff, FlowFileChunk *ffc) { - //TODO: To implement - return NULL; +/** + * \brief Store a chunk of file data in the flow. The open "flowfile" + * will be used. + * + * \param ffc the container + * \param data data chunk + * \param data_len data chunk len + * + * \retval 0 ok + * \retval -1 error + */ +int FlowFileAppendData(FlowFileContainer *ffc, uint8_t *data, uint32_t data_len) { + SCEnter(); + + if (ffc == NULL || ffc->tail == NULL || data == NULL || data_len == 0) { + SCReturnInt(-1); + } + + if (ffc->tail->state != FLOWFILE_STATE_OPENED) { + SCReturnInt(-1); + } + + FlowFileData *ffd = FlowFileDataAlloc(data, data_len); + if (ffd == NULL) { + SCReturnInt(-1); + } + + /* append the data */ + if (FlowFileAppendFlowFileData(ffc, ffd) < 0) { + FlowFileDataFree(ffd); + SCReturnInt(-1); + } + SCReturnInt(0); } diff --git a/src/flow-file.h b/src/flow-file.h index ad67b5c2a1..1df35c696a 100644 --- a/src/flow-file.h +++ b/src/flow-file.h @@ -28,48 +28,39 @@ #include "flow.h" #include "util-hash.h" - -typedef enum _FlowFileState { - FLOWFILE_STATE_EMPTY, - FLOWFILE_STATE_DATA, - FLOWFILE_STATE_COMPLETED, - FLOWFILE_STATE_STORING, +#define FLOW_FILE_TRUNCATED 0x01 + +typedef enum FlowFileState_ { + FLOWFILE_STATE_NONE = 0, /**< no state */ + FLOWFILE_STATE_OPENED, /**< flow file is opened */ + FLOWFILE_STATE_CLOSED, /**< flow file is completed, + there will be no more data. */ + FLOWFILE_STATE_TRUNCATED, /**< flow file is not complete, but + there will be no more data. */ + FLOWFILE_STATE_STORED, /**< all fully written to disk */ FLOWFILE_STATE_MAX } FlowFileState; -typedef uint8_t FlowFileHash; - -typedef struct _FlowFileChunk { - uint8_t *buf; - uint32_t len; - struct _FlowFileChunk *next; -} FlowFileChunk; - -typedef struct _FlowFile { - uint8_t *name; - uint16_t name_len; - uint8_t *ext; /* ptr to extension portion in "name" */ - uint16_t ext_len; - uint8_t *real_type; - uint16_t real_type_len; - uint8_t *proto_type; /* The content type set at Content-Type in the MIME header (of http or smtp..) */ - uint16_t proto_type_len; - uint16_t alproto; - uint32_t size; - uint8_t flags; - - FlowFileState state; - FlowFileChunk *chunks_start; - FlowFileChunk *chunks_end; - - uint32_t chunk_cnt; - struct _FlowFile *next; +typedef struct FlowFileData_ { + uint8_t *data; + uint32_t len; + int stored; /* true if this chunk has been stored already + * false otherwise */ + struct FlowFileData_ *next; +} FlowFileData; + +typedef struct FlowFile_ { + uint8_t *name; + uint16_t name_len; + FlowFileState state; + FlowFileData *chunks_head; + FlowFileData *chunks_tail; + struct FlowFile_ *next; } FlowFile; -typedef struct _FlowFileContainer { - FlowFile *start; - FlowFile *end; - uint32_t cnt; +typedef struct FlowFileContainer_ { + FlowFile *head; + FlowFile *tail; } FlowFileContainer; FlowFileContainer *FlowFileContainerAlloc(); @@ -77,15 +68,47 @@ void FlowFileContainerFree(FlowFileContainer *); void FlowFileContainerRecycle(FlowFileContainer *); -FlowFileChunk *FlowFileChunkAlloc(); -void FlowFileChunkFree(FlowFileChunk *); +void FlowFileContainerAdd(FlowFileContainer *, FlowFile *); -FlowFile *FlowFileAlloc(); -void FlowFileFree(FlowFile *); +/** + * \brief Open a new FlowFile + * + * \param ffc flow container + * \param name filename character array + * \param name_len filename len + * \param data initial data + * \param data_len initial data len + * + * \retval ff flowfile object + * + * \note filename is not a string, so it's not nul terminated. + */ +FlowFile *FlowFileOpenFile(FlowFileContainer *, uint8_t *name, uint16_t name_len, + uint8_t *data, uint32_t data_len); +/** + * \brief Close a FlowFile + * + * \param ffc the container + * \param data final data if any + * \param data_len data len if any + * \param flags flags + * + * \retval 0 ok + * \retval -1 error + */ +int FlowFileCloseFile(FlowFileContainer *, uint8_t *data, uint32_t data_len, uint8_t flags); -void FlowFileContainerAdd(FlowFileContainer *, FlowFile *); -//FlowFile *FlowFileContainerRetrieve(FlowFileContainer *, uint8_t *, uint16_t, uint8_t *); -FlowFile *FlowFileContainerRetrieve(FlowFileContainer *, uint16_t, uint8_t *, uint16_t); -FlowFile *FlowFileAppendChunk(FlowFile *, FlowFileChunk *); +/** + * \brief Store a chunk of file data in the flow. The open "flowfile" + * will be used. + * + * \param ffc the container + * \param data data chunk + * \param data_len data chunk len + * + * \retval 0 ok + * \retval -1 error + */ +int FlowFileAppendData(FlowFileContainer *, uint8_t *data, uint32_t data_len); #endif /* __FLOW_FILE_H__ */