/* Copyright (C) 2007-2012 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
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

/**
 * \file
 *
 * \author Anoop Saldanha <anoopsaldanha@gmail.com>
 */

#include "suricata.h"
#include "suricata-common.h"
#include "debug.h"
#include "decode.h"
#include "threads.h"

#include "stream-tcp-private.h"
#include "stream-tcp-reassemble.h"
#include "stream-tcp.h"
#include "stream.h"

#include "app-layer.h"
#include "app-layer-detect-proto.h"
#include "app-layer-protos.h"
#include "app-layer-parser.h"
#include "app-layer-smtp.h"

#include "util-mpm.h"
#include "util-debug.h"
#include "util-byte.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"
#include "util-memcmp.h"
#include "flow-util.h"

#include "detect-engine.h"
#include "detect-engine-state.h"
#include "detect-parse.h"

#include "decode-events.h"
#include "conf.h"

#include "util-mem.h"
#include "util-misc.h"

/* content-limit default value */
#define FILEDATA_CONTENT_LIMIT 100000
/* content-inspect-min-size default value */
#define FILEDATA_CONTENT_INSPECT_MIN_SIZE 32768
/* content-inspect-window default value */
#define FILEDATA_CONTENT_INSPECT_WINDOW 4096

#define SMTP_MAX_REQUEST_AND_REPLY_LINE_LENGTH 510

#define SMTP_COMMAND_BUFFER_STEPS 5

/* we are in process of parsing a fresh command.  Just a placeholder.  If we
 * are not in STATE_COMMAND_DATA_MODE, we have to be in this mode */
#define SMTP_PARSER_STATE_COMMAND_MODE            0x00
/* we are in mode of parsing a command's data.  Used when we are parsing tls
 * or accepting the rfc 2822 mail after DATA command */
#define SMTP_PARSER_STATE_COMMAND_DATA_MODE       0x01
/* Used when we are still in the process of parsing a server command.  Used
 * with multi-line replies and the stream is fragmented before all the lines
 * for a response is seen */
#define SMTP_PARSER_STATE_PARSING_SERVER_RESPONSE 0x02
/* Used to indicate that the parser has seen the first reply */
#define SMTP_PARSER_STATE_FIRST_REPLY_SEEN        0x04
/* Used to indicate that the parser is parsing a multiline reply */
#define SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY 0x08

/* Various SMTP commands
 * We currently have var-ified just STARTTLS and DATA, since we need to them
 * for state transitions.  The rest are just indicate as OTHER_CMD.  Other
 * commands would be introduced as and when needed */
#define SMTP_COMMAND_STARTTLS  1
#define SMTP_COMMAND_DATA      2
#define SMTP_COMMAND_BDAT      3
/* not an actual command per se, but the mode where we accept the mail after
 * DATA has it's own reply code for completion, from the server.  We give this
 * stage a pseudo command of it's own, so that we can add this to the command
 * buffer to match with the reply */
#define SMTP_COMMAND_DATA_MODE 4
/* All other commands are represented by this var */
#define SMTP_COMMAND_OTHER_CMD 5

/* Different EHLO extensions.  Not used now. */
#define SMTP_EHLO_EXTENSION_PIPELINING
#define SMTP_EHLO_EXTENSION_SIZE
#define SMTP_EHLO_EXTENSION_DSN
#define SMTP_EHLO_EXTENSION_STARTTLS
#define SMTP_EHLO_EXTENSION_8BITMIME

SCEnumCharMap smtp_decoder_event_table[ ] = {
    { "INVALID_REPLY",           SMTP_DECODER_EVENT_INVALID_REPLY },
    { "UNABLE_TO_MATCH_REPLY_WITH_REQUEST",
      SMTP_DECODER_EVENT_UNABLE_TO_MATCH_REPLY_WITH_REQUEST },
    { "MAX_COMMAND_LINE_LEN_EXCEEDED",
      SMTP_DECODER_EVENT_MAX_COMMAND_LINE_LEN_EXCEEDED },
    { "MAX_REPLY_LINE_LEN_EXCEEDED",
      SMTP_DECODER_EVENT_MAX_REPLY_LINE_LEN_EXCEEDED },
    { "INVALID_PIPELINED_SEQUENCE",
      SMTP_DECODER_EVENT_INVALID_PIPELINED_SEQUENCE },
    { "BDAT_CHUNK_LEN_EXCEEDED",
      SMTP_DECODER_EVENT_BDAT_CHUNK_LEN_EXCEEDED },
    { "NO_SERVER_WELCOME_MESSAGE",
      SMTP_DECODER_EVENT_NO_SERVER_WELCOME_MESSAGE },
    { "TLS_REJECTED",
      SMTP_DECODER_EVENT_TLS_REJECTED },
    { "DATA_COMMAND_REJECTED",
      SMTP_DECODER_EVENT_DATA_COMMAND_REJECTED },

    /* MIME Events */
    { "MIME_PARSE_FAILED",
      SMTP_DECODER_EVENT_MIME_PARSE_FAILED },
    { "MIME_MALFORMED_MSG",
      SMTP_DECODER_EVENT_MIME_MALFORMED_MSG },
    { "MIME_INVALID_BASE64",
      SMTP_DECODER_EVENT_MIME_INVALID_BASE64 },
    { "MIME_INVALID_QP",
      SMTP_DECODER_EVENT_MIME_INVALID_QP },
    { "MIME_LONG_LINE",
      SMTP_DECODER_EVENT_MIME_LONG_LINE },
    { "MIME_LONG_ENC_LINE",
      SMTP_DECODER_EVENT_MIME_LONG_ENC_LINE },
    { "MIME_LONG_HEADER_NAME",
      SMTP_DECODER_EVENT_MIME_LONG_HEADER_NAME },
    { "MIME_LONG_HEADER_VALUE",
      SMTP_DECODER_EVENT_MIME_LONG_HEADER_VALUE },
    { "MIME_LONG_BOUNDARY",
      SMTP_DECODER_EVENT_MIME_BOUNDARY_TOO_LONG },

    /* Invalid behavior or content */
    { "DUPLICATE_FIELDS",
      SMTP_DECODER_EVENT_DUPLICATE_FIELDS },
    { "UNPARSABLE_CONTENT",
      SMTP_DECODER_EVENT_UNPARSABLE_CONTENT },

    { NULL,                      -1 },
};

typedef struct SMTPThreadCtx_ {
    MpmThreadCtx *smtp_mpm_thread_ctx;
    PrefilterRuleStore *pmq;
} SMTPThreadCtx;

#define SMTP_MPM DEFAULT_MPM

static MpmCtx *smtp_mpm_ctx = NULL;

/* smtp reply codes.  If an entry is made here, please make a simultaneous
 * entry in smtp_reply_map */
enum {
    SMTP_REPLY_211,
    SMTP_REPLY_214,
    SMTP_REPLY_220,
    SMTP_REPLY_221,
    SMTP_REPLY_235,
    SMTP_REPLY_250,
    SMTP_REPLY_251,
    SMTP_REPLY_252,

    SMTP_REPLY_334,
    SMTP_REPLY_354,

    SMTP_REPLY_421,
    SMTP_REPLY_450,
    SMTP_REPLY_451,
    SMTP_REPLY_452,
    SMTP_REPLY_455,

    SMTP_REPLY_500,
    SMTP_REPLY_501,
    SMTP_REPLY_502,
    SMTP_REPLY_503,
    SMTP_REPLY_504,
    SMTP_REPLY_550,
    SMTP_REPLY_551,
    SMTP_REPLY_552,
    SMTP_REPLY_553,
    SMTP_REPLY_554,
    SMTP_REPLY_555,
};

SCEnumCharMap smtp_reply_map[ ] = {
    { "211", SMTP_REPLY_211 },
    { "214", SMTP_REPLY_214 },
    { "220", SMTP_REPLY_220 },
    { "221", SMTP_REPLY_221 },
    { "235", SMTP_REPLY_235 },
    { "250", SMTP_REPLY_250 },
    { "251", SMTP_REPLY_251 },
    { "252", SMTP_REPLY_252 },

    { "334", SMTP_REPLY_334 },
    { "354", SMTP_REPLY_354 },

    { "421", SMTP_REPLY_421 },
    { "450", SMTP_REPLY_450 },
    { "451", SMTP_REPLY_451 },
    { "452", SMTP_REPLY_452 },
    { "455", SMTP_REPLY_455 },

    { "500", SMTP_REPLY_500 },
    { "501", SMTP_REPLY_501 },
    { "502", SMTP_REPLY_502 },
    { "503", SMTP_REPLY_503 },
    { "504", SMTP_REPLY_504 },
    { "550", SMTP_REPLY_550 },
    { "551", SMTP_REPLY_551 },
    { "552", SMTP_REPLY_552 },
    { "553", SMTP_REPLY_553 },
    { "554", SMTP_REPLY_554 },
    { "555", SMTP_REPLY_555 },
    {  NULL,  -1 },
};

/* Create SMTP config structure */
SMTPConfig smtp_config = { 0, { 0, 0, 0, 0, 0 }, 0, 0, 0, STREAMING_BUFFER_CONFIG_INITIALIZER};

static SMTPString *SMTPStringAlloc(void);

/**
 * \brief Configure SMTP Mime Decoder by parsing out mime section of YAML
 * config file
 *
 * \return none
 */
static void SMTPConfigure(void) {

    SCEnter();
    int ret = 0, val;
    intmax_t imval;
    uint32_t content_limit = 0;
    uint32_t content_inspect_min_size = 0;
    uint32_t content_inspect_window = 0;

    ConfNode *config = ConfGetNode("app-layer.protocols.smtp.mime");
    if (config != NULL) {

        ret = ConfGetChildValueBool(config, "decode-mime", &val);
        if (ret) {
            smtp_config.decode_mime = val;
        }

        ret = ConfGetChildValueBool(config, "decode-base64", &val);
        if (ret) {
            smtp_config.mime_config.decode_base64 = val;
        }

        ret = ConfGetChildValueBool(config, "decode-quoted-printable", &val);
        if (ret) {
            smtp_config.mime_config.decode_quoted_printable = val;
        }

        ret = ConfGetChildValueInt(config, "header-value-depth", &imval);
        if (ret) {
            smtp_config.mime_config.header_value_depth = (uint32_t) imval;
        }

        ret = ConfGetChildValueBool(config, "extract-urls", &val);
        if (ret) {
            smtp_config.mime_config.extract_urls = val;
        }

        ret = ConfGetChildValueBool(config, "body-md5", &val);
        if (ret) {
            smtp_config.mime_config.body_md5 = val;
        }
    }

    /* Pass mime config data to MimeDec API */
    MimeDecSetConfig(&smtp_config.mime_config);

    smtp_config.content_limit = FILEDATA_CONTENT_LIMIT;
    smtp_config.content_inspect_window = FILEDATA_CONTENT_INSPECT_WINDOW;
    smtp_config.content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE;

    ConfNode *t = ConfGetNode("app-layer.protocols.smtp.inspected-tracker");
    ConfNode *p = NULL;

    if (t != NULL) {
        TAILQ_FOREACH(p, &t->head, next) {
            if (strcasecmp("content-limit", p->name) == 0) {
                if (ParseSizeStringU32(p->val, &content_limit) < 0) {
                    SCLogWarning(SC_ERR_SIZE_PARSE,
                            "parsing content-limit %s failed", p->val);
                    content_limit = FILEDATA_CONTENT_LIMIT;
                }
                smtp_config.content_limit = content_limit;
            }

            if (strcasecmp("content-inspect-min-size", p->name) == 0) {
                if (ParseSizeStringU32(p->val, &content_inspect_min_size) < 0) {
                    SCLogWarning(SC_ERR_SIZE_PARSE,
                            "parsing content-inspect-min-size %s failed", p->val);
                    content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE;
                }
                smtp_config.content_inspect_min_size = content_inspect_min_size;
            }

            if (strcasecmp("content-inspect-window", p->name) == 0) {
                if (ParseSizeStringU32(p->val, &content_inspect_window) < 0) {
                    SCLogWarning(SC_ERR_SIZE_PARSE,
                            "parsing content-inspect-window %s failed", p->val);
                    content_inspect_window = FILEDATA_CONTENT_INSPECT_WINDOW;
                }
                smtp_config.content_inspect_window = content_inspect_window;
            }
        }
    }

    smtp_config.sbcfg.buf_size = content_limit ? content_limit : 256;

    SCReturn;
}

void SMTPSetEvent(SMTPState *s, uint8_t e)
{
    SCLogDebug("setting event %u", e);

    if (s->curr_tx != NULL) {
        AppLayerDecoderEventsSetEventRaw(&s->curr_tx->decoder_events, e);
//        s->events++;
        return;
    }
    SCLogDebug("couldn't set event %u", e);
}

static SMTPTransaction *SMTPTransactionCreate(void)
{
    SMTPTransaction *tx = SCCalloc(1, sizeof(*tx));
    if (tx == NULL) {
        return NULL;
    }

    TAILQ_INIT(&tx->rcpt_to_list);
    tx->mime_state = NULL;
    return tx;
}

/** \internal
 *  \brief update inspected tracker if it gets to far behind
 *
 *  As smtp uses the FILE_USE_DETECT flag in the file API, we are responsible
 *  for making sure that File::content_inspected is not getting too far
 *  behind.
 */
static void SMTPPruneFiles(FileContainer *files)
{
    SCLogDebug("cfg: win %"PRIu32" min_size %"PRIu32,
            smtp_config.content_inspect_window, smtp_config.content_inspect_min_size);

    File *file = files->head;
    while (file) {
        SCLogDebug("file %p", file);
        uint32_t window = smtp_config.content_inspect_window;
        if (file->sb->stream_offset == 0)
            window = MAX(window, smtp_config.content_inspect_min_size);

        uint64_t file_size = FileSize(file);
        uint64_t data_size = file_size - file->sb->stream_offset;

        SCLogDebug("window %"PRIu32", file_size %"PRIu64", data_size %"PRIu64,
                window, file_size, data_size);

        if (data_size > (window * 3)) {
            uint64_t left_edge = file_size - window;
            SCLogDebug("file->content_inspected now %"PRIu64, left_edge);
            file->content_inspected = left_edge;
        }

        file = file->next;
    }
}

static void FlagDetectStateNewFile(SMTPTransaction *tx)
{
    if (tx && tx->de_state) {
        SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW set");
        tx->de_state->dir_state[0].flags |= DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW;
    } else if (tx == NULL) {
        SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW NOT set, no TX");
    } else if (tx->de_state == NULL) {
        SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW NOT set, no TX DESTATE");
    }
}

int SMTPProcessDataChunk(const uint8_t *chunk, uint32_t len,
        MimeDecParseState *state)
{
    SCEnter();
    int ret = MIME_DEC_OK;
    Flow *flow = (Flow *) state->data;
    SMTPState *smtp_state = (SMTPState *) flow->alstate;
    MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
    FileContainer *files = NULL;

    uint16_t flags = FileFlowToFlags(flow, STREAM_TOSERVER);
    /* we depend on detection engine for file pruning */
    flags |= FILE_USE_DETECT;

    /* Find file */
    if (entity->ctnt_flags & CTNT_IS_ATTACHMENT) {

        /* Make sure file container allocated */
        if (smtp_state->files_ts == NULL) {
            smtp_state->files_ts = FileContainerAlloc();
            if (smtp_state->files_ts == NULL) {
                ret = MIME_DEC_ERR_MEM;
                SCLogError(SC_ERR_MEM_ALLOC, "Could not create file container");
                SCReturnInt(ret);
            }
        }
        files = smtp_state->files_ts;

        /* Open file if necessary */
        if (state->body_begin) {

            if (SCLogDebugEnabled()) {
                SCLogDebug("Opening file...%u bytes", len);
                printf("File - ");
                for (uint32_t i = 0; i < entity->filename_len; i++) {
                    printf("%c", entity->filename[i]);
                }
                printf("\n");
            }

            /* Set storage flag if applicable since only the first file in the
             * flow seems to be processed by the 'filestore' detector */
            if (files->head != NULL && (files->head->flags & FILE_STORE)) {
                flags |= FILE_STORE;
            }

            if (FileOpenFile(files, &smtp_config.sbcfg, (uint8_t *) entity->filename, entity->filename_len,
                    (uint8_t *) chunk, len, flags) == NULL) {
                ret = MIME_DEC_ERR_DATA;
                SCLogDebug("FileOpenFile() failed");
            }
            FlagDetectStateNewFile(smtp_state->curr_tx);

            /* If close in the same chunk, then pass in empty bytes */
            if (state->body_end) {

                SCLogDebug("Closing file...%u bytes", len);

                if (files->tail->state == FILE_STATE_OPENED) {
                    ret = FileCloseFile(files, (uint8_t *) NULL, 0, flags);
                    if (ret != 0) {
                        SCLogDebug("FileCloseFile() failed: %d", ret);
                        ret = MIME_DEC_ERR_DATA;
                    }
                } else {
                    SCLogDebug("File already closed");
                }
            }
        } else if (state->body_end) {
            /* Close file */
            SCLogDebug("Closing file...%u bytes", len);

            if (files && files->tail && files->tail->state == FILE_STATE_OPENED) {
                ret = FileCloseFile(files, (uint8_t *) chunk, len, flags);
                if (ret != 0) {
                    SCLogDebug("FileCloseFile() failed: %d", ret);
                    ret = MIME_DEC_ERR_DATA;
                }
            } else {
                SCLogDebug("File already closed");
            }
        } else {
            /* Append data chunk to file */
            SCLogDebug("Appending file...%u bytes", len);

            /* 0 is ok, -2 is not stored, -1 is error */
            ret = FileAppendData(files, (uint8_t *) chunk, len);
            if (ret == -2) {
                ret = 0;
                SCLogDebug("FileAppendData() - file no longer being extracted");
            } else if (ret < 0) {
                SCLogDebug("FileAppendData() failed: %d", ret);
                ret = MIME_DEC_ERR_DATA;
            }
        }

        if (ret == 0) {
            SCLogDebug("Successfully processed file data!");
        }
    } else {
        SCLogDebug("Body not a Ctnt_attachment");
    }

    if (files != NULL) {
        SMTPPruneFiles(files);
        FilePrune(files);
    }

    SCReturnInt(ret);
}

/**
 * \internal
 * \brief Get the next line from input.  It doesn't do any length validation.
 *
 * \param state The smtp state.
 *
 * \retval  0 On suceess.
 * \retval -1 Either when we don't have any new lines to supply anymore or
 *            on failure.
 */
static int SMTPGetLine(SMTPState *state)
{
    SCEnter();
    void *ptmp;

    /* we have run out of input */
    if (state->input_len <= 0)
        return -1;

    /* toserver */
    if (state->direction == 0) {
        if (state->ts_current_line_lf_seen == 1) {
            /* we have seen the lf for the previous line.  Clear the parser
             * details to parse new line */
            state->ts_current_line_lf_seen = 0;
            if (state->ts_current_line_db == 1) {
                state->ts_current_line_db = 0;
                SCFree(state->ts_db);
                state->ts_db = NULL;
                state->ts_db_len = 0;
                state->current_line = NULL;
                state->current_line_len = 0;
            }
        }

        uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);

        if (lf_idx == NULL) {
            /* fragmented lines.  Decoder event for special cases.  Not all
             * fragmented lines should be treated as a possible evasion
             * attempt.  With multi payload smtp chunks we can have valid
             * cases of fragmentation.  But within the same segment chunk
             * if we see fragmentation then it's definitely something you
             * should alert about */
            if (state->ts_current_line_db == 0) {
                state->ts_db = SCMalloc(state->input_len);
                if (state->ts_db == NULL) {
                    return -1;
                }
                state->ts_current_line_db = 1;
                memcpy(state->ts_db, state->input, state->input_len);
                state->ts_db_len = state->input_len;
            } else {
                ptmp = SCRealloc(state->ts_db,
                                 (state->ts_db_len + state->input_len));
                if (ptmp == NULL) {
                    SCFree(state->ts_db);
                    state->ts_db = NULL;
                    state->ts_db_len = 0;
                    return -1;
                }
                state->ts_db = ptmp;

                memcpy(state->ts_db + state->ts_db_len,
                       state->input, state->input_len);
                state->ts_db_len += state->input_len;
            } /* else */
            state->input += state->input_len;
            state->input_len = 0;

            return -1;

        } else {
            state->ts_current_line_lf_seen = 1;

            if (state->ts_current_line_db == 1) {
                ptmp = SCRealloc(state->ts_db,
                                 (state->ts_db_len + (lf_idx + 1 - state->input)));
                if (ptmp == NULL) {
                    SCFree(state->ts_db);
                    state->ts_db = NULL;
                    state->ts_db_len = 0;
                    return -1;
                }
                state->ts_db = ptmp;

                memcpy(state->ts_db + state->ts_db_len,
                       state->input, (lf_idx + 1 - state->input));
                state->ts_db_len += (lf_idx + 1 - state->input);

                if (state->ts_db_len > 1 &&
                    state->ts_db[state->ts_db_len - 2] == 0x0D) {
                    state->ts_db_len -= 2;
                    state->current_line_delimiter_len = 2;
                } else {
                    state->ts_db_len -= 1;
                    state->current_line_delimiter_len = 1;
                }

                state->current_line = state->ts_db;
                state->current_line_len = state->ts_db_len;

            } else {
                state->current_line = state->input;
                state->current_line_len = lf_idx - state->input;

                if (state->input != lf_idx &&
                    *(lf_idx - 1) == 0x0D) {
                    state->current_line_len--;
                    state->current_line_delimiter_len = 2;
                } else {
                    state->current_line_delimiter_len = 1;
                }
            }

            state->input_len -= (lf_idx - state->input) + 1;
            state->input = (lf_idx + 1);

            return 0;
        }

        /* toclient */
    } else {
        if (state->tc_current_line_lf_seen == 1) {
            /* we have seen the lf for the previous line.  Clear the parser
             * details to parse new line */
            state->tc_current_line_lf_seen = 0;
            if (state->tc_current_line_db == 1) {
                state->tc_current_line_db = 0;
                SCFree(state->tc_db);
                state->tc_db = NULL;
                state->tc_db_len = 0;
                state->current_line = NULL;
                state->current_line_len = 0;
            }
        }

        uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);

        if (lf_idx == NULL) {
            /* fragmented lines.  Decoder event for special cases.  Not all
             * fragmented lines should be treated as a possible evasion
             * attempt.  With multi payload smtp chunks we can have valid
             * cases of fragmentation.  But within the same segment chunk
             * if we see fragmentation then it's definitely something you
             * should alert about */
            if (state->tc_current_line_db == 0) {
                state->tc_db = SCMalloc(state->input_len);
                if (state->tc_db == NULL) {
                    return -1;
                }
                state->tc_current_line_db = 1;
                memcpy(state->tc_db, state->input, state->input_len);
                state->tc_db_len = state->input_len;
            } else {
                ptmp = SCRealloc(state->tc_db,
                                 (state->tc_db_len + state->input_len));
                if (ptmp == NULL) {
                    SCFree(state->tc_db);
                    state->tc_db = NULL;
                    state->tc_db_len = 0;
                    return -1;
                }
                state->tc_db = ptmp;

                memcpy(state->tc_db + state->tc_db_len,
                       state->input, state->input_len);
                state->tc_db_len += state->input_len;
            } /* else */
            state->input += state->input_len;
            state->input_len = 0;

            return -1;

        } else {
            state->tc_current_line_lf_seen = 1;

            if (state->tc_current_line_db == 1) {
                ptmp = SCRealloc(state->tc_db,
                                 (state->tc_db_len + (lf_idx + 1 - state->input)));
                if (ptmp == NULL) {
                    SCFree(state->tc_db);
                    state->tc_db = NULL;
                    state->tc_db_len = 0;
                    return -1;
                }
                state->tc_db = ptmp;

                memcpy(state->tc_db + state->tc_db_len,
                       state->input, (lf_idx + 1 - state->input));
                state->tc_db_len += (lf_idx + 1 - state->input);

                if (state->tc_db_len > 1 &&
                    state->tc_db[state->tc_db_len - 2] == 0x0D) {
                    state->tc_db_len -= 2;
                    state->current_line_delimiter_len = 2;
                } else {
                    state->tc_db_len -= 1;
                    state->current_line_delimiter_len = 1;
                }

                state->current_line = state->tc_db;
                state->current_line_len = state->tc_db_len;

            } else {
                state->current_line = state->input;
                state->current_line_len = lf_idx - state->input;

                if (state->input != lf_idx &&
                    *(lf_idx - 1) == 0x0D) {
                    state->current_line_len--;
                    state->current_line_delimiter_len = 2;
                } else {
                    state->current_line_delimiter_len = 1;
                }
            }

            state->input_len -= (lf_idx - state->input) + 1;
            state->input = (lf_idx + 1);

            return 0;
        } /* else - if (lf_idx == NULL) */
    }

}

static int SMTPInsertCommandIntoCommandBuffer(uint8_t command, SMTPState *state, Flow *f)
{
    SCEnter();
    void *ptmp;

    if (state->cmds_cnt >= state->cmds_buffer_len) {
        int increment = SMTP_COMMAND_BUFFER_STEPS;
        if ((int)(state->cmds_buffer_len + SMTP_COMMAND_BUFFER_STEPS) > (int)USHRT_MAX) {
            increment = USHRT_MAX - state->cmds_buffer_len;
        }

        ptmp = SCRealloc(state->cmds,
                         sizeof(uint8_t) * (state->cmds_buffer_len + increment));
        if (ptmp == NULL) {
            SCFree(state->cmds);
            state->cmds = NULL;
            SCLogDebug("SCRealloc failure");
            return -1;
        }
        state->cmds = ptmp;

        state->cmds_buffer_len += increment;
    }
    if (state->cmds_cnt >= 1 &&
        ((state->cmds[state->cmds_cnt - 1] == SMTP_COMMAND_STARTTLS) ||
         (state->cmds[state->cmds_cnt - 1] == SMTP_COMMAND_DATA))) {
        /* decoder event */
        SMTPSetEvent(state, SMTP_DECODER_EVENT_INVALID_PIPELINED_SEQUENCE);
        /* we have to have EHLO, DATA, VRFY, EXPN, TURN, QUIT, NOOP,
         * STARTTLS as the last command in pipelined mode */
    }

    /** \todo decoder event */
    if ((int)(state->cmds_cnt + 1) > (int)USHRT_MAX) {
        SCLogDebug("command buffer overflow");
        return -1;
    }

    state->cmds[state->cmds_cnt] = command;
    state->cmds_cnt++;

    return 0;
}

static int SMTPProcessCommandBDAT(SMTPState *state, Flow *f,
                                  AppLayerParserState *pstate)
{
    SCEnter();

    state->bdat_chunk_idx += (state->current_line_len +
                              state->current_line_delimiter_len);
    if (state->bdat_chunk_idx > state->bdat_chunk_len) {
        state->parser_state &= ~SMTP_PARSER_STATE_COMMAND_DATA_MODE;
        /* decoder event */
        SMTPSetEvent(state, SMTP_DECODER_EVENT_BDAT_CHUNK_LEN_EXCEEDED);
        SCReturnInt(-1);
    } else if (state->bdat_chunk_idx == state->bdat_chunk_len) {
        state->parser_state &= ~SMTP_PARSER_STATE_COMMAND_DATA_MODE;
    }

    SCReturnInt(0);
}

static int SMTPProcessCommandDATA(SMTPState *state, Flow *f,
                                  AppLayerParserState *pstate)
{
    SCEnter();

    if (!(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        /* looks like are still waiting for a confirmination from the server */
        return 0;
    }

    if (state->current_line_len == 1 && state->current_line[0] == '.') {
        state->parser_state &= ~SMTP_PARSER_STATE_COMMAND_DATA_MODE;
        /* kinda like a hack.  The mail sent in DATA mode, would be
         * acknowledged with a reply.  We insert a dummy command to
         * the command buffer to be used by the reply handler to match
         * the reply received */
        SMTPInsertCommandIntoCommandBuffer(SMTP_COMMAND_DATA_MODE, state, f);

        if (smtp_config.decode_mime && state->curr_tx->mime_state != NULL) {
            /* Complete parsing task */
            int ret = MimeDecParseComplete(state->curr_tx->mime_state);
            if (ret != MIME_DEC_OK) {

                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_PARSE_FAILED);
                SCLogDebug("MimeDecParseComplete() function failed");
            }

            /* Generate decoder events */
            MimeDecEntity *msg = state->curr_tx->mime_state->msg;
            if (msg->anomaly_flags & ANOM_INVALID_BASE64) {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_INVALID_BASE64);
            }
            if (msg->anomaly_flags & ANOM_INVALID_QP) {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_INVALID_QP);
            }
            if (msg->anomaly_flags & ANOM_LONG_LINE) {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_LONG_LINE);
            }
            if (msg->anomaly_flags & ANOM_LONG_ENC_LINE) {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_LONG_ENC_LINE);
            }
            if (msg->anomaly_flags & ANOM_LONG_HEADER_NAME) {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_LONG_HEADER_NAME);
            }
            if (msg->anomaly_flags & ANOM_LONG_HEADER_VALUE) {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_LONG_HEADER_VALUE);
            }
            if (msg->anomaly_flags & ANOM_MALFORMED_MSG) {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_MALFORMED_MSG);
            }
            if (msg->anomaly_flags & ANOM_LONG_BOUNDARY) {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_MIME_BOUNDARY_TOO_LONG);
            }
        }
        state->curr_tx->done = 1;
        SCLogDebug("marked tx as done");
    }

    /* If DATA, then parse out a MIME message */
    if (state->current_command == SMTP_COMMAND_DATA &&
            (state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        if (smtp_config.decode_mime && state->curr_tx->mime_state) {
            int ret = MimeDecParseLine((const uint8_t *) state->current_line,
                    state->current_line_len, state->current_line_delimiter_len,
                    state->curr_tx->mime_state);
            if (ret != MIME_DEC_OK) {
                SCLogDebug("MimeDecParseLine() function returned an error code: %d", ret);
            }
        }
    }

    return 0;
}

static int SMTPProcessCommandSTARTTLS(SMTPState *state, Flow *f,
                                      AppLayerParserState *pstate)
{
    return 0;
}

static int SMTPProcessReply(SMTPState *state, Flow *f,
                            AppLayerParserState *pstate,
                            SMTPThreadCtx *td)
{
    SCEnter();

    uint64_t reply_code = 0;

    /* the reply code has to contain at least 3 bytes, to hold the 3 digit
     * reply code */
    if (state->current_line_len < 3) {
        /* decoder event */
        SMTPSetEvent(state, SMTP_DECODER_EVENT_INVALID_REPLY);
        return -1;
    }

    if (state->current_line_len >= 4) {
        if (state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY) {
            if (state->current_line[3] != '-') {
                state->parser_state &= ~SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY;
            }
        } else {
            if (state->current_line[3] == '-') {
                state->parser_state |= SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY;
            }
        }
    } else {
        if (state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY) {
            state->parser_state &= ~SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY;
        }
    }

    /* I don't like this pmq reset here.  We'll devise a method later, that
     * should make the use of the mpm very efficient */
    PmqReset(td->pmq);
    int mpm_cnt = mpm_table[SMTP_MPM].Search(smtp_mpm_ctx, td->smtp_mpm_thread_ctx,
                                             td->pmq, state->current_line,
                                             3);
    if (mpm_cnt == 0) {
        /* set decoder event - reply code invalid */
        SMTPSetEvent(state, SMTP_DECODER_EVENT_INVALID_REPLY);
        SCLogDebug("invalid reply code %02x %02x %02x",
                state->current_line[0], state->current_line[1], state->current_line[2]);
        SCReturnInt(-1);
    }
    reply_code = smtp_reply_map[td->pmq->rule_id_array[0]].enum_value;

    if (state->cmds_idx == state->cmds_cnt) {
        if (!(state->parser_state & SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
            /* the first server reply can be a multiline message. Let's
             * flag the fact that we have seen the first reply only at the end
             * of a multiline reply
             */
            if (!(state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY))
                state->parser_state |= SMTP_PARSER_STATE_FIRST_REPLY_SEEN;
            if (reply_code == SMTP_REPLY_220)
                SCReturnInt(0);
            else {
                SMTPSetEvent(state, SMTP_DECODER_EVENT_INVALID_REPLY);
                SCReturnInt(0);
            }
        } else {
            /* decoder event - unable to match reply with request */
            SCLogDebug("unable to match reply with request");
            SCReturnInt(0);
        }
    }

    if (state->cmds_cnt == 0) {
        /* reply but not a command we have stored, fall through */
    } else if (state->cmds[state->cmds_idx] == SMTP_COMMAND_STARTTLS) {
        if (reply_code == SMTP_REPLY_220) {
            /* we are entering STARRTTLS data mode */
            state->parser_state |= SMTP_PARSER_STATE_COMMAND_DATA_MODE;
            AppLayerParserStateSetFlag(pstate,
                                             APP_LAYER_PARSER_NO_INSPECTION |
                                             APP_LAYER_PARSER_NO_REASSEMBLY);
        } else {
            /* decoder event */
            SMTPSetEvent(state, SMTP_DECODER_EVENT_TLS_REJECTED);
        }
    } else if (state->cmds[state->cmds_idx] == SMTP_COMMAND_DATA) {
        if (reply_code == SMTP_REPLY_354) {
            /* Next comes the mail for the DATA command in toserver direction */
            state->parser_state |= SMTP_PARSER_STATE_COMMAND_DATA_MODE;
        } else {
            /* decoder event */
            SMTPSetEvent(state, SMTP_DECODER_EVENT_DATA_COMMAND_REJECTED);
        }
    } else {
        /* we don't care for any other command for now */
        /* check if reply falls in the valid list of replies for SMTP.  If not
         * decoder event */
    }

    /* if it is a multi-line reply, we need to move the index only once for all
     * the line of the reply.  We unset the multiline flag on the last
     * line of the multiline reply, following which we increment the index */
    if (!(state->parser_state & SMTP_PARSER_STATE_PARSING_MULTILINE_REPLY)) {
        state->cmds_idx++;
    }

    /* if we have matched all the buffered commands, reset the cnt and index */
    if (state->cmds_idx == state->cmds_cnt) {
        state->cmds_cnt = 0;
        state->cmds_idx = 0;
    }

    return 0;
}

static int SMTPParseCommandBDAT(SMTPState *state)
{
    SCEnter();

    int i = 4;
    while (i < state->current_line_len) {
        if (state->current_line[i] != ' ') {
            break;
        }
        i++;
    }
    if (i == 4) {
        /* decoder event */
        return -1;
    }
    if (i == state->current_line_len) {
        /* decoder event */
        return -1;
    }
    char *endptr = NULL;
    state->bdat_chunk_len = strtoul((const char *)state->current_line + i,
                                    (char **)&endptr, 10);
    if ((uint8_t *)endptr == state->current_line + i) {
        /* decoder event */
        return -1;
    }

    return 0;
}

static int SMTPParseCommandWithParam(SMTPState *state, uint8_t prefix_len, uint8_t **target, uint16_t *target_len)
{
    int i = prefix_len + 1;
    int spc_i = 0;

    while (i < state->current_line_len) {
        if (state->current_line[i] != ' ') {
            break;
        }
        i++;
    }

    /* rfc1870: with the size extension the mail from can be followed by an option.
       We use the space separator to detect it. */
    spc_i = i;
    while (spc_i < state->current_line_len) {
        if (state->current_line[spc_i] == ' ') {
            break;
        }
        spc_i++;
    }

    *target = SCMalloc(spc_i - i + 1);
    if (*target == NULL)
        return -1;
    memcpy(*target, state->current_line + i, spc_i - i);
    (*target)[spc_i - i] = '\0';
    *target_len = spc_i - i;

    return 0;
}

static int SMTPParseCommandHELO(SMTPState *state)
{
    if (state->helo) {
        SMTPSetEvent(state, SMTP_DECODER_EVENT_DUPLICATE_FIELDS);
        return 0;
    }
    return SMTPParseCommandWithParam(state, 4, &state->helo, &state->helo_len);
}

static int SMTPParseCommandMAILFROM(SMTPState *state)
{
    if (state->curr_tx->mail_from) {
        SMTPSetEvent(state, SMTP_DECODER_EVENT_DUPLICATE_FIELDS);
        return 0;
    }
    return SMTPParseCommandWithParam(state, 9,
                                     &state->curr_tx->mail_from,
                                     &state->curr_tx->mail_from_len);
}

static int SMTPParseCommandRCPTTO(SMTPState *state)
{
    uint8_t *rcptto;
    uint16_t rcptto_len;

    if (SMTPParseCommandWithParam(state, 7, &rcptto, &rcptto_len) == 0) {
        SMTPString *rcptto_str = SMTPStringAlloc();
        if (rcptto_str) {
            rcptto_str->str = rcptto;
            rcptto_str->len = rcptto_len;
            TAILQ_INSERT_TAIL(&state->curr_tx->rcpt_to_list, rcptto_str, next);
        } else {
            SCFree(rcptto);
            return -1;
        }
    } else {
        return -1;
    }
    return 0;
}

/* consider 'rset' and 'quit' to be part of the existing state */
static int NoNewTx(SMTPState *state)
{
    if (!(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        if (state->current_line_len >= 4 &&
            SCMemcmpLowercase("rset", state->current_line, 4) == 0) {
            return 1;
        } else if (state->current_line_len >= 4 &&
            SCMemcmpLowercase("quit", state->current_line, 4) == 0) {
            return 1;
        }
    }
    return 0;
}

static int SMTPProcessRequest(SMTPState *state, Flow *f,
                              AppLayerParserState *pstate)
{
    SCEnter();
    SMTPTransaction *tx = state->curr_tx;

    if (state->curr_tx == NULL || (state->curr_tx->done && !NoNewTx(state))) {
        tx = SMTPTransactionCreate();
        if (tx == NULL)
            return -1;
        state->curr_tx = tx;
        TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
        tx->tx_id = state->tx_cnt++;
    }

    if (!(state->parser_state & SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        SMTPSetEvent(state, SMTP_DECODER_EVENT_NO_SERVER_WELCOME_MESSAGE);
    }

    /* there are 2 commands that can push it into this COMMAND_DATA mode -
     * STARTTLS and DATA */
    if (!(state->parser_state & SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        int r = 0;

        if (state->current_line_len >= 8 &&
            SCMemcmpLowercase("starttls", state->current_line, 8) == 0) {
            state->current_command = SMTP_COMMAND_STARTTLS;
        } else if (state->current_line_len >= 4 &&
                   SCMemcmpLowercase("data", state->current_line, 4) == 0) {
            state->current_command = SMTP_COMMAND_DATA;
            if (smtp_config.decode_mime) {
                if (tx->mime_state) {
                    /* We have 2 chained mails and did not detect the end
                     * of first one. So we start a new transaction. */
                    tx->mime_state->state_flag = PARSE_ERROR;
                    SMTPSetEvent(state, SMTP_DECODER_EVENT_UNPARSABLE_CONTENT);
                    tx = SMTPTransactionCreate();
                    if (tx == NULL)
                        return -1;
                    state->curr_tx = tx;
                    TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
                    tx->tx_id = state->tx_cnt++;
                }
                tx->mime_state = MimeDecInitParser(f, SMTPProcessDataChunk);
                if (tx->mime_state == NULL) {
                    SCLogError(SC_ERR_MEM_ALLOC, "MimeDecInitParser() failed to "
                            "allocate data");
                    return MIME_DEC_ERR_MEM;
                }

                /* Add new MIME message to end of list */
                if (tx->msg_head == NULL) {
                    tx->msg_head = tx->mime_state->msg;
                    tx->msg_tail = tx->mime_state->msg;
                }
                else {
                    tx->msg_tail->next = tx->mime_state->msg;
                    tx->msg_tail = tx->mime_state->msg;
                }
            }

        } else if (state->current_line_len >= 4 &&
                   SCMemcmpLowercase("bdat", state->current_line, 4) == 0) {
            r = SMTPParseCommandBDAT(state);
            if (r == -1) {
                SCReturnInt(-1);
            }
            state->current_command = SMTP_COMMAND_BDAT;
            state->parser_state |= SMTP_PARSER_STATE_COMMAND_DATA_MODE;
        } else if (state->current_line_len >= 4 &&
                   ((SCMemcmpLowercase("helo", state->current_line, 4) == 0) ||
                    SCMemcmpLowercase("ehlo", state->current_line, 4) == 0))  {
            r = SMTPParseCommandHELO(state);
            if (r == -1) {
                SCReturnInt(-1);
            }
            state->current_command = SMTP_COMMAND_OTHER_CMD;
        } else if (state->current_line_len >= 9 &&
                   SCMemcmpLowercase("mail from", state->current_line, 9) == 0) {
            r = SMTPParseCommandMAILFROM(state);
            if (r == -1) {
                SCReturnInt(-1);
            }
            state->current_command = SMTP_COMMAND_OTHER_CMD;
        } else if (state->current_line_len >= 7 &&
                   SCMemcmpLowercase("rcpt to", state->current_line, 7) == 0) {
            r = SMTPParseCommandRCPTTO(state);
            if (r == -1) {
                SCReturnInt(-1);
            }
            state->current_command = SMTP_COMMAND_OTHER_CMD;
        } else {
            state->current_command = SMTP_COMMAND_OTHER_CMD;
        }

        /* Every command is inserted into a command buffer, to be matched
         * against reply(ies) sent by the server */
        if (SMTPInsertCommandIntoCommandBuffer(state->current_command,
                                               state, f) == -1) {
            SCReturnInt(-1);
        }

        SCReturnInt(r);
    }

    switch (state->current_command) {
        case SMTP_COMMAND_STARTTLS:
            return SMTPProcessCommandSTARTTLS(state, f, pstate);

        case SMTP_COMMAND_DATA:
            return SMTPProcessCommandDATA(state, f, pstate);

        case SMTP_COMMAND_BDAT:
            return SMTPProcessCommandBDAT(state, f, pstate);

        default:
            /* we have nothing to do with any other command at this instant.
             * Just let it go through */
            SCReturnInt(0);
    }
}

static int SMTPParse(int direction, Flow *f, SMTPState *state,
                     AppLayerParserState *pstate, uint8_t *input,
                     uint32_t input_len,
                     SMTPThreadCtx *thread_data)
{
    SCEnter();

    if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
        SCReturnInt(1);
    } else if (input == NULL || input_len == 0) {
        SCReturnInt(-1);
    }

    state->input = input;
    state->input_len = input_len;
    state->direction = direction;

    /* toserver */
    if (direction == 0) {
        while (SMTPGetLine(state) >= 0) {
            if (SMTPProcessRequest(state, f, pstate) == -1)
                SCReturnInt(-1);
        }

        /* toclient */
    } else {
        while (SMTPGetLine(state) >= 0) {
            if (SMTPProcessReply(state, f, pstate, thread_data) == -1)
                SCReturnInt(-1);
        }
    }

    SCReturnInt(0);
}

static int SMTPParseClientRecord(Flow *f, void *alstate,
                                 AppLayerParserState *pstate,
                                 uint8_t *input, uint32_t input_len,
                                 void *local_data)
{
    SCEnter();

    /* first arg 0 is toserver */
    return SMTPParse(0, f, alstate, pstate, input, input_len, local_data);
}

static int SMTPParseServerRecord(Flow *f, void *alstate,
                                 AppLayerParserState *pstate,
                                 uint8_t *input, uint32_t input_len,
                                 void *local_data)
{
    SCEnter();

    /* first arg 1 is toclient */
    return SMTPParse(1, f, alstate, pstate, input, input_len, local_data);

    return 0;
}

/**
 * \internal
 * \brief Function to allocate SMTP state memory.
 */
void *SMTPStateAlloc(void)
{
    SMTPState *smtp_state = SCMalloc(sizeof(SMTPState));
    if (unlikely(smtp_state == NULL))
        return NULL;
    memset(smtp_state, 0, sizeof(SMTPState));

    smtp_state->cmds = SCMalloc(sizeof(uint8_t) *
                                SMTP_COMMAND_BUFFER_STEPS);
    if (smtp_state->cmds == NULL) {
        SCFree(smtp_state);
        return NULL;
    }
    smtp_state->cmds_buffer_len = SMTP_COMMAND_BUFFER_STEPS;

    TAILQ_INIT(&smtp_state->tx_list);

    return smtp_state;
}

static SMTPString *SMTPStringAlloc(void)
{
    SMTPString *smtp_string = SCMalloc(sizeof(SMTPString));
    if (unlikely(smtp_string == NULL))
        return NULL;
    memset(smtp_string, 0, sizeof(SMTPString));

    return smtp_string;
}


static void SMTPStringFree(SMTPString *str)
{
    if (str->str) {
        SCFree(str->str);
    }
    SCFree(str);
}

static void *SMTPLocalStorageAlloc(void)
{
    /* needed by the mpm */
    SMTPThreadCtx *td = SCCalloc(1, sizeof(*td));
    if (td == NULL) {
        exit(EXIT_FAILURE);
    }

    td->pmq = SCCalloc(1, sizeof(*td->pmq));
    if (td->pmq == NULL) {
        exit(EXIT_FAILURE);
    }
    PmqSetup(td->pmq);

    td->smtp_mpm_thread_ctx = SCCalloc(1, sizeof(MpmThreadCtx));
    if (unlikely(td->smtp_mpm_thread_ctx == NULL)) {
        exit(EXIT_FAILURE);
    }
    MpmInitThreadCtx(td->smtp_mpm_thread_ctx, SMTP_MPM);
    return td;
}

static void SMTPLocalStorageFree(void *ptr)
{
    SMTPThreadCtx *td = ptr;
    if (td != NULL) {
        if (td->pmq != NULL) {
            PmqFree(td->pmq);
            SCFree(td->pmq);
        }

        if (td->smtp_mpm_thread_ctx != NULL) {
            mpm_table[SMTP_MPM].DestroyThreadCtx(smtp_mpm_ctx, td->smtp_mpm_thread_ctx);
            SCFree(td->smtp_mpm_thread_ctx);
        }

        SCFree(td);
    }

    return;
}

static void SMTPTransactionFree(SMTPTransaction *tx, SMTPState *state)
{
    if (tx->mime_state != NULL) {
        MimeDecDeInitParser(tx->mime_state);
    }
    /* Free list of MIME message recursively */
    MimeDecFreeEntity(tx->msg_head);

    if (tx->decoder_events != NULL)
        AppLayerDecoderEventsFreeEvents(&tx->decoder_events);

    if (tx->de_state != NULL)
        DetectEngineStateFree(tx->de_state);

    if (tx->mail_from)
        SCFree(tx->mail_from);

    SMTPString *str = NULL;
    while ((str = TAILQ_FIRST(&tx->rcpt_to_list))) {
        TAILQ_REMOVE(&tx->rcpt_to_list, str, next);
        SMTPStringFree(str);
    }
#if 0
        if (tx->decoder_events->cnt <= smtp_state->events)
            smtp_state->events -= tx->decoder_events->cnt;
        else
            smtp_state->events = 0;
#endif
    SCFree(tx);
}

/**
 * \internal
 * \brief Function to free SMTP state memory.
 */
static void SMTPStateFree(void *p)
{
    SMTPState *smtp_state = (SMTPState *)p;

    if (smtp_state->cmds != NULL) {
        SCFree(smtp_state->cmds);
    }
    if (smtp_state->ts_current_line_db) {
        SCFree(smtp_state->ts_db);
    }
    if (smtp_state->tc_current_line_db) {
        SCFree(smtp_state->tc_db);
    }

    if (smtp_state->helo) {
        SCFree(smtp_state->helo);
    }

    FileContainerFree(smtp_state->files_ts);

    SMTPTransaction *tx = NULL;
    while ((tx = TAILQ_FIRST(&smtp_state->tx_list))) {
        TAILQ_REMOVE(&smtp_state->tx_list, tx, next);
        SMTPTransactionFree(tx, smtp_state);
    }

    SCFree(smtp_state);

    return;
}

static void SMTPSetMpmState(void)
{
    smtp_mpm_ctx = SCMalloc(sizeof(MpmCtx));
    if (unlikely(smtp_mpm_ctx == NULL)) {
        exit(EXIT_FAILURE);
    }
    memset(smtp_mpm_ctx, 0, sizeof(MpmCtx));
    MpmInitCtx(smtp_mpm_ctx, SMTP_MPM);

    uint32_t i = 0;
    for (i = 0; i < sizeof(smtp_reply_map)/sizeof(SCEnumCharMap) - 1; i++) {
        SCEnumCharMap *map = &smtp_reply_map[i];
        /* The third argument is 3, because reply code is always 3 bytes. */
        MpmAddPatternCI(smtp_mpm_ctx, (uint8_t *)map->enum_name, 3,
                        0 /* defunct */, 0 /* defunct */,
                        i /* pattern id */, i /* rule id */ , 0 /* no flags */);
    }

    mpm_table[SMTP_MPM].Prepare(smtp_mpm_ctx);

}

static void SMTPFreeMpmState(void)
{
    if (smtp_mpm_ctx != NULL) {
        mpm_table[SMTP_MPM].DestroyCtx(smtp_mpm_ctx);
        SCFree(smtp_mpm_ctx);
        smtp_mpm_ctx = NULL;
    }
}

int SMTPStateGetEventInfo(const char *event_name,
                          int *event_id, AppLayerEventType *event_type)
{
    *event_id = SCMapEnumNameToValue(event_name, smtp_decoder_event_table);
    if (*event_id == -1) {
        SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
                   "smtp's enum map table.",  event_name);
        /* yes this is fatal */
        return -1;
    }

    *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;

    return 0;
}

static int SMTPRegisterPatternsForProtocolDetection(void)
{
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMTP,
                                               "EHLO", 4, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMTP,
                                               "HELO", 4, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMTP,
                                               "QUIT", 4, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }

    return 0;
}

static void SMTPStateTransactionFree (void *state, uint64_t tx_id)
{
    SMTPState *smtp_state = state;
    SMTPTransaction *tx = NULL;
    TAILQ_FOREACH(tx, &smtp_state->tx_list, next) {
        if (tx_id < tx->tx_id)
            break;
        else if (tx_id > tx->tx_id)
            continue;

        if (tx == smtp_state->curr_tx)
            smtp_state->curr_tx = NULL;
        TAILQ_REMOVE(&smtp_state->tx_list, tx, next);
        SMTPTransactionFree(tx, state);
        break;
    }


}

/** \retval cnt highest tx id */
static uint64_t SMTPStateGetTxCnt(void *state)
{
    uint64_t cnt = 0;
    SMTPState *smtp_state = state;
    if (smtp_state) {
        cnt = smtp_state->tx_cnt;
    }
    SCLogDebug("returning %"PRIu64, cnt);
    return cnt;
}

static void *SMTPStateGetTx(void *state, uint64_t id)
{
    SMTPState *smtp_state = state;
    if (smtp_state) {
        SMTPTransaction *tx = NULL;

        if (smtp_state->curr_tx == NULL)
            return NULL;
        if (smtp_state->curr_tx->tx_id == id)
            return smtp_state->curr_tx;

        TAILQ_FOREACH(tx, &smtp_state->tx_list, next) {
            if (tx->tx_id == id)
                return tx;
        }
    }
    return NULL;

}

static void SMTPStateSetTxLogged(void *state, void *vtx, uint32_t logger)
{
    SMTPTransaction *tx = vtx;
    tx->logged |= logger;
}

static int SMTPStateGetTxLogged(void *state, void *vtx, uint32_t logger)
{
    SMTPTransaction *tx = vtx;
    if (tx->logged & logger)
        return 1;

    return 0;
}

static int SMTPStateGetAlstateProgressCompletionStatus(uint8_t direction) {
    return 1;
}

static int SMTPStateGetAlstateProgress(void *vtx, uint8_t direction)
{
    SMTPTransaction *tx = vtx;
    return tx->done;
}

static FileContainer *SMTPStateGetFiles(void *state, uint8_t direction)
{
    if (state == NULL)
        return NULL;

    SMTPState *smtp_state = (SMTPState *)state;

    if (direction & STREAM_TOCLIENT) {
        SCReturnPtr(NULL, "FileContainer");
    } else {
        SCLogDebug("smtp_state->files_ts %p", smtp_state->files_ts);
        SCReturnPtr(smtp_state->files_ts, "FileContainer");
    }
}

static void SMTPStateTruncate(void *state, uint8_t direction)
{
    FileContainer *fc = SMTPStateGetFiles(state, direction);
    if (fc != NULL) {
        SCLogDebug("truncating stream, closing files in %s direction (container %p)",
                direction & STREAM_TOCLIENT ? "STREAM_TOCLIENT" : "STREAM_TOSERVER", fc);
        FileTruncateAllOpenFiles(fc);
    }
}

static AppLayerDecoderEvents *SMTPGetEvents(void *state, uint64_t tx_id)
{
    SCLogDebug("get SMTP events for TX %"PRIu64, tx_id);

    SMTPTransaction *tx = SMTPStateGetTx(state, tx_id);
    if (tx != NULL) {
        return tx->decoder_events;
    }
    return NULL;
}

static DetectEngineState *SMTPGetTxDetectState(void *vtx)
{
    SMTPTransaction *tx = (SMTPTransaction *)vtx;
    return tx->de_state;
}

static int SMTPSetTxDetectState(void *state, void *vtx, DetectEngineState *s)
{
    SMTPTransaction *tx = (SMTPTransaction *)vtx;
    tx->de_state = s;
    return 0;
}

/**
 * \brief Register the SMTP Protocol parser.
 */
void RegisterSMTPParsers(void)
{
    char *proto_name = "smtp";

    if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
        AppLayerProtoDetectRegisterProtocol(ALPROTO_SMTP, proto_name);
        if (SMTPRegisterPatternsForProtocolDetection() < 0 )
            return;
    } else {
        SCLogInfo("Protocol detection and parser disabled for %s protocol.",
                  proto_name);
        return;
    }

    if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
        AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateAlloc, SMTPStateFree);

        AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMTP, STREAM_TOSERVER,
                                     SMTPParseClientRecord);
        AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMTP, STREAM_TOCLIENT,
                                     SMTPParseServerRecord);

        AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetEventInfo);
        AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPGetEvents);
        AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_SMTP, NULL,
                                               SMTPGetTxDetectState, SMTPSetTxDetectState);

        AppLayerParserRegisterLocalStorageFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPLocalStorageAlloc,
                                               SMTPLocalStorageFree);

        AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateTransactionFree);
        AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetFiles);
        AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetAlstateProgress);
        AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetTxCnt);
        AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetTx);
        AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetTxLogged,
                                          SMTPStateSetTxLogged);
        AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_SMTP,
                                                               SMTPStateGetAlstateProgressCompletionStatus);
        AppLayerParserRegisterTruncateFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateTruncate);
    } else {
        SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
                  "still on.", proto_name);
    }

    SMTPSetMpmState();

    SMTPConfigure();

#ifdef UNITTESTS
    AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SMTP, SMTPParserRegisterTests);
#endif
    return;
}

/**
 * \brief Free memory allocated for global SMTP parser state.
 */
void SMTPParserCleanup(void)
{
    SMTPFreeMpmState();
}

/***************************************Unittests******************************/

#ifdef UNITTESTS

static void SMTPTestInitConfig(void)
{
    MimeDecSetConfig(&smtp_config.mime_config);

    smtp_config.content_limit = FILEDATA_CONTENT_LIMIT;
    smtp_config.content_inspect_window = FILEDATA_CONTENT_INSPECT_WINDOW;
    smtp_config.content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE;

    smtp_config.sbcfg.buf_size = FILEDATA_CONTENT_INSPECT_WINDOW;
}

/*
 * \test Test STARTTLS.
 */
static int SMTPParserTest01(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    /* 220 mx.google.com ESMTP d15sm986283wfl.6<CR><LF> */
    uint8_t welcome_reply[] = {
        0x32, 0x32, 0x30, 0x20, 0x6d, 0x78, 0x2e, 0x67,
        0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
        0x6d, 0x20, 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20,
        0x64, 0x31, 0x35, 0x73, 0x6d, 0x39, 0x38, 0x36,
        0x32, 0x38, 0x33, 0x77, 0x66, 0x6c, 0x2e, 0x36,
        0x0d, 0x0a
    };
    uint32_t welcome_reply_len = sizeof(welcome_reply);

    /* EHLO [192.168.0.158]<CR><LF> */
    uint8_t request1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x5b, 0x31, 0x39,
        0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x30, 0x2e,
        0x31, 0x35, 0x38, 0x5d, 0x0d, 0x0a
    };
    uint32_t request1_len = sizeof(request1);
    /* 250-mx.google.com at your service, [117.198.115.50]<CR><LF>
     * 250-SIZE 35882577<CR><LF>
     * 250-8BITMIME<CR><LF>
     * 250-STARTTLS<CR><LF>
     * 250 ENHANCEDSTATUSCODES<CR><LF>
     */
    uint8_t reply1[] = {
        0x32, 0x35, 0x30, 0x2d, 0x6d, 0x78, 0x2e, 0x67,
        0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
        0x6d, 0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75,
        0x72, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
        0x65, 0x2c, 0x20, 0x5b, 0x31, 0x31, 0x37, 0x2e,
        0x31, 0x39, 0x38, 0x2e, 0x31, 0x31, 0x35, 0x2e,
        0x35, 0x30, 0x5d, 0x0d, 0x0a, 0x32, 0x35, 0x30,
        0x2d, 0x53, 0x49, 0x5a, 0x45, 0x20, 0x33, 0x35,
        0x38, 0x38, 0x32, 0x35, 0x37, 0x37, 0x0d, 0x0a,
        0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49, 0x54,
        0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32, 0x35,
        0x30, 0x2d, 0x53, 0x54, 0x41, 0x52, 0x54, 0x54,
        0x4c, 0x53, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x20,
        0x45, 0x4e, 0x48, 0x41, 0x4e, 0x43, 0x45, 0x44,
        0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x43, 0x4f,
        0x44, 0x45, 0x53, 0x0d, 0x0a
    };
    uint32_t reply1_len = sizeof(reply1);

    /* STARTTLS<CR><LF> */
    uint8_t request2[] = {
        0x53, 0x54, 0x41, 0x52, 0x54, 0x54, 0x4c, 0x53,
        0x0d, 0x0a
    };
    uint32_t request2_len = sizeof(request2);
    /* 220 2.0.0 Ready to start TLS<CR><LF> */
    uint8_t reply2[] = {
        0x32, 0x32, 0x30, 0x20, 0x32, 0x2e, 0x30, 0x2e,
        0x30, 0x20, 0x52, 0x65, 0x61, 0x64, 0x79, 0x20,
        0x74, 0x6f, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74,
        0x20, 0x54, 0x4c, 0x53, 0x0d, 0x0a
    };
    uint32_t reply2_len = sizeof(reply2);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, welcome_reply, welcome_reply_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1, request1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply1, reply1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_STARTTLS ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply2, reply2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    if (!(f.flags & FLOW_NOPAYLOAD_INSPECTION) ||
        !(ssn.flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
        !(((TcpSession *)f.protoctx)->server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
        !(((TcpSession *)f.protoctx)->client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/**
 * \test Test multiple DATA commands(full mail transactions).
 */
static int SMTPParserTest02(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    /* 220 mx.google.com ESMTP d15sm986283wfl.6<CR><LF> */
    uint8_t welcome_reply[] = {
        0x32, 0x32, 0x30, 0x20, 0x6d, 0x78, 0x2e, 0x67,
        0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
        0x6d, 0x20, 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20,
        0x64, 0x31, 0x35, 0x73, 0x6d, 0x39, 0x38, 0x36,
        0x32, 0x38, 0x33, 0x77, 0x66, 0x6c, 0x2e, 0x36,
        0x0d, 0x0a
    };
    uint32_t welcome_reply_len = sizeof(welcome_reply);

    /* EHLO boo.com<CR><LF> */
    uint8_t request1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a
    };
    uint32_t request1_len = sizeof(request1);
    /* 250-mx.google.com at your service, [117.198.115.50]<CR><LF>
     * 250-SIZE 35882577<CR><LF>
     * 250-8BITMIME<CR><LF>
     * 250-STARTTLS<CR><LF>
     * 250 ENHANCEDSTATUSCODES<CR><LF>
     */
    uint8_t reply1[] = {
        0x32, 0x35, 0x30, 0x2d, 0x70, 0x6f, 0x6f, 0x6e,
        0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
        0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
        0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x50, 0x49, 0x50,
        0x45, 0x4c, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53, 0x49, 0x5a,
        0x45, 0x20, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30,
        0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
        0x56, 0x52, 0x46, 0x59, 0x0d, 0x0a, 0x32, 0x35,
        0x30, 0x2d, 0x45, 0x54, 0x52, 0x4e, 0x0d, 0x0a,
        0x32, 0x35, 0x30, 0x2d, 0x45, 0x4e, 0x48, 0x41,
        0x4e, 0x43, 0x45, 0x44, 0x53, 0x54, 0x41, 0x54,
        0x55, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x53, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49,
        0x54, 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32,
        0x35, 0x30, 0x20, 0x44, 0x53, 0x4e, 0x0d, 0x0a
    };
    uint32_t reply1_len = sizeof(reply1);

    /* MAIL FROM:asdff@asdf.com<CR><LF> */
    uint8_t request2[] = {
        0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
        0x4d, 0x3a, 0x61, 0x73, 0x64, 0x66, 0x66, 0x40,
        0x61, 0x73, 0x64, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
        0x0d, 0x0a
    };
    uint32_t request2_len = sizeof(request2);
    /* 250 2.1.0 Ok<CR><LF> */
    uint8_t reply2[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
        0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
    };
    uint32_t reply2_len = sizeof(reply2);

    /* RCPT TO:bimbs@gmail.com<CR><LF> */
    uint8_t request3[] = {
        0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a,
        0x62, 0x69, 0x6d, 0x62, 0x73, 0x40, 0x67, 0x6d,
        0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
        0x0a
    };
    uint32_t request3_len = sizeof(request3);
    /* 250 2.1.5 Ok<CR><LF> */
    uint8_t reply3[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
        0x35, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
    };
    uint32_t reply3_len = sizeof(reply3);

    /* DATA<CR><LF> */
    uint8_t request4[] = {
        0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a
    };
    uint32_t request4_len = sizeof(request4);
    /* 354 End data with <CR><LF>.<CR><LF>|<CR><LF>| */
    uint8_t reply4[] = {
        0x33, 0x35, 0x34, 0x20, 0x45, 0x6e, 0x64, 0x20,
        0x64, 0x61, 0x74, 0x61, 0x20, 0x77, 0x69, 0x74,
        0x68, 0x20, 0x3c, 0x43, 0x52, 0x3e, 0x3c, 0x4c,
        0x46, 0x3e, 0x2e, 0x3c, 0x43, 0x52, 0x3e, 0x3c,
        0x4c, 0x46, 0x3e, 0x0d, 0x0a
    };
    uint32_t reply4_len = sizeof(reply4);

    /* FROM:asdff@asdf.com<CR><LF> */
    uint8_t request5_1[] = {
        0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x61, 0x73, 0x64,
        0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x66, 0x2e,
        0x63, 0x6f, 0x6d, 0x0d, 0x0a
    };
    uint32_t request5_1_len = sizeof(request5_1);
    /* TO:bimbs@gmail.com<CR><LF> */
    uint8_t request5_2[] = {
        0x54, 0x4f, 0x3a, 0x62, 0x69, 0x6d, 0x62, 0x73,
        0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63,
        0x6f, 0x6d, 0x0d, 0x0a
    };
    uint32_t request5_2_len = sizeof(request5_2);
    /* <CR><LF> */
    uint8_t request5_3[] = {
        0x0d, 0x0a
    };
    uint32_t request5_3_len = sizeof(request5_3);
    /* this is test mail1<CR><LF> */
    uint8_t request5_4[] = {
        0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
        0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x61, 0x69,
        0x6c, 0x31, 0x0d, 0x0a
    };
    uint32_t request5_4_len = sizeof(request5_4);
    /* .<CR><LF> */
    uint8_t request5_5[] = {
        0x2e, 0x0d, 0x0a
    };
    uint32_t request5_5_len = sizeof(request5_5);
    /* 250 2.0.0 Ok: queued as 6A1AF20BF2<CR><LF> */
    uint8_t reply5[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x30, 0x2e,
        0x30, 0x20, 0x4f, 0x6b, 0x3a, 0x20, 0x71, 0x75,
        0x65, 0x75, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20,
        0x36, 0x41, 0x31, 0x41, 0x46, 0x32, 0x30, 0x42,
        0x46, 0x32, 0x0d, 0x0a
    };
    uint32_t reply5_len = sizeof(reply5);

    /* MAIL FROM:asdfg@asdf.com<CR><LF> */
    uint8_t request6[] = {
        0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
        0x4d, 0x3a, 0x61, 0x73, 0x64, 0x66, 0x67, 0x40,
        0x61, 0x73, 0x64, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
        0x0d, 0x0a
    };
    uint32_t request6_len = sizeof(request6);
    /* 250 2.1.0 Ok<CR><LF> */
    uint8_t reply6[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
        0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
    };
    uint32_t reply6_len = sizeof(reply6);

    /* RCPT TO:bimbs@gmail.com<CR><LF> */
    uint8_t request7[] = {
        0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a,
        0x62, 0x69, 0x6d, 0x62, 0x73, 0x40, 0x67, 0x6d,
        0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
        0x0a
    };
    uint32_t request7_len = sizeof(request7);
    /* 250 2.1.5 Ok<CR><LF> */
    uint8_t reply7[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
        0x35, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
    };
    uint32_t reply7_len = sizeof(reply7);

    /* DATA<CR><LF> */
    uint8_t request8[] = {
        0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a
    };
    uint32_t request8_len = sizeof(request8);
    /* 354 End data with <CR><LF>.<CR><LF>|<CR><LF>| */
    uint8_t reply8[] = {
        0x33, 0x35, 0x34, 0x20, 0x45, 0x6e, 0x64, 0x20,
        0x64, 0x61, 0x74, 0x61, 0x20, 0x77, 0x69, 0x74,
        0x68, 0x20, 0x3c, 0x43, 0x52, 0x3e, 0x3c, 0x4c,
        0x46, 0x3e, 0x2e, 0x3c, 0x43, 0x52, 0x3e, 0x3c,
        0x4c, 0x46, 0x3e, 0x0d, 0x0a
    };
    uint32_t reply8_len = sizeof(reply8);

    /* FROM:asdfg@gmail.com<CR><LF> */
    uint8_t request9_1[] = {
        0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x61, 0x73, 0x64,
        0x66, 0x67, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a
    };
    uint32_t request9_1_len = sizeof(request9_1);
    /* TO:bimbs@gmail.com<CR><LF> */
    uint8_t request9_2[] = {
        0x54, 0x4f, 0x3a, 0x62, 0x69, 0x6d, 0x62, 0x73,
        0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63,
        0x6f, 0x6d, 0x0d, 0x0a
    };
    uint32_t request9_2_len = sizeof(request9_2);
    /* <CR><LF> */
    uint8_t request9_3[] = {
        0x0d, 0x0a
    };
    uint32_t request9_3_len = sizeof(request9_3);
    /* this is test mail2<CR><LF> */
    uint8_t request9_4[] = {
        0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
        0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x61, 0x69,
        0x6c, 0x32, 0x0d, 0x0a
    };
    uint32_t request9_4_len = sizeof(request9_4);
    /* .<CR><LF> */
    uint8_t request9_5[] = {
        0x2e, 0x0d, 0x0a
    };
    uint32_t request9_5_len = sizeof(request9_5);
    /* 250 2.0.0 Ok: queued as 28CFF20BF2<CR><LF> */
    uint8_t reply9[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x30, 0x2e,
        0x30, 0x20, 0x4f, 0x6b, 0x3a, 0x20, 0x71, 0x75,
        0x65, 0x75, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20,
        0x32, 0x38, 0x43, 0x46, 0x46, 0x32, 0x30, 0x42,
        0x46, 0x32, 0x0d, 0x0a
    };
    uint32_t reply9_len = sizeof(reply9);

    /* QUIT<CR><LF> */
    uint8_t request10[] = {
        0x51, 0x55, 0x49, 0x54, 0x0d, 0x0a
    };
    uint32_t request10_len = sizeof(request10);
    /* 221 2.0.0 Bye<CR><LF> */
    uint8_t reply10[] = {
        0x32, 0x32, 0x31, 0x20, 0x32, 0x2e, 0x30, 0x2e,
        0x30, 0x20, 0x42, 0x79, 0x65, 0x0d, 0x0a
    };
    uint32_t reply10_len = sizeof(reply10);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, welcome_reply, welcome_reply_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1, request1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply1, reply1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply2, reply2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request3, request3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply3, reply3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request4, request4_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_DATA ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply4, reply4_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request5_1, request5_1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request5_2, request5_2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request5_3, request5_3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request5_4, request5_4_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request5_5, request5_5_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_DATA_MODE ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply5, reply5_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request6, request6_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply6, reply6_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request7, request7_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply7, reply7_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request8, request8_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_DATA ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply8, reply8_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request9_1, request9_1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request9_2, request9_2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request9_3, request9_3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request9_4, request9_4_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {

        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request9_5, request9_5_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_DATA_MODE ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply9, reply9_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request10, request10_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply10, reply10_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/**
 * \test Testing parsing pipelined commands.
 */
static int SMTPParserTest03(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    /* 220 poona_slack_vm1.localdomain ESMTP Postfix<CR><LF> */
    uint8_t welcome_reply[] = {
        0x32, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x6f, 0x6e,
        0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
        0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
        0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
        0x45, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x50, 0x6f,
        0x73, 0x74, 0x66, 0x69, 0x78, 0x0d, 0x0a
    };
    uint32_t welcome_reply_len = sizeof(welcome_reply);

    /* EHLO boo.com<CR><LF> */
    uint8_t request1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0a
    };
    uint32_t request1_len = sizeof(request1);
    /* 250-poona_slack_vm1.localdomain<CR><LF>
     * 250-PIPELINING<CR><LF>
     * 250-SIZE 10240000<CR><LF>
     * 250-VRFY<CR><LF>
     * 250-ETRN<CR><LF>
     * 250-ENHANCEDSTATUSCODES<CR><LF>
     * 250-8BITMIME<CR><LF>
     * 250 DSN<CR><LF>
     */
    uint8_t reply1[] = {
        0x32, 0x35, 0x30, 0x2d, 0x70, 0x6f, 0x6f, 0x6e,
        0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
        0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
        0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x50, 0x49, 0x50,
        0x45, 0x4c, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53, 0x49, 0x5a,
        0x45, 0x20, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30,
        0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
        0x56, 0x52, 0x46, 0x59, 0x0d, 0x0a, 0x32, 0x35,
        0x30, 0x2d, 0x45, 0x54, 0x52, 0x4e, 0x0d, 0x0a,
        0x32, 0x35, 0x30, 0x2d, 0x45, 0x4e, 0x48, 0x41,
        0x4e, 0x43, 0x45, 0x44, 0x53, 0x54, 0x41, 0x54,
        0x55, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x53, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49,
        0x54, 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32,
        0x35, 0x30, 0x20, 0x44, 0x53, 0x4e, 0x0d, 0x0a
    };
    uint32_t reply1_len = sizeof(reply1);

    /* MAIL FROM:pbsf@asdfs.com<CR><LF>
     * RCPT TO:pbsf@asdfs.com<CR><LF>
     * DATA<CR><LF>
     */
    uint8_t request2[] = {
        0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
        0x4d, 0x3a, 0x70, 0x62, 0x73, 0x66, 0x40, 0x61,
        0x73, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
        0x0d, 0x0a, 0x52, 0x43, 0x50, 0x54, 0x20, 0x54,
        0x4f, 0x3a, 0x70, 0x62, 0x73, 0x66, 0x40, 0x61,
        0x73, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
        0x0d, 0x0a, 0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a
    };
    uint32_t request2_len = sizeof(request2);
    /* 250 2.1.0 Ok<CR><LF>
     * 250 2.1.5 Ok<CR><LF>
     * 354 End data with <CR><LF>.<CR><LF>|<CR><LF>|
     */
    uint8_t reply2[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
        0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a, 0x32, 0x35,
        0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e, 0x35, 0x20,
        0x4f, 0x6b, 0x0d, 0x0a, 0x33, 0x35, 0x34, 0x20,
        0x45, 0x6e, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61,
        0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x3c, 0x43,
        0x52, 0x3e, 0x3c, 0x4c, 0x46, 0x3e, 0x2e, 0x3c,
        0x43, 0x52, 0x3e, 0x3c, 0x4c, 0x46, 0x3e, 0x0d,
        0x0a
    };
    uint32_t reply2_len = sizeof(reply2);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, welcome_reply, welcome_reply_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1, request1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply1, reply1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 3 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->cmds[1] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->cmds[2] != SMTP_COMMAND_DATA ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply2, reply2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/*
 * \test Test smtp with just <LF> delimter instead of <CR><LF>.
 */
static int SMTPParserTest04(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    /* 220 poona_slack_vm1.localdomain ESMTP Postfix<CR><LF> */
    uint8_t welcome_reply[] = {
        0x32, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x6f, 0x6e,
        0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
        0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
        0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
        0x45, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x50, 0x6f,
        0x73, 0x74, 0x66, 0x69, 0x78, 0x0d, 0x0a
    };
    uint32_t welcome_reply_len = sizeof(welcome_reply);

    /* EHLO boo.com<CR><LF> */
    uint8_t request1[] = {
        0x32, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x6f, 0x6e,
        0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
        0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
        0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
        0x45, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x50, 0x6f,
        0x73, 0x74, 0x66, 0x69, 0x78, 0x0d, 0x0a
    };
    uint32_t request1_len = sizeof(request1);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, welcome_reply, welcome_reply_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1, request1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/*
 * \test Test STARTTLS fail.
 */
static int SMTPParserTest05(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    /* 220 poona_slack_vm1.localdomain ESMTP Postfix<CR><LF> */
    uint8_t welcome_reply[] = {
        0x32, 0x32, 0x30, 0x20, 0x70, 0x6f, 0x6f, 0x6e,
        0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
        0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
        0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20,
        0x45, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x50, 0x6f,
        0x73, 0x74, 0x66, 0x69, 0x78, 0x0d, 0x0a
    };
    uint32_t welcome_reply_len = sizeof(welcome_reply);

    /* EHLO boo.com<CR><LF> */
    uint8_t request1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a
    };
    uint32_t request1_len = sizeof(request1);
    /* 250-poona_slack_vm1.localdomain<CR><LF>
     * 250-PIPELINING<CR><LF>
     * 250-SIZE 10240000<CR><LF>
     * 250-VRFY<CR><LF>
     * 250-ETRN<CR><LF>
     * 250-ENHANCEDSTATUSCODES<CR><LF>
     * 250-8BITMIME<CR><LF>
     * 250 DSN<CR><LF>
     */
    uint8_t reply1[] = {
        0x32, 0x35, 0x30, 0x2d, 0x70, 0x6f, 0x6f, 0x6e,
        0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
        0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
        0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x50, 0x49, 0x50,
        0x45, 0x4c, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53, 0x49, 0x5a,
        0x45, 0x20, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30,
        0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
        0x56, 0x52, 0x46, 0x59, 0x0d, 0x0a, 0x32, 0x35,
        0x30, 0x2d, 0x45, 0x54, 0x52, 0x4e, 0x0d, 0x0a,
        0x32, 0x35, 0x30, 0x2d, 0x45, 0x4e, 0x48, 0x41,
        0x4e, 0x43, 0x45, 0x44, 0x53, 0x54, 0x41, 0x54,
        0x55, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x53, 0x0d,
        0x0a, 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49,
        0x54, 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32,
        0x35, 0x30, 0x20, 0x44, 0x53, 0x4e, 0x0d, 0x0a
    };
    uint32_t reply1_len = sizeof(reply1);

    /* STARTTLS<CR><LF> */
    uint8_t request2[] = {
        0x53, 0x54, 0x41, 0x52, 0x54, 0x54, 0x4c, 0x53,
        0x0d, 0x0a
    };
    uint32_t request2_len = sizeof(request2);
    /* 502 5.5.2 Error: command not recognized<CR><LF> */
    uint8_t reply2[] = {
        0x35, 0x30, 0x32, 0x20, 0x35, 0x2e, 0x35, 0x2e,
        0x32, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x3a,
        0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
        0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x63,
        0x6f, 0x67, 0x6e, 0x69, 0x7a, 0x65, 0x64, 0x0d,
        0x0a
    };
    uint32_t reply2_len = sizeof(reply2);

    /* QUIT<CR><LF> */
    uint8_t request3[] = {
        0x51, 0x55, 0x49, 0x54, 0x0d, 0x0a

    };
    uint32_t request3_len = sizeof(request3);
    /* 221 2.0.0 Bye<CR><LF> */
    uint8_t reply3[] = {
        0x32, 0x32, 0x31, 0x20, 0x32, 0x2e, 0x30, 0x2e,
        0x30, 0x20, 0x42, 0x79, 0x65, 0x0d, 0x0a
    };
    uint32_t reply3_len = sizeof(reply3);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, welcome_reply, welcome_reply_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1, request1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply1, reply1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_STARTTLS ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply2, reply2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    if ((f.flags & FLOW_NOPAYLOAD_INSPECTION) ||
        (ssn.flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) ||
        (((TcpSession *)f.protoctx)->server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
        (((TcpSession *)f.protoctx)->client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request3, request3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply3, reply3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/**
 * \test Test multiple DATA commands(full mail transactions).
 */
static int SMTPParserTest06(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    uint8_t welcome_reply[] = {
        0x32, 0x32, 0x30, 0x20, 0x62, 0x61, 0x79, 0x30,
        0x2d, 0x6d, 0x63, 0x36, 0x2d, 0x66, 0x31, 0x30,
        0x2e, 0x62, 0x61, 0x79, 0x30, 0x2e, 0x68, 0x6f,
        0x74, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f,
        0x6d, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x69, 0x6e,
        0x67, 0x20, 0x75, 0x6e, 0x73, 0x6f, 0x6c, 0x69,
        0x63, 0x69, 0x74, 0x65, 0x64, 0x20, 0x63, 0x6f,
        0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c,
        0x20, 0x6f, 0x72, 0x20, 0x62, 0x75, 0x6c, 0x6b,
        0x20, 0x65, 0x2d, 0x6d, 0x61, 0x69, 0x6c, 0x20,
        0x74, 0x6f, 0x20, 0x4d, 0x69, 0x63, 0x72, 0x6f,
        0x73, 0x6f, 0x66, 0x74, 0x27, 0x73, 0x20, 0x63,
        0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x20,
        0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20,
        0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x68, 0x69,
        0x62, 0x69, 0x74, 0x65, 0x64, 0x2e, 0x20, 0x4f,
        0x74, 0x68, 0x65, 0x72, 0x20, 0x72, 0x65, 0x73,
        0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e,
        0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x66, 0x6f,
        0x75, 0x6e, 0x64, 0x20, 0x61, 0x74, 0x20, 0x68,
        0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x72,
        0x69, 0x76, 0x61, 0x63, 0x79, 0x2e, 0x6d, 0x73,
        0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6e,
        0x74, 0x69, 0x2d, 0x73, 0x70, 0x61, 0x6d, 0x2f,
        0x2e, 0x20, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74,
        0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69, 0x6c,
        0x6c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
        0x20, 0x69, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20,
        0x6f, 0x66, 0x20, 0x65, 0x71, 0x75, 0x69, 0x70,
        0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x63,
        0x61, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20,
        0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e,
        0x69, 0x61, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f,
        0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x74, 0x61,
        0x74, 0x65, 0x73, 0x2e, 0x20, 0x46, 0x72, 0x69,
        0x2c, 0x20, 0x31, 0x36, 0x20, 0x46, 0x65, 0x62,
        0x20, 0x32, 0x30, 0x30, 0x37, 0x20, 0x30, 0x35,
        0x3a, 0x30, 0x33, 0x3a, 0x32, 0x33, 0x20, 0x2d,
        0x30, 0x38, 0x30, 0x30, 0x20, 0x0d, 0x0a
    };
    uint32_t welcome_reply_len = sizeof(welcome_reply);

    uint8_t request1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x45, 0x58, 0x43,
        0x48, 0x41, 0x4e, 0x47, 0x45, 0x32, 0x2e, 0x63,
        0x67, 0x63, 0x65, 0x6e, 0x74, 0x2e, 0x6d, 0x69,
        0x61, 0x6d, 0x69, 0x2e, 0x65, 0x64, 0x75, 0x0d,
        0x0a
    };
    uint32_t request1_len = sizeof(request1);

    uint8_t reply1[] = {
        0x32, 0x35, 0x30, 0x2d, 0x62, 0x61, 0x79, 0x30,
        0x2d, 0x6d, 0x63, 0x36, 0x2d, 0x66, 0x31, 0x30,
        0x2e, 0x62, 0x61, 0x79, 0x30, 0x2e, 0x68, 0x6f,
        0x74, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f,
        0x6d, 0x20, 0x28, 0x33, 0x2e, 0x33, 0x2e, 0x31,
        0x2e, 0x34, 0x29, 0x20, 0x48, 0x65, 0x6c, 0x6c,
        0x6f, 0x20, 0x5b, 0x31, 0x32, 0x39, 0x2e, 0x31,
        0x37, 0x31, 0x2e, 0x33, 0x32, 0x2e, 0x35, 0x39,
        0x5d, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53,
        0x49, 0x5a, 0x45, 0x20, 0x32, 0x39, 0x36, 0x39,
        0x36, 0x30, 0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35,
        0x30, 0x2d, 0x50, 0x49, 0x50, 0x45, 0x4c, 0x49,
        0x4e, 0x49, 0x4e, 0x47, 0x0d, 0x0a, 0x32, 0x35,
        0x30, 0x2d, 0x38, 0x62, 0x69, 0x74, 0x6d, 0x69,
        0x6d, 0x65, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
        0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x4d, 0x49,
        0x4d, 0x45, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
        0x43, 0x48, 0x55, 0x4e, 0x4b, 0x49, 0x4e, 0x47,
        0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x41, 0x55,
        0x54, 0x48, 0x20, 0x4c, 0x4f, 0x47, 0x49, 0x4e,
        0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d, 0x41, 0x55,
        0x54, 0x48, 0x3d, 0x4c, 0x4f, 0x47, 0x49, 0x4e,
        0x0d, 0x0a, 0x32, 0x35, 0x30, 0x20, 0x4f, 0x4b,
        0x0d, 0x0a
    };
    uint32_t reply1_len = sizeof(reply1);

    /* MAIL FROM:asdff@asdf.com<CR><LF> */
    uint8_t request2[] = {
        0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
        0x4d, 0x3a, 0x61, 0x73, 0x64, 0x66, 0x66, 0x40,
        0x61, 0x73, 0x64, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
        0x0d, 0x0a
    };
    uint32_t request2_len = sizeof(request2);
    /* 250 2.1.0 Ok<CR><LF> */
    uint8_t reply2[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
        0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
    };
    uint32_t reply2_len = sizeof(reply2);

    /* RCPT TO:bimbs@gmail.com<CR><LF> */
    uint8_t request3[] = {
        0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a,
        0x62, 0x69, 0x6d, 0x62, 0x73, 0x40, 0x67, 0x6d,
        0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
        0x0a
    };
    uint32_t request3_len = sizeof(request3);
    /* 250 2.1.5 Ok<CR><LF> */
    uint8_t reply3[] = {
        0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
        0x35, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
    };
    uint32_t reply3_len = sizeof(reply3);

    /* BDAT 51<CR><LF> */
    uint8_t request4[] = {
        0x42, 0x44, 0x41, 0x54, 0x20, 0x35, 0x31, 0x0d,
        0x0a,
    };
    uint32_t request4_len = sizeof(request4);

    uint8_t request5[] = {
        0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x61, 0x73, 0x64,
        0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x66, 0x2e,
        0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x66, 0x2e,
        0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x0d, 0x0a,
    };
    uint32_t request5_len = sizeof(request5);

    uint8_t request6[] = {
        0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x61, 0x73, 0x64,
        0x66, 0x66, 0x40, 0x61, 0x73, 0x64, 0x66, 0x2e,
        0x66, 0x0d, 0x0a,
    };
    uint32_t request6_len = sizeof(request6);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, welcome_reply, welcome_reply_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1, request1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply1, reply1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply2, reply2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request3, request3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply3, reply3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 0 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request4, request4_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->cmds[0] != SMTP_COMMAND_BDAT ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE) ||
        smtp_state->bdat_chunk_len != 51 ||
        smtp_state->bdat_chunk_idx != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request5, request5_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                                     SMTP_PARSER_STATE_COMMAND_DATA_MODE) ||
        smtp_state->bdat_chunk_len != 51 ||
        smtp_state->bdat_chunk_idx != 32) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request6, request6_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
        smtp_state->cmds_cnt != 1 ||
        smtp_state->cmds_idx != 0 ||
        smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN ||
        smtp_state->bdat_chunk_len != 51 ||
        smtp_state->bdat_chunk_idx != 51) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/*
 * \test Test retrieving lines when frag'ed.
 */
static int SMTPParserTest07(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    const char *request1_str = "EHLO boo.com";
    /* EHLO boo.com<CR> */
    uint8_t request1_1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d,
    };
    int32_t request1_1_len = sizeof(request1_1);

    /* <LF> */
    uint8_t request1_2[] = {
        0x0a
    };
    int32_t request1_2_len = sizeof(request1_2);

    /* EHLO boo.com<CR><LF> */
    uint8_t request2[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
    };
    int32_t request2_len = sizeof(request2);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1_1, request1_1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->current_line != NULL ||
        smtp_state->current_line_len != 0 ||
        smtp_state->ts_current_line_db != 1 ||
        smtp_state->ts_db == NULL ||
        smtp_state->ts_db_len != request1_1_len ||
        memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1_2, request1_2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 1 ||
        smtp_state->ts_db == NULL ||
        smtp_state->ts_db_len != (int32_t)strlen(request1_str) ||
        memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 ||
        smtp_state->current_line != smtp_state->ts_db ||
        smtp_state->current_line_len != smtp_state->ts_db_len) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 0 ||
        smtp_state->ts_db != NULL ||
        smtp_state->ts_db_len != 0 ||
        smtp_state->current_line == NULL ||
        smtp_state->current_line_len != (int32_t)strlen(request1_str) ||
        memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/*
 * \test Test retrieving lines when frag'ed.
 */
static int SMTPParserTest08(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    const char *request1_str = "EHLO boo.com";
    /* EHLO boo.com */
    uint8_t request1_1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d,
    };
    int32_t request1_1_len = sizeof(request1_1);

    /* <CR><LF> */
    uint8_t request1_2[] = {
        0x0d, 0x0a
    };
    int32_t request1_2_len = sizeof(request1_2);

    /* EHLO boo.com<CR><LF> */
    uint8_t request2[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
    };
    int32_t request2_len = sizeof(request2);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1_1, request1_1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->current_line != NULL ||
        smtp_state->current_line_len != 0 ||
        smtp_state->ts_current_line_db != 1 ||
        smtp_state->ts_db == NULL ||
        smtp_state->ts_db_len != request1_1_len ||
        memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1_2, request1_2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 1 ||
        smtp_state->ts_db == NULL ||
        smtp_state->ts_db_len != (int32_t)strlen(request1_str) ||
        memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 ||
        smtp_state->current_line != smtp_state->ts_db ||
        smtp_state->current_line_len != smtp_state->ts_db_len) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 0 ||
        smtp_state->ts_db != NULL ||
        smtp_state->ts_db_len != 0 ||
        smtp_state->current_line == NULL ||
        smtp_state->current_line_len != (int32_t)strlen(request1_str) ||
        memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/*
 * \test Test retrieving lines when frag'ed.
 */
static int SMTPParserTest09(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    const char *request1_str = "EHLO boo.com";
    /* EHLO boo. */
    uint8_t request1_1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e,
    };
    int32_t request1_1_len = sizeof(request1_1);

    /* com<CR><LF> */
    uint8_t request1_2[] = {
        0x63, 0x6f, 0x6d, 0x0d, 0x0a
    };
    int32_t request1_2_len = sizeof(request1_2);

    /* EHLO boo.com<CR><LF> */
    uint8_t request2[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
    };
    int32_t request2_len = sizeof(request2);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1_1, request1_1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->current_line != NULL ||
        smtp_state->current_line_len != 0 ||
        smtp_state->ts_current_line_db != 1 ||
        smtp_state->ts_db == NULL ||
        smtp_state->ts_db_len != request1_1_len ||
        memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1_2, request1_2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 1 ||
        smtp_state->ts_db == NULL ||
        smtp_state->ts_db_len != (int32_t)strlen(request1_str) ||
        memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 ||
        smtp_state->current_line != smtp_state->ts_db ||
        smtp_state->current_line_len != smtp_state->ts_db_len) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 0 ||
        smtp_state->ts_db != NULL ||
        smtp_state->ts_db_len != 0 ||
        smtp_state->current_line == NULL ||
        smtp_state->current_line_len != (int32_t)strlen(request1_str) ||
        memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/*
 * \test Test retrieving lines when frag'ed.
 */
static int SMTPParserTest10(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    const char *request1_str = "";
    /* EHLO boo. */
    uint8_t request1_1[] = {
        0x0d,
    };
    int32_t request1_1_len = sizeof(request1_1);

    /* com<CR><LF> */
    uint8_t request1_2[] = {
        0x0a,
    };
    int32_t request1_2_len = sizeof(request1_2);

    const char *request2_str = "EHLO boo.com";
    /* EHLO boo.com<CR><LF> */
    uint8_t request2[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
    };
    int32_t request2_len = sizeof(request2);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1_1, request1_1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->current_line != NULL ||
        smtp_state->current_line_len != 0 ||
        smtp_state->ts_current_line_db != 1 ||
        smtp_state->ts_db == NULL ||
        smtp_state->ts_db_len != request1_1_len ||
        memcmp(smtp_state->ts_db, request1_1, request1_1_len) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1_2, request1_2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 1 ||
        smtp_state->ts_db == NULL ||
        smtp_state->ts_db_len != (int32_t)strlen(request1_str) ||
        memcmp(smtp_state->ts_db, request1_str, strlen(request1_str)) != 0 ||
        smtp_state->current_line != smtp_state->ts_db ||
        smtp_state->current_line_len != smtp_state->ts_db_len) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 0 ||
        smtp_state->ts_db != NULL ||
        smtp_state->ts_db_len != 0 ||
        smtp_state->current_line == NULL ||
        smtp_state->current_line_len != (int32_t)strlen(request2_str) ||
        memcmp(smtp_state->current_line, request2_str, strlen(request2_str)) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

/*
 * \test Test retrieving lines when frag'ed.
 */
static int SMTPParserTest11(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    const char *request1_str = "";
    /* EHLO boo. */
    uint8_t request1[] = {
        0x0a,
    };
    int32_t request1_len = sizeof(request1);

    const char *request2_str = "EHLO boo.com";
    /* EHLO boo.com<CR><LF> */
    uint8_t request2[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
    };
    int32_t request2_len = sizeof(request2);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1, request1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->current_line == NULL ||
        smtp_state->current_line_len != 0 ||
        smtp_state->ts_current_line_db == 1 ||
        smtp_state->ts_db != NULL ||
        smtp_state->ts_db_len != 0 ||
        memcmp(smtp_state->current_line, request1_str, strlen(request1_str)) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->ts_current_line_db != 0 ||
        smtp_state->ts_db != NULL ||
        smtp_state->ts_db_len != 0 ||
        smtp_state->current_line == NULL ||
        smtp_state->current_line_len != (int32_t)strlen(request2_str) ||
        memcmp(smtp_state->current_line, request2_str, strlen(request2_str)) != 0) {
        printf("smtp parser in inconsistent state\n");
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

static int SMTPParserTest12(void)
{
    int result = 0;
    Signature *s = NULL;
    ThreadVars th_v;
    Packet *p = NULL;
    Flow f;
    TcpSession ssn;
    DetectEngineThreadCtx *det_ctx = NULL;
    DetectEngineCtx *de_ctx = NULL;
    SMTPState *smtp_state = NULL;
    int r = 0;

    /* EHLO boo.com<CR><LF> */
    uint8_t request1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
    };
    int32_t request1_len = sizeof(request1);

    /* 388<CR><LF>
     */
    uint8_t reply1[] = {
        0x31, 0x38, 0x38, 0x0d, 0x0a,
    };
    uint32_t reply1_len = sizeof(reply1);

    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&th_v, 0, sizeof(th_v));
    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;
    p->flow = &f;
    p->flowflags |= FLOW_PKT_TOSERVER;
    p->flowflags |= FLOW_PKT_ESTABLISHED;
    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
    f.alproto = ALPROTO_SMTP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    de_ctx = DetectEngineCtxInit();
    if (de_ctx == NULL)
        goto end;

    de_ctx->flags |= DE_QUIET;

    s = DetectEngineAppendSig(de_ctx,"alert tcp any any -> any any "
                                   "(msg:\"SMTP event handling\"; "
                                   "app-layer-event: smtp.invalid_reply; "
                                   "sid:1;)");
    if (s == NULL)
        goto end;

    SigGroupBuild(de_ctx);
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER | STREAM_START, request1,
                            request1_len);
    if (r != 0) {
        printf("AppLayerParse for smtp failed.  Returned %" PRId32, r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);

    smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }

    /* do detect */
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);

    if (PacketAlertCheck(p, 1)) {
        printf("sid 1 matched.  It shouldn't match: ");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT | STREAM_TOCLIENT, reply1,
                            reply1_len);
    if (r == 0) {
        printf("AppLayerParse for smtp failed.  Returned %" PRId32, r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);

    /* do detect */
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);

    if (!PacketAlertCheck(p, 1)) {
        printf("sid 1 didn't match.  Should have matched: ");
        goto end;
    }

    result = 1;

end:
    SigGroupCleanup(de_ctx);
    SigCleanSignatures(de_ctx);

    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
    DetectEngineCtxFree(de_ctx);

    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    UTHFreePackets(&p, 1);
    return result;
}

static int SMTPParserTest13(void)
{
    int result = 0;
    Signature *s = NULL;
    ThreadVars th_v;
    Packet *p = NULL;
    Flow f;
    TcpSession ssn;
    DetectEngineThreadCtx *det_ctx = NULL;
    DetectEngineCtx *de_ctx = NULL;
    SMTPState *smtp_state = NULL;
    int r = 0;

    /* EHLO boo.com<CR><LF> */
    uint8_t request1[] = {
        0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
        0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a,
    };
    int32_t request1_len = sizeof(request1);

    /* 250<CR><LF>
     */
    uint8_t reply1[] = {
        0x32, 0x35, 0x30, 0x0d, 0x0a,
    };
    uint32_t reply1_len = sizeof(reply1);

    /* MAIL FROM:pbsf@asdfs.com<CR><LF>
     * RCPT TO:pbsf@asdfs.com<CR><LF>
     * DATA<CR><LF>
     * STARTTLS<CR><LF>
     */
    uint8_t request2[] = {
        0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
        0x4d, 0x3a, 0x70, 0x62, 0x73, 0x66, 0x40, 0x61,
        0x73, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
        0x0d, 0x0a, 0x52, 0x43, 0x50, 0x54, 0x20, 0x54,
        0x4f, 0x3a, 0x70, 0x62, 0x73, 0x66, 0x40, 0x61,
        0x73, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
        0x0d, 0x0a, 0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a,
        0x53, 0x54, 0x41, 0x52, 0x54, 0x54, 0x4c, 0x53,
        0x0d, 0x0a
    };
    uint32_t request2_len = sizeof(request2);

    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&th_v, 0, sizeof(th_v));
    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;
    p->flow = &f;
    p->flowflags |= FLOW_PKT_TOSERVER;
    p->flowflags |= FLOW_PKT_ESTABLISHED;
    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
    f.alproto = ALPROTO_SMTP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    de_ctx = DetectEngineCtxInit();
    if (de_ctx == NULL)
        goto end;

    de_ctx->flags |= DE_QUIET;

    s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
                              "(msg:\"SMTP event handling\"; "
                              "app-layer-event: "
                              "smtp.invalid_pipelined_sequence; "
                              "sid:1;)");
    if (s == NULL)
        goto end;

    SigGroupBuild(de_ctx);
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER | STREAM_START, request1,
                            request1_len);
    if (r != 0) {
        printf("AppLayerParse for smtp failed.  Returned %" PRId32, r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);

    smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }

    /* do detect */
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);

    if (PacketAlertCheck(p, 1)) {
        printf("sid 1 matched.  It shouldn't match: ");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply1, reply1_len);
    if (r != 0) {
        printf("AppLayerParse for smtp failed.  Returned %" PRId32, r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);

    /* do detect */
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);

    if (PacketAlertCheck(p, 1)) {
        printf("sid 1 matched.  It shouldn't match: ");
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("AppLayerParse for smtp failed.  Returned %" PRId32, r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);

    /* do detect */
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);

    if (!PacketAlertCheck(p, 1)) {
        printf("sid 1 didn't match.  Should have matched: ");
        goto end;
    }

    result = 1;

end:
    SigGroupCleanup(de_ctx);
    SigCleanSignatures(de_ctx);

    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
    DetectEngineCtxFree(de_ctx);

    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    UTHFreePackets(&p, 1);
    return result;
}

/**
 * \test Test DATA command w/MIME message.
 */
static int SMTPParserTest14(void)
{
    int result = 0;
    Flow f;
    int r = 0;

    /* 220 mx.google.com ESMTP d15sm986283wfl.6<CR><LF> */
    static uint8_t welcome_reply[] = {
            0x32, 0x32, 0x30, 0x20, 0x6d, 0x78, 0x2e, 0x67,
            0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
            0x6d, 0x20, 0x45, 0x53, 0x4d, 0x54, 0x50, 0x20,
            0x64, 0x31, 0x35, 0x73, 0x6d, 0x39, 0x38, 0x36,
            0x32, 0x38, 0x33, 0x77, 0x66, 0x6c, 0x2e, 0x36,
            0x0d, 0x0a
    };
    static uint32_t welcome_reply_len = sizeof(welcome_reply);

    /* EHLO boo.com<CR><LF> */
    static uint8_t request1[] = {
            0x45, 0x48, 0x4c, 0x4f, 0x20, 0x62, 0x6f, 0x6f,
            0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a
    };
    static uint32_t request1_len = sizeof(request1);
    /* 250-mx.google.com at your service, [117.198.115.50]<CR><LF>
     * 250-SIZE 35882577<CR><LF>
     * 250-8BITMIME<CR><LF>
     * 250-STARTTLS<CR><LF>
     * 250 ENHANCEDSTATUSCODES<CR><LF>
     */
    static uint8_t reply1[] = {
            0x32, 0x35, 0x30, 0x2d, 0x70, 0x6f, 0x6f, 0x6e,
            0x61, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f,
            0x76, 0x6d, 0x31, 0x2e, 0x6c, 0x6f, 0x63, 0x61,
            0x6c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0d,
            0x0a, 0x32, 0x35, 0x30, 0x2d, 0x50, 0x49, 0x50,
            0x45, 0x4c, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x0d,
            0x0a, 0x32, 0x35, 0x30, 0x2d, 0x53, 0x49, 0x5a,
            0x45, 0x20, 0x31, 0x30, 0x32, 0x34, 0x30, 0x30,
            0x30, 0x30, 0x0d, 0x0a, 0x32, 0x35, 0x30, 0x2d,
            0x56, 0x52, 0x46, 0x59, 0x0d, 0x0a, 0x32, 0x35,
            0x30, 0x2d, 0x45, 0x54, 0x52, 0x4e, 0x0d, 0x0a,
            0x32, 0x35, 0x30, 0x2d, 0x45, 0x4e, 0x48, 0x41,
            0x4e, 0x43, 0x45, 0x44, 0x53, 0x54, 0x41, 0x54,
            0x55, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x53, 0x0d,
            0x0a, 0x32, 0x35, 0x30, 0x2d, 0x38, 0x42, 0x49,
            0x54, 0x4d, 0x49, 0x4d, 0x45, 0x0d, 0x0a, 0x32,
            0x35, 0x30, 0x20, 0x44, 0x53, 0x4e, 0x0d, 0x0a
    };
    static uint32_t reply1_len = sizeof(reply1);

    /* MAIL FROM:asdff@asdf.com<CR><LF> */
    static uint8_t request2[] = {
            0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f,
            0x4d, 0x3a, 0x61, 0x73, 0x64, 0x66, 0x66, 0x40,
            0x61, 0x73, 0x64, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
            0x0d, 0x0a
    };
    static uint32_t request2_len = sizeof(request2);
    /* 250 2.1.0 Ok<CR><LF> */
    static uint8_t reply2[] = {
            0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
            0x30, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
    };
    static uint32_t reply2_len = sizeof(reply2);

    /* RCPT TO:bimbs@gmail.com<CR><LF> */
    static uint8_t request3[] = {
            0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a,
            0x62, 0x69, 0x6d, 0x62, 0x73, 0x40, 0x67, 0x6d,
            0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0d,
            0x0a
    };
    static uint32_t request3_len = sizeof(request3);
    /* 250 2.1.5 Ok<CR><LF> */
    static uint8_t reply3[] = {
            0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x31, 0x2e,
            0x35, 0x20, 0x4f, 0x6b, 0x0d, 0x0a
    };
    static uint32_t reply3_len = sizeof(reply3);

    /* DATA<CR><LF> */
    static uint8_t request4[] = {
            0x44, 0x41, 0x54, 0x41, 0x0d, 0x0a
    };
    static uint32_t request4_len = sizeof(request4);
    /* 354 End data with <CR><LF>.<CR><LF>|<CR><LF>| */
    static uint8_t reply4[] = {
            0x33, 0x35, 0x34, 0x20, 0x45, 0x6e, 0x64, 0x20,
            0x64, 0x61, 0x74, 0x61, 0x20, 0x77, 0x69, 0x74,
            0x68, 0x20, 0x3c, 0x43, 0x52, 0x3e, 0x3c, 0x4c,
            0x46, 0x3e, 0x2e, 0x3c, 0x43, 0x52, 0x3e, 0x3c,
            0x4c, 0x46, 0x3e, 0x0d, 0x0a
    };
    static uint32_t reply4_len = sizeof(reply4);

    /* MIME_MSG */
    static uint64_t filesize = 133;
    static uint8_t request4_msg[] = {
            0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72,
            0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E,
            0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
            0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A,
            0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61,
            0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6F, 0x63, 0x74,
            0x65, 0x74, 0x2D, 0x73, 0x74, 0x72, 0x65, 0x61,
            0x6D, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
            0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61, 0x6E, 0x73,
            0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E, 0x63, 0x6F,
            0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x62, 0x61,
            0x73, 0x65, 0x36, 0x34, 0x0D, 0x0A, 0x43, 0x6F,
            0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, 0x44, 0x69,
            0x73, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F,
            0x6E, 0x3A, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63,
            0x68, 0x6D, 0x65, 0x6E, 0x74, 0x3B, 0x20, 0x66,
            0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3D,
            0x22, 0x74, 0x65, 0x73, 0x74, 0x2E, 0x65, 0x78,
            0x65, 0x22, 0x3B, 0x0D, 0x0A, 0x0D, 0x0A, 0x54,
            0x56, 0x6F, 0x41, 0x41, 0x46, 0x42, 0x46, 0x41,
            0x41, 0x42, 0x4D, 0x41, 0x51, 0x45, 0x41, 0x61,
            0x69, 0x70, 0x59, 0x77, 0x77, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42,
            0x41, 0x41, 0x44, 0x41, 0x51, 0x73, 0x42, 0x43,
            0x41, 0x41, 0x42, 0x41, 0x41, 0x43, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x48, 0x6B, 0x41, 0x41,
            0x41, 0x41, 0x4D, 0x41, 0x41, 0x41, 0x41, 0x65,
            0x51, 0x41, 0x41, 0x41, 0x41, 0x77, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x45, 0x41, 0x41, 0x42,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41,
            0x41, 0x42, 0x30, 0x41, 0x41, 0x41, 0x41, 0x49,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42,
            0x41, 0x45, 0x41, 0x41, 0x49, 0x67, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x67, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x42, 0x63, 0x58, 0x44, 0x59, 0x32, 0x4C,
            0x6A, 0x6B, 0x7A, 0x4C, 0x6A, 0x59, 0x34, 0x4C,
            0x6A, 0x5A, 0x63, 0x65, 0x67, 0x41, 0x41, 0x4F,
            0x41, 0x3D, 0x3D, 0x0D,0x0A    };
    static uint32_t request4_msg_len = sizeof(request4_msg);

    /* DATA COMPLETED */
    static uint8_t request4_end[] = {
            0x0d, 0x0a, 0x2e, 0x0d, 0x0a
    };
    static uint32_t request4_end_len = sizeof(request4_end);
    /* 250 2.0.0 Ok: queued as 6A1AF20BF2<CR><LF> */
    static uint8_t reply4_end[] = {
            0x32, 0x35, 0x30, 0x20, 0x32, 0x2e, 0x30, 0x2e,
            0x30, 0x20, 0x4f, 0x6b, 0x3a, 0x20, 0x71, 0x75,
            0x65, 0x75, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20,
            0x36, 0x41, 0x31, 0x41, 0x46, 0x32, 0x30, 0x42,
            0x46, 0x32, 0x0d, 0x0a
    };
    static uint32_t reply4_end_len = sizeof(reply4_end);

    /* QUIT<CR><LF> */
    static uint8_t request5[] = {
            0x51, 0x55, 0x49, 0x54, 0x0d, 0x0a
    };
    static uint32_t request5_len = sizeof(request5);
    /* 221 2.0.0 Bye<CR><LF> */
    static uint8_t reply5[] = {
            0x32, 0x32, 0x31, 0x20, 0x32, 0x2e, 0x30, 0x2e,
            0x30, 0x20, 0x42, 0x79, 0x65, 0x0d, 0x0a
    };
    static uint32_t reply5_len = sizeof(reply5);

    TcpSession ssn;
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();

    memset(&f, 0, sizeof(f));
    memset(&ssn, 0, sizeof(ssn));

    FLOW_INITIALIZE(&f);
    f.protoctx = (void *)&ssn;
    f.proto = IPPROTO_TCP;

    StreamTcpInitConfig(TRUE);
    SMTPTestInitConfig();

    FLOWLOCK_WRLOCK(&f);
    /* Welcome reply */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, welcome_reply, welcome_reply_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    SMTPState *smtp_state = f.alstate;
    if (smtp_state == NULL) {
        printf("no smtp state: ");
        goto end;
    }
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 0 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request1, request1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 1 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
            smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* EHLO Reply */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply1, reply1_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }

    if ((smtp_state->helo_len != 7) || strncmp("boo.com", (char *)smtp_state->helo, 7)) {
        printf("incorrect parsing of HELO field '%s' (%d)\n", smtp_state->helo, smtp_state->helo_len);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }

    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 0 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* MAIL FROM Request */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request2, request2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 1 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
            smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* MAIL FROM Reply */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply2, reply2_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }

    if ((smtp_state->curr_tx->mail_from_len != 14) ||
        strncmp("asdff@asdf.com", (char *)smtp_state->curr_tx->mail_from, 14)) {
        printf("incorrect parsing of MAIL FROM field '%s' (%d)\n",
               smtp_state->curr_tx->mail_from,
               smtp_state->curr_tx->mail_from_len);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }

    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 0 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* RCPT TO Request */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request3, request3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 1 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
            smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* RCPT TO Reply */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply3, reply3_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 0 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    /* Enable mime decoding */
    smtp_config.decode_mime = 1;
    smtp_config.mime_config.decode_base64 = 1;
    smtp_config.mime_config.decode_quoted_printable = 1;
    MimeDecSetConfig(&smtp_config.mime_config);

    FLOWLOCK_WRLOCK(&f);
    /* DATA request */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request4, request4_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);

    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 1 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->cmds[0] != SMTP_COMMAND_DATA ||
            smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* Data reply */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply4, reply4_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 0 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                    SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* DATA message */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request4_msg, request4_msg_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);

    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 0 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->curr_tx->mime_state == NULL || smtp_state->curr_tx->msg_head == NULL || /* MIME data structures */
            smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN |
                    SMTP_PARSER_STATE_COMMAND_DATA_MODE)) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* DATA . request */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request4_end, request4_end_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);

    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 1 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->cmds[0] != SMTP_COMMAND_DATA_MODE ||
            smtp_state->curr_tx->mime_state == NULL || smtp_state->curr_tx->msg_head == NULL || /* MIME data structures */
            smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    SMTPState *state = (SMTPState *) f.alstate;
    FileContainer *files = state->files_ts;
    if (files != NULL && files->head != NULL) {
        File *file = files->head;

        if(strncmp((const char *)file->name, "test.exe", 8) != 0){
            printf("smtp-mime file name is incorrect");
            goto end;
        }
        if (FileSize(file) != filesize){
            printf("smtp-mime file size %"PRIu64" is incorrect", FileSize(file));
            goto end;
        }
        static uint8_t org_binary[] = {
                0x4D, 0x5A, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00,
                0x4C, 0x01, 0x01, 0x00, 0x6A, 0x2A, 0x58, 0xC3,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x04, 0x00, 0x03, 0x01, 0x0B, 0x01, 0x08, 0x00,
                0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
                0x79, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
                0x79, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00,
                0x04, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
                0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00,
                0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x5C, 0x5C, 0x36, 0x36,
                0x2E, 0x39, 0x33, 0x2E, 0x36, 0x38, 0x2E, 0x36,
                0x5C, 0x7A, 0x00, 0x00, 0x38,};

        if (StreamingBufferCompareRawData(file->sb,
                    org_binary, sizeof(org_binary)) != 1)
        {
            printf("smtp-mime file data incorrect\n");
            goto end;
        }
    }

    FLOWLOCK_WRLOCK(&f);
    /* DATA . reply */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply4_end, reply4_end_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 0 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* QUIT Request */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOSERVER, request5, request5_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 1 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->cmds[0] != SMTP_COMMAND_OTHER_CMD ||
            smtp_state->parser_state != SMTP_PARSER_STATE_FIRST_REPLY_SEEN) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    FLOWLOCK_WRLOCK(&f);
    /* QUIT Reply */
    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SMTP,
                            STREAM_TOCLIENT, reply5, reply5_len);
    if (r != 0) {
        printf("smtp check returned %" PRId32 ", expected 0: ", r);
        FLOWLOCK_UNLOCK(&f);
        goto end;
    }
    FLOWLOCK_UNLOCK(&f);
    if (smtp_state->input_len != 0 ||
            smtp_state->cmds_cnt != 0 ||
            smtp_state->cmds_idx != 0 ||
            smtp_state->parser_state != (SMTP_PARSER_STATE_FIRST_REPLY_SEEN)) {
        printf("smtp parser in inconsistent state l.%d\n", __LINE__);
        goto end;
    }

    result = 1;
end:
    if (alp_tctx != NULL)
        AppLayerParserThreadCtxFree(alp_tctx);
    StreamTcpFreeConfig(TRUE);
    FLOW_DESTROY(&f);
    return result;
}

static int SMTPProcessDataChunkTest01(void){
    Flow f;
    FLOW_INITIALIZE(&f);
    f.flags = FLOW_FILE_NO_STORE_TS;
    MimeDecParseState *state = MimeDecInitParser(&f, NULL);
    int ret;
    ret = SMTPProcessDataChunk(NULL, 0, state);

    return ret == 0;
}


static int SMTPProcessDataChunkTest02(void){
    char mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72,
            0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E,
            0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
            0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A,
            0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61,
            0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6F, 0x63, 0x74,
            0x65, 0x74, 0x2D, 0x73, 0x74, 0x72, 0x65, 0x61,
            0x6D, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
            0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61, 0x6E, 0x73,
            0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E, 0x63, 0x6F,
            0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x62, 0x61,
            0x73, 0x65, 0x36, 0x34, 0x0D, 0x0A, 0x43, 0x6F,
            0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, 0x44, 0x69,
            0x73, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F,
            0x6E, 0x3A, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63,
            0x68, 0x6D, 0x65, 0x6E, 0x74, 0x3B, 0x20, 0x66,
            0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x3D,
            0x22, 0x74, 0x65, 0x73, 0x74, 0x2E, 0x65, 0x78,
            0x65, 0x22, 0x3B, 0x0D, 0x0A, 0x0D, 0x0A, 0x54,
            0x56, 0x6F, 0x41, 0x41, 0x46, 0x42, 0x46, 0x41,
            0x41, 0x42, 0x4D, 0x41, 0x51, 0x45, 0x41, 0x61,
            0x69, 0x70, 0x59, 0x77, 0x77, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42,
            0x41, 0x41, 0x44, 0x41, 0x51, 0x73, 0x42, 0x43,
            0x41, 0x41, 0x42, 0x41, 0x41, 0x43, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x48, 0x6B, 0x41, 0x41,
            0x41, 0x41, 0x4D, 0x41, 0x41, 0x41, 0x41, 0x65,
            0x51, 0x41, 0x41, 0x41, 0x41, 0x77, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x45, 0x41, 0x41, 0x42,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41,
            0x41, 0x42, 0x30, 0x41, 0x41, 0x41, 0x41, 0x49,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42,
            0x41, 0x45, 0x41, 0x41, 0x49, 0x67, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x67, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x42, 0x63, 0x58, 0x44, 0x59, 0x32, 0x4C,
            0x6A, 0x6B, 0x7A, 0x4C, 0x6A, 0x59, 0x34, 0x4C,
            0x6A, 0x5A, 0x63, 0x65, 0x67, 0x41, 0x41, 0x4F,
            0x41, 0x3D, 0x3D, 0x0D, 0x0A,};

    Flow f;
    FLOW_INITIALIZE(&f);
    f.alstate = SMTPStateAlloc();
    MimeDecParseState *state = MimeDecInitParser(&f, NULL);
    ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
    state->body_begin = 1;
    int ret;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state);

    return ret == 0;
}



static int SMTPProcessDataChunkTest03(void){
    char mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72, };
    char mimemsg2[] = {0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E, };
    char mimemsg3[] = {0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, };
    char mimemsg4[] = {0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A, };
    char mimemsg5[] = {0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, };
    char mimemsg6[] = {0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6F, 0x63, 0x74, };
    char mimemsg7[] = {0x65, 0x74, 0x2D, 0x73, 0x74, 0x72, 0x65, 0x61, };
    char mimemsg8[] = {0x6D, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, };
    char mimemsg9[] = {0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61, 0x6E, 0x73, };
    char mimemsg10[] = {0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E, 0x63, 0x6F, };
    char mimemsg11[] = {0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x62, 0x61, };
    char mimemsg12[] = {0x73, 0x65, 0x36, 0x34, 0x0D, 0x0A, 0x43, 0x6F, };

    Flow f;
    FLOW_INITIALIZE(&f);
    f.alstate = SMTPStateAlloc();
    MimeDecParseState *state = MimeDecInitParser(&f, NULL);
    ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
    int ret;

    state->body_begin = 1;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state);
    if(ret) goto end;
    state->body_begin = 0;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg2, sizeof(mimemsg2), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg3, sizeof(mimemsg3), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg4, sizeof(mimemsg4), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg5, sizeof(mimemsg5), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg6, sizeof(mimemsg6), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg7, sizeof(mimemsg7), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg8, sizeof(mimemsg8), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg9, sizeof(mimemsg9), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg10, sizeof(mimemsg10), state);
    if(ret) goto end;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg11, sizeof(mimemsg11), state);
    if(ret) goto end;
    state->body_end = 1;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg12, sizeof(mimemsg12), state);
    if(ret) goto end;

    end:
    return ret == 0;
}


static int SMTPProcessDataChunkTest04(void){
    char mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72, };
    char mimemsg2[] = {0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E, };
    char mimemsg3[] = {0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, };
    char mimemsg4[] = {0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A, };
    char mimemsg5[] = {0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, };
    char mimemsg6[] = {0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6F, 0x63, 0x74, };
    char mimemsg7[] = {0x65, 0x74, 0x2D, 0x73, 0x74, 0x72, 0x65, 0x61, };
    char mimemsg8[] = {0x6D, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, };
    char mimemsg9[] = {0x6E, 0x74, 0x2D, 0x54, 0x72, 0x61, 0x6E, 0x73, };
    char mimemsg10[] = {0x66, 0x65, 0x72, 0x2D, 0x45, 0x6E, 0x63, 0x6F, };
    char mimemsg11[] = {0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x62, 0x61, };

    Flow f;
    FLOW_INITIALIZE(&f);
    f.alstate = SMTPStateAlloc();
    MimeDecParseState *state = MimeDecInitParser(&f, NULL);
    ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
    int ret = MIME_DEC_OK;

    state->body_begin = 1;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state) != 0) goto end;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg2, sizeof(mimemsg2), state) != 0) goto end;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg3, sizeof(mimemsg3), state) != 0) goto end;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg4, sizeof(mimemsg4), state) != 0) goto end;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg5, sizeof(mimemsg5), state) != 0) goto end;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg6, sizeof(mimemsg6), state) != 0) goto end;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg7, sizeof(mimemsg7), state) != 0) goto end;
    state->body_begin = 0;
    state->body_end = 1;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg8, sizeof(mimemsg8), state) != 0) goto end;
    state->body_end = 0;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg9, sizeof(mimemsg9), state) != 0) goto end;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg10, sizeof(mimemsg10), state) != 0) goto end;
    if(SMTPProcessDataChunk((uint8_t *)mimemsg11, sizeof(mimemsg11), state) != 0) goto end;

    end:
    return ret == 0;
}

static int SMTPProcessDataChunkTest05(void){
    char mimemsg[] = {0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72,
            0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x31, 0x2E,
            0x30, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65,
            0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A,
            0x6A, 0x6B, 0x7A, 0x4C, 0x6A, 0x59, 0x34, 0x4C,
            0x6A, 0x5A, 0x63, 0x65, 0x67, 0x41, 0x41, 0x4F,
            0x41, 0x3D, 0x3D, 0x0D, 0x0A,};

    Flow f;
    int ret;
    FLOW_INITIALIZE(&f);
    f.alstate = SMTPStateAlloc();
    FAIL_IF(f.alstate == NULL);
    MimeDecParseState *state = MimeDecInitParser(&f, NULL);
    ((MimeDecEntity *)state->stack->top->data)->ctnt_flags = CTNT_IS_ATTACHMENT;
    FAIL_IF(state == NULL);
    state->body_begin = 1;
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state);
    FAIL_IF(ret != 0);
    state->body_begin = 0;
    SMTPState *smtp_state = (SMTPState *)((Flow *)state->data)->alstate;
    FileContainer *files = smtp_state->files_ts;
    FAIL_IF(files == NULL);
    File *file = files->head;
    FAIL_IF(file == NULL);
    ret = SMTPProcessDataChunk((uint8_t *)mimemsg, sizeof(mimemsg), state);
    FAIL_IF(ret != 0);
    FAIL_IF((uint32_t)FileSize(file) != 106);
    SMTPStateFree(smtp_state);
    FLOW_DESTROY(&f);
    PASS;
}

#endif /* UNITTESTS */

void SMTPParserRegisterTests(void)
{
#ifdef UNITTESTS
    UtRegisterTest("SMTPParserTest01", SMTPParserTest01);
    UtRegisterTest("SMTPParserTest02", SMTPParserTest02);
    UtRegisterTest("SMTPParserTest03", SMTPParserTest03);
    UtRegisterTest("SMTPParserTest04", SMTPParserTest04);
    UtRegisterTest("SMTPParserTest05", SMTPParserTest05);
    UtRegisterTest("SMTPParserTest06", SMTPParserTest06);
    UtRegisterTest("SMTPParserTest07", SMTPParserTest07);
    UtRegisterTest("SMTPParserTest08", SMTPParserTest08);
    UtRegisterTest("SMTPParserTest09", SMTPParserTest09);
    UtRegisterTest("SMTPParserTest10", SMTPParserTest10);
    UtRegisterTest("SMTPParserTest11", SMTPParserTest11);
    UtRegisterTest("SMTPParserTest12", SMTPParserTest12);
    UtRegisterTest("SMTPParserTest13", SMTPParserTest13);
    UtRegisterTest("SMTPParserTest14", SMTPParserTest14);
    UtRegisterTest("SMTPProcessDataChunkTest01", SMTPProcessDataChunkTest01);
    UtRegisterTest("SMTPProcessDataChunkTest02", SMTPProcessDataChunkTest02);
    UtRegisterTest("SMTPProcessDataChunkTest03", SMTPProcessDataChunkTest03);
    UtRegisterTest("SMTPProcessDataChunkTest04", SMTPProcessDataChunkTest04);
    UtRegisterTest("SMTPProcessDataChunkTest05", SMTPProcessDataChunkTest05);
#endif /* UNITTESTS */

    return;
}
