summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzhuzhenjun <[email protected]>2024-11-18 10:19:53 +0000
committerzhuzhenjun <[email protected]>2024-11-18 10:20:14 +0000
commit185a61e8aa06e0f9f4e561947af43c86940babf1 (patch)
treeb5334138897ded709196ce391785ee54f3e27138
parentd0a868591470a4a9d71a65a5d540058e72c8d92c (diff)
mail decoder refactoringdev-mail-refactoring-back
-rw-r--r--decoders/mail/CMakeLists.txt14
-rw-r--r--decoders/mail/mail.c514
-rw-r--r--decoders/mail/mail_email.c1692
-rw-r--r--decoders/mail/mail_email.h342
-rw-r--r--decoders/mail/mail_imap.c1966
-rw-r--r--decoders/mail/mail_imap.h123
-rw-r--r--decoders/mail/mail_internal.h104
-rw-r--r--decoders/mail/mail_mime.c707
-rw-r--r--decoders/mail/mail_mime.h118
-rw-r--r--decoders/mail/mail_pop3.c468
-rw-r--r--decoders/mail/mail_pop3.h99
-rw-r--r--decoders/mail/mail_smtp.c503
-rw-r--r--decoders/mail/mail_smtp.h90
-rw-r--r--decoders/mail/mail_util.c1453
-rw-r--r--decoders/mail/mail_util.h77
-rw-r--r--decoders/mail/version.map13
-rw-r--r--include/stellar/mail.h96
-rw-r--r--test/decoders/mail/CMakeLists.txt53
-rw-r--r--test/decoders/mail/mail_test_main.cpp80
-rw-r--r--test/decoders/mail/mail_test_module.cpp367
-rw-r--r--test/decoders/mail/pcap/01-imap-mail-account-in-command-with-base64.pcapbin0 -> 17486 bytes
-rw-r--r--test/decoders/mail/pcap/02-imap-starttls.pcapbin0 -> 555955 bytes
-rw-r--r--test/decoders/mail/pcap/03-smtp-s2c-mail.pcapbin0 -> 1618 bytes
-rw-r--r--test/decoders/mail/pcap/04-smtp-attachment-charset-unknown.pcapbin0 -> 4013 bytes
-rw-r--r--test/decoders/mail/pcap/05-smtp-curl-mails.pcapbin0 -> 7456 bytes
-rw-r--r--test/decoders/mail/pcap/06-smtp-starttls.pcapbin0 -> 4220 bytes
-rw-r--r--test/decoders/mail/pcap/07-smtp-auth-xoauth2.pcapbin0 -> 4866 bytes
-rw-r--r--test/decoders/mail/pcap/08-smtp-mime-header-rfc2047.pcapbin0 -> 3063 bytes
-rw-r--r--test/decoders/mail/pcap/09-pop3-c2s-55325-1100.pcapbin0 -> 2767 bytes
-rw-r--r--test/decoders/mail/pcap/10-pop3-s2c-1100-55325.pcapbin0 -> 18034 bytes
-rw-r--r--test/decoders/mail/pcap/11-pop3-c2s.pcapbin0 -> 2767 bytes
-rw-r--r--test/decoders/mail/pcap/12-pop3-s2c.pcapbin0 -> 18034 bytes
-rw-r--r--test/decoders/mail/pcap/13-pop3-mail.pcapbin0 -> 14121 bytes
-rw-r--r--test/decoders/mail/pcap/14-pop3-starttls.pcapbin0 -> 9400 bytes
-rw-r--r--test/decoders/mail/pcap/15-pop3-capa.pcapbin0 -> 1656785 bytes
-rw-r--r--test/decoders/mail/pcap/16-ftp-c2s-8569-8021.pcapbin0 -> 1565 bytes
-rw-r--r--test/decoders/mail/pcap/17-ftp-c2s-8569-8080.pcapbin0 -> 1565 bytes
-rw-r--r--test/decoders/mail/pcap/18-http-delete.pcapbin0 -> 1642 bytes
-rw-r--r--test/decoders/mail/result/01-imap-mail-account-in-command-with-base64.json8
39 files changed, 8887 insertions, 0 deletions
diff --git a/decoders/mail/CMakeLists.txt b/decoders/mail/CMakeLists.txt
new file mode 100644
index 0000000..4e1a1f9
--- /dev/null
+++ b/decoders/mail/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(DECODER_NAME mail_decoder)
+
+file(GLOB DECODER_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${DECODER_NAME}*.c")
+
+add_library(
+ ${DECODER_NAME}
+ ${DECODER_SRC}
+)
+
+set_target_properties(
+ ${DECODER_NAME} PROPERTIES
+ LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/version.map"
+)
+
diff --git a/decoders/mail/mail.c b/decoders/mail/mail.c
new file mode 100644
index 0000000..7516548
--- /dev/null
+++ b/decoders/mail/mail.c
@@ -0,0 +1,514 @@
+#include <stdio.h>
+#include <limits.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/time.h>
+
+#include "stellar/packet.h"
+#include "stellar/utils.h"
+#include "stellar/mq.h"
+#include "stellar/session.h"
+#include "stellar/mail.h"
+
+#include "mail_internal.h"
+#include "mail_parser.h"
+
+#define PORT_SMTP 25
+#define PORT_POP3 110
+#define PORT_IMAP 143
+
+static int is_current_packet_c2s(struct session *sess)
+{
+ const struct layer *first_pkt_layer, *curr_pkt_layer;
+
+ const struct packet *first_pkt, *curr_pkt;
+
+ first_pkt = session_get_first_packet(sess, FLOW_TYPE_C2S);
+ curr_pkt = session_get_current_packet(sess);
+
+ int i = 0;
+ int layer_count = packet_get_layer_count(first_pkt);
+
+ for (i = 0; i < 2 /* l3/l4 */; i++) {
+ first_pkt_layer = packet_get_layer_by_idx(first_pkt, layer_count - 1 -i);
+ curr_pkt_layer = packet_get_layer_by_idx(curr_pkt, layer_count - 1 -i);
+
+ if (first_pkt_layer->proto != curr_pkt_layer->proto) {
+ return 0;
+ }
+ switch (first_pkt_layer->proto) {
+ case LAYER_PROTO_TCP:
+ if (0 != memcmp(first_pkt_layer->hdr.tcp, curr_pkt_layer->hdr.tcp, sizeof(struct tcphdr))) {
+ return 0;
+ }
+ break;
+ case LAYER_PROTO_UDP:
+ if (0 != memcmp(first_pkt_layer->hdr.udp, curr_pkt_layer->hdr.udp, sizeof(struct udphdr))) {
+ return 0;
+ }
+ break;
+ case LAYER_PROTO_IPV4:
+ if (0 != memcmp(first_pkt_layer->hdr.ip4, curr_pkt_layer->hdr.ip4, sizeof(struct ip))) {
+ return 0;
+ }
+ break;
+ case LAYER_PROTO_IPV6:
+ if (0 != memcmp(first_pkt_layer->hdr.ip6, curr_pkt_layer->hdr.ip6, sizeof(struct ip6_hdr))) {
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ }
+
+ return 1;
+}
+
+static int mail_subscribe_command(struct mail_env *mail_env, mail_command_callback_func *cb, void *arg)
+{
+ int topic;
+ struct mq_schema *schema;
+
+ schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref);
+ if (schema == NULL) {
+ return -1;
+ }
+
+ topic = mq_schema_get_topic_id(schema, MAIL_COMMAND_TOPIC_NAME);
+ if (topic < 0) {
+ return -1;
+ }
+
+ return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg);
+}
+
+static int mail_subscribe_header(struct mail_env *mail_env, mail_header_callback_func *cb, void *arg)
+{
+ int topic;
+ struct mq_schema *schema;
+
+ schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref);
+ if (schema == NULL) {
+ return -1;
+ }
+
+ topic = mq_schema_get_topic_id(schema, MAIL_HEADER_TOPIC_NAME);
+ if (topic < 0) {
+ return -1;
+ }
+
+ return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg);
+}
+
+static int mail_subscribe_body(struct mail_env *mail_env, mail_body_callback_func *cb, void *arg)
+{
+ int topic;
+ struct mq_schema *schema;
+
+ schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref);
+ if (schema == NULL) {
+ return -1;
+ }
+
+ topic = mq_schema_get_topic_id(schema, MAIL_BODY_TOPIC_NAME);
+ if (topic < 0) {
+ return -1;
+ }
+
+ return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg);
+}
+
+static int mail_subscribe_attachment(struct mail_env *mail_env, mail_attachment_callback_func *cb, void *arg)
+{
+ int topic;
+ struct mq_schema *schema;
+
+ schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref);
+ if (schema == NULL) {
+ return -1;
+ }
+
+ topic = mq_schema_get_topic_id(schema, MAIL_ATTACHMENT_TOPIC_NAME);
+ if (topic < 0) {
+ return -1;
+ }
+
+ return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg);
+}
+
+static int mail_subscribe_eml(struct mail_env *mail_env, mail_eml_callback_func *cb, void *arg)
+{
+ int topic;
+ struct mq_schema *schema;
+
+ schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref);
+ if (schema == NULL) {
+ return -1;
+ }
+
+ topic = mq_schema_get_topic_id(schema, MAIL_EML_TOPIC_NAME);
+ if (topic < 0) {
+ return -1;
+ }
+
+ return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg);
+}
+
+static void mail_dispatch(int topic, void *msg, on_msg_cb_func *msg_cb, void *arg, void *dispatch_arg)
+{
+ struct mail_message *mail_msg;
+ struct mail_env *mail_env;
+
+ mail_msg = (struct mail_message *)msg;
+ mail_env = (struct mail_env *)dispatch_arg;
+
+ if (topic == mail_env->command_topic_id) {
+ mail_command_callback_func *command_cb = (mail_command_callback_func *)(void *)msg_cb;
+ command_cb(mail_msg->sess_ref, mail_msg->mail_protocol, mail_msg->mail_seq,
+ mail_msg->command, mail_msg->command_param, mail_msg->command_param_len,
+ mail_msg->command_line, mail_msg->command_line_len,
+ arg);
+ return;
+ }
+
+ if (topic == mail_env->header_topic_id) {
+ mail_header_callback_func *header_cb = (mail_header_callback_func *)(void *)msg_cb;
+ header_cb(mail_msg->sess_ref, mail_msg->mail_protocol, mail_msg->mail_seq, &mail_msg->header, arg);
+ return;
+ }
+
+ if (topic == mail_env->body_topic_id) {
+ mail_body_callback_func *body_cb = (mail_body_callback_func *)(void *)msg_cb;
+ body_cb(mail_msg->sess_ref, mail_msg->mail_protocol, mail_msg->mail_seq,
+ mail_msg->body, mail_msg->body_len,
+ mail_msg->body_offset, mail_msg->is_body_finished,
+ arg);
+ return;
+ }
+
+ if (topic == mail_env->attachment_topic_id) {
+ mail_attachment_callback_func *attachment_cb = (mail_attachment_callback_func *)(void *)msg_cb;
+ attachment_cb(mail_msg->sess_ref, mail_msg->mail_protocol, mail_msg->mail_seq,
+ mail_msg->attachment_name, mail_msg->attachment_name_len,
+ mail_msg->attachment, mail_msg->attachment_len,
+ mail_msg->attachment_offset, mail_msg->is_attachment_finished,
+ arg);
+ return;
+ }
+
+ if (topic == mail_env->eml_topic_id) {
+ mail_eml_callback_func *eml_cb = (mail_eml_callback_func *)(void *)msg_cb;
+ eml_cb(mail_msg->sess_ref, mail_msg->mail_protocol,
+ mail_msg->eml, mail_msg->eml_len,
+ mail_msg->eml_offset, mail_msg->is_eml_finished,
+ arg);
+ return;
+ }
+}
+
+int mail_subscribe(struct mail_env *mail_env,
+ mail_command_callback_func command_cb,
+ mail_header_callback_func *header_cb,
+ mail_body_callback_func *body_cb,
+ mail_attachment_callback_func *attachment_cb,
+ mail_eml_callback_func *eml_cb,
+ void *arg)
+{
+ int ret;
+
+ ret = mail_subscribe_command(mail_env, command_cb, arg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = mail_subscribe_header(mail_env, header_cb, arg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = mail_subscribe_body(mail_env, body_cb, arg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = mail_subscribe_attachment(mail_env, attachment_cb, arg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = mail_subscribe_eml(mail_env, eml_cb, arg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mail_session_ctx_free(struct mail_session_ctx *session_ctx)
+{
+ switch (session_ctx->protocol) {
+ case MAIL_PROTOCOL_SMTP:
+ if (session_ctx->smtp_parser) {
+ smtp_parser_free(session_ctx->smtp_parser);
+ }
+ break;
+ case MAIL_PROTOCOL_POP3:
+ break;
+ case MAIL_PROTOCOL_IMAP:
+ break;
+ default:
+ break;
+ }
+ free(session_ctx);
+}
+
+static struct mail_session_ctx *mail_session_ctx_new(void)
+{
+ struct mail_session_ctx *session_ctx;
+ session_ctx = (struct mail_session_ctx *)calloc(1, sizeof(struct mail_session_ctx));
+ return session_ctx;
+}
+
+static int mail_info_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx, const char *payload, size_t payload_len, int is_c2s)
+{
+ int ret;
+
+ ret = smtp_parser_get_command(session_ctx->smtp_parser);
+ if (ret == 0) {
+ mail_publish_command(mail_env, sess, mail_protocol, mail_seq, cmd, cmd_param, cmd_param_len, cmd_line, cmd_line_len);
+ }
+
+ struct mail_info *mail;
+ mail = smtp_parser_get_mailinfo(session_ctx->smtp_parser);
+ if (mail) {
+ mail_info_get_header();
+ mail_info_get_body();
+ mail_info_get_attachment();
+ }
+}
+
+static int mail_imap_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx, const char *payload, size_t payload_len, int is_c2s)
+{
+ int ret;
+
+ ret = smtp_parser_process(session_ctx->smtp_parser, payload, payload_len , is_c2s);
+ if (ret != 0) {
+ return;
+ }
+
+ ret = smtp_parser_get_command(session_ctx->smtp_parser);
+ if (ret == 0) {
+ mail_publish_command(mail_env, sess, mail_protocol, mail_seq, cmd, cmd_param, cmd_param_len, cmd_line, cmd_line_len);
+ }
+
+ struct mail_info *mail;
+ mail = smtp_parser_get_mailinfo(session_ctx->smtp_parser);
+ if (mail) {
+ mail_info_get_header();
+ mail_info_get_body();
+ mail_info_get_attachment();
+ }
+}
+
+static int mail_pop3_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx, const char *payload, size_t payload_len, int is_c2s)
+{
+ int ret;
+
+ ret = smtp_parser_process(session_ctx->smtp_parser, payload, payload_len , is_c2s);
+ if (ret != 0) {
+ return;
+ }
+
+ ret = smtp_parser_get_command(session_ctx->smtp_parser);
+ if (ret == 0) {
+ mail_publish_command(mail_env, sess, mail_protocol, mail_seq, cmd, cmd_param, cmd_param_len, cmd_line, cmd_line_len);
+ }
+
+ struct mail_info *mail;
+ mail = smtp_parser_get_mailinfo(session_ctx->smtp_parser);
+ if (mail) {
+ mail_info_get_header();
+ mail_info_get_body();
+ mail_info_get_attachment();
+ }
+}
+
+static int mail_smtp_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx, const char *payload, size_t payload_len, int is_c2s)
+{
+ int ret;
+
+ if (session_ctx->smtp_parser == NULL) {
+ session_ctx->smtp_parser = smtp_parser_new(mail_env);
+ }
+
+ ret = smtp_parser_process(session_ctx->smtp_parser, payload, payload_len , is_c2s);
+ if (ret != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void mail_on_tcp_payload(struct session *sess, enum session_state state, const char *payload, uint32_t payload_len, void *arg)
+{
+ (void)state;
+ int ret;
+ int is_packet_c2s;
+ struct mail_session_ctx *session_ctx;
+ struct mail_env *mail_env = (struct mail_env *)arg;
+
+ if (payload == NULL || payload_len == 0) {
+ return;
+ }
+
+ session_ctx = (struct mail_session_ctx *)session_get_exdata(sess, mail_env->exdata_id);
+ if (session_ctx == NULL) {
+ session_ctx = mail_session_ctx_new();
+ session_ctx->mail_env_ref = mail_env;
+ session_ctx->sess_ref = sess;
+ session_ctx->protocol = MAIL_PROTOCOL_MAX;
+ session_ctx->is_mail_protocol = 1;
+ session_set_exdata(sess, mail_env->exdata_id, session_ctx);
+
+ if (smtp_identify(payload, payload_len, is_packet_c2s)) {
+ session_ctx->protocol = MAIL_PROTOCOL_SMTP;
+ }
+ //if (pop3_identify((char *)payload, payload_len, is_packet_c2s)) {
+ // session_ctx->protocol = MAIL_PROTOCOL_POP3;
+ //}
+
+ //if (imap_identify((char *)payload, payload_len, is_packet_c2s)) {
+ // session_ctx->protocol = MAIL_PROTOCOL_IMAP;
+ //}
+ }
+
+ is_packet_c2s = is_current_packet_c2s(sess);
+
+ switch (session_ctx->protocol) {
+ case MAIL_PROTOCOL_SMTP:
+ mail_smtp_process(mail_env, session_ctx, payload, payload_len, is_packet_c2s);
+ break;
+ case MAIL_PROTOCOL_POP3:
+ //ret = pop3_parser_process(session_ctx->smtp, payload, payload_len , is_packet_c2s);
+ //if (ret != 0) {
+ // return;
+ //}
+ break;
+ case MAIL_PROTOCOL_IMAP:
+ //ret = imap_parser_process(session_ctx->smtp, payload, payload_len , is_packet_c2s);
+ //if (ret != 0) {
+ // return;
+ //}
+ break;
+ default:
+ break;
+ }
+}
+
+void mail_on_exdata_free(int idx, void *ex_ptr, void *arg)
+{
+ (void)(idx);
+ (void)(arg);
+ struct mail_session_ctx *session_ctx = (struct mail_session_ctx *)ex_ptr;
+
+ if (session_ctx) {
+ mail_session_ctx_free(session_ctx);
+ }
+}
+
+static void mail_on_message_free(void *msg, void *arg)
+{
+ (void)(arg);
+ struct mail_message *mail_msg;
+
+ mail_msg = (struct mail_message *)msg;
+ if (mail_msg) {
+ free(mail_msg);
+ }
+}
+
+void mail_exit(struct module_manager *mod_mgr, struct module *mod)
+{
+ (void)(mod_mgr);
+ struct mail_env *mail_env;
+ struct mq_schema *schema;
+
+ if (mod) {
+ mail_env = (struct mail_env *)module_get_ctx(mod);
+ if (mail_env) {
+ schema = module_manager_get_mq_schema(mod_mgr);
+ mq_schema_destroy_topic(schema, mail_env->command_topic_id);
+ mq_schema_destroy_topic(schema, mail_env->header_topic_id);
+ mq_schema_destroy_topic(schema, mail_env->body_topic_id);
+ mq_schema_destroy_topic(schema, mail_env->attachment_topic_id);
+ mq_schema_destroy_topic(schema, mail_env->eml_topic_id);
+ free(mail_env);
+ }
+
+ module_free(mod);
+ }
+}
+
+struct module* mail_init(struct module_manager *mod_mgr)
+{
+ int ret;
+ struct module *mod;
+ struct mq_schema *schema;
+ struct session_manager *sess_mgr;
+ struct mail_env *mail_env;
+
+ mail_env = (struct mail_env *)calloc(1, sizeof(struct mail_env));
+ mail_env->mod_mgr_ref = mod_mgr;
+ mod = module_new(MAIL_MODULE_NAME, mail_env);
+ sess_mgr = module_to_session_manager(module_manager_get_module(mod_mgr, SESSION_MANAGER_MODULE_NAME));
+ schema = module_manager_get_mq_schema(mod_mgr);
+
+ if (sess_mgr == NULL || schema == NULL) {
+ goto exit;
+ }
+
+ ret = session_manager_subscribe_tcp_stream(sess_mgr, mail_on_tcp_payload, mail_env);
+ if (ret < 0) {
+ goto exit;
+ }
+
+ mail_env->exdata_id = session_manager_new_session_exdata_index(sess_mgr, MAIL_EXDATA_NAME, mail_on_exdata_free, NULL);
+ if (mail_env->exdata_id < 0) {
+ goto exit;
+ }
+
+ mail_env->command_topic_id = mq_schema_create_topic(schema, MAIL_COMMAND_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL);
+ if (mail_env->command_topic_id < 0) {
+ goto exit;
+ }
+ mail_env->header_topic_id = mq_schema_create_topic(schema, MAIL_HEADER_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL);
+ if (mail_env->header_topic_id < 0) {
+ goto exit;
+ }
+ mail_env->body_topic_id = mq_schema_create_topic(schema, MAIL_BODY_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL);
+ if (mail_env->body_topic_id < 0) {
+ goto exit;
+ }
+ mail_env->attachment_topic_id = mq_schema_create_topic(schema, MAIL_ATTACHMENT_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL);
+ if (mail_env->attachment_topic_id < 0) {
+ goto exit;
+ }
+ mail_env->eml_topic_id = mq_schema_create_topic(schema, MAIL_EML_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL);
+ if (mail_env->eml_topic_id < 0) {
+ goto exit;
+ }
+
+ return mod;
+exit:
+ mail_exit(mod_mgr, mod);
+ return NULL;
+}
+
+struct mail_env *module_to_mail_env(struct module *mod)
+{
+ assert(mod);
+ assert(strcmp(module_get_name(mod), MAIL_MODULE_NAME) == 0);
+ return module_get_ctx(mod);
+}
diff --git a/decoders/mail/mail_email.c b/decoders/mail/mail_email.c
new file mode 100644
index 0000000..55a3e48
--- /dev/null
+++ b/decoders/mail/mail_email.c
@@ -0,0 +1,1692 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "mail_email.h"
+
+/*********************************************************************
+函数名称:buf_elem_check_size
+功能简介:
+输入参数:
+输出参数:无
+返回值:-1 出错 0 有足够空间;1-重新分配空间
+*********************************************************************/
+int buf_cache_check_size(stBufCache *pdstBuf, int wantedlen)
+{
+ int ret=0, needlen, block_num=0;
+
+ if(pdstBuf->max_len > wantedlen + pdstBuf->len)
+ return 0;
+
+ needlen = wantedlen + pdstBuf->len - pdstBuf->max_len;
+ block_num = needlen/REALLOC_BLOCK_SIZE;
+ if(needlen%REALLOC_BLOCK_SIZE)
+ block_num += 1;
+
+ pdstBuf->max_len += block_num*REALLOC_BLOCK_SIZE;
+
+ if(pdstBuf->max_len > MAX_MALLOC_SIZE)
+ {
+ pdstBuf->max_len -= block_num*REALLOC_BLOCK_SIZE;
+ return -1;
+ }
+
+ if(pdstBuf->buf == NULL)
+ {
+ pdstBuf->buf = (char *)malloc(pdstBuf->max_len);
+ }
+ else
+ {
+ pdstBuf->buf = (char *)realloc(pdstBuf->buf, pdstBuf->max_len);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/*********************************************************************
+函数名称:buf_elem_check_size
+功能简介:
+输入参数:
+输出参数:无
+返回值:-1 出错 0 正常
+*********************************************************************/
+int buf_elem_check_size(stMailElem *pdstBuf, int wantedlen)
+{
+ int needlen, block_num=0;
+
+ if(pdstBuf->max_len > wantedlen + pdstBuf->buflen)
+ return 0;
+
+ needlen = wantedlen + pdstBuf->buflen + 1 - pdstBuf->max_len; // +1 for '\0'
+ block_num = needlen/BUF_ELEM_BLOCK;
+ if(needlen%BUF_ELEM_BLOCK)
+ block_num += 1;
+
+ pdstBuf->max_len += block_num*BUF_ELEM_BLOCK;
+
+ if(pdstBuf->buf == NULL)
+ {
+ pdstBuf->buf = (char *)malloc(pdstBuf->max_len);
+ }
+ else
+ {
+ pdstBuf->buf = (char *)realloc(pdstBuf->buf, pdstBuf->max_len);
+ }
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:process_originaldata
+功能简介:将data拷贝到originaldata的buffer中
+输入参数:mailinfo:邮件关键信息
+ data:本次要拷贝的原始数据
+ datalen:本次要拷贝的原始数据的长度
+输出参数:
+返回值:-1 出错 0 正常
+*********************************************************************/
+int save_original_data(stBufCache *pbuf, const char *data, int datalen)
+{
+ if(buf_cache_check_size(pbuf, datalen) < 0)
+ {
+ return -1;
+ }
+
+ memcpy((char *)pbuf->buf + pbuf->len, data, datalen);
+ pbuf->len += datalen;
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:mail_clear_mailinfo
+功能简介:清空邮件处理数据结构(tdMailInfo: session_info->app_info),释放缓冲区
+参数:mailpme:
+返回值: 无
+*********************************************************************/
+static void mail_clear_mailinfo(tdMailInfo *plug_mailinfo)
+{
+ if(plug_mailinfo->pMailInfo->username->buf)
+ free(plug_mailinfo->pMailInfo->username->buf);
+ if(plug_mailinfo->pMailInfo->password->buf)
+ free(plug_mailinfo->pMailInfo->password->buf);
+ if(plug_mailinfo->pMailInfo->sendaddr->buf)
+ free(plug_mailinfo->pMailInfo->sendaddr->buf);
+ if(plug_mailinfo->pMailInfo->recvaddr->buf)
+ free(plug_mailinfo->pMailInfo->recvaddr->buf);
+ if(plug_mailinfo->pMailInfo->subject->buf)
+ free(plug_mailinfo->pMailInfo->subject->buf);
+ if(plug_mailinfo->pMailInfo->date->buf)
+ free(plug_mailinfo->pMailInfo->date->buf);
+
+ if(plug_mailinfo->elem != NULL)
+ free(plug_mailinfo->elem);
+}
+
+
+/*********************************************************************
+函数名称:clear_mailpme
+功能简介:清空邮件处理数据结构,释放缓冲区
+参数:mailpme:
+返回值: 无
+*********************************************************************/
+void mail_clear_mailpme(stMailPme * mailpme)
+{
+ if(mailpme->is_called)
+ {
+ mailpme->plug_mailinfo.buf = (char *)mailpme->pOriginalData.buf;
+ mailpme->plug_mailinfo.buflen = mailpme->pOriginalData.len;
+ }
+ mail_clear_mailinfo(&mailpme->plug_mailinfo);
+ clear_mime_info(mailpme->mimeinfo);
+
+ if(mailpme->pDataLineBuf.buf)
+ free(mailpme->pDataLineBuf.buf);
+ if(mailpme->pOriginalData.buf)
+ free(mailpme->pOriginalData.buf);
+ if(mailpme->pMimeDecodeBuf.buf)
+ free(mailpme->pMimeDecodeBuf.buf);
+ if(mailpme->pBizRegion.buf)
+ free(mailpme->pBizRegion.buf);
+ free(mailpme);
+
+}
+
+/*********************************************************************
+函数名称:init_mailinfo
+功能简介:初始化mailinfo结构体;
+参数:mailinfo:stMailInfoOld 结构体;
+返回值:succ:0 error:<0;
+*********************************************************************/
+static int mail_init_mailinfo(stMailPme *mailpme)
+{
+ tdMailInfo *pmailinfo = &mailpme->plug_mailinfo;
+
+ memset(pmailinfo, 0, sizeof(tdMailInfo));
+
+ if(mailpme->iProtoType == MAIL_SERVICE_SMTP)
+ pmailinfo->protocol = SMTP_PROTOCOL;
+ else if(mailpme->iProtoType == MAIL_SERVICE_POP3)
+ pmailinfo->protocol = POP3_PROTOCOL;
+ else
+ pmailinfo->protocol = IMAP_PROTOCOL;
+
+ pmailinfo->pMailInfo = &mailpme->pMailinfo;
+ pmailinfo->pMailInfo->username = &mailpme->keyinfo.username;
+ pmailinfo->pMailInfo->password = &mailpme->keyinfo.password;
+ pmailinfo->pMailInfo->sendaddr= &mailpme->keyinfo.sendaddr;
+ pmailinfo->pMailInfo->recvaddr = &mailpme->keyinfo.recvaddr;
+ pmailinfo->pMailInfo->subject = &mailpme->keyinfo.subject;
+ pmailinfo->pMailInfo->date = &mailpme->keyinfo.date;
+
+ pmailinfo->elem = (stFieldElem *)malloc(sizeof(stFieldElem)*MAIL_ELEM_NUM);
+ mailpme->max_elem_num = MAIL_ELEM_NUM;
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:init_mailpme
+功能简介:初始化邮件处理数据结构
+输入参数:mailpme:
+输出参数:无
+返回值: -1:出错, 0:正常
+*********************************************************************/
+char mail_init_mailpme(stMailPme** mailpme_pointer,char ServiceType)
+{
+ stMailPme* mailpme = NULL;
+
+ mailpme = (stMailPme *)malloc(sizeof(stMailPme));
+ memset(mailpme, 0, sizeof(stMailPme)); //MUST DO IT!
+ mailpme->iProtoType = ServiceType;
+
+ mail_init_mailinfo(mailpme);
+
+ init_mime_info(&mailpme->mimeinfo);
+
+ //other info
+ mailpme->EmlAnalyseState=MAIL_STAT_INIT;
+ mailpme->MailInfoState=SMTP_NO_USER;
+ mailpme->pre_dir = -1;
+ mailpme->pending_state = 1;
+
+ (*mailpme_pointer)=mailpme;
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:init_mailinfo
+功能简介:初始化mailinfo结构体;
+参数:mailinfo:stMailInfoOld 结构体;
+返回值:succ:0 error:<0;
+*********************************************************************/
+static void mail_reset_mailinfo(tdMailInfo *pmailinfo)
+{
+ /*if(pmailinfo->pMailInfo->username->buf != NULL) //同一个链接中最初有用户名密码,后续的会话可以继续获得它
+ {
+ pmailinfo->pMailInfo->username->buflen = 0;
+ pmailinfo->pMailInfo->username->buf[0] = '\0';
+ }
+ if(pmailinfo->pMailInfo->password->buf != NULL)
+ {
+ pmailinfo->pMailInfo->password->buflen = 0;
+ pmailinfo->pMailInfo->password->buf[0] = '\0';
+ }*/
+ if(pmailinfo->pMailInfo->sendaddr->buf != NULL)
+ {
+ pmailinfo->pMailInfo->sendaddr->buflen = 0;
+ pmailinfo->pMailInfo->sendaddr->buf[0] = '\0';
+ }
+ if(pmailinfo->pMailInfo->recvaddr->buf != NULL)
+ {
+ pmailinfo->pMailInfo->recvaddr->buflen = 0;
+ pmailinfo->pMailInfo->recvaddr->buf[0] = '\0';
+ }
+ if(pmailinfo->pMailInfo->subject->buf != NULL)
+ {
+ pmailinfo->pMailInfo->subject->buflen = 0;
+ pmailinfo->pMailInfo->subject->buf[0] = '\0';
+ }
+ if(pmailinfo->pMailInfo->date->buf != NULL)
+ {
+ pmailinfo->pMailInfo->date->buflen = 0;
+ pmailinfo->pMailInfo->date->buf[0] = '\0';
+ }
+
+ pmailinfo->trans_enc = MAIL_TRANS_ENC_UNKNOWN;
+ pmailinfo->session_seq++;
+ pmailinfo->buflen = 0;
+ pmailinfo->buf = NULL;
+ pmailinfo->current_charset = NULL;
+
+ if(pmailinfo->elem_num > 0)
+ {
+ pmailinfo->elem_num = 0;
+ }
+}
+
+
+/*********************************************************************
+函数名称:mail_reset_mailpme_noclose
+功能简介:为新会话重置邮件处理数据结构;不调用业务层SESSION_CLOSE
+输入参数:mailpme:
+输出参数:无
+返回值: 无
+*********************************************************************/
+char mail_reset_mailpme_noclose(stMailPme * mailpme)
+{
+ mailpme->pDataLineBuf.len = 0;
+ mailpme->pOriginalData.len = 0;
+ mailpme->pMimeDecodeBuf.len = 0;
+ mailpme->pBizRegion.len = 0;
+
+ mail_reset_mailinfo(&mailpme->plug_mailinfo);
+ reset_mime_info(mailpme->mimeinfo, 0);
+
+ mailpme->subject_charset[0] = '\0';
+ mailpme->EmlAnalyseState = MAIL_STAT_INIT;
+ mailpme->MailInfoState = SMTP_NO_USER;
+ //mailpme->s_total_loss = 0;
+
+ mailpme->maybe_end = 0; //IMAP协议,上一次MAYBE_END,下一个是FETCH或结束命令,需要清空该状态
+ mailpme->is_called = 0;
+ mailpme->pending_state = 1;
+ return 0;
+}
+
+void mail_close_business(stMailPme * mailpme)
+{
+ if(mailpme->is_called)
+ {
+ mailpme->is_called = 0;
+ }
+}
+
+/*********************************************************************
+函数名称:reset_mailpme
+功能简介:为新会话重置邮件处理数据结构;先调用业务层SESSION_CLOSE
+输入参数:mailpme:
+输出参数:无
+返回值: 无
+*********************************************************************/
+char mail_reset_mailpme(stMailPme * mailpme)
+{
+ return mail_reset_mailpme_noclose(mailpme);
+}
+
+/*********************************************************************
+函数名称:process_payload_r
+功能简介:处理tcpdetail结构体中data数据,根据pDataLineBuf及payload来获取一完整行;
+ 如果从payload中取得\r后,\r之后还有\n\t或\n空格,则更新payload,调用者
+ 接着进行循环,重复上述操作。
+参数:mailpme:
+返回值:
+ MAIL_INPUT_END: 输入结束了
+ MAIL_FOLD_LINE_END: 找到完整行了
+ MAIL_FOLD_LINE_START: 需要进一步循环
+ MAIL_GETLINE_ERR:出错
+*********************************************************************/
+static int process_payload_r(char **p_payload,int *p_payload_len,char **line_start,int *plinelen,stMailPme * mailpme)
+{
+ int len;
+ char *poss_n = NULL;
+ char *payload = *p_payload;
+ int payload_len = *p_payload_len;
+
+ poss_n = (char *)memchr(payload, '\n', payload_len);
+ if(poss_n != NULL && poss_n >= payload + payload_len)
+ {
+ poss_n = NULL;
+ }
+
+ if(poss_n == NULL || poss_n + 1 >= payload + payload_len) //不足以断定是折叠行,继续缓存
+ {
+ while(mailpme->pDataLineBuf.len >0 && (*((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len-1)=='\r' || *((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len-1)=='\n'))
+ mailpme->pDataLineBuf.len--;
+
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, payload_len)<0)
+ {
+ return MAIL_GETLINE_ERR;
+ }
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, payload_len);
+ mailpme->pDataLineBuf.len += payload_len;
+ return MAIL_INPUT_END;
+ }
+ else if(*(poss_n+1) != '\t' && *(poss_n+1) != ' ') //a fold line
+ {
+ len = poss_n - payload + 1; //包含换行符长度
+ mailpme->iDataPointer += len;
+
+ if(mailpme->pDataLineBuf.len > 0)
+ {
+ while(mailpme->pDataLineBuf.len >0 && (*((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len-1)=='\r' || *((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len-1)=='\n'))
+ mailpme->pDataLineBuf.len--;
+
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, len)<0)
+ {
+ return MAIL_GETLINE_ERR;
+ }
+
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, len);
+ mailpme->pDataLineBuf.len += len;
+
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ }
+ else
+ {
+ *line_start = payload;
+ *plinelen = len;
+ }
+ return MAIL_FOLD_LINE_END;
+ }
+ else //未找到换行符或是折叠行,缓存之
+ {
+ len = poss_n - payload + 1;
+
+ while(mailpme->pDataLineBuf.len>0 && (*((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len-1)=='\r' || *((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len-1)=='\n'))
+ mailpme->pDataLineBuf.len--;
+
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, len)<0)
+ {
+ return MAIL_GETLINE_ERR;
+ }
+
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, len);
+ mailpme->pDataLineBuf.len += len;
+ payload += len;
+ payload_len -= len;
+ mailpme->iDataPointer += len;
+
+ *p_payload = payload;
+ *p_payload_len = payload_len;
+ return MAIL_FOLD_LINE_START;
+ }
+}
+
+/*********************************************************************
+函数名称:GetMailFoldLine
+功能简介:从之前缓冲区和当前待处理数据中找到完整行,
+ 如果能够找到完整行,则提取完整行内容,并且去除特殊符号
+ 如果不能找到完整行,则当前数据拷贝到缓冲区中待下次处理
+输入参数:payload:当前待处理数据
+ payload_len:当前待处理数据的长度
+ mailpme:上下文信息,其中pDataLineBuf为缓冲区
+ thread_seq:线程号,主要为了dictator系列参数使用
+输出参数:line_start:目的地址指针
+ plinelen :目标长度
+返回值:是否处理完毕
+*********************************************************************/
+static int mail_get_fold_line(char *payload,int payload_len,char **line_start,int *plinelen, stMailPme *mailpme)
+{
+ char *posd_n = NULL;
+ int ret, flag=0;
+
+ if(payload_len == 0)
+ return MAIL_INPUT_END;
+
+ while(payload_len > 0)
+ {
+ if(mailpme->pDataLineBuf.len > 0)
+ {
+ posd_n = (char *)memrchr(mailpme->pDataLineBuf.buf, '\n', mailpme->pDataLineBuf.len);
+ if(posd_n != NULL && posd_n >= (char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len)
+ {
+ posd_n = NULL;
+ }
+ }
+ else
+ posd_n = NULL;
+
+ if(posd_n != NULL)
+ {
+ if(posd_n+1 < (char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len && *(posd_n+1)=='\t')
+ {
+ mailpme->pDataLineBuf.len -= 1;//\t
+ ret = process_payload_r(&payload, &payload_len, line_start, plinelen, mailpme);
+ if(ret == MAIL_FOLD_LINE_START)
+ continue;
+ else
+ return ret;
+ }
+ else if(*payload != '\t' && *payload != ' ')
+ {
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ return MAIL_FOLD_LINE_END;
+ }
+ else
+ {
+ while(payload_len>0 && (*payload == '\t' || *payload == ' '))
+ {
+ payload++;
+ payload_len--;
+ mailpme->iDataPointer++;
+ flag = 1;
+ }
+ if(payload_len == 0)
+ {
+ if(flag)
+ {
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, 1)<0)
+ return MAIL_GETLINE_ERR;
+ *((char*)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len) = '\t';
+ mailpme->pDataLineBuf.len += 1;
+ }
+ return MAIL_INPUT_END;
+ }
+ flag = 0;
+
+ ret = process_payload_r(&payload, &payload_len, line_start, plinelen, mailpme);
+ if(ret == MAIL_FOLD_LINE_START)
+ continue;
+ else
+ return ret;
+ }
+ }
+ else
+ {
+ if(mailpme->pDataLineBuf.len == 0)
+ {
+ if(*payload=='\n') //一个\n空行
+ {
+ mailpme->iDataPointer += 1;
+ *line_start = payload;
+ *plinelen = 1;
+ return MAIL_FOLD_LINE_END;
+ }
+ else if(payload_len>=2 && *payload=='\r' && *(payload+1) == '\n') //一个\r\n空行
+ {
+ mailpme->iDataPointer += 2;
+ *line_start = payload;
+ *plinelen = 2;
+ return MAIL_FOLD_LINE_END;
+ }
+ }
+
+ ret = process_payload_r(&payload, &payload_len, line_start, plinelen, mailpme);
+ if(ret == MAIL_FOLD_LINE_START)
+ continue;
+ else
+ return ret;
+ }
+ }
+
+ return MAIL_INPUT_END;
+}
+
+
+/*********************************************************************
+函数名称:GetEmlLineCore
+功能简介:非折叠行时,从给定的缓冲区内,提取一行数据,并返回;
+输入参数:wantedlen:需要的空间大小;rn:输出是否带换行符
+输出参数:line_start:目的地址指针
+ plinelen :目标长度
+返回值:-1 出错 0 正常
+*********************************************************************/
+int mail_get_one_tcpline(char *payload,int payload_len,char **line_start,int *plinelen,stMailPme * mailpme, int rn)
+{
+ char *pos_n = NULL;
+ int datalen;
+
+ pos_n = (char *)memchr(payload, '\n', payload_len);
+ if(pos_n == NULL) //直接缓存,不管大小?这一行还没传输完;
+ {
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, payload_len)<0)
+ {
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ return MAIL_LINE_END;
+ }
+
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, payload_len);
+ mailpme->pDataLineBuf.len += payload_len;
+ mailpme->iDataPointer += payload_len;
+ return MAIL_INPUT_END;
+ }
+ else //找到了一个完整行
+ {
+ if(rn)
+ {
+ datalen = pos_n - payload + 1;
+ mailpme->iDataPointer += datalen;
+ }
+ else
+ {
+ datalen = pos_n - payload;
+ mailpme->iDataPointer += datalen + 1; //include \n
+ }
+
+ //上一个包缓冲了部分数据,则首先进行拼接
+ if(mailpme->pDataLineBuf.len > 0)
+ {
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, datalen)<0)
+ {
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ mailpme->iDataPointer -= rn?datalen:(datalen+1);
+ return MAIL_LINE_END;
+ }
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, datalen);
+ mailpme->pDataLineBuf.len += datalen;
+
+ if(rn==0)
+ {
+ pos_n = (char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len - 1;
+ if(*pos_n == '\r') //100% properly
+ mailpme->pDataLineBuf.len -= 1;
+ }
+
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ return MAIL_LINE_END;
+ }
+
+ if(rn==0 && datalen > 0)
+ {
+ if(*(payload+datalen-1) == '\r')
+ datalen = datalen - 1;
+ }
+ *line_start = payload;
+ *plinelen = datalen;
+ return MAIL_LINE_END;
+ }
+}
+
+/*********************************************************************
+函数名称:mail_get_complete_region
+功能简介:非折叠行时,从给定的缓冲区内,提取最大限度的同类型数据,
+ 直到遇到特殊字符或包结束;考虑到了IMAP协议')'之前没有换行的情况
+输入参数:wantedlen:需要的空间大小;
+输出参数:line_start:目的地址指针
+ plinelen :目标长度
+返回值:获取行状态
+*********************************************************************/
+static int mail_get_complete_region(char *payload,int payload_len,char **line_start,int *plinelen,stMailPme * mailpme)
+{
+ char *pos_n = NULL;
+ char *srcend = payload + payload_len, *pc=payload;
+ int datalen;
+
+ //找截止符: \r\n#, where # is '\r' or '-'
+ while(pc < srcend && (pos_n = (char *)memchr(pc, '\n', srcend-pc)) != NULL)
+ {
+ mailpme->iDataPointer += pos_n - pc + 1;
+ pc = pos_n + 1;
+ datalen = pc-payload;
+
+ if(mailpme->pDataLineBuf.len>0)//开头是特殊字符,或者上一次残留数据无换行
+ {
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, datalen)<0)
+ {
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ mailpme->iDataPointer -= datalen;
+ return MAIL_LINE_END;
+ }
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, datalen);
+ mailpme->pDataLineBuf.len += datalen;
+
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ return MAIL_LINE_END;
+ }
+ //本次BODY和下个BOUNDARY之间可能没有空行,所以要比较'-';
+ //结束符"."和前面的数据如果在一个包里,需要从BODY中分离出;POP3常见;
+ //符号')'在IMAP中作为结束符
+ else if(pc < srcend && (*pc=='-' || (*pc=='.' && mailpme->plug_mailinfo.protocol!=IMAP_PROTOCOL) || (*pc==')' && mailpme->plug_mailinfo.protocol==IMAP_PROTOCOL))) //--BB\r\n)\r\n
+ {
+ *line_start = payload;
+ *plinelen = datalen;
+ return MAIL_LINE_END;
+ }
+ else if((payload_len>=2 && *payload=='-' && *(payload+1)=='-') || ((pc-payload<=3) && (*payload=='.' || *payload==')')))
+ {
+ *line_start = payload;
+ *plinelen = datalen;
+ return MAIL_LINE_END;
+ }
+ else if(pc-payload>=3 && *(pc-3)==')' && mailpme->plug_mailinfo.protocol==IMAP_PROTOCOL) //')'之前没有换行;与上一个if语句顺序不能变,因为')'之前可能有换行
+ {
+ mailpme->iDataPointer -= 3; //把')'留在缓冲区等待下次处理
+ pc = pc-3; //包含')',后面会当成'\n'减掉这个长度;征文POP头部也有
+ *line_start = payload;
+ *plinelen = pc-payload;
+ return MAIL_LINE_END;
+ }
+ }
+
+ datalen = pc - payload;
+ if(datalen > 0) //找到了\n
+ {
+ mailpme->iDataPointer += srcend - pc;
+ *line_start = payload;
+ *plinelen = payload_len;
+ return MAIL_PACKET_END;
+ }
+ else
+ {
+ //很长的一行直接返回,有的正文没换行
+ //存储的原因是:有时候处理到包尾部,剩余长度较小,协议比较关键字时可能会误命中
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, payload_len)<0)
+ {
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ return MAIL_LINE_END;
+ }
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, payload_len);
+ mailpme->pDataLineBuf.len += payload_len;
+ mailpme->iDataPointer += payload_len;
+
+ *line_start = NULL;
+ *plinelen = 0;
+ return MAIL_INPUT_END;
+ }
+
+ return MAIL_INPUT_END;
+}
+
+/*********************************************************************
+函数名称:mail_get_tcpdata
+功能简介:从邮件会话的tcp数据流中,获取一个数据行,其中完成了对邮件折叠行
+的拼接处理,去除了数据流中的回车换行。非完整行和非折叠行数据保存在mailpme->sDataline中
+输入参数:a_tcp:邮件数据流
+ mailpme:邮件结构体
+ need_folding:1需要处理折叠行 =0不需要处理折叠行
+ line_start:
+输出参数:line_start:分离出来的数据的指针
+返回值: 标志邮件行处理的进展情况,是行结束,还是输入结束等等
+*********************************************************************/
+int mail_get_tcpdata(const char *payload, size_t payload_len, stMailPme* mailpme, int need_folding, char ** output_buf,int *output_len)
+{
+ char* curpointer = (char*)(payload) + mailpme->iDataPointer;
+
+ if((unsigned int)(mailpme->iDataPointer) >= (unsigned int)(payload_len))
+ return MAIL_INPUT_END;
+
+ if(need_folding == 1)
+ {
+ return mail_get_fold_line(curpointer, payload_len-mailpme->iDataPointer, output_buf, output_len, mailpme);
+ }
+ else if(mailpme->EmlAnalyseState != MAIL_STAT_BODY || mailpme->obtain_line)
+ {
+ if(mailpme->obtain_line)
+ mailpme->obtain_line = 0;
+ return mail_get_one_tcpline(curpointer, payload_len-mailpme->iDataPointer, output_buf, output_len, mailpme, 1);
+ }
+ else
+ {
+ return mail_get_complete_region(curpointer, payload_len-mailpme->iDataPointer, output_buf, output_len, mailpme);
+ }
+}
+
+/*********************************************************************
+函数名称:get_a_tcp_line
+功能简介:从邮件会话的tcp数据流中,获取一个数据行,其中完成了对邮件折叠行
+的拼接处理,去除了数据流中的回车换行。非完整行和非折叠行数据保存在mailpme->sDataline中
+输入参数:a_tcp:邮件数据流
+ mailpme:邮件结构体
+ need_folding:1需要处理折叠行 =0不需要处理折叠行
+ line_start:
+输出参数:line_start:分离出来的数据的指针
+返回值: 标志邮件行处理的进展情况,是行结束,还是输入结束等等
+*********************************************************************/
+int mail_get_atcpline_c2s(const char *payload, size_t payload_len, stMailPme* mailpme, char ** output_buf,int *output_len)
+{
+ char* curpointer = (char*)(payload) + mailpme->iDataPointer;
+
+ if((unsigned int)(mailpme->iDataPointer) >= (unsigned int)(payload_len))
+ return MAIL_INPUT_END;
+
+ return mail_get_one_tcpline(curpointer, payload_len-mailpme->iDataPointer, output_buf, output_len, mailpme, 0);
+}
+
+/*********************************************************************
+函数名称:mail_get_only_mailaddr
+功能简介:从输入串srcstr中提取邮件地址信息
+输入参数:
+ srcstr:输入数据串
+ payload_len: 输入串长度
+输出参数:addrbegin:邮件地址开始位置
+ addrlen:邮件地址长度
+返回值:<0:非法邮件地址;>0: 正常
+*********************************************************************/
+static int mail_get_only_mailaddr(char *srcstr,int payload_len,char **addrbegin,int *addrlen)
+{
+ char *mailbegin=NULL, *mailend=NULL, *mailflag=NULL;
+ char *srcend;
+
+ if(payload_len<=0 || srcstr==NULL)
+ return -1;
+ srcend = srcstr + payload_len;
+
+ //add by lqy 20070529
+ //避免出现传入数据很长,但是都没有<,要对数据进行全部扫描,
+ //认为邮件地址信息不应该很长,只扫描前128个就可以判断出有没有<
+ if(payload_len>128)
+ mailflag=(char *)memchr((void *)srcstr,'<',128);
+ else
+ mailflag=(char *)memchr((void *)srcstr,'<',payload_len);
+
+ if(mailflag!=NULL)
+ {
+ mailbegin = mailflag+1;
+ while(mailbegin<srcend && (*mailbegin==' ' || *mailbegin=='\t' ||*mailbegin=='<'))
+ mailbegin++;
+
+ mailend = (char *)memchr((void *)mailbegin, '>', srcend-mailbegin+1);
+ if(mailend == NULL)
+ {
+ return -2;
+ }
+ if((mailend-mailbegin)>0 && memchr((void *)mailbegin, '@', mailend-mailbegin)!=NULL)
+ {
+ *addrbegin=mailbegin;
+ *addrlen=(int)(mailend-mailbegin);
+ return (mailend-srcstr);
+ }
+ else
+ {
+ //char p[128]={0};
+ //memcpy(p, mailflag, (mailend-mailflag+1)>=128?127:(mailend-mailflag+1));
+ return -3;
+ }
+ }
+ else
+ {
+ mailflag=srcstr;
+ while(mailflag<srcend && (*mailflag)!='@')
+ {
+ mailflag++;
+ }
+ if(((*mailflag)!='@')||(mailflag==srcstr))
+ {
+ return -2;
+ }
+
+ mailbegin=mailflag-1;
+ while(mailbegin>srcstr && *mailbegin!=' ' && *mailbegin!='\t' && *mailbegin!=',' && *mailbegin!=';')
+ {
+ mailbegin--;
+ }
+ mailbegin++;
+
+ mailend=mailflag+1;
+ while(mailend<srcend && *mailend!=' ' && *mailbegin!='\t' && *mailend!=',' && *mailend!=';')
+ {
+ mailend++;
+ }
+
+ if(mailend-mailbegin<3)
+ return -3;
+
+ *addrbegin=mailbegin;
+ *addrlen=(int)(mailend-mailbegin);
+ }
+ return (mailend-srcstr);
+}
+
+
+/*********************************************************************
+函数名称:fn_GetMailAddr
+
+功能简介:从输入串srcstr中提取邮件地址信息,并且添加到地址列表缓冲 pdstBuf当中
+输入参数:pdstBuf:邮件地址缓冲区
+ srcstr:输入数据串
+输出参数:无
+返回值:-1 出错 0 正常
+*********************************************************************/
+int mail_get_mailaddr(stMailElem *pdstBuf, char *srcstr, int datalen)
+{
+ int ret=0;
+ char *addrbuf=NULL;
+ int addrlen=0;
+
+ if(pdstBuf==NULL || srcstr==NULL || datalen<=0)
+ return -5;
+
+ while(datalen>0)
+ {
+ ret=mail_get_only_mailaddr(srcstr, datalen, &addrbuf, &addrlen);
+ if(ret<0)
+ break;
+
+ if(buf_elem_check_size(pdstBuf, addrlen+2) < 0)
+ return -6;
+
+ //添加<标志,添加邮件地址,添加>标志
+ pdstBuf->buf[pdstBuf->buflen] = '<';
+ pdstBuf->buflen += 1;
+ memcpy(pdstBuf->buf + pdstBuf->buflen, addrbuf, addrlen);
+ pdstBuf->buflen += addrlen;
+ pdstBuf->buf[pdstBuf->buflen] = '>';
+ pdstBuf->buflen += 1;
+
+ srcstr += ret;
+ datalen -= ret;
+ }
+
+ if(pdstBuf->buf == NULL)
+ return -7;
+
+ pdstBuf->buf[pdstBuf->buflen] = '\0';
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:process_smtp_cmd_user_pass
+功能简介:处理邮件头data命令,
+输入参数:
+ mailpme:邮件处理结构体
+输出参数:无
+返回值: -1:error
+*********************************************************************/
+int mail_save_mail_elem(char *payload, int datalen, stMailElem *pelem)
+{
+ if(buf_elem_check_size(pelem, datalen))
+ return -1;
+
+ memcpy(pelem->buf+pelem->buflen, payload, datalen);
+ pelem->buflen += datalen;
+ pelem->buf[pelem->buflen] = '\0';
+
+ return 0;
+}
+
+void drop_set_value(stMailPme* mailpme)
+{
+ mailpme->EmlAnalyseState = MAIL_STAT_BODY; //消除获取折叠行的可能
+ mailpme->mimeinfo->text_begin = 1;
+}
+
+
+int mail_get_line(struct evbuffer *line_cache, char *payload, int payload_len, char **line_start, char **line_end)
+{
+ char *pos_n = NULL;
+ int datalen;
+
+ pos_n = (char *)memchr(payload, '\n', payload_len);
+ if(pos_n == NULL) //直接缓存,不管大小?这一行还没传输完;
+ {
+
+ if(buf_cache_check_size(line_cache, payload_len)<0)
+ {
+ *line_start = (char *)line_cache.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ return MAIL_LINE_END;
+ }
+
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, payload_len);
+ mailpme->pDataLineBuf.len += payload_len;
+ mailpme->iDataPointer += payload_len;
+ return MAIL_INPUT_END;
+ }
+ else //找到了一个完整行
+ {
+ if(rn)
+ {
+ datalen = pos_n - payload + 1;
+ mailpme->iDataPointer += datalen;
+ }
+ else
+ {
+ datalen = pos_n - payload;
+ mailpme->iDataPointer += datalen + 1; //include \n
+ }
+
+ //上一个包缓冲了部分数据,则首先进行拼接
+ if(mailpme->pDataLineBuf.len > 0)
+ {
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, datalen)<0)
+ {
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ mailpme->iDataPointer -= rn?datalen:(datalen+1);
+ return MAIL_LINE_END;
+ }
+ memcpy((char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len, payload, datalen);
+ mailpme->pDataLineBuf.len += datalen;
+
+ if(rn==0)
+ {
+ pos_n = (char *)mailpme->pDataLineBuf.buf + mailpme->pDataLineBuf.len - 1;
+ if(*pos_n == '\r') //100% properly
+ mailpme->pDataLineBuf.len -= 1;
+ }
+
+ *line_start = (char *)mailpme->pDataLineBuf.buf;
+ *plinelen = mailpme->pDataLineBuf.len;
+ return MAIL_LINE_END;
+ }
+
+ if(rn==0 && datalen > 0)
+ {
+ if(*(payload+datalen-1) == '\r')
+ datalen = datalen - 1;
+ }
+ *line_start = payload;
+ *plinelen = datalen;
+ return MAIL_LINE_END;
+ }
+}
+
+
+int mail_analy_emlheader(const char *a_mail_line,int datalen,int EmlAnalyseState);
+int process_mail_data(int EML_opst,char *maildata_line,int datalen,stMailPme * mailpme);
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "mail_common.h"
+#include "mail_mime.h"
+#include "mail_analyze.h"
+
+static int save_region_data(stMailPme *mailpme, unsigned long long prot_flag, void *buf, int buflen, char *charset, int copy)
+{
+ if(mailpme->plug_mailinfo.elem_num >= mailpme->max_elem_num)
+ {
+ mailpme->max_elem_num += MAIL_ELEM_NUM;
+ mailpme->plug_mailinfo.elem = (stFieldElem *)realloc(mailpme->plug_mailinfo.elem, sizeof(stFieldElem)*mailpme->max_elem_num);
+ if(mailpme->plug_mailinfo.elem == NULL)
+ return -1;
+ }
+
+ if(copy)
+ {
+ int ret;
+
+ ret = buf_cache_check_size(&mailpme->pBizRegion, buflen);
+ if(ret < 0)
+ return -1;
+ else if(ret == 1) //重新分配了缓存,需要更新一下指针
+ {
+ int i, offset=0;
+
+ for(i=0;i<mailpme->plug_mailinfo.elem_num;i++)
+ {
+ mailpme->plug_mailinfo.elem[i].buf = (char *)mailpme->pBizRegion.buf + offset;
+ offset += mailpme->plug_mailinfo.elem[i].buflen;
+ }
+ }
+ memcpy((char*)mailpme->pBizRegion.buf + mailpme->pBizRegion.len, buf, buflen);
+ mailpme->plug_mailinfo.elem[mailpme->plug_mailinfo.elem_num].buf = (char *)mailpme->pBizRegion.buf + mailpme->pBizRegion.len;
+ mailpme->pBizRegion.len += buflen;//只在回调业务后清空
+ }
+ else
+ {
+ mailpme->plug_mailinfo.elem[mailpme->plug_mailinfo.elem_num].buf = buf;
+ }
+
+ mailpme->plug_mailinfo.elem[mailpme->plug_mailinfo.elem_num].buflen = buflen;
+ mailpme->plug_mailinfo.elem[mailpme->plug_mailinfo.elem_num].prot_flag = prot_flag;
+ mailpme->plug_mailinfo.elem[mailpme->plug_mailinfo.elem_num].current_charset = charset;
+ mailpme->plug_mailinfo.elem_num++;
+
+ return 0;
+}
+
+static int process_date_eml(const char *maildata, int datalen, stMailPme *mailpme)
+{
+ int ret;
+ int decoded_len;
+ char *decoded;
+ char decode_buf[MAX_ORDER_LINE_SIZE+1];
+ char charset_str_buf[MAIL_MAX_CHARSET_LEN];
+ stMailElem *pelem = mailpme->plug_mailinfo.pMailInfo->date;
+
+ if(datalen >= MAX_ORDER_LINE_SIZE) {
+ return 0;
+ }
+ if(datalen <=5) {
+ return -1;
+ }
+
+ maildata += 5;
+ datalen -= 5;
+
+ decoded_len = sizeof(decode_buf);
+ ret = mime_header_decode(maildata, datalen, decode_buf, &decoded_len, charset_str_buf, sizeof(charset_str_buf));
+ if(ret < 0) {
+ decoded = (char *)maildata;
+ decoded_len = datalen;
+ } else {
+ decoded = decode_buf;
+ }
+
+ if(buf_elem_check_size(pelem, decoded_len) < 0) {
+ return -1;
+ }
+ memcpy(pelem->buf, decoded, decoded_len);
+ pelem->buflen = decoded_len;
+ pelem->buf[pelem->buflen] = '\0';
+ return 0;
+}
+
+static int process_mailfrom_eml(char *maildata,int datalen,stMailPme *mailpme)
+{
+ int ret;
+ int decoded_len;
+ char *decoded;
+ char decode_buf[MAX_ORDER_LINE_SIZE+1];
+ char charset_str_buf[MAIL_MAX_CHARSET_LEN];
+
+ //from:
+ if(datalen<=5) {
+ return -1;
+ }
+
+ maildata += 5;
+ datalen -= 5;
+
+ if(mailpme->plug_mailinfo.pMailInfo->sendaddr->buflen<=0)
+ {
+ decoded_len = sizeof(decode_buf);
+ ret = mime_header_decode(maildata, datalen, decode_buf, &decoded_len, charset_str_buf, sizeof(charset_str_buf));
+ if(ret < 0) {
+ decoded = (char *)maildata;
+ decoded_len = datalen;
+ } else {
+ decoded = decode_buf;
+ }
+
+ ret = mail_get_mailaddr(mailpme->plug_mailinfo.pMailInfo->sendaddr, decoded, decoded_len);
+ if(ret<0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int process_mailto_eml(char *maildata,int datalen,stMailPme * mailpme)
+{
+ int ret;
+ int decoded_len;
+ char *decoded;
+ char decode_buf[MAX_ORDER_LINE_SIZE+1];
+ char charset_str_buf[MAIL_MAX_CHARSET_LEN];
+
+ //bcc:
+ if(datalen<=4) {
+ return -1;
+ }
+
+ if(strncmp_one_word_mesa("bcc:", "BCC:", 4, maildata, datalen)) {
+ maildata += 4;//错过长度"bcc: "
+ datalen -= 4;
+ } else {
+ maildata += 3;//错过长度"cc: "或者"to: "
+ datalen -= 3;
+ }
+
+ decoded_len = sizeof(decode_buf);
+ ret = mime_header_decode(maildata, datalen, decode_buf, &decoded_len, charset_str_buf, sizeof(charset_str_buf));
+ if(ret < 0) {
+ decoded = (char *)maildata;
+ decoded_len = datalen;
+ } else {
+ decoded = decode_buf;
+ }
+
+ ret = mail_get_mailaddr(mailpme->plug_mailinfo.pMailInfo->recvaddr, decoded, decoded_len);
+ if(ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int process_subject_eml(char *maildata,int datalen,stMailPme * mailpme)
+{
+ if(datalen>=MAX_ORDER_LINE_SIZE)
+ return -1;
+
+ maildata += 8;datalen -= 8; //subject:
+ while(datalen>0 && (*maildata==' ' || *maildata=='\t'))
+ {
+ maildata++; datalen--;
+ }
+ if(datalen<=0)
+ {
+ return -1;
+ }
+
+ int ret=0;
+ char out[MAX_ORDER_LINE_SIZE+1], *pout=out;
+ int outl = MAX_ORDER_LINE_SIZE+1;
+ stMailElem *pelem = mailpme->plug_mailinfo.pMailInfo->subject;
+
+ ret = mime_header_decode(maildata, datalen, pout, &outl, mailpme->subject_charset, MAIL_MAX_CHARSET_LEN);
+ if(ret<0)
+ {
+ pout = maildata;
+ outl = datalen;
+ }
+
+ if(buf_elem_check_size(pelem, outl) < 0)
+ {
+ return -1;
+ }
+ memcpy(pelem->buf, pout, outl);
+ pelem->buflen = outl;
+ pelem->buf[pelem->buflen] = '\0';
+ return 0;
+}
+
+int mail_analy_emlheader(const char *a_mail_line,int datalen,int EmlAnalyseState)
+{
+ if(EmlAnalyseState == MAIL_STAT_BODY)
+ return EML_DATA_BODY_IN;
+
+ const char *pos_colon;
+
+ /*MAIL_INIT和MAILHEADER 状态*/
+ pos_colon = (const char *)memchr(a_mail_line, ':', datalen>26?26:datalen);
+ if(pos_colon != NULL)
+ {
+ datalen = pos_colon-a_mail_line;
+
+ switch(datalen)
+ {
+ case 2:
+ if(strcmp_one_word_mesa_equal_len("to", "TO", a_mail_line, 2))
+ return EML_DATA_TO;
+ if(strcmp_one_word_mesa_equal_len("cc", "CC", a_mail_line, 2))
+ return EML_DATA_CC;
+ break;
+
+ case 3:
+ if(strcmp_one_word_mesa_equal_len("bcc", "BCC", a_mail_line, 3))
+ return EML_DATA_BCC;
+ break;
+
+ case 4:
+ if(strcmp_one_word_mesa_equal_len("date", "DATE", a_mail_line, 4))
+ return EML_DATA_DATE;
+ if(strcmp_one_word_mesa_equal_len("from", "FROM", a_mail_line, 4))
+ return EML_DATA_FROM;
+ break;
+
+ case 7:
+ if(strcmp_one_word_mesa_equal_len("subject", "SUBJECT", a_mail_line, 7))
+ return EML_DATA_SUBJECT;
+ break;
+
+ case 8:
+ if(strcmp_one_word_mesa_equal_len("received", "RECEIVED", a_mail_line, 8))
+ return EML_DATA_RECEIVED;
+ if(strcmp_one_word_mesa_equal_len("reply-to", "REPLY-TO", a_mail_line, 8))
+ return EML_DATA_REPLY_TO;
+ break;
+
+ case 11:
+ if(strcmp_one_word_mesa_equal_len("return-path", "RETURN-PATH", a_mail_line, 11))
+ return EML_DATA_RETURN_PATH;
+ break;
+
+ case 12:
+ if(strcmp_one_word_mesa_equal_len("delivered-to", "DELIVERED-TO", a_mail_line, 12))
+ return EML_DATA_DELIVERED_TO;
+ if(MAIL_STAT_HEADER == EmlAnalyseState)
+ {
+ if(strcmp_one_word_mesa_equal_len("content-type", "CONTENT-TYPE", a_mail_line, 12))
+ return EML_DATA_CONTENTTYPE;
+ return EML_DATA_HEAD_IN;
+ }
+ break;
+
+ case 25:
+ if(MAIL_STAT_HEADER == EmlAnalyseState)
+ {
+ if(strcmp_one_word_mesa_equal_len("content-transfer-encoding", "CONTENT-TRANSFER-ENCODING", a_mail_line, 25))
+ return EML_DATA_CONTENTTRANSFERENCODING;
+ return EML_DATA_HEAD_IN;
+ }
+ break;
+
+ default:
+ if(MAIL_STAT_HEADER != EmlAnalyseState && strncmp_one_word_mesa("x-", "X-", 2, a_mail_line, datalen))
+ return EML_DATA_HEAD_IN;
+ break;
+ }
+
+ //以下几个状态只有头部状态才能返回 //adjust by lqy 20100204
+ if(MAIL_STAT_HEADER == EmlAnalyseState)
+ {
+ return EML_DATA_HEAD_IN;
+ }
+ }
+ else if(MAIL_STAT_HEADER == EmlAnalyseState)
+ {
+ if((datalen==2 && !strncmp(a_mail_line, "\r\n", 2))||(datalen==1 && *a_mail_line=='\n'))
+ return EML_DATA_HEAD_BODY_BORDER;
+
+ return EML_DATA_HEAD_IN;
+ }
+
+ return EML_UNKNOWN;
+}
+
+static int process_data_bodyin_cmd(char *a_mail_line, int datalen,stMailPme * mailpme)
+{
+ int ret;
+ MimeParse_t * mimeinfo = mailpme->mimeinfo;
+
+ //沿用TODO
+ if((mailpme->MailInfoState == MAIL_GET_FILECOUNT)||(mailpme->MailInfoState == MAIL_GET_FILENAME))
+ {
+ mailpme->MailInfoState = MAIL_GET_FILECOUNT; //邮件附件内容
+ }
+ else if(mailpme->MailInfoState != MAIL_GET_FILECOUNT_END)
+ {
+ mailpme->MailInfoState = MAIL_GET_COUNT; //邮件正文内容
+ }
+
+ mimeinfo->src = a_mail_line; //src是一个指向mime解码前数据的指针
+ mimeinfo->srcLen = datalen;
+
+ if(buf_cache_check_size(&mailpme->pMimeDecodeBuf, mimeinfo->srcLen+1) < 0)
+ {
+ return -1;
+ }
+ mimeinfo->dst = (char *)mailpme->pMimeDecodeBuf.buf;
+ mimeinfo->dstSize = mailpme->pMimeDecodeBuf.max_len;
+ mailpme->pMimeDecodeBuf.len = 0;
+
+ ret = mime_parse_feed(mimeinfo, 1);
+ if(ret<0)
+ {
+ return -1;
+ }
+
+ if((ret == MIME_LINE_CONT_TYPE || ret == MIME_LINE_CONT_DISP) && mimeinfo->filename != NULL)
+ {
+ mailpme->MailInfoState = MAIL_GET_FILENAME;
+ }
+ else if(ret == MIME_LINE_CONT_OTHR) //如果在FILENAME之后来,会冲掉MAIL_GET_FILENAME状态,导致附件内容无法解析
+ {
+ mailpme->mimeinfo->actLen = 0;
+ //mailpme->MailInfoState = MAIL_GET_OTHER;
+ }
+ else if(ret == MIME_LINE_CONT_BODY)
+ {
+ if(mailpme->MailInfoState == MAIL_GET_FILECOUNT || mailpme->MailInfoState == MAIL_GET_FILENAME || \
+ mailpme->MailInfoState == MAIL_GET_FILECOUNT_END)
+ {
+ mailpme->MailInfoState = MAIL_GET_FILECOUNT; //邮件附件内容
+ }
+ else
+ {
+ mailpme->MailInfoState = MAIL_GET_COUNT; //邮件正文内容
+ }
+ }
+ else if(ret == MIME_LINE_BOUNARY || ret == MIME_LINE_BOUNARY_END)
+ {
+ if(mailpme->MailInfoState == MAIL_GET_FILECOUNT)
+ {
+ mailpme->MailInfoState = MAIL_GET_FILECOUNT_END;
+ }
+ }
+
+ return 0;
+}
+
+int process_mail_data(int EML_opst, char *maildata_line,int datalen,stMailPme * mailpme)
+{
+ int processRet, tmp_len, norn_len=datalen;
+ long long prot_flag=0;
+ void *buf=NULL;
+ int buflen=0, region_offest;
+ char *charset = NULL;
+
+ switch(EML_opst)
+ {
+ case EML_DATA_DATE:
+ if(mailpme->EmlAnalyseState != MAIL_STAT_HEADER)
+ {
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+ mailpme->MailInfoState = MAIL_GET_DATE;
+
+ while(norn_len > 0 && (*(maildata_line + norn_len -1)=='\n' || *(maildata_line + norn_len -1)=='\r'))
+ norn_len -= 1;
+
+ processRet=process_date_eml(maildata_line, norn_len, mailpme); //-2:去掉\r\n
+ if(processRet<0)
+ {
+ break;
+ }
+
+ prot_flag = MAIL_DATE;
+ buf = mailpme->plug_mailinfo.pMailInfo->date->buf;
+ buflen = mailpme->plug_mailinfo.pMailInfo->date->buflen;
+ break;
+
+ case EML_DATA_FROM:
+ if(mailpme->EmlAnalyseState != MAIL_STAT_HEADER)
+ {
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+ mailpme->MailInfoState = MAIL_GET_FROM;
+
+ while(norn_len > 0 && (*(maildata_line + norn_len -1)=='\n' || *(maildata_line + norn_len -1)=='\r'))
+ norn_len -= 1;
+
+ mailpme->plug_mailinfo.pMailInfo->sendaddr->buflen = 0;
+ processRet = process_mailfrom_eml(maildata_line, norn_len, mailpme);
+ if(processRet<0)
+ {
+ break;
+ }
+
+ prot_flag = MAIL_FROM;
+ buf = mailpme->plug_mailinfo.pMailInfo->sendaddr->buf;
+ buflen = mailpme->plug_mailinfo.pMailInfo->sendaddr->buflen;
+ break;
+
+ case EML_DATA_TO:
+ if(mailpme->EmlAnalyseState != MAIL_STAT_HEADER)
+ {
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+ mailpme->MailInfoState = MAIL_GET_TO;
+
+ while(norn_len > 0 && (*(maildata_line + norn_len -1)=='\n' || *(maildata_line + norn_len -1)=='\r'))
+ norn_len -= 1;
+
+ tmp_len = mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen;
+
+ processRet = process_mailto_eml(maildata_line, norn_len, mailpme);
+ if(processRet<0)
+ {
+ break;
+ }
+
+ prot_flag = MAIL_TO;
+ buf = mailpme->plug_mailinfo.pMailInfo->recvaddr->buf + tmp_len;
+ buflen = mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen - tmp_len;
+ break;
+
+ case EML_DATA_CC:
+ if(mailpme->EmlAnalyseState != MAIL_STAT_HEADER)
+ {
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+ mailpme->MailInfoState = MAIL_GET_TO; //TODO
+
+ while(norn_len > 0 && (*(maildata_line + norn_len -1)=='\n' || *(maildata_line + norn_len -1)=='\r'))
+ norn_len -= 1;
+
+ tmp_len = mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen;
+
+ processRet = process_mailto_eml(maildata_line, norn_len, mailpme);
+ if(processRet<0)
+ {
+ break;
+ }
+
+ prot_flag = MAIL_CC;
+ buf = mailpme->plug_mailinfo.pMailInfo->recvaddr->buf + tmp_len;
+ buflen = mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen - tmp_len;
+ break;
+
+ case EML_DATA_BCC:
+ if(mailpme->EmlAnalyseState != MAIL_STAT_HEADER)
+ {
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+ mailpme->MailInfoState = MAIL_GET_TO; //TODO
+
+ while(norn_len > 0 && (*(maildata_line + norn_len -1)=='\n' || *(maildata_line + norn_len -1)=='\r'))
+ norn_len -= 1;
+
+ tmp_len = mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen;
+
+ processRet = process_mailto_eml(maildata_line, norn_len, mailpme);
+ if(processRet<0)
+ {
+ break;
+ }
+
+ prot_flag = MAIL_BCC;
+ buf = mailpme->plug_mailinfo.pMailInfo->recvaddr->buf + tmp_len;
+ buflen = mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen - tmp_len;
+ break;
+
+ case EML_DATA_REPLY_TO:
+ if(mailpme->EmlAnalyseState != MAIL_STAT_HEADER)
+ {
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+ mailpme->MailInfoState = MAIL_GET_TO;
+
+ while(norn_len > 0 && (*(maildata_line + norn_len -1)=='\n' || *(maildata_line + norn_len -1)=='\r'))
+ norn_len -= 1;
+
+ tmp_len = mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen;
+ processRet = mail_get_mailaddr(mailpme->plug_mailinfo.pMailInfo->recvaddr, maildata_line+9, norn_len-9);
+ if(processRet<0)
+ {
+ break;
+ }
+
+ prot_flag = MAIL_REPLY_TO;
+ buf = mailpme->plug_mailinfo.pMailInfo->recvaddr->buf + tmp_len;
+ buflen = mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen - tmp_len;
+ mailpme->plug_mailinfo.pMailInfo->recvaddr->buflen = tmp_len; //临时借用缓冲区,之后恢复
+ break;
+ case EML_DATA_SUBJECT:
+ if(mailpme->EmlAnalyseState != MAIL_STAT_HEADER)
+ {
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+ mailpme->MailInfoState = MAIL_GET_SUB; //TODO
+
+ while(norn_len > 0 && (*(maildata_line + norn_len -1)=='\n' || *(maildata_line + norn_len -1)=='\r'))
+ norn_len -= 1;
+
+ processRet = process_subject_eml(maildata_line, norn_len, mailpme);
+ if(processRet<0)
+ {
+ break;
+ }
+
+ prot_flag = MAIL_SUBJECT;
+ buf = mailpme->plug_mailinfo.pMailInfo->subject->buf;
+ buflen = mailpme->plug_mailinfo.pMailInfo->subject->buflen;
+ charset = mailpme->subject_charset;
+ break;
+
+ case EML_DATA_CONTENTTYPE:
+ case EML_DATA_CONTENTTRANSFERENCODING:
+ case EML_DATA_BODY_IN:
+ processRet = process_data_bodyin_cmd(maildata_line, datalen, mailpme);
+ if(processRet < 0)
+ {
+ return -1;;
+ }
+
+ if(mailpme->MailInfoState == MAIL_GET_FILENAME)
+ {
+ charset = mailpme->mimeinfo->filename_charset;
+ prot_flag = MAIL_ATTACH_NAME;
+
+ buflen = mailpme->mimeinfo->filenameLen;
+ if(buflen == 0)
+ {
+ buf = (void *)NULL_FILENAME;
+ buflen = NULL_FILENAME_LEN;
+ }
+ else
+ {
+ buf = mailpme->mimeinfo->filename;
+ }
+ }
+ //Content-type得到附件名后,还有其他Ccontent-xxx字段,此时正文没开始
+ else if(mailpme->MailInfoState == MAIL_GET_FILECOUNT && mailpme->mimeinfo->text_begin==1)
+ {
+ charset = mailpme->mimeinfo->charset;
+ prot_flag = MAIL_ATTACH_CONTENT;
+ buf = mailpme->mimeinfo->dst;
+ buflen = mailpme->mimeinfo->actLen;
+ }
+ else if(mailpme->MailInfoState == MAIL_GET_COUNT)
+ {
+ charset = mailpme->mimeinfo->charset;
+ prot_flag = MAIL_CONTENT;
+ buf = mailpme->mimeinfo->dst;
+ buflen = mailpme->mimeinfo->actLen;
+ }
+ break;
+
+ case EML_DATA_HEAD_BODY_BORDER: //进入消息体处理状态,邮件头部完毕
+ mailpme->EmlAnalyseState = MAIL_STAT_BODY;
+ //对于没有使用MIME的邮件,进入正文后应该设置正文开始标记text_begin
+ //否则按照现在的处理流程,会按照折叠行来获取正文数据
+ body_update_text_begin(mailpme->mimeinfo);
+ break;
+
+ case EML_DATA_RETURN_PATH:
+ case EML_DATA_RECEIVED:
+ case EML_DATA_DELIVERED_TO:
+ case EML_DATA_HEAD_IN:
+ if(mailpme->EmlAnalyseState != MAIL_STAT_HEADER)
+ {
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+
+ while(norn_len > 0 && (*(maildata_line + norn_len -1)=='\n' || *(maildata_line + norn_len -1)=='\r'))
+ norn_len -= 1;
+
+ prot_flag = MAIL_OTHER;
+ region_offest = 0;
+ char *p = (char *)memchr(maildata_line, ':', norn_len);
+ int region_len = (p==NULL)?0:(p-maildata_line);
+ region_offest = region_len+1;
+ while(region_offest<norn_len && (*(maildata_line + region_offest)==' ' || *(maildata_line + region_offest)=='\t'))
+ {
+ region_offest++;
+ }
+
+ buf = maildata_line + region_offest;
+ buflen = norn_len - region_offest; //exlude "\r\n"
+ break;
+
+ default:break;
+ }
+
+ mailpme->plug_mailinfo.cur_offset = mailpme->pOriginalData.len;
+ processRet = save_original_data(&mailpme->pOriginalData, maildata_line, datalen);
+ if(processRet < 0)
+ {
+ return -1;
+ }
+
+ if(buflen > 0)
+ {
+ if(save_region_data(mailpme, prot_flag, buf, buflen, charset, 0))
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "mail_global.h"
+#include "mail_parser.h"
+
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+enum MAIL_STATE {
+ MAIL_STATE_INIT,
+ MAIL_STATE_HEADER_START,
+ MAIL_STATE_HEADER_END,
+ MAIL_STATE_BODY_START,
+ MAIL_STATE_BODY_END,
+ MAIL_STATE_EXIT,
+};
+
+struct mail_parser {
+ enum MAIL_STATE state;
+
+ enum MAIL_PROTOCOL protocol;
+ size_t mail_seq;
+ stMailPme *mailpme;
+};
+
+enum MAIL_STATE mail_parser_get_mail_state(struct mail_parser *parser)
+{
+ return parser->state;
+}
+
+enum MAIL_PROTOCOL mail_parser_get_protocol(struct mail_parser *parser)
+{
+ return parser->protocol;
+}
+
+size_t mail_parser_get_seq(struct mail_parser *parser)
+{
+ return parser->mail_seq;
+}
+
+struct mail_header *mail_parser_get_header(struct mail_parser *parser)
+{
+ return NULL;
+}
+
+int mail_parser_get_body(struct mail_parser *parser, char **body, size_t *body_len, size_t *body_offset, int *is_body_finished)
+{
+ return 0;
+}
+
+int mail_parser_get_attachment(struct mail_parser *parser, char **attachment_name, size_t *attachment_name_len, char **attachment, size_t *attachment_len, size_t *attachment_offset, int *is_attachment_finished)
+{
+ return 0;
+}
+
+int mail_parser_get_eml(struct mail_parser *parser, char **eml, size_t *eml_len, size_t *eml_offset, int *is_eml_finished)
+{
+ return 0;
+}
+
+int mail_parser_process(struct mail_parser *parser,const char *payload, size_t payload_len, int is_c2s)
+{
+ return 0;
+}
+
+void mail_parser_free(struct mail_parser *parser)
+{
+ if (parser) {
+ if (parser->mailpme) {
+ mail_reset_mailpme_noclose(parser->mailpme);
+ }
+ free(parser);
+ }
+}
+
+struct mail_parser *mail_parser_new(enum MAIL_PROTOCOL protocol)
+{
+ struct mail_parser *parser = NULL;
+
+ parser = (struct mail_parser *)calloc(1, sizeof(struct mail_parser));
+ mail_init_mailpme(&parser->mailpme);
+
+ return parser;
+}
diff --git a/decoders/mail/mail_email.h b/decoders/mail/mail_email.h
new file mode 100644
index 0000000..77ca3c7
--- /dev/null
+++ b/decoders/mail/mail_email.h
@@ -0,0 +1,342 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sys/time.h>
+#include "mail_internal.h"
+#include "mail_mime.h"
+
+typedef enum
+{
+ MAIL_USERNAME_MASK=0,
+ MAIL_PASSWARD_MASK,
+ MAIL_FROM_MASK,
+ MAIL_TO_MASK,
+ MAIL_CC_MASK,
+ MAIL_BCC_MASK,
+ MAIL_DATE_MASK,
+ MAIL_SUBJECT_MASK,
+ MAIL_CONTENT_MASK,
+ MAIL_ATTACH_NAME_MASK,
+ MAIL_ATTACH_CONTENT_MASK,
+ MAIL_OTHER_MASK,
+ MAIL_FROM_CMD_MASK,
+ MAIL_TO_CMD_MASK,
+ MAIL_EHLO_CMD_MASK,
+ MAIL_REPLY_TO_MASK,
+
+ /*ADD*/
+ MAIL_REGION_NUM,
+ MAIL_STARTTLS_CMD_MASK,
+}mail_interested_region_mask;
+
+//邮件各字段flag对应值;程序中定义的宏在字段前加"MAIL_",配置文件中注册的字段没有前面的"MAIL_"
+//配置文件中支持"ALL"
+//配置文件注册字段大小写敏感
+#define MAIL_USERNAME ((long long)1<<MAIL_USERNAME_MASK)
+#define MAIL_PASSWARD ((long long)1<<MAIL_PASSWARD_MASK)
+#define MAIL_FROM ((long long)1<<MAIL_FROM_MASK)
+#define MAIL_TO ((long long)1<<MAIL_TO_MASK)
+#define MAIL_CC ((long long)1<<MAIL_CC_MASK)
+#define MAIL_BCC ((long long)1<<MAIL_BCC_MASK)
+#define MAIL_DATE ((long long)1<<MAIL_DATE_MASK)
+#define MAIL_SUBJECT ((long long)1<<MAIL_SUBJECT_MASK)
+#define MAIL_CONTENT ((long long)1<<MAIL_CONTENT_MASK)
+#define MAIL_ATTACH_NAME ((long long)1<<MAIL_ATTACH_NAME_MASK)
+#define MAIL_ATTACH_CONTENT ((long long)1<<MAIL_ATTACH_CONTENT_MASK)
+#define MAIL_OTHER ((long long)1<<MAIL_OTHER_MASK)
+#define MAIL_FROM_CMD ((long long)1<<MAIL_FROM_CMD_MASK)
+#define MAIL_TO_CMD ((long long)1<<MAIL_TO_CMD_MASK)
+#define MAIL_EHLO_CMD ((long long)1<<MAIL_EHLO_CMD_MASK)
+#define MAIL_REPLY_TO ((long long)1<<MAIL_REPLY_TO_MASK)
+#define MAIL_STARTTLS_CMD ((long long)1<<MAIL_STARTTLS_CMD_MASK)
+
+#define SMTP_PROTOCOL 1
+#define POP3_PROTOCOL 2
+#define IMAP_PROTOCOL 3
+
+
+//传输编码
+#define MAIL_TRANS_ENC_UNKNOWN 0
+#define MAIL_TRANS_ENC_BASE64 1
+#define MAIL_TRANS_ENC_QP 2
+
+typedef struct _mail_elem_info
+{
+ int buflen;
+ int max_len;
+ char* buf;
+}stMailElem;
+
+typedef struct _mail_key_info
+{
+ stMailElem *username; //用户名
+ stMailElem *password; //用户密码
+ stMailElem *sendaddr; //发信地址
+ stMailElem *recvaddr; //收信地址
+ stMailElem *subject; //邮件主题
+ stMailElem *date; //发信时间
+} stKeyInfo;
+
+typedef struct _field_element
+{
+ long long prot_flag;
+ char* current_charset; //当前字符集编码格式 ;可能为NULL;
+ void *buf; //当前字段对应原始数据内容, 经过传输编码解析
+ int buflen; //当前字段对应原始数据长度
+}stFieldElem;
+
+typedef struct _mail_pme_info
+{
+ char protocol; //邮件协议SMTP_PROTOCOL/POP3_PROTOCOL/IMAP_PROTOCOL
+ char trans_enc; //传输编码,只有邮件正文、附件有;主题和附件名中是否有传输编码需要业务插件自行解析
+ short session_seq; //当前会话是TCP链接中的第几个会话,从0开始;
+ int buflen; //邮件原始数据长度
+ char* buf; //邮件原始数据内容,不经过传输编码解析
+ char* current_charset; //当前字符集编码格式 ;可能为NULL;
+ stKeyInfo *pMailInfo; //保留邮件的关键信息
+
+ //NEXT: ADD BY ZCW
+ int cur_offset; //当前字段在原始数据buf中的的偏移
+ stFieldElem *elem;
+ int elem_num;
+} tdMailInfo;
+
+
+#define MAIL_GLOBALMODULE "[MAIL_GLOBAL]"
+
+/****************************包获取相关宏******************************/
+/*协议编号*/
+#define MAIL_SERVICE_SMTP 6
+#define MAIL_SERVICE_POP3 7
+#define MAIL_SERVICE_IMAP4 5
+
+/*数据流一行获取状态*/
+#define MAIL_GETLINE_ERR -1
+#define MAIL_INPUT_END 0
+#define MAIL_FOLD_LINE_END 1
+#define MAIL_FOLD_LINE_START 2
+#define MAIL_LINE_END 3
+#define MAIL_PACKET_END 4
+
+/* 当丢包造成的状态错位时,通过检验新来包长度的办法区分正文和命令*/
+#define MAIL_FROM_LEN 120 //包含mail from 的包的应用层数据尺寸的上限。用于处理同一连接多封信时的丢包。
+#define RSETQUIT_CMD_LEN 20 //包含reset/quit命令 的包的应用层数据尺寸的上限。
+#define BDAT_CMD_LEN 50 //包含bdat命令 的包的应用层数据尺寸的上限。
+#define MAX_MAIL_USER_LEN 512
+
+/*lqy 060331 最大保存关键字的长度,用于处理关键字跨包的情况*/
+#define MAX_ORDER_LINE_SIZE 2048
+#define MAIL_LINE_MAX_LEN (20*1024)
+#define MAX_USER_PASS_LEN 50 //用户名密码的最大长度
+
+
+/************************邮件处理相关宏**********************************/
+/*邮件处理状态*/
+#define MAIL_STAT_INIT 0x10//邮件尚未状态
+#define MAIL_STAT_HEADER 0x11//邮件头部处理状态
+#define MAIL_STAT_BODY 0x12//邮件体处理状态
+#define MAIL_STAT_END 0x13//邮件结束状态
+
+/*邮件主体各个部分定义*/
+enum EML_DATA_FIELD
+{
+ EML_DATA_HEAD_IN=1,
+ EML_DATA_BODY_IN,
+ EML_DATA_FROM,
+ EML_DATA_TO,
+ EML_DATA_CC,
+ EML_DATA_BCC, /*6*/
+ EML_DATA_SUBJECT,
+ EML_DATA_CONTENTTYPE,
+ EML_DATA_CONTENTTRANSFERENCODING,
+ EML_DATA_HEAD_BODY_BORDER,
+ EML_DATA_END, /*11*/
+ EML_DATA_RETURN_PATH,
+ EML_DATA_DELIVERED_TO,
+ EML_DATA_RECEIVED,
+ EML_DATA_DATE, /*15*/
+ EML_DATA_REPLY_TO,
+ EML_UNKNOWN,
+};
+
+/*用户密码获取状态,比特位方式;对于AUTH PLAIN方式,用户密码会一起获得*/
+#define SMTP_NO_USER 0
+#define SMTP_HAVE_USER (1<<0)
+#define SMTP_BEFORE_USER (1<<1)
+#define SMTP_BEFORE_PASS (1<<2)
+#define SMTP_AFTER_PASS (1<<3)
+#define SMTP_AUTH_PLAIN (1<<4)
+
+
+#define MAIL_GET_NONE -1
+#define MAIL_GET_FROM 9
+#define MAIL_GET_TO 10
+#define MAIL_GET_SUB 11
+#define MAIL_GET_DATE 12
+#define MAIL_GET_COUNT 13
+#define MAIL_GET_FILENAME 14
+#define MAIL_GET_FILECOUNT 15
+#define MAIL_GET_OTHER 16
+#define MAIL_GET_FILECOUNT_END 17
+
+/*stBufCache缓冲区大小控制相关宏*/
+#define MAX_MALLOC_SIZE (64*1024)
+#define REALLOC_BLOCK_SIZE 1024
+#define BUF_ELEM_BLOCK 128
+
+#ifndef MAIL_MAX_CHARSET_LEN
+#define MAIL_MAX_CHARSET_LEN 32
+#endif
+
+//批量回调时预先分配的单元个数
+#define MAIL_ELEM_NUM 16
+
+//该结构体为避免初始化时分配很多次内存
+typedef struct _new_mail_key_info
+{
+ stMailElem username; //用户名
+ stMailElem password; //用户密码
+ stMailElem sendaddr; // 发信地址
+ stMailElem recvaddr; // 收信地址
+ stMailElem subject; //邮件主题
+ stMailElem date; //发信时间
+}new_stKeyInfo;
+
+typedef struct _buf_cache
+{
+ void *buf;
+ int len;
+ int max_len;
+}stBufCache;
+
+typedef struct _stMailPme
+{
+ stBufCache pDataLineBuf; //获取数据包缓存区
+ stBufCache pOriginalData; //原始邮件信息
+ stBufCache pMimeDecodeBuf; //MIME解码后的存储区
+ stBufCache pBizRegion; //批量回调业务层的字段buf缓存区
+
+ tdMailInfo plug_mailinfo; //session_info->app_info
+ stKeyInfo pMailinfo; //plug_mailinfo.pMailinfo
+ new_stKeyInfo keyinfo; //plug_mailinfo.pMailinfo的成员
+ stFieldElem elem;
+
+ MimeParse_t *mimeinfo; // mime解码结构
+ char subject_charset[MAIL_MAX_CHARSET_LEN]; //主题字符集编码格式
+
+ int iDataPointer; // 当前读取的数据偏移,用于按行读取中
+ int max_elem_num;
+
+ int EmlAnalyseState;// 标志邮件处理状态
+ int MailInfoState; //zcw add, from stMailInfoOld
+
+ int iProtoType; //邮件会话结构的协议类型
+
+ char pending_state; //1-为新的会话刚准备好上下文,0-新会话已到来
+ char is_called; //当前会话回调过业务层
+ char pre_dir; //上一个流的方向,用于双方向缓存数据的时候使用,对于SMTP只考虑单方向的忽略此变量
+
+ //NEXT ONLY IMAP4
+ char is_continue;
+ char maybe_end; //')'不能确定结束与否的时候设置标记
+ short obtain_line; //点名接下来获取一行,级别低于折叠行
+ int pre_opsate; //')'不能确定结束与否的时候保留上一次的行类型
+ long pre_uid; //IMAP协议按照字节数获取邮件内容的情况
+ long uid;
+
+ void* app_pme;
+}stMailPme;
+
+int buf_elem_check_size(stMailElem *pdstBuf, int wantedlen);
+int buf_cache_check_size(stBufCache *pbuf, int wantedlen);
+
+int save_original_data(stBufCache *pbuf, const char *data, int datalen);
+
+char mail_init_mailpme(stMailPme** mailpme_pointer,char ServiceType, int is_c2s);
+void mail_clear_mailpme(stMailPme * mailpme);
+char mail_reset_mailpme(stMailPme * mailpme);
+char mail_reset_mailpme_noclose(stMailPme * mailpme);
+void mail_close_business(stMailPme * mailpme);
+
+int mail_get_atcpline_c2s(const char *payload, size_t payload_len, stMailPme* mailpme, char ** output_buf,int *output_len);
+int mail_get_tcpdata(const char *payload, size_t payload_len, stMailPme* smtpinfo, int need_folding, char ** output_buf,int *output_len);
+int mail_get_mailaddr(stMailElem *pdstBuf,char *srcstr,int datalen);
+int mail_save_mail_elem(char *srcdata, int datalen, stMailElem *pelem);
+
+
+
+
+
+
+#include "stellar/mail.h"
+
+struct email_header {
+ struct iovec *from;
+ struct iovec *to;
+ struct iovec *cc;
+ struct iovec *bcc;
+ struct iovec *reply_to;
+ struct iovec *date;
+ struct iovec *subject;
+
+ struct iovec *header_fields;
+ size_t n_header_fields;
+};
+
+struct email_body {
+ const char *body;
+ size_t body_len;
+ size_t body_offset;
+};
+
+struct email_attachment {
+ const char *attachment_name;
+ size_t attachment_name_len;
+ const char *attachment;
+ size_t attachment_len;
+ size_t attachment_offset;
+};
+
+struct email_eml {
+ const char *eml;
+ size_t eml_len;
+ size_t eml_offset;
+};
+
+enum email_state {
+ MAIL_HEADER,
+ MAIL_,
+ MAIL_CONTENT,
+ MAIL_ATTACHMENT,
+};
+
+struct email_parser {
+ enum email_state state;
+ struct email_header header;
+ struct email_body body;
+ struct email_attachment attachment;
+ struct email_eml eml;
+};
+
+struct mail_parser;
+
+enum MAIL_PROTOCOL email_parser_get_protocol(struct email_parser *parser);
+size_t email_parser_get_seq(struct email_parser *parser);
+int email_parser_get_command(struct email_parser *parser, enum MAIL_COMMAND *cmd, char **cmd_param, size_t *cmd_param_len, char **cmd_line, size_t *cmd_line_len);
+struct email_header *mail_parser_get_header(struct email_parser *parser);
+int email_parser_get_body(struct email_parser *parser, char **body, size_t *body_len, size_t *body_offset, int *is_body_finished);
+int email_parser_get_attachment(struct email_parser *parser, char **attachment_name, size_t *attachment_name_len, char **attachment, size_t *attachment_len, size_t *attachment_offset, int *is_attachment_finished);
+int email_parser_get_eml(struct email_parser *parser, char **eml, size_t *eml_len, size_t *eml_offset, int *is_eml_finished);
+
+int email_parser_process(struct email_parser *parser, const char *payload, size_t payload_len, int is_c2s);
+
+struct mail_parser *email_parser_init();
+void email_parser_exit(struct email_parser *parser);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/decoders/mail/mail_imap.c b/decoders/mail/mail_imap.c
new file mode 100644
index 0000000..dcc6ecf
--- /dev/null
+++ b/decoders/mail/mail_imap.c
@@ -0,0 +1,1966 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <assert.h>
+
+#include "mail_internal.h"
+#include "mail_util.h"
+#include "mail_email.h"
+
+extern stMailLocalInfo g_mail_localinfo;
+
+static void imap_free_node(void *data)
+{
+ mime_item_t *pitem = (mime_item_t *)data;
+
+ if(pitem!=NULL)
+ {
+ if(pitem->mime_header.buf != NULL)
+ dictator_free(pitem->thread_id, pitem->mime_header.buf);
+ if(pitem->attach_filename != NULL)
+ dictator_free(pitem->thread_id, pitem->attach_filename);
+ if(pitem->boundary != NULL)
+ dictator_free(pitem->thread_id, pitem->boundary);
+ dictator_free(pitem->thread_id, pitem);
+ }
+}
+
+void imap_free_htable_cb(void *data)
+{
+ imap_free_node(data);
+
+ if(g_mail_localinfo.stat_trig)
+ {
+ FS_operate(g_mail_localinfo.fsstat_handle, g_mail_localinfo.stat_id[STAT_ID_HTABLE_NUM], 0, FS_OP_ADD, -1);
+ }
+}
+
+/*********************************************************************
+函数名称:get_value_parenthesis
+功能简介:从参数组合列表中取得相应变量的值
+输入参数:name1/name2可能的变量
+输出参数:out:变量的值的字符串起始指针;outl:字符串长度
+返回值:1-取得了变量值;0-未找到
+*********************************************************************/
+static int get_value_parenthesis(const char *ps, const char *p_end, const char *name1, int len1, const char *name2, int len2, const char **out, int *outl)
+{
+ const char *pe;
+
+ if((ps = mesa_memncasemem(ps, p_end-ps, name1, len1)) != NULL)
+ {
+ ps += len1;
+ }
+ else if((ps = mesa_memncasemem(ps, p_end-ps, name2, len2)) != NULL)
+ {
+ ps += len2;
+ }
+ else
+ {
+ return 0;
+ }
+
+ while(ps<p_end && *ps==' ')
+ ps++;
+
+ if(*ps=='"')
+ {
+ pe = ps+1;
+ while(pe<p_end && *pe != '"')
+ pe++;
+ if(pe == p_end)
+ return 0;
+ pe++;
+ }
+ else
+ {
+ pe = ps;
+ while(pe<p_end && *pe!=' ')
+ pe++;
+ }
+
+ *out = ps;
+ *outl = pe-ps;
+ return 1;
+}
+
+/*********************************************************************
+函数名称:look_for_parenthesis_end
+功能简介:查找一对圆括号的结束位置,输入不带第1个左圆括号;
+输入参数:p:输入起始,不带'('字符;p_end:输入结束(不含);
+输出参数:end:结束位置(含右括号);
+返回值:NULL-查找失败;其他-结束位置
+*********************************************************************/
+static const char *look_for_parenthesis_end(const char *p, const char *p_end)
+{
+ int complete=1; //左圆括号在调用处已略过
+
+ while(p<p_end)
+ {
+ if(*p == '"')
+ {
+ p = (const char *)memchr(p+1, '"', p_end-p-1);
+ if(p==NULL)
+ return NULL;
+ }
+ else if(*p == ')')
+ {
+ complete--;
+ }
+ else if(*p == '(')
+ {
+ complete++;
+ }
+ if(complete==0)
+ {
+ return p;
+ }
+ p++;
+ }
+
+ return NULL;
+}
+
+/*********************************************************************
+函数名称:look_for_field_end
+功能简介:查找一个字符串的起始位置,可能带"也可能不带;
+输入参数:p:输入起始;p_end:输入结束(不含);
+输出参数:end:结束位置(不含);
+返回值:NULL-查找失败;其他-字符串起始位置
+*********************************************************************/
+static const char *look_for_string_end(const char *p, const char *p_end, const char **end)
+{
+ const char *ps;
+
+ if(*p == '"')
+ {
+ ps=p+1;
+ p = (const char *)memchr(ps, '"', p_end-ps);
+ if(p==NULL)
+ return NULL;
+ }
+ else
+ {
+ ps = p;
+ p = (const char *)memchr(ps, ' ', p_end-ps);
+ if(p==NULL)
+ return NULL;
+ }
+ *end = p;
+
+ return ps;
+}
+
+/*********************************************************************
+函数名称:parse_not_multipart_line
+功能简介:解析一行multipart块头部,将相应的结果写入mime_item_t结构体
+输入参数:
+输出参数:
+返回值:0-正常;-1-失败
+*********************************************************************/
+static int parse_multipart_line(mime_item_t *pitem, const char *line, int linelen)
+{
+ int i, len;
+ const char *p=line, *p_end=line+linelen, *ps;
+
+ for(i=MULTI_SUBTYPE; i<=MULTI_PARENTHESIS&&(p<p_end); i++)
+ {
+ while(*p==' ')
+ p++;
+
+ switch(i)
+ {
+ case MULTI_SUBTYPE:
+ if((ps = look_for_string_end(p, p_end, &p)) == NULL)
+ {
+ return -1;
+ }
+#ifdef DBG_FULL
+ save_original_data(&pitem->mime_header, "\r\nContent-Type:multipart/", 25, pitem->thread_id);
+#else
+ save_original_data(&pitem->mime_header, "Content-Type:multipart/", 23, pitem->thread_id);
+#endif
+ save_original_data(&pitem->mime_header, ps, p-ps, pitem->thread_id);
+ p++;
+ break;
+
+ case MULTI_PARENTHESIS:
+ if(*p != '(') //没有参数组合列表,后续的不处理了,也可能后续没有扩展字段
+ {
+ p = p_end;
+ break;
+ }
+ ps = p+1;
+ if((p=look_for_parenthesis_end(ps, p_end))==NULL || *p!=')')
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "look_for_parenthesis_end error, input not complete().");
+ break;
+ }
+ if(get_value_parenthesis(ps, p, "\"boundary\" ", 11, "boundary ", 9, &ps, &len))
+ {
+ save_original_data(&pitem->mime_header, ";boundary=", 10, pitem->thread_id);
+ save_original_data(&pitem->mime_header, ps, len, pitem->thread_id);
+
+ if(*ps == '"')
+ {
+ ps++;
+ len-=2;
+ }
+ pitem->boundary = (char *)dictator_malloc(pitem->thread_id, len+1);
+ memcpy(pitem->boundary, ps, len);
+ pitem->boundary[len] = '\0';
+ pitem->bound_len = len;
+ }
+ p++;
+ break;
+
+ case MULTI_DISPOSITION:
+ case MULTI_LANGUAGE:
+ case MULTI_LOCATION:
+ break;
+ default: return -1;
+ }
+ }
+
+ save_original_data(&pitem->mime_header, "\r\n\r\n", 4, pitem->thread_id); //MIME头部和邮件正文之间需要有空行
+ return 0;
+}
+
+/*********************************************************************
+函数名称:parse_not_multipart_line
+功能简介:解析一行非multipart,将响应的结果写入mime_item_t结构体
+输入参数:
+输出参数:
+返回值:0-正常;-1-失败
+*********************************************************************/
+static int parse_not_multipart_line(mime_item_t *pitem, const char *line, int linelen, int is_extend)
+{
+ int i, len, type=CONT_TYPE_OTHER;
+ const char *p=line, *p_end=line+linelen, *ps;
+
+ for(i=NOTMUL_TYPE; i<=NOTMUL_DISPOSITION&&(p<p_end); i++)
+ {
+ while(*p==' ')
+ p++;
+
+ switch(i)
+ {
+ case NOTMUL_TYPE:
+ if((ps = look_for_string_end(p, p_end, &p)) == NULL)
+ {
+ return -1;
+ }
+#ifdef DBG_FULL
+ save_original_data(&pitem->mime_header, "\r\nContent-Type:", 15, pitem->thread_id);
+#else
+ save_original_data(&pitem->mime_header, "Content-Type:", 13, pitem->thread_id);
+#endif
+ save_original_data(&pitem->mime_header, ps, p-ps, pitem->thread_id);
+ save_original_data(&pitem->mime_header, "/", 1, pitem->thread_id);
+ if(is_extend)
+ {
+ if(strcmp_one_word_mesa("text", "TEXT", 4, ps, p-ps))
+ type = CONT_TYPE_TEXT;
+ else if(strcmp_one_word_mesa("message", "MESSAGE", 7, ps, p-ps))
+ type = CONT_TYPE_MESSAGE;
+ }
+ p++;
+ break;
+
+ case NOTMUL_SUBTYPE:
+ if((ps = look_for_string_end(p, p_end, &p)) == NULL)
+ {
+ return -1;
+ }
+ save_original_data(&pitem->mime_header, ps, p-ps, pitem->thread_id);
+ if(type==CONT_TYPE_MESSAGE && strcmp_one_word_mesa("rfc822", "RFC822", 6, ps, p-ps))
+ {
+ type = CONT_TYPE_RFC822;
+ }
+ p++;
+ break;
+
+ case NOTMUL_PARENTHESIS:
+ if(*p != '(') //没有参数组合列表,不处理;跳到下一个字段
+ {
+ p = (const char*)memchr(p, ' ', p_end-p);
+ if(p==NULL)
+ p=p_end;
+ break;
+ }
+ ps = p+1;
+ if((p=look_for_parenthesis_end(ps, p_end))==NULL || *p!=')')
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "look_for_parenthesis_end error, input not complete().");
+ i=NOTMUL_NUM;
+ break;
+ }
+
+ if(get_value_parenthesis(ps, p, "\"charset\" ", 10, "charset ", 8, &ps, &len))
+ {
+ save_original_data(&pitem->mime_header, ";charset=", 9, pitem->thread_id);
+ save_original_data(&pitem->mime_header, ps, len, pitem->thread_id);
+
+ if(*ps == '"')
+ {
+ ps++;
+ len-=2;
+ }
+ if(len >= MAIL_MAX_CHARSET_LEN)
+ {
+ len = MAIL_MAX_CHARSET_LEN - 1;
+ }
+ memcpy(pitem->charset, ps, len);
+ pitem->charset[len] = '\0';
+ }
+ if(get_value_parenthesis(ps, p, "\"name\" ", 7, "name ", 5, &ps, &len)) //查找附件名
+ {
+ save_original_data(&pitem->mime_header, ";name=", 6, pitem->thread_id);
+ save_original_data(&pitem->mime_header, ps, len, pitem->thread_id);
+
+ if(*ps == '"')
+ {
+ ps++;
+ len-=2;
+ }
+ pitem->attach_filename = (char *)dictator_malloc(pitem->thread_id, len+1);
+ mime_header_decode(ps, len, pitem->attach_filename, &len, pitem->filename_charset, MAIL_MAX_CHARSET_LEN, g_mail_localinfo.runtime_log);
+ pitem->attach_filename[len] = '\0';
+ pitem->filename_len = len;
+ }
+ p++;
+ break;
+
+ case NOTMUL_ID:
+ case NOTMUL_DESCR:
+ if((ps = look_for_string_end(p, p_end, &p)) == NULL) //跳过这个字段
+ {
+ return -1;
+ }
+ p++;
+ break;
+
+ case NOTMUL_ENCODE:
+ if((ps = look_for_string_end(p, p_end, &p)) == NULL)
+ {
+ return -1;
+ }
+ if(strcmp_one_word_mesa("base64", "BASE64", 6, ps, p-ps))
+ {
+ save_original_data(&pitem->mime_header, "\r\nContent-Transfer-Encoding:base64", 34, pitem->thread_id);
+ pitem->trans_enc = MIME_TRANSENC_BASE64;
+ }
+ else if(strcmp_one_word_mesa("quoted-printable", "QUOTED-PRINTABLE", 16, ps, p-ps))
+ {
+ save_original_data(&pitem->mime_header, "\r\nContent-Transfer-Encoding:quoted-printable", 44, pitem->thread_id);
+ pitem->trans_enc = MIME_TRANSENC_QP;
+ }
+ else if(!strcmp_one_word_mesa("nil", "NIL", 3, ps, p-ps))
+ {
+ save_original_data(&pitem->mime_header, "\r\nContent-Transfer-Encoding:", 28, pitem->thread_id);
+ save_original_data(&pitem->mime_header, ps, p-ps, pitem->thread_id);
+ }
+ p++;
+ break;
+
+ case NOTMUL_SIZE://有可能是最后一个字段,取结束位置的时候要注意与上述区别
+ if(is_extend==0)// || pitem->attach_filename != NULL) //非扩展类型后面的不处理
+ {
+ i=NOTMUL_NUM; //结束循环
+ break;
+ }
+ else if((p = (const char *)memchr(p, ' ', p_end-p))==NULL) //跳过这个字段
+ {
+ return -1;
+ }
+ p++;
+ break;
+
+ case NOTMUL_EVENLOP:
+ case NOTMUL_BODYSTRUCT:
+ if(type == CONT_TYPE_RFC822) //类型是MESSAGE/RFC822独有这2个字段;跳过这个字段;
+ {
+ if(*p == '(')
+ {
+ if((p=look_for_parenthesis_end(p+1, p_end))==NULL || *p!=')')
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "message/rfc822 input error, not complete().");
+ i=NOTMUL_NUM;
+ break;
+ }
+ }
+ else
+ {
+ p = (const char *)memchr(p, ' ', p_end-p);
+ if(p==NULL)
+ {
+ i=NOTMUL_NUM;
+ break;
+ }
+ }
+ p++;
+ break;
+ }
+ else //类型不是MESSAGE/RFC822,没有这2个字段
+ {
+ i += 2;
+ }
+ //fall through
+ case NOTMUL_LINES:
+ if(type==CONT_TYPE_TEXT || type==CONT_TYPE_RFC822) //跳过这个字段
+ {
+ p = (const char *)memchr(p, ' ', p_end-p);
+ if(p==NULL)
+ return -1;
+ break;
+ }
+ else //主类型不是TEXT,且类型不是MESSAGE/RFC822,没有该字段
+ {
+ i++;
+ }
+ //fall through
+ case NOTMUL_MD5:
+ if((ps = look_for_string_end(p, p_end, &p)) == NULL) //跳过这个字段
+ {
+ return -1;
+ }
+ p++;
+ break;
+
+ case NOTMUL_DISPOSITION:
+ if(*p != '(')
+ {
+ i=NOTMUL_NUM;
+ break;
+ }
+ p++;
+ if((ps = look_for_string_end(p, p_end, &p)) == NULL)
+ {
+ i=NOTMUL_NUM;
+ break;
+ }
+ save_original_data(&pitem->mime_header, "\r\nContent-Disposition:", 22, pitem->thread_id);
+ save_original_data(&pitem->mime_header, ps, p-ps, pitem->thread_id); //例如得到类型ATTATCHMENT
+ while(p<p_end && (*p=='"' || *p==' '))
+ p++;
+ if(p==p_end || *p != '(') //没有参数组合列表,不处理;
+ {
+ i=NOTMUL_NUM;
+ break;
+ }
+ ps = p+1;
+ if((p=look_for_parenthesis_end(ps, p_end))==NULL || *p!=')') //查找参数组合列表结束位置
+ {
+ p = p_end;
+ break;
+ }
+ if(get_value_parenthesis(ps, p, "\"filename\" ", 11, "filename ", 9, &ps, &len)) //查找附件名
+ {
+ save_original_data(&pitem->mime_header, ";filename=", 10, pitem->thread_id);
+ save_original_data(&pitem->mime_header, ps, len, pitem->thread_id);
+
+ if(*ps == '"')
+ {
+ ps++;
+ len-=2;
+ }
+ if(pitem->attach_filename==NULL)
+ {
+ pitem->attach_filename = (char *)dictator_malloc(pitem->thread_id, len+1);
+ mime_header_decode(ps, len, pitem->attach_filename, &len, pitem->filename_charset, MAIL_MAX_CHARSET_LEN, g_mail_localinfo.runtime_log);
+ pitem->attach_filename[len] = '\0';
+ pitem->filename_len = len;
+ }
+ }
+ break;
+ case NOTMUL_LANGUAGE:
+ case NOTMUL_LOCATION:
+ break;
+ default:return -1;
+ }
+ }
+
+ save_original_data(&pitem->mime_header, "\r\n\r\n", 4, pitem->thread_id);
+ return 0;
+}
+
+/*********************************************************************
+函数名称:process_bodystructure_block
+功能简介:解析一个块,加入哈希表
+输入参数:
+输出参数:
+返回值:0-正常;-1-失败;-2-用户名为空,无法拼接key值
+*********************************************************************/
+static int process_bodystructure_block(stMailPme* mailpme, const char *path, const char *block, int blocklen, long uid, int is_multi, int is_extend)
+{
+ stMailElem *pelem = mailpme->plug_mailinfo.pMailInfo->username; //TODO: S2C响应有时候有
+ mime_item_t *pitem;
+ char key[256];
+ long cb_ret;
+
+ if(pelem->buflen==0)
+ return -2;
+
+ snprintf(key, 256, "%s-%ld-%s", pelem->buf, uid, path);
+
+ pitem = (mime_item_t *)MESA_htable_search(g_mail_localinfo.htable_imap[mailpme->thread_num], (unsigned char *)key, strlen(key));
+ if(pitem == NULL) //应该不是很频繁出现BODYSTRUCTURE,暂时不放在CALL_BACK函数里了。
+ {
+ pitem = (mime_item_t *)dictator_malloc(mailpme->thread_num, sizeof(mime_item_t));
+ memset(pitem, 0, sizeof(mime_item_t));
+ pitem->thread_id = mailpme->thread_num;
+ pitem->is_multi = is_multi;
+#ifdef DBG_FULL
+ save_original_data(&pitem->mime_header, block, blocklen, pitem->thread_id);
+#endif
+ if(is_multi==0)
+ {
+ block += 1; //去掉括号
+ blocklen -= 2;
+ if(parse_not_multipart_line(pitem, block, blocklen, is_extend)!=0) //解析一个非multipart行
+ {
+ char buf[512];
+ memcpy(buf, block, blocklen>511?511:blocklen);
+ buf[blocklen>511?511:blocklen] = '\0';
+ imap_free_node(pitem);
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log, RLOG_LV_FATAL, MAILIMAP4_MODULE, "parse_not_multipart_line failed, is_extend:%d, %d-%s", is_extend,blocklen, buf);
+ return -1;
+ }
+ }
+ else if(is_extend)
+ {
+ if(parse_multipart_line(pitem, block, blocklen)!=0)//解析一个multipart行
+ {
+ char buf[512];
+ memcpy(buf, block, blocklen>511?511:blocklen);
+ buf[blocklen>511?511:blocklen] = '\0';
+ imap_free_node(pitem);
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log, RLOG_LV_FATAL, MAILIMAP4_MODULE, "parse_multipart_line failed, %d-%s", blocklen, buf);
+ return -1;
+ }
+ }
+ else //MULTIPART块且无扩展字段,不处理
+ {
+ imap_free_node(pitem);
+ return 0;
+ }
+
+ if((cb_ret = MESA_htable_add(g_mail_localinfo.htable_imap[mailpme->thread_num], (unsigned char *)key, strlen(key), pitem)) < 0)
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log, RLOG_LV_DEBUG, MAILIMAP4_MODULE, "in func process_bodystructure_block, MESA_htable_add failed: %d.", cb_ret);
+ imap_free_node(pitem);
+ return 0;
+ }
+ else if(g_mail_localinfo.stat_trig)
+ {
+ FS_operate(g_mail_localinfo.fsstat_handle, g_mail_localinfo.stat_id[STAT_ID_HTABLE_NUM], 0, FS_OP_ADD, 1);
+ }
+ }
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:parse_bodystructure
+功能简介:处理服务器body/bodystructure响应,提取MIME块及其对应的块号,加入哈希表
+输入参数:
+输出参数:
+返回值:正常返回处理的字节数;<0-失败
+*********************************************************************/
+static int parse_bodystructure(stMailPme* mailpme, const char *bodystruct, int bodylen, long uid, int is_extend)
+{
+ int single=0, depth=0;//depth:总深度
+ long len;
+ const char *pc=bodystruct, *p_end=bodystruct+bodylen, *pstart=NULL;
+ int state=STAT_BLOCK_IDLE, pre_state=STAT_BLOCK_IDLE;
+ char path[128]="0", *ptmp;
+ MESA_lqueue_head lq_head;
+
+ lq_head = MESA_lqueue_create(0, 1024);
+ if(lq_head==NULL)
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "MESA_lqueue_create error.");
+ return -1;
+ }
+
+ while(pc < p_end)
+ {
+ if(*pc == ' ')
+ {
+ pc++;
+ continue;
+ }
+
+ switch(state)
+ {
+ case STAT_BLOCK_IDLE:
+ if(*pc != '(')
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "input error1.");
+ MESA_lqueue_destroy(lq_head, NULL, NULL);
+ return -1;
+ }
+ pre_state = state;
+ state = STAT_BLOCK_START;
+ pstart=pc; //对于单一块需要
+ pc++;
+ depth++; //每遇到一个'(',深度加1,下同;
+ while(pc<p_end && *pc==' ') //查看是否是单一块
+ pc++;
+ if(pc<p_end && *pc != '(')
+ single = 1;
+ break;
+
+ case STAT_BLOCK_START:
+ if(*pc == '(')
+ {
+ if(pre_state == STAT_BLOCK_START)
+ {
+ if(MESA_lqueue_join_head(lq_head, path, strlen(path)+1)) //将上一级的path压栈
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "MESA_lqueue_join_head error.");
+ MESA_lqueue_destroy(lq_head, NULL, NULL);
+ return -2;
+ }
+ snprintf(path+strlen(path), 128-strlen(path), "%s", ".1");
+ }
+ else //++path
+ {
+ if(path[0]=='0' && MESA_lqueue_join_head(lq_head, path, strlen(path)+1)) //将顶级块号0压栈
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "MESA_lqueue_join_head error.");
+ MESA_lqueue_destroy(lq_head, NULL, NULL);
+ return -2;
+ }
+ ptmp = strrchr(path, '.');
+ if(ptmp == NULL)
+ sprintf(path, "%u", atoi(path)+1);
+ else
+ sprintf(ptmp+1, "%u", atoi(ptmp+1)+1);
+ }
+
+ pstart = pc;
+ pre_state = state;
+ state = STAT_BLOCK_START;
+ pc++;
+ depth++;
+ break;//下一次循环
+ }
+ else
+ {
+ pre_state = state;
+ state = STAT_BLOCK_PROC;
+ }
+ //fall through
+
+ case STAT_BLOCK_PROC:
+ if((pc=look_for_parenthesis_end(pc, p_end))==NULL || *pc!=')') //取完当前块
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "input error3");
+ MESA_lqueue_destroy(lq_head, NULL, NULL);
+ return -4;
+ }
+ depth--; //每取完一个块,当前深度减1
+ pc++;
+ pre_state = state;
+ state = STAT_BLOCK_END;
+
+ //对于单一块,最小标号是"1",代表正文;同时标号"0"为body[text]服务;
+ if(single==1 && depth==0)
+ {
+ process_bodystructure_block(mailpme, "1", pstart, pc-pstart, uid, 0, is_extend);
+ }
+ process_bodystructure_block(mailpme, path, pstart, pc-pstart, uid, 0, is_extend);
+ break;
+
+ case STAT_BLOCK_END:
+ if(*pc == '(') //上一块结束后,来了一个同一级别的新块
+ {
+ ptmp = strrchr(path, '.');
+ if(ptmp == NULL) //++path
+ sprintf(path, "%u", atoi(path)+1);
+ else
+ sprintf(ptmp+1, "%u", atoi(ptmp+1)+1);
+ pre_state = state;
+ state = STAT_BLOCK_START;
+ pstart = pc;
+ pc++;
+ depth++;
+ }
+ else if(*pc != ' ') //开始字符不是'(',是MULTIPART块尾部信息
+ {
+ pstart = pc;
+ if((pc=look_for_parenthesis_end(pc, p_end))==NULL || *pc!=')')
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "input error4.");
+ MESA_lqueue_destroy(lq_head, NULL, NULL);
+ return -5;
+ }
+ depth--;
+ len = 128;
+ if(MESA_lqueue_try_get_head(lq_head, path, &len) != 0)
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "MESA_lqueue_try_get_head error.");
+ MESA_lqueue_destroy(lq_head, NULL, NULL);
+ return -6;
+ }
+ pre_state = state;
+ state = STAT_BLOCK_START;
+
+ if(is_extend) //multipart块如果没有扩展字段,没什么可处理的
+ {
+ process_bodystructure_block(mailpme, path, pstart, pc-pstart, uid, 1, is_extend); //MULTIPART BLOCK has no ()
+ }
+ pc++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if(depth==0) //总深度为0,说明整个块已经处理完,后续可能还有别的块,不再处理
+ break;
+ }
+
+ if(MESA_lqueue_try_get_head(lq_head, path, &len) == 0) //栈里还有数据,不正常
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "queue error, it is not NULL.");
+ MESA_lqueue_destroy(lq_head, NULL, NULL);
+ return -7;
+ }
+ MESA_lqueue_destroy(lq_head, NULL, NULL);
+
+ return pc-bodystruct;
+}
+
+/*********************************************************************
+函数名称:process_block_mime_header
+功能简介:对于新来的一个块,根据用户名+uid+块号从哈希表中查找MIME头部
+输入参数:
+输出参数:
+返回值: -1-未查找到;0-查找到了;PROT_SATE_XXX:业务层返回状态
+*********************************************************************/
+static int get_block_mime_header(stMailPme *mailpme, long uid, const char *block_seq, struct streaminfo *a_tcp, const void *raw_pkt)
+{
+ char key[256];
+ int len;
+ mime_item_t *pitem;
+
+ if(mailpme->plug_mailinfo.pMailInfo->username->buflen==0)
+ return -1;
+ snprintf(key, 256, "%s-%ld-%s", mailpme->plug_mailinfo.pMailInfo->username->buf, uid, block_seq);
+
+ pitem = (mime_item_t *)MESA_htable_search(g_mail_localinfo.htable_imap[mailpme->thread_num], (unsigned char *)key, strlen(key));
+ if(pitem != NULL)
+ {
+ if(pitem->is_multi)
+ {
+ if(pitem->boundary)
+ add_boundary(pitem->boundary, pitem->bound_len, &mailpme->mimeinfo->b_header, mailpme->thread_num);
+ save_original_data(&mailpme->pOriginalData, (const char *)pitem->mime_header.buf, pitem->mime_header.len, mailpme->thread_num);
+ }
+ else
+ {
+ save_original_data(&mailpme->pOriginalData, (const char *)pitem->mime_header.buf, pitem->mime_header.len, mailpme->thread_num);
+ mailpme->mimeinfo->TransferEnc = pitem->trans_enc;
+ if((len = strlen(pitem->charset))>0)
+ {
+ memcpy(mailpme->mimeinfo->charset, pitem->charset, len);
+ mailpme->mimeinfo->charset[len] = '\0';
+ }
+ if(pitem->filename_len>0 && (g_mail_localinfo.protocol_flag & MAIL_ATTACH_NAME))
+ {
+ mailpme->MailInfoState = MAIL_GET_FILENAME;
+ len = call_biz_per_line(mailpme, MAIL_ATTACH_NAME, pitem->attach_filename, pitem->filename_len, pitem->filename_charset, a_tcp, raw_pkt);
+ if(len & PROT_STATE_DROPME)
+ {
+ drop_set_value(mailpme);
+ return IMAP_CMD_DROP;
+ }
+ }
+ }
+
+ *((char *)pitem->mime_header.buf + pitem->mime_header.len) = '\0'; //TODO
+ return 0;
+ }
+ else
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_INFO, MAILIMAP4_MODULE, "MESA_htable_search_cb %s, no result found.", key);
+ }
+
+ return -1;
+}
+
+/*********************************************************************
+函数名称:imap4_reset_mailpme
+功能简介:如果来了新块则进行复位
+输入参数:uid新来的块的uid号
+输出参数:
+返回值:
+*********************************************************************/
+static int imap4_reset_mailpme(stMailPme* mailpme, long uid, struct streaminfo *a_tcp, const void *raw_pkt)
+{
+ mailpme->pre_uid = mailpme->uid;
+ mailpme->uid = uid;
+
+ if(mailpme->pending_state==0 && mailpme->uid != mailpme->pre_uid)
+ {
+ if(mailpme->is_drop)
+ mail_reset_mailpme_noclose(mailpme, a_tcp, raw_pkt);
+ else
+ mail_reset_mailpme(mailpme, a_tcp, raw_pkt);
+ }
+ mailpme->pending_state = 0;
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:prepare_parse_bodystructure
+功能简介:准备进行BODYSTRUCTURE解析,先判断是否完整,如果不完整,缓存
+ 原始数据,等待下一个包进行拼接后再重复上述操作。
+输入参数:
+ start:mail_get_tcpdata函数输出的数据指针output_line
+ linelen: mail_get_tcpdata函数输出的数据长度outputlen
+ src: BODYSTRUCTURE结构开始指针
+ srclen: src长度
+输出参数:
+返回值: 0-正常;其他-数据不完整,等待下次
+*********************************************************************/
+static int prepare_parse_bodystructure(stMailPme* mailpme, const char *start, int linelen, const char *src, int srclen, long uid, int is_extend)
+{
+ const char *ptmp, *srcend=src+srclen;
+ int ret;
+
+ if(!strncmp(srcend-3, "}\r\n", 3) || !strncmp(srcend-2, "}\n", 2)) //不完整,后续可能还有;如果没有也就是解析失败而已
+ {
+ ptmp = (const char *)memrchr(src, '{', srclen-3);
+ if(ptmp != NULL)
+ {
+ linelen = ptmp - start;
+ if(start != mailpme->pDataLineBuf.buf)
+ {
+ if(buf_cache_check_size(&mailpme->pDataLineBuf, linelen, mailpme->thread_num)<0)
+ return IMAP_CMD_ANS;
+ memcpy(mailpme->pDataLineBuf.buf, start, linelen);
+ }
+ mailpme->pDataLineBuf.len = linelen;
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER; //后续有可能是折叠行,且后续也是邮件头部(如果有的话)
+ return IMAP_CMD_FETCH_ANS;
+ }
+ }
+
+ ret = parse_bodystructure(mailpme, src, srclen, uid, is_extend); //解析失败也正常返回,丢弃本次BODYSTRUCTURE
+
+ if(ret>0 && (srcend -(src+ret) < 5)) //the second 5 is lengh of "body["
+ return IMAP_CMD_FETCH_ANS;
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:imap_is_wanted_line
+功能简介:根据输入的数据的特征,输入数据长度和会话处理状态,判断并返回
+ imap应答信息类型编号
+输入参数:
+ apply_count:数据包的大小
+ a_imap_line :输入邮件数据,不包含"\r\n"
+ mailpme:邮件数据结构
+输出参数:如果检测到邮件内容开始,则更新mailpme->EmlAnalyseState
+返回值: 对应的命令的编码
+*********************************************************************/
+static int imap_is_wanted_line(const char *a_imap_line,int linelen, struct tcpdetail *ptcpdetail, stMailPme* mailpme, struct streaminfo *a_tcp, const void *raw_pkt)
+{
+ const char *p=a_imap_line, *p_end=a_imap_line+linelen, *pp=NULL;
+ int tmplen, retCMD=0;
+
+ if(linelen>0 && (p=(char *)memchr(a_imap_line, ' ', (linelen>RESPONSE_FETCH_MAX_LEN)?RESPONSE_FETCH_MAX_LEN:linelen)))
+ p++;
+ if(p!=NULL && (pp=(char *)memchr(p,' ',p_end-p))) //提取fetch response中的关键词,如"fetch (body[header"
+ pp++;
+
+ //以下是对邮件开始的判断
+ if(pp!=NULL && (pp+6)<p_end && strncmp_one_word_mesa("FETCH ", "fetch ", 6, pp, p_end-pp))
+ {
+ char templine[MAX_ORDER_LINE_SIZE], block_seq[128], ch;
+ const char *ptmp1, *ptmp2;
+ int tmp;
+ long uid=-1;
+
+ pp = pp+6;
+ tmplen = ((p_end-pp) >= (MAX_ORDER_LINE_SIZE-1))?(MAX_ORDER_LINE_SIZE-1):(p_end-pp);
+ memcpy(templine, pp, tmplen);
+ templine[tmplen]='\0';
+
+ ptmp2=mesa_memncasemem(templine, tmplen, "UID ", 4);
+ if(ptmp2==NULL || sscanf(ptmp2+3, "%*[ ]%ld%c", &uid, &ch)!=2 || ch!=' ') //TODO: 不是字符串
+ {
+ uid = 0;
+ }
+ else if((ptmp1=mesa_memncasemem(pp, p_end-pp, "BODYSTRUCTURE (", 15))!=NULL && mailpme->plug_mailinfo.pMailInfo->username->buflen>0) //含有BASE64编码字符,所以不可以变成小写
+ {
+ //要保留"BODYSTRUCTURE ("中的'(',因为有单一块的情况
+ if((tmp = prepare_parse_bodystructure(mailpme, a_imap_line, linelen, ptmp1+14, p_end-ptmp1-14, uid, 1)) != 0)
+ return tmp;
+ }
+ else if((ptmp1=mesa_memncasemem(pp, p_end-pp, "BODY (", 6))!=NULL && mailpme->plug_mailinfo.pMailInfo->username->buflen>0)
+ {
+ if((tmp = prepare_parse_bodystructure(mailpme, a_imap_line, linelen, ptmp1+5, p_end-ptmp1-5, uid, 0)) != 0)
+ return tmp;
+ }
+
+ //将输入变成小写
+ for(tmp=0;tmp<tmplen;tmp++)
+ {
+ templine[tmp]=tolower(templine[tmp]);
+ }
+
+ ptmp1 = strstr(templine, "body[");
+ ptmp2 = strstr(templine, "body.peek[");
+ //对邮件头开始的判断
+ if((ptmp1!=NULL && (!strncmp(ptmp1+5, "header", 6) || !strncmp(ptmp1+5, "mime", 4))) \
+ || (ptmp2!=NULL && (!strncmp(ptmp2+10, "header", 6) || !strncmp(ptmp2+10, "mime", 4))) || strstr(templine, "rfc822.header"))
+ {
+ mailpme->is_continue = 0;
+ imap4_reset_mailpme(mailpme, uid, a_tcp, raw_pkt);
+ mailpme->EmlAnalyseState=MAIL_STAT_HEADER;
+#ifdef DBG_FULL
+ return EML_DATA_HEAD_IN;
+#else
+ return IMAP_CMD_FETCH_ANS;
+#endif
+ }
+ //对完整邮件开始的判定,一个空的块声明指向整个邮件,包括头部。
+ else if((ptmp1!= NULL && *(ptmp1+5)==']' && *(ptmp1+6)==' ') || (ptmp2!=NULL && *(ptmp2+10)==']' && *(ptmp2+11)==' ') || strstr(templine, "rfc822 {"))
+ {
+ mailpme->is_continue = 0;
+ imap4_reset_mailpme(mailpme, uid, a_tcp, raw_pkt);
+ mailpme->EmlAnalyseState=MAIL_STAT_HEADER;
+#ifdef DBG_FULL
+ return EML_DATA_HEAD_IN;
+#else
+ return IMAP_CMD_FETCH_ANS;
+#endif
+ }
+ //对邮件一个片段进行判定,如C2S:BODY.PEEK[]<262144.262144>; S2C:body[]<262144>
+ else if((ptmp1!= NULL && sscanf(ptmp1+5, "]<%[0-9]%c", block_seq, &ch)==2 && ch=='>') || (ptmp2!=NULL && sscanf(ptmp2+10, "]<%[0-9]%c", block_seq, &ch)==2 && ch=='>'))
+ {
+ mailpme->is_continue = 1;
+ imap4_reset_mailpme(mailpme, uid, a_tcp, raw_pkt);
+
+ if(!strcmp(block_seq, "0"))
+ {
+ mailpme->EmlAnalyseState=MAIL_STAT_HEADER;
+#ifdef DBG_FULL
+ return EML_DATA_HEAD_IN;
+#else
+ return IMAP_CMD_FETCH_ANS;
+#endif
+ }
+ else
+ {
+ return IMAP_CMD_DATA_CONTINUE;
+ }
+ }
+ //对邮件体body开始的判定,是邮件体的所有部分,不包括邮件头
+ else if((ptmp1!=NULL && !strncmp(ptmp1+5, "text", 4)) || (ptmp2!=NULL && !strncmp(ptmp2+10, "text", 4)) || strstr(templine, "rfc822.text"))
+ {
+ mailpme->is_continue = 0;
+ imap4_reset_mailpme(mailpme, uid, a_tcp, raw_pkt);
+
+ tmp = get_block_mime_header(mailpme, uid, "0", a_tcp, raw_pkt);
+ if(tmp<0)
+ {
+ return IMAP_CMD_FETCH_ANS;
+ }
+ else if(tmp==IMAP_CMD_DROP)
+ {
+ return IMAP_CMD_DROP;
+ }
+ return IMAP_CMD_DATA_BORDER;
+ }
+ //邮件的一个块的判定,如body[1.2];block_seq存储其块号
+ else if((ptmp1!=NULL && sscanf(ptmp1+5, "%[0-9.]%c", block_seq, &ch)==2 && ch==']') || (ptmp2!=NULL && sscanf(ptmp2+10, "%[0-9.]%c", block_seq, &ch)==2 && ch==']'))
+ {
+ mailpme->is_continue = 0;
+ imap4_reset_mailpme(mailpme, uid, a_tcp, raw_pkt);
+
+ tmp = get_block_mime_header(mailpme, uid, block_seq, a_tcp, raw_pkt);
+ if(tmp<0)
+ {
+ return IMAP_CMD_FETCH_ANS;
+ }
+ else if(tmp==IMAP_CMD_DROP)
+ {
+ return IMAP_CMD_DROP;
+ }
+ return IMAP_CMD_DATA_BORDER;
+ }
+ }
+
+ if(mailpme->maybe_end == 1)
+ {
+ mailpme->maybe_end = 0;
+ if(p && ((strncmp_one_word_mesa("ok fetch ", "OK FETCH ", 9, p, p_end-p))||(strncmp_one_word_mesa("ok uid fetch ", "OK UID FETCH ", 13, p, p_end-p))))
+ {
+ return IMAP_CMD_DATA_END;
+ }
+ else
+ {
+ return IMAP_CMD_NOT_END;
+ }
+ }
+ else if((linelen==3 && !strncmp(a_imap_line, ")\r\n", 3)) || (linelen==2 && !strncmp(a_imap_line, ")\n", 2)))
+ {
+ mailpme->maybe_end = 1;
+ mailpme->obtain_line = 1; //有可能若干个头部一起传,')'之后紧跟FETCH;后续正文中的空行被MIME略去了
+ return IMAP_CMD_MAYBE_END;
+ }
+
+ if(((!strncmp(a_imap_line, "* ", 2) || !strncmp(a_imap_line, "+ ", 2)) ||
+ (p && linelen<IMAP_LABEL_OK_MAX_LEN && NULL==memchr(a_imap_line, ':', linelen) && /*X-MAR: OK*/
+ (strncmp_one_word_mesa("ok", "OK", 2, p, p_end-p) || strncmp_one_word_mesa("no ", "NO ",3, p, p_end-p) || strncmp_one_word_mesa("bad ", "BAD ",4, p, p_end-p)))))
+ {
+ if(mailpme->EmlAnalyseState==MAIL_STAT_INIT || ptcpdetail->datalen < IMAP_OK_CMD_LEN)
+ return IMAP_CMD_ANS;
+ else
+ retCMD = 1;
+ }
+
+ if((mailpme->EmlAnalyseState!=MAIL_STAT_INIT) &&
+ p && ((strncmp_one_word_mesa("ok fetch completed", "OK FETCH COMPLETED", 18, p, p_end-p))||(strncmp_one_word_mesa("ok uid fetch completed", "OK UID FETCH COMPLETED", 22, p, p_end-p))))
+ {
+ return IMAP_CMD_DATA_END;
+ }
+ else if(retCMD==1)
+ {
+ return IMAP_CMD_ANS;
+ }
+
+ //应该在判断IMAP_CMD_DATA_END之后;
+ //否则会话不正常结束,维持的是DROP的时候的EmlAnalyseState状态,下一个会话要求获取行
+ if(mailpme->is_drop)
+ return IMAP_CMD_DROP;
+
+ retCMD=mail_analy_emlheader(a_imap_line,linelen,mailpme->EmlAnalyseState);
+ if(retCMD!=EML_UNKNOWN)
+ {
+ return retCMD;
+ }
+
+ return IMAP_CMD_UNKNOWN;
+}
+
+/*********************************************************************
+函数名称:imap_process_s2c
+功能简介:处理邮件IMAP会话S2C流上的一个数据包,每个包调用1次该函数
+输入参数:
+ tcp_stream:tcp会话流
+ mailpme:邮件处理结构体
+输出参数:无
+返回值:业务插件的返回值
+*********************************************************************/
+static int imap_process_s2c(stMailPme* mailpme, struct streaminfo *a_stream, const void *raw_pkt)
+{
+ int rec=PROT_STATE_GIVEME, needfolding=0;
+ int line_status = MAIL_FOLD_LINE_START;
+ char *output_line = NULL;
+ int outputlen=0;
+ struct tcpdetail *ptcpdetail = a_stream->ptcpdetail;
+
+ memset(&mailpme->session_info, 0, sizeof(stSessionInfo));
+ mailpme->session_info.plugid = g_mail_localinfo.plugid;
+ mailpme->session_info.session_state = SESSION_STATE_DATA;
+ mailpme->session_info.app_info = &mailpme->plug_mailinfo;
+
+ while(1)
+ {
+ if(mailpme->EmlAnalyseState == MAIL_STAT_HEADER || (mailpme->EmlAnalyseState == MAIL_STAT_BODY && mailpme->mimeinfo->text_begin==0))
+ needfolding=1;
+ else
+ needfolding=0;
+
+ line_status = mail_get_tcpdata(ptcpdetail, mailpme, needfolding, &output_line, &outputlen);
+ if(line_status == MAIL_INPUT_END)
+ break;
+ else if(line_status == MAIL_GETLINE_ERR)
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "imap get a line error!!! drop now!\n");
+ return PROT_STATE_DROPME;
+ }
+ mailpme->pDataLineBuf.len = 0;
+
+ line_status = imap_is_wanted_line(output_line, outputlen, ptcpdetail, mailpme, a_stream, raw_pkt);
+
+ switch(line_status)
+ {
+ case IMAP_CMD_DROP:
+ case IMAP_CMD_FETCH_ANS:
+ case IMAP_CMD_UNKNOWN:
+ case IMAP_CMD_MAYBE_END:
+ case IMAP_CMD_DATA_CONTINUE: //按字节获取邮件,忽略FETCH响应头部
+ break;
+
+ case IMAP_CMD_DATA_BORDER: //本次TCP数据不是原始邮件,不进行缓存
+ mailpme->EmlAnalyseState = MAIL_STAT_BODY;
+ body_update_text_begin(mailpme->mimeinfo);
+ break;
+
+ case IMAP_CMD_ANS: //结束标记和命令丢失,可以借助响应来终止会话
+ case EML_DATA_END:
+ case IMAP_CMD_DATA_END:
+ //是CONTINUE块就不用复位,下一次沿用本次的状态;下一个包只能获取第1行;
+ //如果下一次是新的块,则在判断是新块的地方进行复位。
+ if(mailpme->is_continue)
+ mailpme->obtain_line = 1;
+ //如果不是CONTINUE块,要为下一个会话准备SESSION_PENDING,同时及时尽早结束本块;
+ else if(mailpme->pending_state==0)
+ {
+ if(mailpme->is_drop)
+ mail_reset_mailpme_noclose(mailpme, a_stream, raw_pkt);
+ else
+ mail_reset_mailpme(mailpme, a_stream, raw_pkt);
+
+ //邮件请求块完毕,紧接着下一个邮件头,响应也有可能是折叠行(BODYSTRUCTURE)
+ mailpme->EmlAnalyseState = MAIL_STAT_HEADER;
+ }
+ break;
+
+ case IMAP_CMD_NOT_END:
+ if(mailpme->is_drop)
+ break;
+ line_status = mailpme->pre_opsate;
+ rec = process_mail_data(line_status, (char *)")\r\n", 3, mailpme, a_stream, raw_pkt);
+ if(rec & PROT_STATE_DROPME)
+ {
+ drop_set_value(mailpme);
+ break;
+ }
+ //fall through
+ default:
+ if(mailpme->is_drop)
+ break;
+ mailpme->pre_opsate = line_status;
+ rec = process_mail_data(line_status, output_line, outputlen, mailpme, a_stream, raw_pkt);
+ if(rec & PROT_STATE_DROPME)
+ {
+ drop_set_value(mailpme);
+ }
+ break;
+ }
+ }
+ mailpme->iDataPointer = 0;
+
+ //call biz per packet
+ if(!g_mail_localinfo.callback_per_line && mailpme->plug_mailinfo.elem_num>0)
+ {
+ rec = call_biz_per_packet(mailpme, a_stream, raw_pkt);
+ if(rec & PROT_STATE_DROPME)
+ {
+ drop_set_value(mailpme);
+ }
+ }
+
+ return (rec&PROT_STATE_DROPPKT)?PROT_STATE_DROPPKT:PROT_STATE_GIVEME;
+}
+
+
+/*********************************************************************
+函数名称:pop3_process_c2s
+功能简介:处理邮件IMAP会话C2S流上的一个数据包,每个包调用1次该函数
+输入参数:
+ tcp_stream:tcp会话流
+ mailpme:邮件处理结构体
+输出参数:无
+返回值:业务插件的返回值
+*********************************************************************/
+static int imap_process_c2s(stMailPme* mailpme, struct streaminfo *a_stream, const void *raw_pkt)
+{
+ //只处理用户名密码,只有最初状态有
+ if(mailpme->EmlAnalyseState!=MAIL_STAT_INIT || mailpme->is_drop)
+ return PROT_STATE_GIVEME;
+
+ int rec=PROT_STATE_GIVEME;
+ int line_status = MAIL_FOLD_LINE_START;
+ char *output_line = NULL;
+ int outputlen=0, matchlen;
+ struct tcpdetail *ptcpdetail = a_stream->ptcpdetail;
+ char *p, *p_end;
+
+ //换了方向,上个流缓存的数据清掉.TODO
+ if(mailpme->pre_dir != a_stream->curdir)
+ {
+ mailpme->pre_dir = a_stream->curdir;
+ mailpme->pDataLineBuf.len = 0;
+ }
+
+ while(1)
+ {
+ line_status = mail_get_atcpline_c2s(ptcpdetail, mailpme, &output_line, &outputlen);
+ if(line_status == MAIL_INPUT_END)
+ break;
+
+ if(line_status == MAIL_GETLINE_ERR)
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "imap get a line error!!! drop now!\n");
+ return PROT_STATE_DROPME;
+ }
+ mailpme->pDataLineBuf.len = 0;
+ p_end = output_line+outputlen;
+
+ if(outputlen>0 && (p=(char *)memchr(output_line, ' ', outputlen)))
+ {
+ char *pp1, *pp2;
+ p++;
+ if((matchlen=strncmp_two_word_mesa("authenticate", "AUTHENTICATE", 12, "xoauth2", "XOAUTH2", 7, p, p_end-p))!=0)
+ {
+ mailpme->iDataPointer = 0;
+ return oauth2_username_biz(mailpme, p+matchlen, p_end-p-matchlen, a_stream, raw_pkt);
+ }
+ if(strncmp_one_word_mesa("starttls", "STARTTLS", 8, p, p_end-p)!=0)
+ {
+ if(g_mail_localinfo.protocol_flag&MAIL_STARTTLS_CMD)
+ {
+ rec = call_biz_per_line(mailpme, MAIL_STARTTLS_CMD, p, 8, NULL, a_stream, raw_pkt);// 8 = strlen("STARTTLS")
+ if(rec & PROT_STATE_DROPME)
+ {
+ drop_set_value(mailpme);
+ }
+ return PROT_STATE_DROPME;
+ }
+ }
+ if(strncmp_one_word_mesa("login", "LOGIN", 5, p, p_end-p)==0)
+ {
+ mailpme->iDataPointer = 0;
+ return PROT_STATE_GIVEME;
+ }
+ p += 6; //length of "login "
+ while(*p==' ')
+ p++;
+ if(p>=p_end)
+ {
+ mailpme->iDataPointer = 0;
+ return PROT_STATE_GIVEME;
+ }
+ if(*p == '"')
+ pp1 = (char *)memchr(p+1, '"', p_end - p - 1);
+ else
+ pp1 = (char *)memchr(p, ' ', p_end - p);
+ if(pp1 == NULL) // ""这样的用户名
+ {
+ mailpme->iDataPointer = 0;
+ return PROT_STATE_GIVEME;
+ }
+ pp2 = pp1+1;
+
+ if(*p == '"')
+ p += 1;
+ if(*(pp1-1)=='"')
+ pp1 -= 1;
+
+ while(*pp2 == ' ')
+ pp2++;
+ if(*pp2=='"')
+ pp2 += 1;
+ if(*(p_end-1)=='"')
+ p_end -= 1;
+
+ if(pp1<=p || pp2>=p_end)
+ {
+ mailpme->iDataPointer = 0;
+ return PROT_STATE_GIVEME;
+ }
+
+ mailpme->plug_mailinfo.pMailInfo->password->buflen = 0;
+ mailpme->plug_mailinfo.pMailInfo->username->buflen = 0;
+ mailpme->session_info.session_state = SESSION_STATE_DATA;
+ mail_save_mail_elem(p, pp1-p, mailpme->plug_mailinfo.pMailInfo->username, mailpme->thread_num);
+ if(g_mail_localinfo.protocol_flag&MAIL_USERNAME)
+ {
+ rec = call_biz_per_line(mailpme, MAIL_USERNAME, p, pp1-p, NULL, a_stream, raw_pkt);
+ if(rec & PROT_STATE_DROPME)
+ {
+ drop_set_value(mailpme);
+ break;
+ }
+ }
+
+ mail_save_mail_elem(pp2, p_end-pp2, mailpme->plug_mailinfo.pMailInfo->password, mailpme->thread_num);
+ if(g_mail_localinfo.protocol_flag&MAIL_PASSWARD)
+ {
+ rec = call_biz_per_line(mailpme, MAIL_PASSWARD, pp2, p_end-pp2, NULL, a_stream, raw_pkt);
+ if(rec & PROT_STATE_DROPME)
+ {
+ drop_set_value(mailpme);
+ break;
+ }
+ }
+ }
+
+ if(rec&PROT_STATE_DROPPKT)
+ {
+ break;
+ }
+ }
+ mailpme->iDataPointer = 0;
+
+ return (rec&PROT_STATE_DROPPKT)?PROT_STATE_DROPPKT:PROT_STATE_GIVEME;
+}
+
+/****************************************************************************
+函数名:line_is_imap_cmd()
+功能:判断C->S方向,客户端命令是否是IMAP4的命令
+输入:
+输出:
+返回值: 0-不是;1-是;
+*****************************************************************************/
+static int line_is_imap_cmd(char *data, int datalen)
+{
+ if(strncmp_one_word_mesa("capability", "CAPABILITY", 10, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("authenticate ", "AUTHENTICATE ", 13, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("login ", "LOGIN ", 6, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("noop", "NOOP", 4, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("select ", "SELECT ", 7, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("status ", "STATUS ", 7, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("uid ", "UID ", 4, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("fetch ", "FETCH ", 6, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("store ", "STORE ", 6, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("append ", "APPEND ", 7, data, datalen))
+ return 1;
+ if(strncmp_one_word_mesa("starttls ", "STARTTLS ", 9, data, datalen))
+ return 1;
+
+ return 0;
+}
+
+/****************************************************************************
+函数名:imap4_entry_fun()
+功能:IMAP入口函数
+输入:stream.h
+输出:stream.h
+返回值: APP_STATE_GIVEME/APP_STATE_DROPME/APP_STATE_DROPPKT
+*****************************************************************************/
+char imap4_entry_fun(struct streaminfo *a_tcp, void **pme, int thread_seq,const void *raw_pkt)
+{
+ int rec = PROT_STATE_GIVEME;
+ stMailPme* mailpme = (stMailPme*)(*(pme));
+ struct tcpdetail *ptcpdetail = a_tcp->ptcpdetail;
+
+ switch(a_tcp->opstate)
+ {
+ case OP_STATE_PENDING:
+ if(IMAP_PROTOCOL == mail_identify_imap(a_tcp, (char *)ptcpdetail->pdata, ptcpdetail->datalen, thread_seq))
+ {
+ rec = mail_init_mailpme(&mailpme,MAIL_SERVICE_IMAP4,a_tcp,raw_pkt);
+ if(rec < 0)
+ {
+ MESA_HANDLE_RUNTIME_LOG(g_mail_localinfo.runtime_log,RLOG_LV_FATAL, MAILIMAP4_MODULE, "mail_init_mailpme() error!");
+ return APP_STATE_DROPME;
+ }
+ *pme = (void*)mailpme;
+ project_req_add_uint(a_tcp, g_mail_localinfo.proto_identify_id, IMAP_PROTOCOL);
+ }
+ else
+ {
+ return APP_STATE_DROPME;
+ }
+ //no break here.
+
+ case OP_STATE_DATA:
+ if(a_tcp->curdir == DIR_S2C)
+ {
+ //if(ptcpdetail->lostlen>0)
+ //{
+ // mailpme->s_total_loss += ptcpdetail->lostlen;
+ //}
+
+ rec = imap_process_s2c(mailpme, a_tcp, raw_pkt);
+ if(rec & PROT_STATE_DROPME)
+ {
+ mail_clear_mailpme(mailpme, a_tcp, raw_pkt);
+ return APP_STATE_DROPME;
+ }
+ }
+ else if(a_tcp->curdir == DIR_C2S)
+ {
+ rec = imap_process_c2s(mailpme, a_tcp, raw_pkt);
+ if(rec & PROT_STATE_DROPME)
+ {
+ mail_clear_mailpme(mailpme, a_tcp, raw_pkt);
+ return APP_STATE_DROPME;
+ }
+ }
+ assert(mailpme->thread_num==a_tcp->threadnum);
+ break;
+
+ case OP_STATE_CLOSE:
+ if(g_mail_localinfo.stat_trig)
+ {
+ FS_operate(g_mail_localinfo.fsstat_handle, g_mail_localinfo.stat_id[STAT_ID_MAIL_PKTS], 0, FS_OP_ADD, a_tcp->ptcpdetail->serverpktnum+a_tcp->ptcpdetail->clientpktnum);
+ FS_operate(g_mail_localinfo.fsstat_handle, g_mail_localinfo.stat_id[STAT_ID_MAIL_BYTES], 0, FS_OP_ADD, a_tcp->ptcpdetail->serverbytes+a_tcp->ptcpdetail->clientbytes);
+ }
+ mail_add_proto_tag(g_mail_localinfo.proto_tag_id, a_tcp, "MAIL", strlen("MAIL"));
+ mail_clear_mailpme(mailpme,a_tcp,raw_pkt);
+ return APP_STATE_DROPME;
+
+ default:
+ return APP_STATE_DROPME;
+ }
+
+ if(rec & PROT_STATE_DROPME)
+ return APP_STATE_DROPME;
+ else if(rec & PROT_STATE_DROPPKT)
+ return APP_STATE_DROPPKT;
+ else
+ return APP_STATE_GIVEME;
+}
+
+
+int imap_process(stMailPme* mailpme, const char *payload, size_t payload, int is_c2s)
+{
+
+}
+
+int imap_identify(const char *payload, size_t payload_len, int is_c2s)
+{
+ char *p = NULL;
+ int left_len = 0;
+
+ if (payload == NULL || payload_len < 2) {
+ return 0;
+ }
+
+ p = (char *)memchr(payload, ' ', payload_len);
+ if (p == NULL) {
+ return 0;
+ }
+
+ left_len = payload_len - (p - payload);
+ if (left_len > 1) {
+ p++;
+ left_len--;
+ }
+
+ if (is_c2s) {
+ if (0 == strncmp(payload, "* ", strlen("* ")) ||
+ 0 == strncmp(payload, "+ ", strlen("+ ")) ||
+ 0 == strncmp(p, "OK", strlen("OK")) ||
+ 0 == strncmp(p, "NO", strlen("NO")) ||
+ (left_len >= 3 && 0 == strncmp(p, "BAD", strlen("BAD"))) ||
+ (left_len >=6 && 0 == strncmp(p, "EXISTS", strlen("EXISTS")))) {
+ return 1;
+ }
+ } else {
+ if (line_is_imap_cmd(p, left_len)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+const char* g_imap_command[] = {
+ "unknown",
+ "ehlo",
+ "helo",
+ "auth",
+ "starttls",
+ "mail",
+ "send",
+ "soml",
+ "saml",
+ "rcpt",
+ "data",
+ "vrfy",
+ "expn",
+ "noop",
+ "rset",
+ "quit",
+};
+
+static enum IMAP_COMMAND imap_str2command(const char *payload, size_t payload_len)
+{
+ int i;
+ if (payload == NULL || payload_len == 0) {
+ return IMAP_COMMAND_UNKNOWN;
+ }
+ for(i = 1; i < IMAP_COMMAND_MAX; i++) {
+ if(0 == SAFE_STRNCASECMP(payload, payload_len, g_imap_command[i], strlen(g_imap_command[i]))) {
+ break;
+ }
+ }
+ if (i == IMAP_COMMAND_MAX) {
+ return IMAP_COMMAND_UNKNOWN;
+ }
+ return (enum IMAP_COMMAND)i;
+}
+
+static int is_imap_cmd(const char *data, int datalen)
+{
+ if (IMAP_COMMAND_UNKNOWN == imap_str2command(data, datalen)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int is_imap_res(const char *data, size_t datalen)
+{
+ if (datalen >= strlen(IMAP_STR_220) && 0 == strncmp(data, IMAP_STR_220, strlen(IMAP_STR_220)) {
+ return 1;
+ }
+ return 0;
+}
+
+int imap_identify(const char *payload, size_t payload_len, int is_c2s)
+{
+ if (payload == NULL || payload_len < 4) {
+ return 0;
+ }
+
+ if (is_c2s) {
+ if (is_imap_cmd(payload, payload_len)) {
+ return 1;
+ }
+ } else {
+ if (is_imap_res(payload, payload_len)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int imap_parse_authentication_xoauth2(struct imap_authentication *auth, const char *line_start, size_t line_len)
+{
+ int decoded;
+ char decode_buf[512];
+ char *username;
+ size_t username_len;
+
+ decoded = Base64_DecodeBlock((unsigned char *)line_start, line_len, (unsigned char *)decode_buf, sizeof(decode_buf));
+ if (decoded < 0) {
+ return -1;
+ }
+
+ if (0 != mail_xoauth2_get_username((const char *)decode_buf, decoded, &username, &username_len)) {
+ return -1;
+ }
+
+ if (username_len >= sizeof(auth->username)) {
+ return -1;
+ }
+
+ memcpy(auth->username, username, username_len);
+ auth->username_len = username_len;
+
+ auth->state = IMAP_AUTH_DONE;
+ return 0;
+}
+
+static int imap_parse_authentication_plain(struct imap_authentication *auth, const char *line_start, size_t line_len) {
+ int decoded;
+ char decode_buf[512];
+ char *current_pos;
+ char *username_start = NULL;
+ char *password_start = NULL;
+ size_t remaining_len = decoded;
+
+ decoded = Base64_DecodeBlock((unsigned char *)line_start, line_len, (unsigned char *)decode_buf, sizeof(decode_buf));
+ if (decoded < 0) {
+ return -1;
+ }
+
+ // \0<authorization identity>\0<username>\0<password>
+ current_pos = decode_buf;
+
+ while (remaining_len > 0 && *current_pos == '\0') {
+ current_pos++;
+ remaining_len--;
+ }
+
+ username_start = current_pos;
+ while (remaining_len > 0 && *current_pos != '\0') {
+ current_pos++;
+ remaining_len--;
+ }
+
+ if (remaining_len > 0 && *current_pos == '\0') {
+ current_pos++;
+ remaining_len--;
+ }
+
+ password_start = current_pos;
+
+ size_t username_len = username_start ? (size_t)(current_pos - username_start - 1) : 0;
+ size_t password_len = password_start ? (size_t)(decode_buf + decoded - password_start - 1) : 0;
+
+ if (username_len >= IMAP_USERNAME_LEN_MAX || password_len >= IMAP_PASSWORD_LEN_MAX) {
+ return -1;
+ }
+
+ if (username_len > 0) {
+ memcpy(auth->username, username_start, username_len);
+ auth->username_len = username_len;
+ } else {
+ auth->username_len = 0;
+ }
+
+ if (password_len > 0) {
+ memcpy(auth->password, password_start, password_len);
+ auth->password_len = password_len;
+ } else {
+ auth->password_len = 0;
+ }
+
+ auth->state = IMAP_AUTH_DONE;
+ return 0;
+}
+
+static int imap_parse_authentication_login(struct imap_authentication *auth, const char *line_start, size_t line_len)
+{
+ int decoded;
+ char decode_buf[512];
+
+ decoded = Base64_DecodeBlock((unsigned char *)line_start, line_len, (unsigned char *)decode_buf, sizeof(decode_buf));
+ if (decoded < 0) {
+ return -1;
+ }
+
+ if (auth->state == IMAP_AUTH_WAITING_USERNAME) {
+ auth->state = IMAP_AUTH_WAITING_PASSWORD;
+
+ if (decoded > sizeof(auth->username)) {
+ return -1;
+ }
+ memcpy(auth->username, decode_buf, decoded);
+ return 0;
+ }
+
+ if (auth->state == IMAP_AUTH_WAITING_PASSWORD) {
+ auth->state = IMAP_AUTH_DONE;
+
+ if (decoded > IMAP_PASSWORD_LEN_MAX) {
+ return -1;
+ }
+ memcpy(auth->password, decode_buf, decoded);
+ return 0;
+ }
+
+ return 0;
+}
+
+static enum IMAP_AUTH_STATE imap_parse_authentication(struct imap_authentication *auth, enum IMAP_AUTH_TYPE type, const char *line_start, size_t line_len)
+{
+ int ret = 0;
+
+ if (auth->state == IMAP_AUTH_DONE) {
+ return IMAP_AUTH_DONE;
+ }
+
+ switch (type) {
+ case IMAP_AUTH_TYPE_LOGIN:
+ ret = imap_parse_authentication_login(auth, line_start, line_len);
+ case IMAP_AUTH_TYPE_PLAIN:
+ ret = imap_parse_authentication_plain(auth, line_start, line_len);
+ case IMAP_AUTH_TYPE_XOAUTH2:
+ ret =imap_parse_authentication_xoauth2(auth, line_start, line_len);
+ default:
+ break;
+ }
+
+ if (ret != 0) {
+ return IMAP_AUTH_DONE;
+ }
+
+ return auth->state;;
+}
+
+int imap_parse_command_auth(struct imap_authentication *auth, struct imap_command *imap_cmd)
+{
+ if (0 == SAFE_STRNCASECMP(imap_cmd.arg, imap_cmd.arg_len, "login", strlen("login"))) {
+ auth->type = IMAP_AUTH_TYPE_LOGIN;
+ auth->state = IMAP_AUTH_WAITING_USERNAME;
+ }
+ if (0 == SAFE_STRNCASECMP(imap_cmd.arg, imap_cmd.arg_len, "plain", strlen("plain"))) {
+ auth->type = IMAP_AUTH_TYPE_PLAIN;
+ auth->state = IMAP_AUTH_WAITING_USERNAME;
+ }
+ if (0 == SAFE_STRNCASECMP(imap_cmd.arg, imap_cmd.arg_len, "xoauth2", strlen("xoauth2"))) {
+ auth->type = IMAP_AUTH_TYPE_XOAUTH2;
+ auth->state = IMAP_AUTH_WAITING_USERNAME;
+ }
+ return 0;
+}
+
+int imap_parse_command(struct imap_command *imap_cmd, const char *line_start, size_t line_len)
+{
+ int ret;
+ struct iovec parts[IMAP_COMMAND_LINE_PART_NUM];
+
+ memset(imap_cmd, 0 , sizeof(struct imap_command));
+
+ ret = strnsplit(parts, sizeof(parts)/sizeof(parts[0]), ' ', line_start, line_len);
+ if (ret <= 0 || ret > IMAP_COMMAND_LINE_PART_NUM) {
+ return -1;
+ }
+
+ imap_cmd->cmd = imap_str2command(line_start, line_len);
+ imap_cmd->cmd_line = line_start;
+ imap_cmd->cmd_line_len = line_len;
+
+ if (ret == IMAP_COMMAND_LINE_PART_NUM) {
+ imap_cmd->arg = parts[IMAP_COMMAND_LINE_PART_ARG].iov_base;
+ imap_cmd->arg_len = parts[IMAP_COMMAND_LINE_PART_ARG].iov_len;
+ }
+
+ return 0;
+}
+
+static int imap_parser_publish_command(struct imap_parser *parser, struct session *sess, size_t mail_seq, struct imap_command *imap_cmd)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_IMAP;
+ msg->command->cmd = MAIL_CMD_OTHER;
+ msg->command->arg = imap_cmd->arg;
+ msg->command->arg_len = imap_cmd->arg_len;
+ msg->command->cmd_line = imap_cmd->cmd_line;
+ msg->command->cmd_line_len = imap_cmd->cmd_line_len;
+
+ switch (imap_cmd->cmd) {
+ case IMAP_COMMAND_MAIL:
+ struct iovec mail_from;
+ ret = mail_extract_email_address(imap_cmd.arg, imap_cmd.arg_len, mail_from);
+ if (ret != 0) {
+ break;
+ }
+ msg->command.cmd = MAIL_CMD_MAIL_FROM;
+ msg->command.arg = mail_from.iov_base;
+ msg->command.arg_len = mail_from.iov_len;
+ break;
+ case IMAP_COMMAND_RCPT:
+ case IMAP_COMMAND_SEND:
+ case IMAP_COMMAND_SAML:
+ case IMAP_COMMAND_SOML:
+ struct iovec rcpt_to;
+ ret = mail_extract_email_address(imap_cmd.arg, imap_cmd.arg_len, mail_from);
+ if (ret != 0) {
+ break;
+ }
+ msg->command.cmd = MAIL_CMD_RCPT_TO;
+ msg->command.arg = mail_from.iov_base;
+ msg->command.arg_len = mail_from.iov_len;
+ break;
+ case IMAP_COMMAND_STARTTLS:
+ msg->command.cmd = MAIL_CMD_STARTTLS;
+ msg->command.arg = NULL;
+ msg->command.arg_len = 0;
+ break;
+ case IMAP_COMMAND_EHLO:
+ case IMAP_COMMAND_HELO:
+ msg->command.cmd = MAIL_CMD_EHLO;
+ break;
+ default:
+ break;
+ }
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+}
+
+static int imap_parser_publish_username(struct imap_parser *parser, struct session *sess, size_t mail_seq, const char *username, size_t username_len)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ if (username == NULL || username_len == 0) {
+ return -1;
+ }
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_IMAP;
+ msg->command->cmd = MAIL_CMD_USERNAME;
+ msg->command->arg = username;
+ msg->command->arg_len = username_len;
+ msg->command->cmd_line = username;
+ msg->command->cmd_line_len = username_len;
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+
+ return 0;
+}
+
+static int imap_parser_publish_password(struct imap_parser *parser, struct session *sess, size_t mail_seq, const char *password, size_t password_len)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ if (password == NULL || password_len == 0) {
+ return -1;
+ }
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_IMAP;
+ msg->command->cmd = MAIL_CMD_PASSWORD;
+ msg->command->arg = password;
+ msg->command->arg_len = password_len;
+ msg->command->cmd_line = password;
+ msg->command->cmd_line_len = password_len;
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+
+ return 0;
+}
+
+int imap_parser_process_c2s(struct imap_parser *imap, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ int ret;
+ struct imap_command *imap_cmd;
+ enum IMAP_AUTH_STATE auth_state;
+
+ if (imap->state == IMAP_STATE_EXIT) {
+ return 0;
+ }
+
+ // mail
+ if (imap->state == IMAP_STATE_WAITING_MAIL_DATA) {
+ ret = mail_parser_process(imap->mail_parser, payload, payload_len);
+ if (ret != 0) {
+ imap->state = IMAP_STATE_EXIT;
+ return -1;
+ }
+ return 0;
+ }
+
+ line_buffer_clear(imap->line_buffer); // clear completed line
+ line_buffer_write(imap->line_buffer, payload, payload_len);
+ while (1) {
+ const char *line_start;
+ size_t line_len;
+
+ // line
+ ret = line_buffer_readln(imap->line_buffer, &line_start, &line_len);
+ if (ret != 0) {
+ imap->state = IMAP_STATE_EXIT;
+ return -1;
+ }
+
+ if (line_start == NULL || line_len == 0) {
+ break;
+ }
+
+ switch (imap->state) {
+ // auth
+ case IMAP_STATE_WAITING_AUTHENTICATION:
+ auth_state = imap_parse_authentication(&imap->authentication, line_start, line_len);
+ if (auth_state == IMAP_AUTH_DONE) {
+ imap_parser_publish_username(imap, sess, mail_parser_get_seq(imap->mail_parser), imap->authentication.username, imap->authentication.username_len);
+ imap_parser_publish_password(imap, sess, mail_parser_get_seq(imap->mail_parser), imap->authentication.password, imap->authentication.password_len);
+ imap->state = IMAP_STATE_WAITING_COMMAND;
+ }
+ break;
+ // command
+ case IMAP_STATE_WAITING_COMMAND:
+ struct imap_command imap_cmd;
+ ret = imap_parse_command(&imap_cmd, line_start, line_len);
+ if (ret != 0) {
+ imap->state = IMAP_STATE_EXIT;
+ return -1;
+ }
+
+ switch (imap_cmd.cmd) {
+ case IMAP_COMMAND_AUTH:
+ imap_parse_command_auth(&imap->authentication, &imap_cmd);
+ imap->state = IMAP_STATE_WAITING_AUTHENTICATION;
+ break;
+ case IMAP_COMMAND_DATA:
+ imap->state = IMAP_STATE_WAITING_MAIL_DATA;
+ break;
+ case IMAP_COMMAND_RSET:
+ case IMAP_COMMAND_QUIT:
+ case IMAP_COMMAND_STARTTLS:
+ imap->state = IMAP_STATE_EXIT;
+ break;
+ default:
+ break;
+ }
+
+ imap_parser_publish_command(imap, sess, mail_parser_get_seq(imap->mail_parser), imap_cmd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int imap_parser_process_s2c(struct imap_parser *imap, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ return 0;
+}
+
+int imap_parser_process(struct imap_parser *parser, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ if (is_c2s) {
+ return imap_parser_process_c2s(parser, sess, payload, payload_len);
+ } else {
+ return imap_parser_process_s2c(parser, sess, payload, payload_len);
+ }
+}
+
+void imap_parser_free(struct imap_parser *parser)
+{
+ if (parser) {
+ if (parser->line_buffer) {
+ stream_buffer_deinit(parser->line_buffer);
+ }
+ if (parser->mail_parser) {
+ mail_parser_free(parser->mail_parser);
+ }
+ free(parser);
+ }
+}
+
+struct imap_parser * imap_parser_new(struct mail_env *mail_env)
+{
+ struct imap_parser *parser = (struct imap_parser *)calloc(1, sizeof(struct imap_parser));
+ parser->mail_env_ref = mail_env;
+ parser->mail_parser = mail_parser_new(mail_env);
+ stream_buffer_init(&parser->line_buffer, IMAP_LINE_BUFFER_MAX);
+ return parser;
+}
+
diff --git a/decoders/mail/mail_imap.h b/decoders/mail/mail_imap.h
new file mode 100644
index 0000000..b90aa06
--- /dev/null
+++ b/decoders/mail/mail_imap.h
@@ -0,0 +1,123 @@
+#pragma once
+
+#include "mail_global.h"
+
+#define MAILIMAP4_MODULE "[MAIL_IMAP4]"
+
+#define RESPONSE_FETCH_MAX_LEN 32 //取得FETCH响应第2个空格最大长度
+
+#define CONT_TYPE_TEXT 1
+#define CONT_TYPE_MESSAGE 2
+#define CONT_TYPE_RFC822 3
+#define CONT_TYPE_OTHER 4
+
+//解析BODYSTRUCTURE一行时的过程状态
+typedef enum
+{
+ STAT_BLOCK_IDLE=0,
+ STAT_BLOCK_START,
+ STAT_BLOCK_PROC,
+ STAT_BLOCK_END,
+}STAT_BODYSTRUCT_t;
+
+//multipart块所有的字段顺序
+typedef enum
+{
+ MULTI_SUBTYPE=0,
+ MULTI_PARENTHESIS, //参数组合列表;从该字段往后都是扩展字段
+ MULTI_DISPOSITION,
+ MULTI_LANGUAGE,
+ MULTI_LOCATION,
+ MULTI_NUM,
+}MULTI_FIELD_t;
+
+//非multipart块所有的字段顺序
+typedef enum
+{
+ NOTMUL_TYPE=0,
+ NOTMUL_SUBTYPE,
+ NOTMUL_PARENTHESIS,
+ NOTMUL_ID,
+ NOTMUL_DESCR, /*5*/
+ NOTMUL_ENCODE,
+ NOTMUL_SIZE,
+ NOTMUL_EVENLOP, //MESSAGE/RFC822独有,从该字段往后都是扩展字段
+ NOTMUL_BODYSTRUCT, //MESSAGE/RFC822独有
+ NOTMUL_LINES, //主类型NOTMUL_TYPE为"TEXT"或类型为MESSAGE/RFC822才有该字段;
+ NOTMUL_MD5,
+ NOTMUL_DISPOSITION, /*10*/
+ NOTMUL_LANGUAGE,
+ NOTMUL_LOCATION,
+ NOTMUL_NUM,
+}NOTMUL_FIELD_t;
+
+#define IMAP_OK_CMD_LEN 30 //IMAP协议FETCH响应结束的长度,如"B00964 OK Fetch completed"
+#define IMAP_LABEL_OK_MAX_LEN 100
+
+//邮件命令编号从20开始
+typedef enum IMAP_CMD_STATE
+{
+ IMAP_CMD_ANS=20,
+ IMAP_CMD_FETCH_ANS, //不需要进行复位的响应
+ IMAP_CMD_DATA_END,
+ IMAP_CMD_MAYBE_END, //遇到")\r\n"的状态
+ IMAP_CMD_NOT_END, //判断上一次')'结束标记IMAP_CMD_MAYBE_END不是真正的结束
+ IMAP_CMD_DATA_CONTINUE, //按字节获取邮件的后续块
+ IMAP_CMD_DATA_BORDER, //来的是一个块的开始,是BODY内容
+ IMAP_CMD_DROP, //处理一个标号块时发现有附件名,回调业务层之后业务层给DROP
+ IMAP_CMD_UNKNOWN,
+}IMAP_CMD_STATE_t;
+
+//为body/bodystructure解析设置的哈希节点
+typedef struct __mime_item
+{
+ stBufCache mime_header; //用于存储构造MIME头部
+ char charset[MAIL_MAX_CHARSET_LEN];
+ char filename_charset[MAIL_MAX_CHARSET_LEN];
+ char *attach_filename;
+ char *boundary;
+ int filename_len;
+ int bound_len;
+ int trans_enc; //传输编码
+ int is_multi; //是否是MULTIPART块
+ int thread_id;
+}mime_item_t;
+
+enum IMAP_COMMAND {
+ IMAP_COMMAND_UNKNOWN,
+ IMAP_COMMAND_CAPABILITY ,
+ IMAP_COMMAND_NOOP ,
+ IMAP_COMMAND_LOGOUT ,
+ IMAP_COMMAND_Client ,
+ IMAP_COMMAND_STARTTLS ,
+ IMAP_COMMAND_AUTHENTICATE ,
+ IMAP_COMMAND_LOGIN ,
+ IMAP_COMMAND_Client ,
+ IMAP_COMMAND_SELECT ,
+ IMAP_COMMAND_EXAMINE ,
+ IMAP_COMMAND_CREATE ,
+ IMAP_COMMAND_DELETE ,
+ IMAP_COMMAND_RENAME ,
+ IMAP_COMMAND_SUBSCRIBE ,
+ IMAP_COMMAND_UNSUBSCRIBE ,
+ IMAP_COMMAND_LIST ,
+ IMAP_COMMAND_LSUB ,
+ IMAP_COMMAND_STATUS ,
+ IMAP_COMMAND_APPEND ,
+ IMAP_COMMAND_Client ,
+ IMAP_COMMAND_CHECK ,
+ IMAP_COMMAND_CLOSE ,
+ IMAP_COMMAND_EXPUNGE ,
+ IMAP_COMMAND_SEARCH ,
+ IMAP_COMMAND_FETCH ,
+ IMAP_COMMAND_STORE ,
+ IMAP_COMMAND_COPY ,
+ IMAP_COMMAND_UID,
+ IMAP_COMMAND_MAX,
+};
+
+int imap_identify(const char *payload, size_t payload_len, int is_c2s);
+int imap_parser_process(struct pop3_parser *parser, struct session *sess, const char *payload, size_t payload_len, int is_c2s);
+
+
+
diff --git a/decoders/mail/mail_internal.h b/decoders/mail/mail_internal.h
new file mode 100644
index 0000000..c200172
--- /dev/null
+++ b/decoders/mail/mail_internal.h
@@ -0,0 +1,104 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stellar/mail.h>
+
+#define MAIL_EXDATA_NAME "MAIL_EXDATA"
+#define MAIL_COMMAND_TOPIC_NAME "MAIL_COMMAND"
+#define MAIL_HEADER_TOPIC_NAME "MAIL_HEADER"
+#define MAIL_BODY_TOPIC_NAME "MAIL_BODY"
+#define MAIL_ATTACHMENT_TOPIC_NAME "MAIL_ATTACHMENT"
+#define MAIL_EML_TOPIC_NAME "MAIL_EML"
+
+enum MAIL_MESSAGE_TYPE {
+ MAIL_MESSAGE_TYPE_COMMAND,
+ MAIL_MESSAGE_TYPE_HEADER,
+ MAIL_MESSAGE_TYPE_BODY,
+ MAIL_MESSAGE_TYPE_ATTACHMENT,
+ MAIL_MESSAGE_TYPE_EML,
+ MAIL_MESSAGE_TYPE_MAX,
+};
+
+struct mail_authentication {
+ const char *username;
+ size_t username_len;
+ const char *password;
+ size_t password_len;
+};
+
+struct mail_command {
+ enum MAIL_COMMAND cmd;
+ const char *arg;
+ size_t arg_len;
+ const char *cmd_line;
+ size_t cmd_line_len;
+};
+
+struct mail_body {
+ char *body;
+ size_t body_len;
+ size_t body_offset;
+ int is_body_finished;
+};
+
+struct mail_attachment {
+ char *attachment_name;
+ size_t attachment_name_len;
+ char *attachment;
+ size_t attachment_len;
+ size_t attachment_offset;
+ int is_attachment_finished;
+};
+
+struct mail_eml {
+ char *eml;
+ size_t eml_len;
+ size_t eml_offset;
+ int is_eml_finished;
+};
+
+struct mail_message {
+ enum MAIL_MESSAGE_TYPE type;
+ enum MAIL_PROTOCOL mail_protocol;
+ size_t mail_seq;
+
+ struct mail_command command;
+ struct mail_header header;
+ struct mail_body body;
+ struct mail_attachment attachment;
+ struct mail_eml eml;
+
+ struct session *sess_ref;
+};
+
+struct mail_session_ctx {
+ enum MAIL_PROTOCOL protocol;
+
+ struct smtp_parser *smtp_parser;
+ //struct imap_parser *imap;
+ //struct pop3_parser *pop3;
+
+ int is_mail_protocol;
+ struct mail_env *mail_env_ref;
+ struct session *sess_ref;
+};
+
+struct mail_env {
+ int command_topic_id;
+ int header_topic_id;
+ int body_topic_id;
+ int attachment_topic_id;
+ int eml_topic_id;
+ int exdata_id;
+
+ struct module_manager *mod_mgr_ref;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/decoders/mail/mail_mime.c b/decoders/mail/mail_mime.c
new file mode 100644
index 0000000..d38b8cb
--- /dev/null
+++ b/decoders/mail/mail_mime.c
@@ -0,0 +1,707 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "mail_mime.h"
+
+static void urldecode2(char *dst, const char *src)
+{
+ char a, b;
+ while (*src) {
+ if ((*src == '%') &&
+ ((a = src[1]) && (b = src[2])) &&
+ (isxdigit(a) && isxdigit(b))) {
+ if (a >= 'a')
+ a -= 'a'-'A';
+ if (a >= 'A')
+ a -= ('A' - 10);
+ else
+ a -= '0';
+ if (b >= 'a')
+ b -= 'a'-'A';
+ if (b >= 'A')
+ b -= ('A' - 10);
+ else
+ b -= '0';
+ *dst++ = 16*a+b;
+ src+=3;
+ } else if (*src == '+') {
+ *dst++ = ' ';
+ src++;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ *dst++ = '\0';
+}
+
+static int check_mem_size(int wantedlen, char **pmem, int *len)
+{
+ if(wantedlen < *len)
+ return 0;
+
+ if(*pmem == NULL)
+ *pmem = (char *)malloc(wantedlen);
+ else
+ *pmem = (char *)realloc(*pmem, wantedlen);
+ *len = wantedlen;
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:subject_decode
+功能简介:对主题和附件名进行解码
+ 形如:=?gbk?Q?=B2=E2=CA=D4=D2=BB=CF=C2?=,支持一行多段这样的输入;
+ 解码结果在out中,如果失败,out存储原始数据
+输入参数:in: 带解码数据;inl:in的长度
+ outl:out的大小
+输出参数:out: 解码后缓冲区; outl: 解码后长度
+ charset:编码类型,如gbk
+返回值:0
+*********************************************************************/
+#ifdef __cplusplus
+ extern "C"
+ {
+#endif
+int decode_mime_header(const char *in, int inl, char *out, int *outl, char* charset, int max_charset_len)
+{
+ return mime_header_decode(in, inl, out, outl, charset, max_charset_len);
+}
+#ifdef __cplusplus
+}
+#endif
+
+
+int mime_header_decode(const char *in, int inl, char *out, int *outl, char* charset, int max_charset_len)
+{
+ const char *in_end=in+inl, *begin, *chset_end, *encode_end, *end;
+ int outsize = *outl, total_len=0;
+ int ret, charset_len;
+
+ //找到编码开始位置
+ begin = (char *)mail_memmem(in, inl, "=?", 2);
+ if(begin == NULL)
+ goto out_cp;
+
+ //第一个问号是字符集编码结束位置
+ chset_end = (char *)memchr(begin+2, '?', in_end-begin-2);
+ if(chset_end == NULL)
+ goto out_cp;
+
+ //第2个问号是传输编码结束位置
+ encode_end = (char *)memchr(chset_end+1, '?', in_end-chset_end-1);
+ if(encode_end == NULL)
+ goto out_cp;
+
+ //本段编码结束位置
+ end = (char *)mail_memmem(encode_end+1, in_end-encode_end-1, "?=", 2);
+ if(end == NULL)
+ goto out_cp;
+
+ charset_len = (chset_end-begin-2)>=max_charset_len?(max_charset_len-1):(chset_end-begin-2);
+ memcpy(charset, begin+2, charset_len);
+ charset[charset_len] = '\0';
+
+ if(*(encode_end-1) == 'B' || *(encode_end-1) == 'b')
+ {
+ if(begin - in > 0)
+ {
+ memcpy(out, in, begin-in);
+ total_len = begin-in;
+ }
+
+ ret = Base64_DecodeBlock((const unsigned char *)(encode_end+1), end-encode_end-1, (unsigned char *)out+total_len, outsize-total_len);
+ if(ret < 0)
+ {
+ char buf[128]={0};
+ int datalen = (end-encode_end-1)>=128?127:(end-encode_end-1);
+ memcpy(buf, encode_end+1, datalen);
+ buf[datalen] = '\0';
+ goto out_cp;
+ }
+ total_len += ret;
+ }
+ else if(*(encode_end-1) == 'Q' || *(encode_end-1) == 'q')
+ {
+ if(begin - in > 0)
+ {
+ memcpy(out, in, begin-in);
+ total_len = begin-in;
+ }
+
+ ret = QP_DecodeBlock((const unsigned char *)(encode_end+1), end-encode_end-1, (unsigned char *)out+total_len);
+ if(ret < 0)
+ {
+ char buf[128]={0};
+ int datalen = (end-encode_end-1)>=128?127:(end-encode_end-1);
+ memcpy(buf, encode_end+1, datalen);
+ buf[datalen] = '\0';
+ goto out_cp;
+ }
+ total_len += ret;
+ }
+ else
+ goto out_cp;
+
+ if(in_end - end - 2 > 0)
+ {
+ outsize -= total_len;
+ mime_header_decode(end+2, in_end-end-2, out+total_len, &outsize, charset, max_charset_len);
+ total_len += outsize;
+ }
+
+ *outl = total_len;
+
+ return 0;
+
+out_cp:
+ memcpy(out, in, inl);
+ *outl = inl;
+ return 0;
+}
+
+/*********************************************************************
+函数名称:add_boundary(char *line, boundary_list **Head)
+功能简介:将找到的界限加入界限链表
+输入参数:src:当前行
+ n:行长度
+ Head:指向包含boundary_list域的结构体指针的指针
+
+输出参数:
+返回值:-1 分配内存失败,正常处理返回0
+*********************************************************************/
+int add_boundary(const char *src, int n, boundary_list **Head)
+{
+ boundary_list *p;
+ int len;
+
+ if(src == NULL || n <= 0)
+ return -1;
+
+ p = (boundary_list *)malloc(sizeof(boundary_list));
+ if(*Head==NULL)
+ {
+ p->main_bound = BOUNDARY_TYPE_MAIN;
+ }
+ else
+ {
+ p->main_bound = BOUNDARY_TYPE_OTHER;
+ }
+ len = (n>=BOUNDARY_SIZE)?(BOUNDARY_SIZE-1):n;
+ memcpy(p->str, src, len);
+ p->str_len = len;
+
+ p->next = *Head;
+ *Head = p;
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:search_boundary
+功能简介:查找某行是否有界限。
+输入参数:src:要查找的行
+ head:指向界限链表头的指针
+输出参数:
+返回值:找到界限,返回1;未找到,返回0
+*********************************************************************/
+static int search_boundary(char *src, int srclen, boundary_list *head)
+{
+ boundary_list *p;
+
+ for(p=head; p!=NULL; p=p->next)
+ {
+ if(srclen != p->str_len)
+ continue;
+
+ if (strncmp(src, p->str, srclen) == 0)
+ return p->main_bound;
+ }
+ return 0;
+}
+
+/*********************************************************************
+函数名称:boundary_free
+功能简介:释放界线链表
+输入参数:head:指向界限链表头的指针
+输出参数:无
+返回值: 无
+*********************************************************************/
+static void free_boundary( boundary_list *head)
+{
+ boundary_list *p = head, *q;
+
+ while(p != NULL)
+ {
+ q = p;
+ p = p->next;
+
+ free(q);
+ }
+}
+
+static int get_value_th(char *temp, int len,char **ppval,int *val_len)
+{
+ char *temp2=NULL;
+ char *tp=NULL;
+ char *tpend=NULL;
+
+ tp=temp;
+ tpend=tp+len;
+ while((tp[0]==' ')&&(tp<tpend))
+ tp=tp+1;
+
+ if(tp==tpend)
+ {
+ *ppval = tp;
+ *val_len = 0;
+ return 0;
+ }
+
+ if(tp[0]=='"')
+ {
+ tp=tp+1;
+ *ppval = tp;
+
+ temp2=(char *)memchr(tp,'"',tpend-tp);
+ if(temp2!=NULL)
+ {
+ *val_len = temp2-tp;
+ }
+ else
+ *val_len = tpend-tp;
+ }
+ else //eg. charset=us-ascii (Plain Text)
+ {
+ if(((temp2=(char *)memchr(tp,';',tpend-tp))!=NULL)||((temp2=(char *)memchr(tp,' ',tpend-tp))!=NULL)||\
+ ((temp2=(char *)memchr(tp,'\r',tpend-tp))!=NULL) || ((temp2=(char *)memchr(tp,'\n',tpend-tp))!=NULL))
+ *val_len = temp2-tp;
+ else
+ *val_len = tpend-tp;
+
+ *ppval = tp;
+ }
+
+ return 0;
+}
+
+static int check_name_th(char *input, int inputlen, const char *name, char **ppval,int *val_len)
+{
+ char *temp=NULL;
+ int tmplen=strlen(name);
+
+ //注意memncasemem_sub128对第2个子串的长度限制,为了速度,为了不分配内存
+ if((temp=(char*)mesa_memncasemem(input,inputlen,name,tmplen))!=NULL)//adjust by lqy 09-01
+ {
+ temp=temp+tmplen;
+ if(get_value_th(temp,inputlen-(temp-input),ppval,val_len)==0)
+ return 0;
+ }
+
+ return -1;
+}
+
+/*********************************************************************
+函数名称:init_mimeinfo
+功能简介:初始化邮件MIME处理数据结构
+输入参数:mimeinfo:指向MimeParse_t结构体指针的指针,为其分配内存
+输出参数:无
+返回值: -1:出错
+ 0:正常
+*********************************************************************/
+int init_mime_info(MimeParse_t **mimeinfo)
+{
+ MimeParse_t *pmimeinfo;
+
+ pmimeinfo = (MimeParse_t *)malloc(sizeof(MimeParse_t));
+ memset(pmimeinfo, 0, sizeof(MimeParse_t));
+
+ pmimeinfo->TransferEnc = MIME_TRANSENC_UNKNOWN;
+
+ pmimeinfo->handle.length=0; //handle_init();
+ pmimeinfo->line_type = MIME_LINE_NULL;
+
+ *mimeinfo = pmimeinfo;
+
+ return 0;
+}
+
+/*********************************************************************
+函数名称:clear_mimeinfo
+功能简介:清空数据缓冲区数据
+输入参数:mimeinfo:
+输出参数:无
+返回值: 无
+*********************************************************************/
+void clear_mime_info(MimeParse_t *mimeinfo)
+{
+ free_boundary(mimeinfo->b_header);
+
+ if(mimeinfo->filename!=NULL)
+ free(mimeinfo->filename);
+
+ free(mimeinfo);
+}
+
+/*********************************************************************
+函数名称:reset_mimeinfo
+功能简介:清空MIME数据结构体,如果是邮件的部分内容结束,则不清空边界链表,
+ 否则清空边界链表
+输入参数:mimeinfo:
+ PartEnd:1是邮件部分内容结束,0 整体邮件处理结束
+输出参数:无
+返回值: 无
+*********************************************************************/
+void reset_mime_info(MimeParse_t * mimeinfo,int PartEnd)
+{
+ if(PartEnd==0)//一个区块完成
+ {
+ free_boundary(mimeinfo->b_header);
+ mimeinfo->b_header = NULL;
+ mimeinfo->bound_comes = 0;
+ }
+
+ mimeinfo->src = NULL;
+ mimeinfo->dst = NULL;
+ mimeinfo->srcLen = 0;
+ mimeinfo->dstSize = 0;
+ mimeinfo->actLen = 0;
+ mimeinfo->text_begin = 0;
+ mimeinfo->filenameLen = 0;
+ if(mimeinfo->filename!=NULL)
+ {
+ free(mimeinfo->filename);
+ mimeinfo->filename = NULL;
+ }
+
+ mimeinfo->filename_charset[0] = 0;
+ mimeinfo->charset[0] = 0;
+
+ mimeinfo->TransferEnc = MIME_TRANSENC_UNKNOWN;
+ mimeinfo->line_type = MIME_LINE_NULL;
+
+ mimeinfo->handle.length=0;
+}
+
+/*********************************************************************
+函数名称:detect_charenc
+功能简介:把字符串的传输编码方式转换成相应的整数定义
+输入参数:charset : 传输编码方式字符串(in)
+输出参数:无
+返回值: 对应的编码方式代码或者未判别标志
+*********************************************************************/
+static int detect_transenc(char * transenc, int len)
+{
+ while(*transenc == ' ')
+ {
+ transenc++;
+ len--;
+ }
+
+ if(strncmp_one_word_mesa("base64", "BASE64", 6, transenc, len))
+ return MIME_TRANSENC_BASE64;
+ if(strncmp_one_word_mesa("quoted-printable", "QUOTED-PRINTABLE", 16, transenc, len))
+ return MIME_TRANSENC_QP;
+
+ return MIME_TRANSENC_UNKNOWN;
+}
+
+/*********************************************************************
+函数名称:line_is_mime_header
+功能简介:
+输入参数:pInfo->src是完整行,没有支持开头是空格的情况
+输出参数:无
+返回值: <0: error; >0: header or null-line; 0: CONTENT
+*********************************************************************/
+static int mime_line_identify(MimeParse_t *pInfo)
+{
+ if(pInfo->srcLen==0 || (pInfo->srcLen==2 && pInfo->src[0]=='\r' && pInfo->src[1]=='\n')||(pInfo->srcLen==1 && pInfo->src[0]=='\n')) //一个空行
+ {
+ return MIME_LINE_NULL;
+ }
+
+ if(pInfo->srcLen>2 && strncmp(pInfo->src, "--", 2) == 0)
+ {
+ int len = pInfo->srcLen;
+
+ while(len>2 && (*(pInfo->src+len-1)=='\r' || *(pInfo->src+len-1)=='\n'))
+ len--;
+
+ if(!search_boundary(pInfo->src + 2, len-2, pInfo->b_header))
+ {
+ int type;
+ if(len >=4 && pInfo->src[len-1] == '-' && pInfo->src[len-2] == '-' && \
+ (type=search_boundary(pInfo->src + 2, len-4, pInfo->b_header))>0)
+ {
+ if(type==BOUNDARY_TYPE_MAIN)
+ {
+ pInfo->bound_comes = 0;
+ pInfo->line_type = MIME_LINE_COMMENT;
+ }
+ return MIME_LINE_BOUNARY_END;
+ }
+ //TODO: 正文中明文有---的情况,如发送失败的回传邮件
+ if(pInfo->bound_comes==0 && pInfo->b_header != NULL)
+ return MIME_LINE_COMMENT;
+ else
+ return MIME_LINE_CONT_BODY;
+ }
+ return MIME_LINE_BOUNARY;
+ }
+
+ if(pInfo->line_type == MIME_LINE_CONT_BODY || pInfo->line_type == MIME_LINE_COMMENT)
+ return pInfo->line_type;
+
+ if(strncmp_one_word_mesa("content-type:", "CONTENT-TYPE:", PARTHEAD_CONTTYPE_LEN, pInfo->src, pInfo->srcLen))
+ return MIME_LINE_CONT_TYPE;
+ if(strncmp_one_word_mesa("content-transfer-encoding:", "CONTENT-TRANSFER-ENCODING:", PARTHEAD_TRANSENC_LEN, pInfo->src, pInfo->srcLen))
+ return MIME_LINE_CONT_ENC;
+ if(strncmp_one_word_mesa("content-disposition:", "CONTENT-DISPOSITION:", PARTHEAD_CONTENTDISPOSITION_LEN, pInfo->src, pInfo->srcLen))
+ return MIME_LINE_CONT_DISP;
+// if(check_str_begin(pInfo->src, pInfo->srcLen, "Content"))
+// return MIME_LINE_CONT_OTHR;
+
+ if(pInfo->bound_comes==0 && pInfo->b_header != NULL)
+ return MIME_LINE_COMMENT;
+ if(pInfo->boundary_pending)
+ return MIME_LINE_CONT_OTHR;
+ //TODO:如果开头是空格
+
+ return MIME_LINE_CONT_BODY;
+}
+
+/*********************************************************************
+函数名称:mime_parse_feed
+功能简介:进行MIME解码,输入内容为MIME原始编码数据,调用者
+ 根据返回值进行相应的操作。
+注意事项:如果不进行解码或不是编码字符,目的缓冲区指向源缓冲区,
+ 因此在未使用完目的缓冲区前,源缓冲区不可改变内容或指针;
+输入参数:mimeinfo:
+ decode:是否进行传输编码解码,1-是;0-否
+输出参数:无
+返回值:MIME行类型
+*********************************************************************/
+int mime_parse_feed(MimeParse_t *pInfo, int decode)
+{
+ int ret;
+ char *pout = NULL, *pc;
+ int outlen=0, pclen;
+ int len;
+
+ if(pInfo->dst==NULL)
+ return -10;
+
+ pInfo->actLen = 0;
+
+ pInfo->line_type = mime_line_identify(pInfo);
+ switch(pInfo->line_type)
+ {
+ case MIME_LINE_ERR:
+ return -2;
+
+ case MIME_LINE_NULL:
+ // MIME头和体之间必然有空格;但是体和下一个boundary之间就不一定有空格
+ pInfo->text_begin = 1;
+ pInfo->boundary_pending = 0;
+
+ if(pInfo->bound_comes==0 && pInfo->b_header != NULL)
+ pInfo->line_type = MIME_LINE_COMMENT;
+ else
+ pInfo->line_type = MIME_LINE_CONT_BODY; //TODO: 正文中出现"\r\n---anystr",前面的空行被丢弃了
+ break;
+
+ case MIME_LINE_BOUNARY:
+ reset_mime_info(pInfo, 1); //set text_begin=0
+ pInfo->boundary_pending = 1;
+
+ if(pInfo->bound_comes == 0)
+ pInfo->bound_comes = 1;
+ break;
+
+ case MIME_LINE_BOUNARY_END:
+ pInfo->text_begin = 0; //一般后续要么结束,要么是新的BOUNARY;为了IMAP结尾的')'
+ break;
+
+ case MIME_LINE_CONT_TYPE:
+ pc = pInfo->src + PARTHEAD_CONTTYPE_LEN;
+ pclen = pInfo->srcLen - PARTHEAD_CONTTYPE_LEN;
+
+ if(check_name_th(pc, pclen, PARTHEAD_CHARSET, &pout, &outlen)==0)
+ {
+ len = (outlen>=MAIL_MAX_CHARSET_LEN)?(MAIL_MAX_CHARSET_LEN-1):outlen;
+ memcpy(pInfo->charset, pout, len);
+ pInfo->charset[len] = '\0';
+ }
+
+ if(pInfo->filename != NULL)
+ {
+ free(pInfo->filename);
+ pInfo->filename = NULL;
+ pInfo->filenameLen = 0;
+ }
+ else if(check_name_th(pc, pclen, PARTHEAD_NAME, &pout, &outlen)==0)
+ {
+ //tmplen为0的情况,pInfo->filename!=NULL && pInfo->filenameLen==0, 调用者自行处理
+ if(check_mem_size(outlen+1, &pInfo->filename, &pInfo->filenameLen))
+ return -3;
+
+ mime_header_decode(pout, outlen, pInfo->filename, &pInfo->filenameLen, pInfo->filename_charset, MAIL_MAX_CHARSET_LEN);
+ pInfo->filename[pInfo->filenameLen] = '\0'; //ENDPART reset时会清理,释放
+ }
+
+ if(check_name_th(pc, pclen, PARTHEAD_BOUNDARY, &pout, &outlen)==0)
+ {
+ if(search_boundary(pout, outlen, pInfo->b_header)==0)
+ {
+ if(add_boundary(pout, outlen, &pInfo->b_header))
+ return -4;
+ }
+ }
+ break;
+
+ case MIME_LINE_CONT_DISP:
+ if (pInfo->filename != NULL) // && strlen(pInfo->filename)>0)
+ {
+ free(pInfo->filename);
+ pInfo->filename = NULL; //避免重复调用业务层
+ pInfo->filenameLen = 0;
+ break;
+ }
+
+ pc = pInfo->src + PARTHEAD_CONTENTDISPOSITION_LEN;
+ pclen = pInfo->srcLen - PARTHEAD_CONTENTDISPOSITION_LEN;
+
+ if (check_name_th(pc, pclen, PARTHEAD_FILENAME, &pout, &outlen) == 0)
+ {
+ //tmplen为0的情况,pInfo->filename!=NULL && pInfo->filenameLen==0, 调用者自行处理
+ if (check_mem_size(outlen + 1, &pInfo->filename, &pInfo->filenameLen))
+ return -5;
+
+ mime_header_decode(pout, outlen, pInfo->filename, &pInfo->filenameLen, pInfo->filename_charset, MAIL_MAX_CHARSET_LEN);
+ pInfo->filename[pInfo->filenameLen] = '\0'; //ENDPART reset时会清理,释放
+ }
+ else if (check_name_th(pc, pclen, PARTHEAD_FILENAME_EXT, &pout, &outlen) == 0)
+ {
+ // support RFC 6266, ext value, like filename*=utf-8''%e2%82%ac%20rates
+ const char *p_first_quote = NULL, *p_second_quote = NULL, *p_actual_value = NULL;
+ int actual_value_len = 0;
+ // define in RFC 2231.section 4, filename ext value format [charset'language'actual value]
+ for (int i = 0; i < outlen; i++)
+ {
+ if (pout[i] == '\'')
+ {
+ if (p_first_quote == NULL)
+ {
+ p_first_quote = (pout + i);
+ }
+ else
+ {
+ p_second_quote = (pout + i);
+ break;
+ }
+ }
+ }
+ // must include two single quote
+ if (p_first_quote == NULL || p_second_quote == NULL)
+ {
+ p_actual_value = pout;
+ actual_value_len = outlen;
+ }
+ else
+ // copy charset if possible
+ {
+ p_actual_value = (p_second_quote + 1);
+ actual_value_len = outlen - (p_actual_value - pout);
+ if (p_first_quote != pout && (p_first_quote - pout) < MAIL_MAX_CHARSET_LEN)
+ {
+ memcpy(pInfo->filename_charset, pout, (p_first_quote - pout));
+ }
+ }
+ if (check_mem_size(actual_value_len + 1, &pInfo->filename, &pInfo->filenameLen))
+ return -3;
+ memset(pInfo->filename, 0, actual_value_len + 1);
+ memcpy(pInfo->filename, p_actual_value, actual_value_len);
+ char *decode_buff = (char *)malloc(actual_value_len + 1);
+ urldecode2(decode_buff, pInfo->filename);
+ memset(pInfo->filename, 0, actual_value_len + 1);
+ memcpy(pInfo->filename, decode_buff, strlen(decode_buff));
+ pInfo->filenameLen = strlen(decode_buff);
+ free(decode_buff);
+ }
+ else
+ {
+ while (pclen > 0 && (*pc == ' ' || *pc == '\t'))
+ {
+ pc++;
+ pclen--;
+ }
+
+ if (pclen > 0 && strncmp_one_word_mesa("attachment", "ATTACHMENT", 10, pc, pclen))
+ {
+ pInfo->filename = (char *)malloc(1);
+ pInfo->filenameLen = 0;
+ pInfo->filename[0] = '\0';
+ }
+ }
+ break;
+
+ case MIME_LINE_CONT_ENC:
+ pInfo->TransferEnc = detect_transenc(pInfo->src + PARTHEAD_TRANSENC_LEN, pInfo->srcLen-PARTHEAD_TRANSENC_LEN);
+ break;
+
+ case MIME_LINE_CONT_OTHR:
+ pInfo->dst = pInfo->src;
+ pInfo->actLen = pInfo->srcLen;
+ break;
+
+ case MIME_LINE_COMMENT: //注释行
+ break;
+
+ case MIME_LINE_CONT_BODY:
+ //正文或附件内容
+ if((pInfo->TransferEnc==MIME_TRANSENC_BASE64 || pInfo->TransferEnc==MIME_TRANSENC_QP) && decode!=0)
+ {
+ if(pInfo->TransferEnc==MIME_TRANSENC_BASE64)
+ {
+ ret = Base64_DecodeFeed_r_n(&pInfo->handle, (unsigned char *)pInfo->src, pInfo->srcLen, (unsigned char *)pInfo->dst, pInfo->dstSize);
+ if(ret < 0)
+ {
+ char buf[128]={0};
+ memcpy(buf, pInfo->src, pInfo->srcLen>=128?127:pInfo->srcLen);
+ return ret;
+ }
+ }
+ else
+ {
+ ret = QP_DecodeFeed(&pInfo->handle, (unsigned char *)pInfo->src, pInfo->srcLen, (unsigned char *)pInfo->dst);
+ if(ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ pInfo->actLen = ret;
+ }
+ else
+ {
+ pInfo->dst = pInfo->src;
+ pInfo->actLen = pInfo->srcLen;
+ //pInfo->text_begin=1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return pInfo->line_type;
+}
+
+void body_update_text_begin(MimeParse_t *pInfo)
+{
+ if(pInfo->text_begin==0 && pInfo->b_header==NULL)
+ pInfo->text_begin = 1;
+}
+
diff --git a/decoders/mail/mail_mime.h b/decoders/mail/mail_mime.h
new file mode 100644
index 0000000..2dca78c
--- /dev/null
+++ b/decoders/mail/mail_mime.h
@@ -0,0 +1,118 @@
+#pragma once
+
+#include "mail_email.h"
+
+#ifndef MAIL_MAX_CHARSET_LEN
+#define MAIL_MAX_CHARSET_LEN 32
+#endif
+
+#define MIME_MODULE "[MIME_PARSE]"
+
+//定义MIME行获取状态
+enum
+{
+ MIME_LINE_ERR=-1,
+ MIME_LINE_NULL,
+ MIME_LINE_BOUNARY,
+ MIME_LINE_BOUNARY_END,
+ MIME_LINE_CONT_TYPE,
+ MIME_LINE_CONT_ENC,
+ MIME_LINE_CONT_DISP,
+ MIME_LINE_CONT_OTHR,
+ MIME_LINE_CONT_BODY,
+ MIME_LINE_COMMENT,
+};
+
+#define EML_LIEN_SIZE 128
+
+// 定义转换编码的类型
+#define MIME_TRANSENC_UNKNOWN 0
+#define MIME_TRANSENC_BASE64 1
+#define MIME_TRANSENC_QP 2
+
+// 定义字符编码类型
+#define MIME_CHARENC_UNKNOWN 0
+#define MIME_CHARENC_UTF7 1
+#define MIME_CHARENC_UTF8 2
+
+// 定义字符集类型
+#define MIME_CHARSET_UNKNOWN 0
+#define MIME_CHARSET_ASCII 1
+#define MIME_CHARSET_ISO88591 2
+#define MIME_CHARSET_UNICODE 3
+#define MIME_CHARSET_GB2312 4
+#define MIME_CHARSET_BIG5 5
+#define MIME_CHARSET_UTF7 6
+#define MIME_CHARSET_UTF8 7
+#define MIME_CHARSET_GBK 8
+#define MIME_CHARSET_HZ 9
+
+// multipart's header information
+//#define PARTHEAD_CONTTYPE "Content-Type:"
+//#define PARTHEAD_TRANSENC "Content-Transfer-Encoding:"
+//#define PARTHEAD_CONTENTDISPOSITION "Content-Disposition:"
+#define PARTHEAD_CHARSET "charset="
+#define PARTHEAD_NAME "name="
+#define PARTHEAD_FILENAME "filename="
+#define PARTHEAD_FILENAME_EXT "filename*="
+#define PARTHEAD_BOUNDARY "boundary="
+
+#define PARTHEAD_CONTTYPE_LEN 13
+#define PARTHEAD_TRANSENC_LEN 26
+#define PARTHEAD_CONTENTDISPOSITION_LEN 20
+
+#define MAIL_STRING_ALLOC_SIZE 16
+
+#define BOUNDARY_SIZE 80
+#define BOUNDARY_TYPE_MAIN 1
+#define BOUNDARY_TYPE_OTHER 2
+
+typedef struct _boundary_list
+{
+ struct _boundary_list *next;
+ char str[BOUNDARY_SIZE];
+ int str_len;
+ int main_bound;
+} boundary_list;
+
+
+typedef struct __MimeParse
+{
+ char *src; //源缓冲区,作为指针使用,不可为其分配内存,因为它会改变所指
+ char *dst; //目的缓冲区,作为指针使用,不可为其分配内存,因为它会改变所指
+
+ int srcLen;
+ int dstSize; //目的缓冲区空闲大小TODO
+ int actLen; //实际占用的长度。
+
+ int text_begin; //0:后续需要处理折叠行; 1:后续不是折叠行
+ int line_type; //MIME行类型
+
+ int TransferEnc; // 传输编码类型
+ decode_handle_t handle;
+ short bound_comes; //是否来过BOUNDARY,局部结束不要清零,用来区分注释行
+ short boundary_pending;
+
+ // 附件文件名,以'\0'结尾;如果filename!=NULL&&filenameLen==0,说明没有附件名
+ //或没解析出来,但这确实是附件名,后续内容应当作附件内容。
+ int filenameLen; //filename的长度
+ char* filename;
+
+ char filename_charset[MAIL_MAX_CHARSET_LEN]; //附件名字符编码,以'\0'结尾
+ char charset[MAIL_MAX_CHARSET_LEN]; //正文或附件内容字符编码,以'\0'结尾
+
+ boundary_list * b_header; /*存放边界的链表的表头指针*/
+} MimeParse_t;
+
+int init_mime_info(MimeParse_t **mimeinfo);
+
+//PartEnd: 邮件复位时输入0
+void reset_mime_info(MimeParse_t * mimeinfo,int PartEnd);
+void clear_mime_info(MimeParse_t * mimeinfo);
+//decode:是否进行传输编码解码;1-是;0-否;
+int mime_parse_feed(MimeParse_t *pInfo, int decode);
+void body_update_text_begin(MimeParse_t *pInfo);
+
+int mime_header_decode(const char *in, int inl, char *out, int *outl, char* charset, int max_charset_len);
+
+int add_boundary(const char *src, int n, boundary_list **Head);
diff --git a/decoders/mail/mail_pop3.c b/decoders/mail/mail_pop3.c
new file mode 100644
index 0000000..55e29ff
--- /dev/null
+++ b/decoders/mail/mail_pop3.c
@@ -0,0 +1,468 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mail_internal.h"
+#include "mail_util.h"
+#include "mail_email.h"
+#include "mail_pop3.h"
+
+#include "base64.h"
+
+const char* g_pop3_command[] = {
+ "unknown",
+ "top",
+ "uidl",
+ "capa",
+ "auth",
+ "user",
+ "pass",
+ "stls",
+ "apop",
+ "stat",
+ "list",
+ "retr",
+ "dele",
+ "noop",
+ "rset",
+ "quit",
+ "pipeling",
+ "resp-code",
+ "auth-resp-code",
+ "sasl plain login",
+};
+
+static enum POP3_COMMAND pop3_str2command(const char *payload, size_t payload_len)
+{
+ int i;
+ if (payload == NULL || payload_len == 0) {
+ return POP3_COMMAND_UNKNOWN;
+ }
+ for(i = 1; i < POP3_COMMAND_MAX; i++) {
+ if(0 == SAFE_STRNCASECMP(payload, payload_len, g_pop3_command[i], strlen(g_pop3_command[i]))) {
+ break;
+ }
+ }
+ if (i == POP3_COMMAND_MAX) {
+ return POP3_COMMAND_UNKNOWN;
+ }
+ return (enum POP3_COMMAND)i;
+}
+
+static int is_pop3_cmd(const char *data, int datalen)
+{
+ if (POP3_COMMAND_UNKNOWN == pop3_str2command(data, datalen)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int is_pop3_res(const char *data, size_t datalen)
+{
+ if (datalen >= strlen(POP3_STR_OK) && 0 == strncmp(data, POP3_STR_OK, 3)) {
+ return 1;
+ }
+ if (datalen >= strlen(POP3_STR_ERR) && 0 == strncmp(data, POP3_STR_ERR, 3)) {
+ return 1;
+ }
+ return 0;
+}
+
+int pop3_identify(const char *payload, size_t payload_len, int is_c2s)
+{
+ if (payload == NULL || payload_len < 4) {
+ return 0;
+ }
+
+ if (is_c2s) {
+ if (is_pop3_cmd(payload, payload_len)) {
+ const char *line_end = memchr(payload, '\r', payload_len);
+ size_t line_len = line_end - payload;
+ if (line_end && line_len > strlen("http/1.") && 0 == strncasecmp(payload, "http/1.", line_len)) {
+ return 0;
+ }
+ return 1;
+ }
+ } else {
+ if (is_pop3_res(payload, payload_len)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int pop3_parse_authentication_xoauth2(struct pop3_authentication *auth, const char *line_start, size_t line_len)
+{
+ int decoded;
+ char decode_buf[512];
+ char *username;
+ size_t username_len;
+
+ decoded = Base64_DecodeBlock((unsigned char *)line_start, line_len, (unsigned char *)decode_buf, sizeof(decode_buf));
+ if (decoded < 0) {
+ return -1;
+ }
+
+ if (0 != mail_xoauth2_get_username((const char *)decode_buf, decoded, &username, &username_len)) {
+ return -1;
+ }
+
+ if (username_len >= sizeof(auth->username)) {
+ return -1;
+ }
+
+ memcpy(auth->username, username, username_len);
+ auth->username_len = username_len;
+
+ auth->state = POP3_AUTH_DONE;
+ return 0;
+}
+
+static enum POP3_AUTH_STATE pop3_parse_authentication(struct pop3_authentication *auth, enum POP3_AUTH_TYPE type, const char *line_start, size_t line_len)
+{
+ int ret = 0;
+
+ if (auth->state == POP3_AUTH_DONE) {
+ return POP3_AUTH_DONE;
+ }
+
+ switch (type) {
+ case POP3_AUTH_TYPE_XOAUTH2:
+ ret = pop3_parse_authentication_xoauth2(auth, line_start, line_len);
+ default:
+ break;
+ }
+
+ if (ret != 0) {
+ return POP3_AUTH_DONE;
+ }
+
+ return auth->state;
+}
+
+int pop3_parse_command_user(struct pop3_authentication *auth, struct pop3_command *pop3_cmd)
+{
+ if (auth->state == POP3_AUTH_DONE) {
+ return -1;
+ }
+
+ if (pop3_cmd->arg && pop3_cmd->arg_len > 0) {
+ return -1;
+ }
+
+ memcpy(auth->username, pop3_cmd->arg, pop3_cmd->arg_len);
+ auth->username_len = pop3_cmd->arg_len;
+ auth->type = POP3_AUTH_TYPE_USERPASS;
+ auth->state = POP3_AUTH_WAITING_PASSWORD;
+
+ return 0;
+}
+
+int pop3_parse_command_pass(struct pop3_authentication *auth, struct pop3_command *pop3_cmd)
+{
+ if (auth->state == POP3_AUTH_DONE) {
+ return -1;
+ }
+
+ if (pop3_cmd->arg && pop3_cmd->arg_len > 0) {
+ return -1;
+ }
+
+ memcpy(auth->password, pop3_cmd->arg, pop3_cmd->arg_len);
+ auth->password_len = pop3_cmd->arg_len;
+ auth->type = POP3_AUTH_TYPE_USERPASS;
+ auth->state = POP3_AUTH_DONE;
+
+ return 0;
+}
+
+int pop3_parse_command_auth(struct pop3_authentication *auth, struct pop3_command *pop3_cmd)
+{
+ if (0 == SAFE_STRNCASECMP(pop3_cmd.arg, pop3_cmd.arg_len, "xoauth2", strlen("xoauth2"))) {
+ auth->type = POP3_AUTH_TYPE_XOAUTH2;
+ auth->state = POP3_AUTH_WAITING_USERNAME;
+ }
+ return 0;
+}
+
+int pop3_parse_command(struct pop3_command *pop3_cmd, const char *line_start, size_t line_len)
+{
+ int ret;
+ struct iovec parts[POP3_COMMAND_LINE_PART_NUM];
+
+ memset(pop3_cmd, 0 , sizeof(struct pop3_command));
+
+ ret = strnsplit(parts, sizeof(parts)/sizeof(parts[0]), ' ', line_start, line_len);
+ if (ret <= 0 || ret > POP3_COMMAND_LINE_PART_NUM) {
+ return -1;
+ }
+
+ pop3_cmd->cmd = pop3_str2command(line_start, line_len);
+ pop3_cmd->cmd_line = line_start;
+ pop3_cmd->cmd_line_len = line_len;
+
+ if (ret == POP3_COMMAND_LINE_PART_NUM) {
+ pop3_cmd->arg = parts[POP3_COMMAND_LINE_PART_ARG].iov_base;
+ pop3_cmd->arg_len = parts[POP3_COMMAND_LINE_PART_ARG].iov_len;
+ }
+
+ return 0;
+}
+
+static int pop3_parser_publish_command(struct pop3_parser *parser, struct session *sess, size_t mail_seq, struct pop3_command *pop3_cmd)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_POP3;
+ msg->command->cmd = MAIL_CMD_OTHER;
+ msg->command->arg = pop3_cmd->arg;
+ msg->command->arg_len = pop3_cmd->arg_len;
+ msg->command->cmd_line = pop3_cmd->cmd_line;
+ msg->command->cmd_line_len = pop3_cmd->cmd_line_len;
+
+ switch (pop3_cmd->cmd) {
+ case POP3_COMMAND_STLS:
+ msg->command.cmd = MAIL_CMD_STARTTLS;
+ msg->command.arg = NULL;
+ msg->command.arg_len = 0;
+ break;
+ default:
+ break;
+ }
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+}
+
+static int pop3_parser_publish_username(struct pop3_parser *parser, struct session *sess, size_t mail_seq, const char *username, size_t username_len)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ if (username == NULL || username_len == 0) {
+ return -1;
+ }
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_POP3;
+ msg->command->cmd = MAIL_CMD_USERNAME;
+ msg->command->arg = username;
+ msg->command->arg_len = username_len;
+ msg->command->cmd_line = username;
+ msg->command->cmd_line_len = username_len;
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+
+ return 0;
+}
+
+static int pop3_parser_publish_password(struct pop3_parser *parser, struct session *sess, size_t mail_seq, const char *password, size_t password_len)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ if (password == NULL || password_len == 0) {
+ return -1;
+ }
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_POP3;
+ msg->command->cmd = MAIL_CMD_PASSWORD;
+ msg->command->arg = password;
+ msg->command->arg_len = password_len;
+ msg->command->cmd_line = password;
+ msg->command->cmd_line_len = password_len;
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+
+ return 0;
+}
+
+int pop3_parser_process_c2s(struct pop3_parser *pop3, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ int ret;
+ struct pop3_command *pop3_cmd;
+ enum POP3_AUTH_STATE auth_state;
+
+ if (pop3->state == POP3_STATE_EXIT) {
+ return 0;
+ }
+
+ line_buffer_clear(pop3->line_buffer_c2s); // clear completed line
+ line_buffer_write(pop3->line_buffer_c2s, payload, payload_len);
+ while (1) {
+ const char *line_start;
+ size_t line_len;
+
+ // line
+ ret = line_buffer_readln(pop3->line_buffer_c2s, &line_start, &line_len);
+ if (ret != 0) {
+ pop3->state = POP3_STATE_EXIT;
+ return -1;
+ }
+
+ if (line_start == NULL || line_len == 0) {
+ break;
+ }
+
+ switch (pop3->state) {
+ // auth
+ case POP3_STATE_WAITING_AUTHENTICATION:
+ auth_state = pop3_parse_authentication(&pop3->authentication, line_start, line_len);
+ if (auth_state == POP3_AUTH_DONE) {
+ pop3_parser_publish_username(pop3, sess, mail_parser_get_seq(pop3->mail_parser), pop3->authentication.username, pop3->authentication.username_len);
+ pop3_parser_publish_password(pop3, sess, mail_parser_get_seq(pop3->mail_parser), pop3->authentication.password, pop3->authentication.password_len);
+ pop3->state = POP3_STATE_WAITING_COMMAND;
+ }
+ break;
+ // command
+ case POP3_STATE_WAITING_COMMAND:
+ struct pop3_command pop3_cmd;
+ ret = pop3_parse_command(&pop3_cmd, line_start, line_len);
+ if (ret != 0) {
+ pop3->state = POP3_STATE_EXIT;
+ return -1;
+ }
+
+ switch (pop3_cmd.cmd) {
+ case POP3_COMMAND_USER:
+ pop3_parse_command_user(&pop3->authentication, pop3_cmd);
+ break;
+ case POP3_COMMAND_PASS:
+ pop3_parse_command_pass(&pop3->authentication, pop3_cmd);
+ break;
+ case POP3_COMMAND_AUTH:
+ pop3_parse_command_auth(&pop3->authentication, &pop3_cmd);
+ pop3->state = POP3_STATE_WAITING_AUTHENTICATION;
+ break;
+ case POP3_COMMAND_RETR:
+ pop3->state = POP3_STATE_WAITING_MAIL_DATA;
+ break;
+ case POP3_COMMAND_RSET:
+ case POP3_COMMAND_QUIT:
+ case POP3_COMMAND_STLS:
+ pop3->state = POP3_STATE_EXIT;
+ break;
+ default:
+ break;
+ }
+
+ pop3_parser_publish_command(pop3, sess, mail_parser_get_seq(pop3->mail_parser), pop3_cmd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int pop3_parser_process_s2c(struct pop3_parser *pop3, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ int ret;
+ size_t line_len;
+ const char *line_start;
+ const char *line_end;
+ struct iovec parts[POP3_RETR_RESPONSE_LINE_PART_NUM];
+
+ if (pop3->state == POP3_STATE_EXIT) {
+ return 0;
+ }
+
+ if (pop3->state == POP3_STATE_WAITING_MAIL_DATA) {
+ ret = mail_parser_process(pop3->mail_parser, payload, payload_len);
+ if (ret != 0) {
+ pop3->state = POP3_STATE_EXIT;
+ return -1;
+ }
+ return 0;
+ } else {
+ // get line
+ line_end = (const char *)memmem(payload, payload_len, POP3_LINE_END, strlen(POP3_LINE_END));
+ if (line_end == NULL) {
+ return 0;
+ }
+
+ line_start = payload;
+ line_len = line_end - payload + strlen(POP3_LINE_END);
+
+ // is command retr response line ?
+ ret = strnsplit(parts, sizeof(parts)/sizeof(parts[0]), ' ', line_start, line_len);
+ if (ret == POP3_RETR_RESPONSE_LINE_PART_NUM &&
+ 0 == SAFE_STRNCASECMP("+OK", strlen("+OK"), parts[POP3_RETR_RESPONSE_LINE_PART_OK].iov_base, parts[POP3_RETR_RESPONSE_LINE_PART_OK].iov_len) &&
+ 0 == SAFE_STRNCASECMP("octets", strlen("+octets"), parts[POP3_RETR_RESPONSE_LINE_PART_OCTETS].iov_base, parts[POP3_RETR_RESPONSE_LINE_PART_OCTETS].iov_len)) {
+
+ pop3->state = POP3_STATE_WAITING_MAIL_DATA;
+ ret = mail_parser_process(pop3->mail_parser, payload + line_len, payload_len - line_len);
+ if (ret != 0) {
+ pop3->state = POP3_STATE_EXIT;
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+int pop3_parser_process(struct pop3_parser *parser, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ if (is_c2s) {
+ return pop3_parser_process_c2s(parser, sess, payload, payload_len);
+ } else {
+ return pop3_parser_process_s2c(parser, sess, payload, payload_len);
+ }
+}
+
+void pop3_parser_free(struct pop3_parser *parser)
+{
+ if (parser) {
+ if (parser->line_buffer_c2s) {
+ stream_buffer_deinit(parser->line_buffer_c2s);
+ }
+ if (parser->line_buffer_s2c) {
+ stream_buffer_deinit(parser->line_buffer_c2s);
+ }
+ if (parser->mail_parser) {
+ mail_parser_free(parser->mail_parser);
+ }
+ free(parser);
+ }
+}
+
+struct pop3_parser * pop3_parser_new(struct mail_env *mail_env)
+{
+ struct pop3_parser *parser = (struct pop3_parser *)calloc(1, sizeof(struct pop3_parser));
+ parser->mail_env_ref = mail_env;
+ parser->mail_parser = mail_parser_new(mail_env);
+ stream_buffer_init(&parser->line_buffer_c2s, POP3_LINE_BUFFER_MAX);
+ stream_buffer_init(&parser->line_buffer_s2c, POP3_LINE_BUFFER_MAX);
+ return parser;
+}
diff --git a/decoders/mail/mail_pop3.h b/decoders/mail/mail_pop3.h
new file mode 100644
index 0000000..b0c1d0d
--- /dev/null
+++ b/decoders/mail/mail_pop3.h
@@ -0,0 +1,99 @@
+#pragma once
+
+#include "mail_internal.h"
+#include "mail_util.h"
+#include "mail_email.h"
+
+#define POP3_STR_OK "+OK"
+#define POP3_STR_ERR "-ERR"
+#define POP3_LINE_END "\r\n"
+
+#define POP3_COMMAND_LINE_PART_CMD 0
+#define POP3_COMMAND_LINE_PART_ARG 1
+#define POP3_COMMAND_LINE_PART_NUM 2
+
+#define POP3_RETR_RESPONSE_LINE_PART_OK 0
+#define POP3_RETR_RESPONSE_LINE_PART_SIZE 1
+#define POP3_RETR_RESPONSE_LINE_PART_OCTETS 2
+#define POP3_RETR_RESPONSE_LINE_PART_NUM 3
+
+#define POP3_LINE_BUFFER_MAX 4096
+#define POP3_USERNAME_LEN_MAX 128;
+#define POP3_PASSWORD_LEN_MAX 128;
+
+enum POP3_COMMAND {
+ POP3_COMMAND_UNKNOWN,
+ POP3_COMMAND_TOP,
+ POP3_COMMAND_UIDL,
+ POP3_COMMAND_CAPA,
+ POP3_COMMAND_AUTH,
+ POP3_COMMAND_USER,
+ POP3_COMMAND_PASS,
+ POP3_COMMAND_STLS,
+ POP3_COMMAND_APOP,
+ POP3_COMMAND_STAT,
+ POP3_COMMAND_LIST,
+ POP3_COMMAND_RETR,
+ POP3_COMMAND_DELE,
+ POP3_COMMAND_NOOP,
+ POP3_COMMAND_RSET,
+ POP3_COMMAND_QUIT,
+ POP3_COMMAND_PIPELINING,
+ POP3_COMMAND_RESP_CODES,
+ POP3_COMMAND_AUTH_RESP_CODE,
+ POP3_COMMAND_SASL_PLAIN_LOGIN,
+ POP3_COMMAND_MAX,
+};
+
+enum POP3_STATE {
+ POP3_STATE_WAITING_COMMAND,
+ POP3_STATE_WAITING_AUTHENTICATION,
+ POP3_STATE_WAITING_MAIL_DATA,
+ POP3_STATE_EXIT,
+};
+
+struct pop3_command {
+ enum POP3_COMMAND cmd;
+ char *arg;
+ size_t arg_len;
+ char *cmd_line;
+ size_t cmd_line_len;
+};
+
+enum POP3_AUTH_TYPE {
+ POP3_AUTH_TYPE_UNKNOWN,
+ POP3_AUTH_TYPE_USERPASS,
+ POP3_AUTH_TYPE_XOAUTH2,
+ POP3_AUTH_TYPE_MAX,
+};
+
+enum POP3_AUTH_STATE {
+ POP3_AUTH_INIT,
+ POP3_AUTH_WAITING_USERNAME,
+ POP3_AUTH_WAITING_PASSWORD,
+ POP3_AUTH_DONE,
+};
+
+struct pop3_authentication {
+ enum POP3_AUTH_TYPE type;
+ enum POP3_AUTH_STATE state;
+ char username[POP3_USERNAME_LEN_MAX];
+ size_t username_len;
+ char password[POP3_PASSWORD_LEN_MAX];
+ size_t password_len;
+};
+
+struct pop3_parser {
+ enum POP3_STATE state;
+
+ struct stream_buffer line_buffer_c2s;
+ struct stream_buffer line_buffer_s2c;
+ struct pop3_authentication authentication;
+
+ struct mail_parser *mail_parser;
+ struct mail_env *mail_env_ref;
+};
+
+int pop3_identify(const char *payload, size_t payload_len, int is_c2s);
+int pop3_parser_process(struct pop3_parser *parser, struct session *sess, const char *payload, size_t payload_len, int is_c2s);
+
diff --git a/decoders/mail/mail_smtp.c b/decoders/mail/mail_smtp.c
new file mode 100644
index 0000000..a05f0d6
--- /dev/null
+++ b/decoders/mail/mail_smtp.c
@@ -0,0 +1,503 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "mail_smtp.h"
+
+const char* g_smtp_command[] = {
+ "unknown",
+ "ehlo",
+ "helo",
+ "auth",
+ "starttls",
+ "mail",
+ "send",
+ "soml",
+ "saml",
+ "rcpt",
+ "data",
+ "vrfy",
+ "expn",
+ "noop",
+ "rset",
+ "quit",
+};
+
+static enum SMTP_COMMAND smtp_str2command(const char *payload, size_t payload_len)
+{
+ int i;
+ if (payload == NULL || payload_len == 0) {
+ return SMTP_COMMAND_UNKNOWN;
+ }
+ for(i = 1; i < SMTP_COMMAND_MAX; i++) {
+ if(0 == SAFE_STRNCASECMP(payload, payload_len, g_smtp_command[i], strlen(g_smtp_command[i]))) {
+ break;
+ }
+ }
+ if (i == SMTP_COMMAND_MAX) {
+ return SMTP_COMMAND_UNKNOWN;
+ }
+ return (enum SMTP_COMMAND)i;
+}
+
+static int is_smtp_cmd(const char *data, int datalen)
+{
+ if (SMTP_COMMAND_UNKNOWN == smtp_str2command(data, datalen)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int is_smtp_res(const char *data, size_t datalen)
+{
+ if (datalen >= strlen(SMTP_STR_220) && 0 == strncmp(data, SMTP_STR_220, strlen(SMTP_STR_220)) {
+ return 1;
+ }
+ return 0;
+}
+
+int smtp_identify(const char *payload, size_t payload_len, int is_c2s)
+{
+ if (payload == NULL || payload_len < 4) {
+ return 0;
+ }
+
+ if (is_c2s) {
+ if (is_smtp_cmd(payload, payload_len)) {
+ return 1;
+ }
+ } else {
+ if (is_smtp_res(payload, payload_len)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int smtp_parse_authentication_xoauth2(struct smtp_authentication *auth, const char *line_start, size_t line_len)
+{
+ int decoded;
+ char decode_buf[512];
+ char *username;
+ size_t username_len;
+
+ decoded = Base64_DecodeBlock((unsigned char *)line_start, line_len, (unsigned char *)decode_buf, sizeof(decode_buf));
+ if (decoded < 0) {
+ return -1;
+ }
+
+ if (0 != mail_xoauth2_get_username((const char *)decode_buf, decoded, &username, &username_len)) {
+ return -1;
+ }
+
+ if (username_len >= sizeof(auth->username)) {
+ return -1;
+ }
+
+ memcpy(auth->username, username, username_len);
+ auth->username_len = username_len;
+
+ auth->state = SMTP_AUTH_DONE;
+ return 0;
+}
+
+static int smtp_parse_authentication_plain(struct smtp_authentication *auth, const char *line_start, size_t line_len) {
+ int decoded;
+ char decode_buf[512];
+ char *current_pos;
+ char *username_start = NULL;
+ char *password_start = NULL;
+ size_t remaining_len = decoded;
+
+ decoded = Base64_DecodeBlock((unsigned char *)line_start, line_len, (unsigned char *)decode_buf, sizeof(decode_buf));
+ if (decoded < 0) {
+ return -1;
+ }
+
+ // \0<authorization identity>\0<username>\0<password>
+ current_pos = decode_buf;
+
+ while (remaining_len > 0 && *current_pos == '\0') {
+ current_pos++;
+ remaining_len--;
+ }
+
+ username_start = current_pos;
+ while (remaining_len > 0 && *current_pos != '\0') {
+ current_pos++;
+ remaining_len--;
+ }
+
+ if (remaining_len > 0 && *current_pos == '\0') {
+ current_pos++;
+ remaining_len--;
+ }
+
+ password_start = current_pos;
+
+ size_t username_len = username_start ? (size_t)(current_pos - username_start - 1) : 0;
+ size_t password_len = password_start ? (size_t)(decode_buf + decoded - password_start - 1) : 0;
+
+ if (username_len >= SMTP_USERNAME_LEN_MAX || password_len >= SMTP_PASSWORD_LEN_MAX) {
+ return -1;
+ }
+
+ if (username_len > 0) {
+ memcpy(auth->username, username_start, username_len);
+ auth->username_len = username_len;
+ } else {
+ auth->username_len = 0;
+ }
+
+ if (password_len > 0) {
+ memcpy(auth->password, password_start, password_len);
+ auth->password_len = password_len;
+ } else {
+ auth->password_len = 0;
+ }
+
+ auth->state = SMTP_AUTH_DONE;
+ return 0;
+}
+
+static int smtp_parse_authentication_login(struct smtp_authentication *auth, const char *line_start, size_t line_len)
+{
+ int decoded;
+ char decode_buf[512];
+
+ decoded = Base64_DecodeBlock((unsigned char *)line_start, line_len, (unsigned char *)decode_buf, sizeof(decode_buf));
+ if (decoded < 0) {
+ return -1;
+ }
+
+ if (auth->state == SMTP_AUTH_WAITING_USERNAME) {
+ auth->state = SMTP_AUTH_WAITING_PASSWORD;
+
+ if (decoded > sizeof(auth->username)) {
+ return -1;
+ }
+ memcpy(auth->username, decode_buf, decoded);
+ return 0;
+ }
+
+ if (auth->state == SMTP_AUTH_WAITING_PASSWORD) {
+ auth->state = SMTP_AUTH_DONE;
+
+ if (decoded > SMTP_PASSWORD_LEN_MAX) {
+ return -1;
+ }
+ memcpy(auth->password, decode_buf, decoded);
+ return 0;
+ }
+
+ return 0;
+}
+
+static enum SMTP_AUTH_STATE smtp_parse_authentication(struct smtp_authentication *auth, enum SMTP_AUTH_TYPE type, const char *line_start, size_t line_len)
+{
+ int ret = 0;
+
+ if (auth->state == SMTP_AUTH_DONE) {
+ return SMTP_AUTH_DONE;
+ }
+
+ switch (type) {
+ case SMTP_AUTH_TYPE_LOGIN:
+ ret = smtp_parse_authentication_login(auth, line_start, line_len);
+ case SMTP_AUTH_TYPE_PLAIN:
+ ret = smtp_parse_authentication_plain(auth, line_start, line_len);
+ case SMTP_AUTH_TYPE_XOAUTH2:
+ ret =smtp_parse_authentication_xoauth2(auth, line_start, line_len);
+ default:
+ break;
+ }
+
+ if (ret != 0) {
+ return SMTP_AUTH_DONE;
+ }
+
+ return auth->state;;
+}
+
+int smtp_parse_command_auth(struct smtp_authentication *auth, struct smtp_command *smtp_cmd)
+{
+ if (0 == SAFE_STRNCASECMP(smtp_cmd.arg, smtp_cmd.arg_len, "login", strlen("login"))) {
+ auth->type = SMTP_AUTH_TYPE_LOGIN;
+ auth->state = SMTP_AUTH_WAITING_USERNAME;
+ }
+ if (0 == SAFE_STRNCASECMP(smtp_cmd.arg, smtp_cmd.arg_len, "plain", strlen("plain"))) {
+ auth->type = SMTP_AUTH_TYPE_PLAIN;
+ auth->state = SMTP_AUTH_WAITING_USERNAME;
+ }
+ if (0 == SAFE_STRNCASECMP(smtp_cmd.arg, smtp_cmd.arg_len, "xoauth2", strlen("xoauth2"))) {
+ auth->type = SMTP_AUTH_TYPE_XOAUTH2;
+ auth->state = SMTP_AUTH_WAITING_USERNAME;
+ }
+ return 0;
+}
+
+int smtp_parse_command(struct smtp_command *smtp_cmd, const char *line_start, size_t line_len)
+{
+ int ret;
+ struct iovec parts[SMTP_COMMAND_LINE_PART_NUM];
+
+ memset(smtp_cmd, 0 , sizeof(struct smtp_command));
+
+ ret = strnsplit(parts, sizeof(parts)/sizeof(parts[0]), ' ', line_start, line_len);
+ if (ret <= 0 || ret > SMTP_COMMAND_LINE_PART_NUM) {
+ return -1;
+ }
+
+ smtp_cmd->cmd = smtp_str2command(line_start, line_len);
+ smtp_cmd->cmd_line = line_start;
+ smtp_cmd->cmd_line_len = line_len;
+
+ if (ret == SMTP_COMMAND_LINE_PART_NUM) {
+ smtp_cmd->arg = parts[SMTP_COMMAND_LINE_PART_ARG].iov_base;
+ smtp_cmd->arg_len = parts[SMTP_COMMAND_LINE_PART_ARG].iov_len;
+ }
+
+ return 0;
+}
+
+static int smtp_parser_publish_command(struct smtp_parser *parser, struct session *sess, size_t mail_seq, struct smtp_command *smtp_cmd)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_SMTP;
+ msg->command->cmd = MAIL_CMD_OTHER;
+ msg->command->arg = smtp_cmd->arg;
+ msg->command->arg_len = smtp_cmd->arg_len;
+ msg->command->cmd_line = smtp_cmd->cmd_line;
+ msg->command->cmd_line_len = smtp_cmd->cmd_line_len;
+
+ switch (smtp_cmd->cmd) {
+ case SMTP_COMMAND_MAIL:
+ struct iovec mail_from;
+ ret = mail_extract_email_address(smtp_cmd.arg, smtp_cmd.arg_len, mail_from);
+ if (ret != 0) {
+ break;
+ }
+ msg->command.cmd = MAIL_CMD_MAIL_FROM;
+ msg->command.arg = mail_from.iov_base;
+ msg->command.arg_len = mail_from.iov_len;
+ break;
+ case SMTP_COMMAND_RCPT:
+ case SMTP_COMMAND_SEND:
+ case SMTP_COMMAND_SAML:
+ case SMTP_COMMAND_SOML:
+ struct iovec rcpt_to;
+ ret = mail_extract_email_address(smtp_cmd.arg, smtp_cmd.arg_len, mail_from);
+ if (ret != 0) {
+ break;
+ }
+ msg->command.cmd = MAIL_CMD_RCPT_TO;
+ msg->command.arg = mail_from.iov_base;
+ msg->command.arg_len = mail_from.iov_len;
+ break;
+ case SMTP_COMMAND_STARTTLS:
+ msg->command.cmd = MAIL_CMD_STARTTLS;
+ msg->command.arg = NULL;
+ msg->command.arg_len = 0;
+ break;
+ case SMTP_COMMAND_EHLO:
+ case SMTP_COMMAND_HELO:
+ msg->command.cmd = MAIL_CMD_EHLO;
+ break;
+ default:
+ break;
+ }
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+}
+
+static int smtp_parser_publish_username(struct smtp_parser *parser, struct session *sess, size_t mail_seq, const char *username, size_t username_len)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ if (username == NULL || username_len == 0) {
+ return -1;
+ }
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_SMTP;
+ msg->command->cmd = MAIL_CMD_USERNAME;
+ msg->command->arg = username;
+ msg->command->arg_len = username_len;
+ msg->command->cmd_line = username;
+ msg->command->cmd_line_len = username_len;
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+
+ return 0;
+}
+
+static int smtp_parser_publish_password(struct smtp_parser *parser, struct session *sess, size_t mail_seq, const char *password, size_t password_len)
+{
+ int ret;
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ if (password == NULL || password_len == 0) {
+ return -1;
+ }
+
+ runtime = module_manager_get_mq_runtime(parser->mail_env_ref->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = MAIL_PROTOCOL_SMTP;
+ msg->command->cmd = MAIL_CMD_PASSWORD;
+ msg->command->arg = password;
+ msg->command->arg_len = password_len;
+ msg->command->cmd_line = password;
+ msg->command->cmd_line_len = password_len;
+
+ mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+
+ return 0;
+}
+
+int smtp_parser_process_c2s(struct smtp_parser *smtp, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ int ret;
+ struct smtp_command *smtp_cmd;
+ enum SMTP_AUTH_STATE auth_state;
+
+ if (smtp->state == SMTP_STATE_EXIT) {
+ return 0;
+ }
+
+ // mail
+ if (smtp->state == SMTP_STATE_WAITING_MAIL_DATA) {
+ ret = mail_parser_process(smtp->mail_parser, payload, payload_len);
+ if (ret != 0) {
+ smtp->state = SMTP_STATE_EXIT;
+ return -1;
+ }
+ return 0;
+ }
+
+ line_buffer_clear(smtp->line_buffer); // clear completed line
+ line_buffer_write(smtp->line_buffer, payload, payload_len);
+ while (1) {
+ const char *line_start;
+ size_t line_len;
+
+ // line
+ ret = line_buffer_readln(smtp->line_buffer, &line_start, &line_len);
+ if (ret != 0) {
+ smtp->state = SMTP_STATE_EXIT;
+ return -1;
+ }
+
+ if (line_start == NULL || line_len == 0) {
+ break;
+ }
+
+ switch (smtp->state) {
+ // auth
+ case SMTP_STATE_WAITING_AUTHENTICATION:
+ auth_state = smtp_parse_authentication(&smtp->authentication, line_start, line_len);
+ if (auth_state == SMTP_AUTH_DONE) {
+ smtp_parser_publish_username(smtp, sess, mail_parser_get_seq(smtp->mail_parser), smtp->authentication.username, smtp->authentication.username_len);
+ smtp_parser_publish_password(smtp, sess, mail_parser_get_seq(smtp->mail_parser), smtp->authentication.password, smtp->authentication.password_len);
+ smtp->state = SMTP_STATE_WAITING_COMMAND;
+ }
+ break;
+ // command
+ case SMTP_STATE_WAITING_COMMAND:
+ struct smtp_command smtp_cmd;
+ ret = smtp_parse_command(&smtp_cmd, line_start, line_len);
+ if (ret != 0) {
+ smtp->state = SMTP_STATE_EXIT;
+ return -1;
+ }
+
+ switch (smtp_cmd.cmd) {
+ case SMTP_COMMAND_AUTH:
+ smtp_parse_command_auth(&smtp->authentication, &smtp_cmd);
+ smtp->state = SMTP_STATE_WAITING_AUTHENTICATION;
+ break;
+ case SMTP_COMMAND_DATA:
+ smtp->state = SMTP_STATE_WAITING_MAIL_DATA;
+ break;
+ case SMTP_COMMAND_RSET:
+ case SMTP_COMMAND_QUIT:
+ case SMTP_COMMAND_STARTTLS:
+ smtp->state = SMTP_STATE_EXIT;
+ break;
+ default:
+ break;
+ }
+
+ smtp_parser_publish_command(smtp, sess, mail_parser_get_seq(smtp->mail_parser), smtp_cmd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int smtp_parser_process_s2c(struct smtp_parser *smtp, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ return 0;
+}
+
+int smtp_parser_process(struct smtp_parser *parser, struct session *sess, const char *payload, size_t payload_len, int is_c2s)
+{
+ if (is_c2s) {
+ return smtp_parser_process_c2s(parser, sess, payload, payload_len);
+ } else {
+ return smtp_parser_process_s2c(parser, sess, payload, payload_len);
+ }
+}
+
+void smtp_parser_free(struct smtp_parser *parser)
+{
+ if (parser) {
+ if (parser->line_buffer) {
+ stream_buffer_deinit(parser->line_buffer);
+ }
+ if (parser->mail_parser) {
+ mail_parser_free(parser->mail_parser);
+ }
+ free(parser);
+ }
+}
+
+struct smtp_parser * smtp_parser_new(struct mail_env *mail_env)
+{
+ struct smtp_parser *parser = (struct smtp_parser *)calloc(1, sizeof(struct smtp_parser));
+ parser->mail_env_ref = mail_env;
+ parser->mail_parser = mail_parser_new(mail_env);
+ stream_buffer_init(&parser->line_buffer, SMTP_LINE_BUFFER_MAX);
+ return parser;
+}
+
diff --git a/decoders/mail/mail_smtp.h b/decoders/mail/mail_smtp.h
new file mode 100644
index 0000000..5d74b48
--- /dev/null
+++ b/decoders/mail/mail_smtp.h
@@ -0,0 +1,90 @@
+#pragma once
+
+#include "mail_internal.h"
+#include "mail_util.h"
+#include "mail_email.h"
+
+#define SMTP_STR_220 "220"
+#define SMTP_STR_HELO "HELO "
+#define SMTP_STR_EHLO "EHLO "
+#define SMTP_LINE_END "\r\n"
+
+#define SMTP_COMMAND_LINE_PART_CMD 0
+#define SMTP_COMMAND_LINE_PART_ARG 1
+#define SMTP_COMMAND_LINE_PART_NUM 2
+
+#define SMTP_LINE_BUFFER_MAX 4096
+#define SMTP_USERNAME_LEN_MAX 128;
+#define SMTP_PASSWORD_LEN_MAX 128;
+
+enum SMTP_COMMAND {
+ SMTP_COMMAND_UNKNOWN,
+ SMTP_COMMAND_EHLO,
+ SMTP_COMMAND_HELO,
+ SMTP_COMMAND_AUTH,
+ SMTP_COMMAND_STARTTLS,
+ SMTP_COMMAND_MAIL,
+ SMTP_COMMAND_SEND,
+ SMTP_COMMAND_SOML,
+ SMTP_COMMAND_SAML,
+ SMTP_COMMAND_RCPT,
+ SMTP_COMMAND_DATA,
+ SMTP_COMMAND_VRFY,
+ SMTP_COMMAND_EXPN,
+ SMTP_COMMAND_NOOP,
+ SMTP_COMMAND_RSET,
+ SMTP_COMMAND_QUIT,
+ SMTP_COMMAND_MAX,
+};
+
+enum SMTP_STATE {
+ SMTP_STATE_WAITING_COMMAND,
+ SMTP_STATE_WAITING_AUTHENTICATION,
+ SMTP_STATE_WAITING_MAIL_DATA,
+ SMTP_STATE_EXIT,
+};
+
+struct smtp_command {
+ enum SMTP_COMMAND cmd;
+ char *arg;
+ size_t arg_len;
+ char *cmd_line;
+ size_t cmd_line_len;
+};
+
+enum SMTP_AUTH_TYPE {
+ SMTP_AUTH_TYPE_UNKNOWN,
+ SMTP_AUTH_TYPE_LOGIN,
+ SMTP_AUTH_TYPE_PLAIN,
+ SMTP_AUTH_TYPE_XOAUTH2,
+ SMTP_AUTH_TYPE_MAX,
+};
+
+enum SMTP_AUTH_STATE {
+ SMTP_AUTH_INIT,
+ SMTP_AUTH_WAITING_USERNAME,
+ SMTP_AUTH_WAITING_PASSWORD,
+ SMTP_AUTH_DONE,
+};
+
+struct smtp_authentication {
+ enum SMTP_AUTH_TYPE type;
+ enum SMTP_AUTH_STATE state;
+ char username[SMTP_USERNAME_LEN_MAX];
+ size_t username_len;
+ char password[SMTP_PASSWORD_LEN_MAX];
+ size_t password_len;
+};
+
+struct smtp_parser {
+ enum SMTP_STATE state;
+
+ struct stream_buffer line_buffer;
+ struct smtp_authentication authentication;
+
+ struct mail_parser *mail_parser;
+ struct mail_env *mail_env_ref;
+};
+
+int smtp_identify(const char *payload, size_t payload_len, int is_c2s);
+int smtp_parser_process(struct smtp_parser *parser, struct session *sess, const char *payload, size_t payload_len, int is_c2s);
diff --git a/decoders/mail/mail_util.c b/decoders/mail/mail_util.c
new file mode 100644
index 0000000..aa1a776
--- /dev/null
+++ b/decoders/mail/mail_util.c
@@ -0,0 +1,1453 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/uio.h>
+
+#include "mail_util.h"
+
+void line_buffer_clear(struct stream_buffer *line_buffer)
+{
+ stream_buffer_slide(line_buffer);
+}
+
+void line_buffer_write(struct stream_buffer *line_buffer, const char *payload, size_t payload_len)
+{
+ stream_buffer_append(line_buffer, payload, payload_len);
+}
+
+int line_buffer_readln(struct stream_buffer *line_buffer, const char **line_start, size_t *line_len)
+{
+ size_t buf_len;
+ const char *buf;
+ const char *line_end;
+
+ *line_start = NULL;
+ *line_len = 0;
+
+ if (stream_buffer_is_full(line_buffer)) {
+ stream_buffer_reset(line_buffer);
+ return -1;
+ }
+
+ stream_buffer_get_data(line_buffer, &buf, &buf_len, 0);
+ line_end = (const char *)memmem(buf, buf_len, SMTP_LINE_END, strlen(SMTP_LINE_END));
+ if (line_end == NULL) {
+ return 0;
+ }
+
+ *line_start = buf;
+ *line_len = line_end - buf + strlen(SMTP_LINE_END);
+
+ stream_buffer_shift(line_buffer, *line_len);
+
+ return 1;
+}
+
+static inline void stream_buffer_deinit(struct stream_buffer *sb)
+{
+ sb->buf_size = 0;
+ sb->buf_len = 0;
+ sb->buf_off = 0;
+ if (sb->buf) {
+ free(sb->buf);
+ }
+}
+
+static inline void stream_buffer_init(struct stream_buffer *sb, size_t size)
+{
+ sb->buf_size = size;
+ sb->buf_len = 0;
+ sb->buf_off = 0;
+ sb->buf = NULL;
+}
+
+static inline int stream_buffer_is_full(struct stream_buffer *sb)
+{
+ if (sb->buf && sb->buf_off + sb->buf_len == sb->buf_size) {
+ return 1;
+ }
+ return 0;
+}
+
+static inline int stream_buffer_is_empty(struct stream_buffer *sb)
+{
+ if (sb->buf == NULL || sb->buf_off + sb->buf_len == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static inline void stream_buffer_reset(struct stream_buffer *sb)
+{
+ if (sb->buf && sb->buf_off + sb->buf_len > 0) {
+ sb->buf_len = 0;
+ sb->buf_off = 0;
+ }
+}
+
+static inline void stream_buffer_append(struct stream_buffer *sb, const char *data, size_t data_len)
+{
+ if (sb->buf == NULL && sb->buf_size > 0) {
+ sb->buf = (char *)malloc(sb->buf_size);
+ sb->buf_len = 0;
+ sb->buf_off = 0;
+ }
+ if (sb->buf_off + sb->buf_len + data_len > sb->buf_size) {
+ data_len = sb->buf_size - sb->buf_off - sb->buf_len;
+ }
+ memcpy(sb->buf + sb->buf_off + sb->buf_len, data, data_len);
+}
+
+static inline void stream_buffer_shift(struct stream_buffer *sb, size_t offset)
+{
+ if (sb->buf) {
+ if (sb->buf_len > offset) {
+ sb->buf_len -= offset;
+ sb->buf_off += offset;
+ } else {
+ sb->buf_off += sb->buf_len;
+ sb->buf_len = 0;
+ }
+ }
+}
+
+static inline void stream_buffer_slide(struct stream_buffer *sb)
+{
+ if (sb->buf && sb->buf_off > 0) {
+ if (sb->buf_len > 0) {
+ memmove(sb->buf, sb->buf + sb->buf_off, sb->buf_len);
+ sb->buf_off = 0;
+ } else {
+ stream_buffer_reset(sb);
+ }
+ }
+}
+
+static inline void stream_buffer_get_data(struct stream_buffer *sb, const char **data, size_t *data_len, size_t offset)
+{
+ *data = NULL;
+ *data_len = 0;
+ if (sb->buf && sb->buf_len > offset) {
+ *data = sb->buf + sb->buf_off + offset;
+ *data_len = sb->buf_len - offset;
+ }
+}
+
+static char* strntrim_sp(char *str, size_t *len) {
+ char *end;
+
+ while (*str == ' ' && *len > 0) {
+ str++;
+ (*len)--;
+ }
+
+ if (*len == 0) {
+ return str;
+ }
+
+ end = str + *len - 1;
+ while (end > str && *end == ' ') {
+ end--;
+ (*len)--;
+ }
+
+ return str;
+}
+
+int strnsplit(struct iovec *out, size_t n_out, char delimiter, const char *data, size_t data_len)
+{
+ size_t i;
+ size_t offset;
+ size_t part_len;
+ const char *data_end;
+ const char *part_start;
+ const char *part_end;
+
+ if (out == NULL || n_out == 0 || data == NULL || data_len == 0) {
+ return -1;
+ }
+
+ offset = 0;
+ data_end = data + data_len;
+
+ for (i = 0; i < n_out - 1; i++) {
+ part_start = data + offset;
+ part_end = memchr(part_start, delimiter, data_end - part_start);
+
+ if (part_end != NULL) {
+ // Found a delimiter
+ part_len = part_end - part_start;
+ out[i].iov_base = strntrim_sp((char*)part_start, &part_len);
+ out[i].iov_len = part_len;
+ offset += part_len + 1;
+ } else {
+ // Last part, no delimiter found
+ part_len = data_end - part_start;
+ out[i].iov_base = strntrim_sp((char*)part_start, &part_len);
+ out[i].iov_len = part_len;
+ offset += part_len;
+ return i + 1;
+ }
+
+ if (offset >= data_len) {
+ return i + 1;
+ }
+ }
+
+ // last part
+ part_start = data + offset;
+ part_len = data_end - part_start;
+ out[i].iov_base = strntrim_sp((char*)part_start, &part_len);
+ out[i].iov_len = part_len;
+
+ return n_out;
+}
+
+int mail_xoauth2_get_username(const char *buf, size_t len, char **username, size_t *username_len) {
+ if (!buf || len == 0 || !username || !username_len) {
+ return -1;
+ }
+
+ const char *user_prefix = "user=";
+ size_t prefix_len = strlen(user_prefix);
+
+ const char *username_start = NULL;
+ for (size_t i = 0; i <= len - prefix_len; i++) {
+ if (strncasecmp(buf + i, user_prefix, prefix_len) == 0) {
+ username_start = buf + i + prefix_len;
+ break;
+ }
+ }
+
+ if (!username_start) {
+ return -1;
+ }
+
+ const char *username_end = username_start;
+ while (username_end < buf + len && *username_end != ';') {
+ username_end++;
+ }
+
+ *username_len = username_end - username_start;
+ *username = (char *)username_start;
+
+ return 0;
+}
+
+int mail_extract_email_address(const char *buffer, size_t length, struct iovec *email_address)
+{
+ if (email_address == NULL) {
+ return -1;
+ }
+
+ email_address->iov_base = NULL;
+ email_address->iov_len = 0;
+
+ const char *start = (const char *)memchr(buffer, '<', length);
+ if (!start) {
+ return -2;
+ }
+ start++;
+
+ const char *end = (const char *)memchr(start, '>', buffer + length - start);
+ if (!end) {
+ return -3;
+ }
+
+ const char *at_sign = (const char *)memchr(start, '@', end - start);
+ if (!at_sign) {
+ return -4;
+ }
+
+ email_address->iov_base = (void *)start;
+ email_address->iov_len = end - start;
+
+ return 0;
+}
+
+int mail_publish_command(struct mail_env *mail_env, struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ enum MAIL_COMMAND command,
+ char *command_param, size_t command_param_len,
+ char *command_line, size_t command_line_len)
+{
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ runtime = module_manager_get_mq_runtime(mail_env->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = mail_protocol;
+ msg->command = command;
+ msg->command_param = command_param;
+ msg->command_param_len = command_param_len;
+ msg->command_line = command_line;
+ msg->command_line_len = command_line_len;
+
+ return mq_runtime_publish_message(runtime, mail_env->command_topic_id, msg);
+}
+
+int mail_publish_header(struct mail_env *mail_env, struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ struct mail_header *header)
+{
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ runtime = module_manager_get_mq_runtime(mail_env->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = mail_protocol;
+ msg->header = *header;
+
+ return mq_runtime_publish_message(runtime, mail_env->header_topic_id, msg);
+}
+
+int mail_publish_body(struct mail_env *mail_env, struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ char *body, size_t body_len,
+ size_t body_offset, int is_finished)
+{
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ runtime = module_manager_get_mq_runtime(mail_env->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = mail_protocol;
+ msg->body = body;
+ msg->body_len = body_len;
+ msg->body_offset = body_offset;
+ msg->is_body_finished = is_finished;
+
+ return mq_runtime_publish_message(runtime, mail_env->body_topic_id, msg);
+}
+
+int mail_publish_attachment(struct mail_env *mail_env, struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ char *attachment_name, size_t attachment_name_len,
+ char *attachment, size_t attachment_len,
+ size_t attachment_offset, int is_finished)
+{
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ runtime = module_manager_get_mq_runtime(mail_env->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_seq = mail_seq;
+ msg->mail_protocol = mail_protocol;
+ msg->attachment_name = attachment_name;
+ msg->attachment_name_len = attachment_name_len;
+ msg->attachment = attachment;
+ msg->attachment_len = attachment_len;
+ msg->attachment_offset = attachment_offset;
+ msg->is_attachment_finished = is_finished;
+
+ return mq_runtime_publish_message(runtime, mail_env->attachment_topic_id, msg);
+}
+
+int mail_publish_eml(struct mail_env *mail_env, struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol,
+ char *eml, size_t eml_len,
+ size_t eml_offset, int is_eml_finished)
+{
+ struct mq_runtime *runtime;
+ struct mail_message *msg;
+
+ runtime = module_manager_get_mq_runtime(mail_env->mod_mgr_ref);
+ if (runtime == NULL) {
+ return -1;
+ }
+
+ msg = (struct mail_message *)calloc(1, sizeof(struct mail_message));
+ msg->sess_ref = sess;
+ msg->mail_protocol = mail_protocol;
+ msg->eml = eml;
+ msg->eml_len = eml_len;
+ msg->eml_offset = eml_offset;
+ msg->is_eml_finished = is_eml_finished;
+
+ return mq_runtime_publish_message(runtime, mail_env->eml_topic_id, msg);
+}
+
+int mail_message_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx)
+{
+ size_t mail_seq;
+ enum MAIL_PROTOCOL mail_protocol;
+
+ mail_seq = mail_parser_get_seq(session_ctx->smtp);
+ mail_protocol = mail_parser_get_protocol(session_ctx->parser);
+
+ enum MAIL_COMMAND cmd;
+ char *cmd_param;
+ size_t cmd_param_len;
+ char *cmd_line;
+ size_t cmd_line_len;
+
+ ret = mail_parser_get_command(session_ctx->parser, &cmd, &cmd_param, &cmd_param_len, &cmd_line, &cmd_line_len);
+ if (ret == 0) {
+ mail_publish_command(mail_env, sess, mail_protocol, mail_seq, cmd, cmd_param, cmd_param_len, cmd_line, cmd_line_len);
+ }
+
+ struct mail_header *header;
+
+ header = mail_parser_get_header(session_ctx->parser);
+ if (header) {
+ mail_publish_header(mail_env, sess, mail_protocol, mail_seq, header);
+ }
+
+ char *body;
+ size_t body_len;
+ size_t body_offset;
+ int is_body_finished;
+
+ ret = mail_parser_get_body(session_ctx->parser, &body, &body_len, &body_offset, &is_body_finished);
+ if (ret == 0) {
+ mail_publish_body(mail_env, sess, mail_protocol, mail_seq,
+ body, body_len,
+ body_offset, is_body_finished);
+ }
+
+ char *attachment_name;
+ size_t attachment_name_len;
+ char *attachment;
+ size_t attachment_len;
+ size_t attachment_offset;
+ int is_attachment_finished;
+
+ ret = mail_parser_get_attachment(session_ctx->parser, &attachment_name, &attachment_name_len, &attachment, &attachment_len, &attachment_offset, &is_attachment_finished);
+ if (ret == 0) {
+ mail_publish_attachment(mail_env, sess, mail_protocol, mail_seq,
+ attachment_name, attachment_name_len,
+ attachment, attachment_len,
+ attachment_offset, is_attachment_finished);
+ }
+
+ char *eml;
+ size_t eml_len;
+ size_t eml_offset;
+ int is_eml_finished;
+
+ ret = mail_parser_get_eml(session_ctx->parser, &eml, &eml_len, &eml_offset, &is_eml_finished);
+ if (ret == 0) {
+ mail_publish_eml(mail_env, sess, mail_protocol,
+ eml, eml_len,
+ eml_offset, is_eml_finished);
+ }
+
+}
+
+
+
+
+/*-
+ * 64 char lines
+ * pad input with 0
+ * left over chars are set to =
+ * 1 byte => xx==
+ * 2 bytes => xxx=
+ * 3 bytes => xxxx
+ */
+static const char data_bin2ascii[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define conv_bin2ascii(a) (data_bin2ascii[(a)&0x3f])
+
+/*-
+ * 0xF0 is a EOLN
+ * 0xF1 is ignore but next needs to be 0xF0 (for \r\n processing).
+ * 0xF2 is EOF
+ * 0xE0 is ignore at start of line.
+ * 0xFF is error
+ */
+#define B64_EOLN 0xF0
+#define B64_CR 0xF1
+#define B64_EOF 0xF2
+#define B64_WS 0xE0
+#define B64_ERROR 0xFF
+#define B64_NOT_BASE64(a) (((a)|0x13) == 0xF3)
+#define B64_BASE64(a) !B64_NOT_BASE64(a)
+
+static const unsigned char data_ascii2bin[128] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xE0, 0xF0, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xF2, 0xFF, 0x3F,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
+ 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+ 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+ 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+//切记不可以如此调用: conv_ascii2bin(x++); 因为会++两次
+#define conv_ascii2bin(aa) (((aa) & 0x80)?(0xFF):data_ascii2bin[(aa)])
+
+/*static unsigned char conv_ascii2bin(unsigned char a)
+{
+ if (a & 0x80)
+ return B64_ERROR;
+ return data_ascii2bin[a];
+ //return (((a) & 0x80)?B64_ERROR:data_ascii2bin[(a)]);
+}
+*/
+
+/*********************************************************************
+函数名称:Base64_EncodeBlock
+功能简介:对一完整BASE64编码块进行编码
+输入参数:in:待编码的字符;
+ inl:in的长度
+输出参数:out:编码后存储的缓冲区;
+返回值:编码后的长度
+*********************************************************************/
+int Base64_EncodeBlock(const unsigned char *in, int inl, unsigned char *out)
+{
+ int i, ret = 0;
+ unsigned long l;
+
+ for (i = inl; i > 0; i -= 3) {
+ if (i >= 3) {
+ l = (((unsigned long)in[0]) << 16L) |
+ (((unsigned long)in[1]) << 8L) | in[2];
+ *(out++) = conv_bin2ascii(l >> 18L);
+ *(out++) = conv_bin2ascii(l >> 12L);
+ *(out++) = conv_bin2ascii(l >> 6L);
+ *(out++) = conv_bin2ascii(l);
+ } else {
+ l = ((unsigned long)in[0]) << 16L;
+ if (i == 2)
+ l |= ((unsigned long)in[1] << 8L);
+
+ *(out++) = conv_bin2ascii(l >> 18L);
+ *(out++) = conv_bin2ascii(l >> 12L);
+ *(out++) = (i == 1) ? '=' : conv_bin2ascii(l >> 6L);
+ *(out++) = '=';
+ }
+ ret += 4;
+ in += 3;
+ }
+
+ *out = '\0';
+ return (ret);
+}
+
+/*********************************************************************
+函数名称:Base64_DecodeBlock
+功能简介:对一完整BASE64编码块进行解析,自动忽略首尾非BASE64编码字符
+输入参数:in:待解码的字符;
+ inl:in的长度
+输出参数:out:解码后存储的缓冲区;
+ 必须保证有足够的空间,一般达到@inl大小即可;
+返回值:<0:失败;>=0:解码后的长度
+*********************************************************************/
+int Base64_DecodeBlock(const unsigned char *in, int inl, unsigned char *out, int outsize)
+{
+ int i, ret = 0;
+ unsigned char a, b, c, d;
+ unsigned long l;
+
+ /* ignore not-base64-encoded charactor. */
+ while ((conv_ascii2bin(*in) == B64_WS) && (inl > 0))
+ {
+ in++;
+ inl--;
+ }
+ while ((inl > 3) && (B64_NOT_BASE64(conv_ascii2bin(in[inl - 1]))))
+ inl--;
+
+ if (inl % 4 != 0)
+ return -1;
+
+ if(outsize < (inl*3)/4)
+ return -2;
+
+ for (i = 0; i < inl; i += 4)
+ {
+ a = conv_ascii2bin(*(in));
+ b = conv_ascii2bin(*(in+1));
+ c = conv_ascii2bin(*(in+2));
+ d = conv_ascii2bin(*(in+3));
+ if ((a & 0x80) || (b & 0x80) || (c & 0x80) || (d & 0x80))
+ return (-1);
+ l = ((((unsigned long)a) << 18L) | (((unsigned long)b) << 12L) |
+ (((unsigned long)c) << 6L) | (((unsigned long)d)));
+ *(out++) = (unsigned char)(l >> 16L) & 0xff;
+ *(out++) = (unsigned char)(l >> 8L) & 0xff;
+ *(out++) = (unsigned char)(l) & 0xff;
+ ret += 3;
+ in+=4;
+ }
+
+ for(i = inl; i > 0; i -= 4)
+ {
+ if(*(in-3) == '=')
+ {
+ in -= 4;
+ ret -= 3;
+ continue;
+ }
+
+ while(*(--in) == '=')
+ ret -= 1;
+
+ break;
+ }
+
+ return ret;
+}
+
+void Base64_DecodeInit(decode_handle_t *handle)
+{
+ handle->length = 0;
+}
+
+/*********************************************************************
+函数名称:Base64_DecodeFeed
+功能简介:对部分BASE64编码块进行解析,自动忽略首尾非BASE64编码字符
+输入参数:in:待解码的字符;
+ inl:in的长度,可能不是4的倍数,对于小于4的部分,会转存一下,
+ 下次解码时将其拼接到输入数据的头部;转存的最大长度为3,
+ 因为如果达到了4,就可以将该部分解码出来;
+输出参数:out:解码后存储的缓冲区;
+返回值:-1:失败;0:需要更多的数据;>0:解码后的长度
+*********************************************************************/
+int Base64_DecodeFeed(decode_handle_t *handle, const unsigned char *in, int inl, unsigned char *out, int outsize)
+{
+ int i, j=0, dst_len = 0;
+ const unsigned char *pc=in, *pend=in+inl;
+ unsigned char de[4];
+ unsigned long l;
+
+ /* ignore not-base64-encoded charactor. */
+ while(pc<pend && (conv_ascii2bin(*pc) == B64_WS))
+ {
+ pc++;
+ }
+ while(pend>pc && B64_NOT_BASE64(conv_ascii2bin(*(pend-1))))
+ pend--;
+
+ if(handle->length>0)
+ {
+ while(pc<pend && handle->length<4)
+ {
+ handle->enc_data[handle->length++] = *pc;
+ pc++;
+ }
+ if(handle->length < 4)
+ {
+ return 0;
+ }
+ else
+ {
+ for(i=0; i<4; i++)
+ {
+ de[j++] = conv_ascii2bin(handle->enc_data[i]);
+ }
+ }
+ }
+
+ do
+ {
+ while(j < 4 && pc<pend)
+ {
+ de[j++] = conv_ascii2bin(*pc);
+ pc++;
+ }
+ if(j < 4)
+ break;
+ j = 0;
+
+ if(dst_len + 3 > outsize)
+ return -2;
+
+ if ((de[0] & 0x80) || (de[1] & 0x80) || (de[2] & 0x80) || (de[3] & 0x80))
+ return (-1);
+ l = ((((unsigned long)de[0]) << 18L) | (((unsigned long)de[1]) << 12L) |
+ (((unsigned long)de[2]) << 6L) | (((unsigned long)de[3])));
+ *(out++) = (unsigned char)(l >> 16L) & 0xff;
+ *(out++) = (unsigned char)(l >> 8L) & 0xff;
+ *(out++) = (unsigned char)(l) & 0xff;
+
+ dst_len += 3;
+ }while(pc < pend);
+
+ pc--;
+ //有残留,进行缓存,不要存换行符
+ if(j>0)
+ {
+ handle->length = j;
+ while(j>0 && pc>=in)
+ {
+ handle->enc_data[j-1] = *pc;
+ pc--;
+ j--;
+ }
+ return dst_len;
+ }
+ //无解码残留
+ else if(pc>=in && *pc == '=')
+ {
+ dst_len -= 1;
+ pc--;
+ if((pc>=in && *pc == '=') ||(handle->length>0 && handle->enc_data[handle->length-1]=='='))
+ dst_len -= 1;
+ }
+ handle->length = 0;
+
+ return dst_len;
+}
+
+/*********************************************************************
+函数名称:Base64_DecodeFeed_r_n
+功能简介:与Base64_DecodeFeed不同之处在于:
+ 输入数据任何位置有可能出现'\r'或'\n',或一起出现;
+输入参数:同Base64_DecodeFeed
+输出参数:同Base64_DecodeFeed
+返回值:同Base64_DecodeFeed
+*********************************************************************/
+int Base64_DecodeFeed_r_n(decode_handle_t *handle, const unsigned char *in, int inl, unsigned char *out, int outsize)
+{
+ int i, j=0, dst_len = 0;
+ const unsigned char *pc=in, *pend=in+inl;
+ unsigned char de[4];
+ unsigned long l;
+
+ /* ignore not-base64-encoded charactor. */
+ while(pc<pend && (conv_ascii2bin(*pc) == B64_WS))
+ {
+ pc++;
+ }
+ while(pend>pc && B64_NOT_BASE64(conv_ascii2bin(*(pend-1))))
+ pend--;
+
+ //上一次有残留,先补齐4个字符到handle(为了结尾处判断残留是否是换行符)
+ if(handle->length>0)
+ {
+ while(pc<pend && handle->length<4)
+ {
+ if(*pc=='\r' || *pc=='\n' || *pc==' ' || *pc=='\t')
+ {
+ pc++;
+ continue;
+ }
+ handle->enc_data[handle->length++] = *pc;
+ pc++;
+ }
+ if(handle->length < 4)
+ {
+ return 0;
+ }
+ else
+ {
+ for(i=0; i<4; i++)
+ {
+ de[j++] = conv_ascii2bin(handle->enc_data[i]);
+ }
+ }
+ }
+
+ do
+ {
+ while(j < 4)
+ {
+ while(pc<pend && (*pc == '\r' || *pc == '\n' || *pc==' ' || *pc=='\t'))
+ {
+ pc++;
+ continue;
+ }
+ if(pc >= pend)
+ break;
+
+ de[j++] = conv_ascii2bin(*pc);
+ pc++;
+ }
+ if(j < 4)
+ break;
+ j = 0;
+
+ if(dst_len + 3 > outsize)
+ return -2;
+
+ if ((de[0] & 0x80) || (de[1] & 0x80) || (de[2] & 0x80) || (de[3] & 0x80))
+ return (-1);
+ l = ((((unsigned long)de[0]) << 18L) | (((unsigned long)de[1]) << 12L) |
+ (((unsigned long)de[2]) << 6L) | (((unsigned long)de[3])));
+ *(out++) = (unsigned char)(l >> 16L) & 0xff;
+ *(out++) = (unsigned char)(l >> 8L) & 0xff;
+ *(out++) = (unsigned char)(l) & 0xff;
+
+ dst_len += 3;
+ }while(pc < pend);
+
+ pc--;
+ //有残留,进行缓存,不要存换行符
+ if(j>0)
+ {
+ handle->length = j;
+ while(j>0)
+ {
+ while(pc>=in && (*pc == '\r' || *pc == '\n' || *pc==' ' || *pc=='\t'))
+ {
+ pc--;
+ continue;
+ }
+ handle->enc_data[j-1] = *pc;
+ pc--;
+ j--;
+ }
+ return dst_len;
+ }
+ //无解码残留,如果尾部是=,修正实际的解码后长度
+ else if(pc>=in && *pc == '=')
+ {
+ dst_len -= 1;
+ pc--;
+ if((pc>=in && *pc == '=') ||(handle->length>0 && handle->enc_data[handle->length-1]=='='))
+ dst_len -= 1;
+ }
+ handle->length = 0;
+
+ return dst_len;
+}
+
+static unsigned char decode_hex( char *streamptr )
+{
+ unsigned char result = 0;
+ char blivit;
+
+ blivit = *streamptr++;
+ if ( blivit >= 'a' )
+ blivit -= ' ';
+ if ( blivit > '9' )
+ blivit -= 0x07;
+
+ result = blivit & 0x0F;
+ result <<= 4;
+
+ blivit = *streamptr++;
+ if ( blivit >= 'a' )
+ blivit -= ' ';
+ if ( blivit > '9' )
+ blivit -= 0x07;
+
+ result |= blivit & 0x0F;
+
+ return result;
+}
+
+/*********************************************************************
+函数名称:QP_DecodeBlock
+功能简介:对一完整QP编码块进行解析,会忽略'\r''\n'字符
+输入参数:in:待解码的字符;
+ inl:in的长度
+输出参数:out:解码后存储的缓冲区,
+ 必须保证有足够的空间,一般达到@inl大小即可;
+返回值:解码后的长度
+*********************************************************************/
+//"PPPPPP=X",如果X是一16进制数,是否保留"=X"?目前是保留。
+int QP_DecodeBlock(const unsigned char *in, int inl, unsigned char *out)
+{
+ int index=0, index2=0; //分别是buf、enc_data里内容长度
+ const unsigned char *temp=in, *inputend=in+inl;
+ unsigned char value;
+ int state=0;
+ char buf[2];
+ int dst_len=0;
+
+ if(inl == 0)
+ return 0;
+
+ while(temp<inputend)
+ {
+ switch(state)
+ {
+ case 0:
+ if(*temp == '=')
+ {
+ index2++;
+ state=1;
+ }
+ else
+ {
+ out[dst_len++]=(unsigned char)(*temp);
+ out[dst_len]='\0';
+ }
+ temp=temp+1;
+ break;
+
+ case 1:
+ if(*temp=='\n' || *temp=='\r')
+ {
+ index2++;
+ state = 2;
+ }
+ //不是可解码的字符
+ else if (((*temp < '0') || (*temp > '9')) && ((*temp < 'A') || (*temp > 'F')) && ((*temp < 'a') || (*temp > 'f')))
+ {
+ out[dst_len++] = '=';
+ out[dst_len] = '\0';
+
+ if(index!=0) //遇到了=XY串,其中X是16进制数,Y不是
+ {
+ out[dst_len++] = buf[0];
+ out[dst_len] = '\0';
+ }
+ if(*temp== '=')
+ {
+ state = 1;
+ }
+ else
+ {
+ out[dst_len++] = *temp;
+ out[dst_len] = '\0';
+ state=0;
+ index2=0;
+ }
+ index=0;
+ }
+ //是可解码的字符,但是不够
+ else if(index<1)
+ {
+ buf[index++]=*temp;
+ }
+ //进行解码
+ else
+ {
+ buf[index++]=*temp;
+ value=decode_hex(buf);
+ out[dst_len++] = value;
+ out[dst_len]='\0';
+ index=0;
+ state=0;
+ index2=0;
+ }
+ temp=temp+1;
+ break;
+
+ case 2:
+ state = 0;
+ index = 0;
+ index2 = 0;
+
+ if(*temp == '=')
+ {
+ index2++;
+ state=1;
+ }
+ else if(*temp!='\n' && *temp!='\r')
+ {
+ out[dst_len++] = *temp;
+ out[dst_len]='\0';
+ }
+ temp++;
+ break;
+
+ default: break;
+ }
+ }
+
+ //保留最后不足以进行解码的数据
+ if(index2!=0)
+ {
+ out[dst_len++] = '=';
+ out[dst_len]='\0';
+
+ if(index!=0)
+ {
+ out[dst_len++] = buf[0];
+ out[dst_len]='\0';
+ }
+ }
+
+ return dst_len;
+}
+
+void QP_DecodeInit(decode_handle_t *handle)
+{
+ handle->length = 0;
+}
+
+/*********************************************************************
+函数名称:QP_DecodeFeed
+功能简介:对部分QP编码块进行解析
+输入参数:in:待解码的字符;
+ inl:in的长度,可能不足以解码出来,对于不足以解码的部分,
+ 会转存一下,下次解码时优先解决该部分,转存的最大长度为2;
+ 如果为1,必然是'=';如果为2,必然是"=X";X是一个16进制数;
+输出参数:out:解码后存储的缓冲区;
+返回值:-1:失败;0:需要更多的数据;>0:解码后的长度
+*********************************************************************/
+int QP_DecodeFeed(decode_handle_t *handle,const unsigned char *in, int inl, unsigned char *out)
+{
+ int index=0, index2=0; //分别是buf、enc_data里内容长度
+ const unsigned char *temp=in, *inputend=in+inl;
+ unsigned char value;
+ int state=0;
+ char buf[2];
+ int dst_len=0;
+
+ if(inl == 0)
+ return 0;
+
+ if(handle->length > 0)
+ {
+ if(handle->length == 1)
+ {
+ state = 1;
+ index2 = 1;
+ }
+ else if(handle->length == 2)
+ {
+ if(handle->enc_data[1] == '\n')
+ {
+ state = 2;
+ index2 = 2;
+ }
+ else
+ {
+ state = 1;
+ index2 = 1;
+ index = 1;
+ buf[0] = handle->enc_data[1];
+ }
+ }
+
+ handle->length = 0;
+ }
+
+ while(temp<inputend)
+ {
+ switch(state)
+ {
+ case 0:
+ if(*temp == '=')
+ {
+ index2++;
+ state=1;
+ }
+ else
+ {
+ out[dst_len++]=(unsigned char)(*temp);
+ out[dst_len]='\0';
+ }
+ temp=temp+1;
+ break;
+
+ case 1:
+ if(*temp=='\n' || *temp=='\r')
+ {
+ index2++;
+ state = 2;
+ }
+ //不是可解码的字符
+ else if (((*temp < '0') || (*temp > '9')) && ((*temp < 'A') || (*temp > 'F')) && ((*temp < 'a') || (*temp > 'f')))
+ {
+ out[dst_len++] = '=';
+ out[dst_len] = '\0';
+
+ if(index!=0) //遇到了=XY串,其中X是16进制数,Y不是
+ {
+ out[dst_len++] = buf[0];
+ out[dst_len] = '\0';
+ }
+ if(*temp== '=')
+ {
+ state = 1;
+ }
+ else
+ {
+ out[dst_len++] = *temp;
+ out[dst_len] = '\0';
+ state=0;
+ index2=0;
+ }
+ index=0;
+ }
+ //是可解码的字符,但是不够
+ else if(index<1)
+ {
+ buf[index++]=*temp;
+ }
+ //进行解码
+ else
+ {
+ buf[index++]=*temp;
+ value=decode_hex(buf);
+ out[dst_len++] = value;
+ out[dst_len]='\0';
+ index=0;
+ state=0;
+ index2=0;
+ }
+ temp=temp+1;
+ break;
+
+ case 2:
+ state = 0;
+ index = 0;
+ index2 = 0;
+
+ if(*temp == '=')
+ {
+ index2++;
+ state=1;
+ }
+ else if(*temp!='\n' && *temp!='\r')
+ {
+ out[dst_len++] = *temp;
+ out[dst_len]='\0';
+ }
+ temp++;
+ break;
+
+ default: break;
+ }
+ }
+
+ if(index2!=0)
+ {
+ handle->enc_data[0] = '=';
+ handle->length = 1;
+
+ if(index2 == 2)
+ handle->enc_data[handle->length++] = '\n';
+ else if(index!=0)
+ {
+ handle->enc_data[1] = buf[0];
+ handle->length = 2;
+ }
+ }
+
+ return dst_len;
+}
+
+
+int strncasecmp_one_word_mesa(const char *str1, size_t len1, const char *str2, size_t len2)
+{
+ if (len2 != len1 || len1==0)
+ return 0;
+
+ do {
+ if(*str1 - 'a' > 0) //str1小写
+ {
+ if (*str1 == *str2 || toupper(*str1) == *str2)
+ {
+ ++str1;
+ ++str2;
+ continue;
+ }
+ }
+ else
+ {
+ if (*str1 == *str2 || tolower(*str1) == *str2)
+ {
+ ++str1;
+ ++str2;
+ continue;
+ }
+ }
+ return 0;
+ } while (--len1);
+
+ return 1;
+}
+
+
+int strncmp_one_word_mesa(const char *s1_lowercase, const char *s1_uppercase, size_t len1, const char *s2, size_t len2)
+{
+ if (len1 > len2)
+ return 0;
+
+ const unsigned char *s1, *s12;
+
+ if (islower(s2[len1-1]))
+ {
+ s1 = (const unsigned char *)s1_lowercase;
+ s12= (const unsigned char *)s1_uppercase;
+ }
+ else
+ {
+ s1 = (const unsigned char *)s1_uppercase;
+ s12= (const unsigned char *)s1_lowercase;
+ }
+
+ while(len1--) {
+ if (*s1 == *s2 || *s12 == *s2)
+ {
+ ++s1;
+ ++s12;
+ ++s2;
+ continue;
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+* mesa_memicmp2
+* FUNCTION
+* compare s2 with s1, ignorance of case
+* INPUT
+* @s1_lowercase MUST be lowercase
+* @s1_uppercase MUST be uppercase
+* @len1 is the length of s1
+* @s2 is the string you want to be compared
+* @len2 is the length of s2
+* OUTPUT:
+* 0:s1==s2
+* -1:s1!=s2
+*/
+int strcmp_one_word_mesa(const char *s1_lowercase, const char *s1_uppercase, size_t len1, const char *s2, size_t len2)
+{
+ unsigned char *s1,*s12;
+
+ if (len1 != len2)
+ return 0;
+
+ if (s2[len2-1]-'a'>=0)
+ {
+ s1 = (unsigned char *)s1_lowercase;
+ s12= (unsigned char *)s1_uppercase;
+ }
+ else
+ {
+ s1 = (unsigned char *)s1_uppercase;
+ s12= (unsigned char *)s1_lowercase;
+ }
+
+ do {
+ if (*s1 == *s2 || *s12 == *s2)
+ {
+ ++s1;
+ ++s12;
+ ++s2;
+ continue;
+ }
+ return 0;
+ } while (--len1);
+
+ return 1;
+}
+
+//相对于strcmp_one_word_mesa仅仅去掉了长度相等的比较
+int strcmp_one_word_mesa_equal_len(const char *s1_lowercase, const char *s1_uppercase, const char *s2, size_t len)
+{
+ unsigned char *s1,*s12;
+
+ if (s2[len-1]-'a'>=0)
+ {
+ s1 = (unsigned char *)s1_lowercase;
+ s12= (unsigned char *)s1_uppercase;
+ }
+ else
+ {
+ s1 = (unsigned char *)s1_uppercase;
+ s12= (unsigned char *)s1_lowercase;
+ }
+
+ do {
+ if (*s1 == *s2 || *s12 == *s2)
+ {
+ ++s1;
+ ++s12;
+ ++s2;
+ continue;
+ }
+ return 0;
+ } while (--len);
+
+ return 1;
+}
+
+int strncmp_two_word_mesa(const char *s1_lower, const char *s1_upper, size_t len1,
+ const char *s2_lower, const char *s2_upper, size_t len2, const char *data, int dlen)
+{
+ int matchlen=0;
+
+ if(!strncmp_one_word_mesa(s1_lower, s1_upper, len1, data, dlen))
+ {
+ return 0;
+ }
+ data += len1; dlen-=len1; matchlen+=len1;
+ while(dlen>0 && *data==' ')
+ {
+ data++;dlen--;matchlen++;
+ }
+ if(dlen == 0) return 0;
+
+ if(!strncmp_one_word_mesa(s2_lower, s2_upper, len2, data, dlen))
+ {
+ return 0;
+ }
+ data += len2; dlen-=len2; matchlen+=len2;
+ while(dlen>0 && *data==' ')
+ {
+ data++;dlen--;matchlen++;
+ }
+ return matchlen;
+}
+
+/*********************************************************************
+函数名称:mail_strcmp_two_word
+功能简介:判断一个由2个单词组成的字符串是不是目标SMTP命令字符串,含':',
+ 忽略目标字符串2个单词及':'之间的空格,如"mail from :",忽略大小写
+输入参数:srcdata:
+输出参数:无
+返回值: 1:是
+ 0:不是
+*********************************************************************/
+int strcmp_two_word(char *srcdata,int datalen, const char *first, int flen, const char *second, int slen)
+{
+ char * tok;
+
+ if(datalen < flen+slen+1)
+ return 0;
+
+ if(strncasecmp(srcdata, first, flen) != 0)
+ {
+ return 0;
+ }
+
+ tok = srcdata + flen;
+ while(*tok==' ')
+ tok++;
+
+ if(strncasecmp(tok, second, slen) != 0)
+ return 0;
+
+ tok += slen;
+ while(*tok==' ')
+ tok++;
+ if( *tok != ':' )
+ return 0;
+
+ return 1;
+}
+
+
+//查找开始位置
+const void *mail_memmem(const void *start, unsigned int s_len, const void *find, unsigned int f_len)
+{
+ const char *p, *q;
+ unsigned int len;
+
+ p = (const char *)start, q = (const char *)find;
+ len = 0;
+ while((p - (const char *)start + f_len) <= s_len)
+ {
+ while(*p++ == *q++)
+ {
+ len++;
+ if(len == f_len)
+ return(p - f_len);
+ };
+ q = (const char *)find;
+ len = 0;
+ };
+
+ return NULL;
+}
+
+/*
+* mesa_memncasemem3
+* FUNCTION
+* find needle in haystack, ignorance of case
+* INPUT
+* @needle MUST be lowercase
+* @needlelen is the length of needle
+* OUTPUT
+* NULL if not found
+*/
+char *mesa_memncasemem(const char *haystack, size_t haystacklen,const char *needle, size_t needlelen)
+{
+ unsigned char *psub,*psrc,*p,*pendsrc,*pendsubstr;
+ int diff=-32; //A=a-32;
+
+ if ((haystack==NULL) || needle==NULL)
+ return NULL;
+ if (haystacklen<needlelen)
+ return NULL;
+
+ pendsrc = (unsigned char *)haystack + haystacklen - needlelen + 1;
+ pendsubstr = (unsigned char *)needle + needlelen;
+ for (psrc =(unsigned char *)haystack; psrc != pendsrc; psrc++)
+ {
+ for (p = psrc,psub = (unsigned char *)needle; psub != pendsubstr; psub++,p++)
+ {
+ if (*p!=*psub && *p!=*psub + diff)
+ break;
+ }
+ if (psub == pendsubstr)
+ {
+ return (char *)psrc;
+ }
+ }
+ return NULL;
+}
+
+
+/*
+* FUNCTION
+* find last needle in haystack, ignorance of case
+* INPUT
+* @needle MUST be lowercase
+* @needlelen is the length of needle
+* OUTPUT
+* NULL if not found
+*/
+char *mesa_memncasemem_reverse(const char *haystack, size_t haystacklen,const char *needle, size_t needlelen)
+{
+ unsigned char *p,*psrc,*psub,*pendsubstr;
+ int diff=-32; //A=a-32;
+
+ if ((haystack==NULL) || needle==NULL)
+ return NULL;
+ if (haystacklen<needlelen)
+ return NULL;
+
+ pendsubstr = (unsigned char *)needle + needlelen;
+ for (psrc = (unsigned char *)haystack + haystacklen - needlelen; psrc != (unsigned char *)haystack-1; psrc--)
+ {
+ for (p = psrc,psub = (unsigned char *)needle; psub != pendsubstr; p++,psub++)
+ {
+ if (*p!=*psub && *p!=*psub + diff)
+ break;
+ }
+ if (psub == pendsubstr)
+ {
+ return (char *)psrc;
+ }
+ }
+ return NULL;
+}
diff --git a/decoders/mail/mail_util.h b/decoders/mail/mail_util.h
new file mode 100644
index 0000000..1edc4c8
--- /dev/null
+++ b/decoders/mail/mail_util.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#define SAFE_STRNCASECMP(s1, s1_len, s2, s2_len) ((s1_len) >= (s2_len) ? strncasecmp(s1, s2, s2_len) : -1)
+
+struct stream_buffer {
+ char *buf;
+ size_t buf_off;
+ size_t buf_len;
+ size_t buf_size;
+};
+
+// clear already readed complete lines
+void line_buffer_clear(struct stream_buffer *line_buffer);
+void line_buffer_write(struct stream_buffer *line_buffer, const char *payload, size_t payload_len);
+int line_buffer_readln(struct stream_buffer *line_buffer, const char **line_start, size_t *line_len);
+
+int mail_extract_email_address(const char *buffer, size_t length, struct iovec *email_address);
+
+
+
+#define MAIL_DIM(a) (sizeof (a) / sizeof ((a)[0]))
+
+//用户一般不用关心该结构体成员,除非想关注解码后是否有数据残留;
+//但是在进行新的解码之前需要调用QP_DecodeInit重新进行初始化;
+typedef struct __decode_handle {
+ int length; //解码残留的数据长度
+ unsigned char enc_data[4];
+} decode_handle_t;
+
+void QP_DecodeInit(decode_handle_t *handle);
+
+/****************************QP_DecodeBlock*****************************************
+函数名称:QP_DecodeFeed/QP_DecodeBlock
+输入参数:in:待解码的字符,会忽略'\r''\n'字符
+ inl:f的长度
+输出参数:out:解码后存储的缓冲区;
+ 必须保证有足够的空间,否则引起内存访问错误,一般达到@inl大小即可;
+返回值:<0:失败;>=0:解码后的长度
+*********************************************************************/
+//输入数据为任意长度,如果不足以解码出来,会转存到下次;
+int QP_DecodeFeed(decode_handle_t *handle,const unsigned char *in, int inl, unsigned char *out);
+
+//对一个完整块进行解码
+int QP_DecodeBlock(const unsigned char *in, int inl, unsigned char *out);
+
+int Base64_EncodeBlock(const unsigned char *in, int inl, unsigned char *out);
+
+void Base64_DecodeInit(decode_handle_t *handle);
+
+/*********************************************************************
+函数名称:Base64_DecodeBlock/Base64_DecodeFeed/Base64_DecodeFeed_r_n
+输入参数:in:待解码的字符;
+ inl:in的长度
+输出参数:out:解码后存储的缓冲区;
+ 必须保证有足够的空间,否则引起内存访问错误,一般达到@inl大小即可;
+返回值:<0:失败;>=0:解码后的长度
+*********************************************************************/
+//输入数据为任意长度,如果不足以解码出来,会转存到下次;
+int Base64_DecodeFeed(decode_handle_t *handle, const unsigned char *in, int inl, unsigned char *out, int outsize);
+
+//与Base64_DecodeFeed不同之处在于: 输入数据任何位置有可能出现'\r'或'\n',或一起出现;
+int Base64_DecodeFeed_r_n(decode_handle_t *handle, const unsigned char *in, int inl, unsigned char *out, int outsize);
+
+//对一个完整块进行解码
+int Base64_DecodeBlock(const unsigned char *in, int inl, unsigned char *out, int outsize);
+
+int strncasecmp_one_word_mesa(const char *str1, size_t len1, const char *str2, size_t len2);
+int strcmp_one_word_mesa(const char *s1_lowercase, const char *s1_uppercase, size_t len1, const char *s2, size_t len2);
+int strncmp_one_word_mesa(const char *s1_lowercase, const char *s1_uppercase, size_t len1, const char *s2, size_t len2);
+int strcmp_one_word_mesa_equal_len(const char *s1_lowercase, const char *s1_uppercase, const char *s2, size_t len);
+
+int strncmp_two_word_mesa(const char *s1_lower, const char *s1_upper, size_t len1,
+ const char *s2_lower, const char *s2_upper, size_t len2, const char *data, int dlen);
+int strcmp_two_word(char *srcdata,int datalen, const char *first, int flen, const char *second, int slen);
+
+const void *mail_memmem(const void *start, unsigned int s_len, const void *find, unsigned int f_len);
+char *mesa_memncasemem(const char *haystack, size_t haystacklen,const char *needle, size_t needlelen);
diff --git a/decoders/mail/version.map b/decoders/mail/version.map
new file mode 100644
index 0000000..ec98aad
--- /dev/null
+++ b/decoders/mail/version.map
@@ -0,0 +1,13 @@
+VERS_2.4{
+global:
+extern "C" {
+ mail_init;
+ mail_exit;
+ mail_subscribe;
+ module_to_mail_decoder;
+ GIT_VERSION_*;
+};
+
+local: *;
+};
+
diff --git a/include/stellar/mail.h b/include/stellar/mail.h
new file mode 100644
index 0000000..a7e8a27
--- /dev/null
+++ b/include/stellar/mail.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "stellar/session.h"
+#include "stellar/module.h"
+
+#define MAIL_MODULE_NAME "MAIL_MODULE"
+
+enum MAIL_COMMAND {
+ MAIL_CMD_USERNAME,
+ MAIL_CMD_PASSWORD,
+ MAIL_CMD_STARTTLS,
+ MAIL_CMD_MAIL_FROM,
+ MAIL_CMD_RCPT_TO,
+ MAIL_CMD_EHLO,
+ MAIL_CMD_OTHER,
+ MAIL_CMD_MAX,
+};
+
+enum MAIL_PROTOCOL {
+ MAIL_PROTOCOL_SMTP,
+ MAIL_PROTOCOL_POP3,
+ MAIL_PROTOCOL_IMAP,
+ MAIL_PROTOCOL_MAX,
+};
+
+struct mail_header_field {
+ char *field_name;
+ size_t field_name_len;
+ char *field_value;
+ size_t field_value_len;
+};
+
+struct mail_header {
+ struct mail_header_field *from;
+ struct mail_header_field *to;
+ struct mail_header_field *cc;
+ struct mail_header_field *bcc;
+ struct mail_header_field *reply_to;
+ struct mail_header_field *date;
+ struct mail_header_field *subject;
+
+ struct mail_header_field *header_fields;
+ size_t n_header_fields;
+};
+
+typedef void mail_command_callback_func(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ enum MAIL_COMMAND command,
+ char *cmd_arg, size_t cmd_arg_len,
+ char *cmd_line, size_t cmd_line_len,
+ void *arg);
+
+typedef void mail_header_callback_func(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ struct mail_header *header,
+ void *arg);
+
+typedef void mail_body_callback_func(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ char *body, size_t body_len, size_t offset, int is_finish,
+ void *arg);
+
+typedef void mail_attachment_callback_func(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ char *name, size_t name_len,
+ char *attachment, size_t attachment_len, size_t offset, int is_finish,
+ void *arg);
+
+typedef void mail_eml_callback_func(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol,
+ char *eml, size_t eml_len, size_t offset, int is_finish,
+ void *arg);
+
+
+struct mail_decoder;
+struct mail_decoder *module_to_mail_decoder(struct module *module);
+int mail_subscribe(struct mail_decoder *decoder,
+ mail_command_callback_func *command_cb,
+ mail_header_callback_func *header_cb,
+ mail_body_callback_func *data_cb,
+ mail_attachment_callback_func *attachment_cb,
+ mail_eml_callback_func *eml_cb,
+ void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/test/decoders/mail/CMakeLists.txt b/test/decoders/mail/CMakeLists.txt
new file mode 100644
index 0000000..378f401
--- /dev/null
+++ b/test/decoders/mail/CMakeLists.txt
@@ -0,0 +1,53 @@
+set(TEST_NAME mail_test)
+set(TEST_MAIN ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME})
+
+file(GLOB TEST_SRC "${TEST_NAME}*.cpp")
+
+add_executable(
+ ${TEST_NAME}
+ ${TEST_SRC}
+)
+
+target_include_directories(
+ ${TEST_NAME} PRIVATE
+ ${CMAKE_SOURCE_DIR}/deps/
+ ${CMAKE_SOURCE_DIR}/decoders/
+)
+
+target_link_libraries(
+ ${TEST_NAME}
+ mail
+ stellar_lib
+ cjson-static
+ dl "-rdynamic"
+ gtest
+ gmock
+)
+
+add_test( NAME ${TEST_NAME}.SETUP
+ COMMAND sh -c "
+ mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/result/ &&
+ mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/log/ &&
+ mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/conf/ &&
+ cat ${CMAKE_SOURCE_DIR}/conf/stellar.toml > ${CMAKE_CURRENT_BINARY_DIR}/conf/stellar.toml &&
+ cat ${CMAKE_CURRENT_SOURCE_DIR}/conf/spec.toml >> ${CMAKE_CURRENT_BINARY_DIR}/conf/stellar.toml &&
+ sed -i 's/mode = \"pcapfile\"/mode = \"pcaplist\"/g' ${CMAKE_CURRENT_BINARY_DIR}/conf/stellar.toml &&
+ sed -i 's/pcap_path = \"\\\/tmp\\\/test.pcap\"/pcap_path = \"pcaplist.txt\"/g' ${CMAKE_CURRENT_BINARY_DIR}/conf/stellar.toml &&
+ find ${CMAKE_CURRENT_SOURCE_DIR}/result/ -type f | xargs -i cp {} ${CMAKE_CURRENT_BINARY_DIR}/result/ &&
+ find ${CMAKE_CURRENT_SOURCE_DIR}/pcap/ -type f > ${CMAKE_CURRENT_BINARY_DIR}/pcaplist.all
+ "
+)
+
+add_test(
+ NAME ${TEST_NAME}.imap
+ COMMAND sh -c "
+ cat ${CMAKE_CURRENT_BINARY_DIR}/pcaplist.all | grep '01-imap-mail-account-in-command-with-base64' > ${CMAKE_CURRENT_BINARY_DIR}/pcaplist.txt &&
+ ${TEST_MAIN} -m -c ./conf/stellar.toml -f ${CMAKE_CURRENT_BINARY_DIR}/result/01-imap-mail-account-in-command-with-base64.json
+ "
+)
+
+set_tests_properties(
+ ${TEST_NAME}.imap
+ PROPERTIES
+ FIXTURES_REQUIRED ${TEST_NAME}.SETUP
+)
diff --git a/test/decoders/mail/mail_test_main.cpp b/test/decoders/mail/mail_test_main.cpp
new file mode 100644
index 0000000..8cbaa1b
--- /dev/null
+++ b/test/decoders/mail/mail_test_main.cpp
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "stellar/stellar.h"
+
+#include <gtest/gtest.h>
+
+const char *usage = "Usage: %s [-m] [-u test_filter] [-c config_file] [-f result_file]\n";
+
+int main(int argc, char **argv) {
+ int opt;
+ int run_unittest = 0;
+ int run_stellar = 0;
+ const char *test_filter = NULL;
+ const char *result_filename = NULL;
+ const char *conf_filename = NULL;
+
+ if (argc == 1) {
+ fprintf(stderr, usage, argv[0]);
+ exit(-1);
+ }
+
+ while ((opt = getopt(argc, argv, "hmu:c:f:")) != -1) {
+ switch (opt) {
+ case 'u':
+ run_unittest = 1;
+ test_filter = optarg;
+ break;
+ case 'm':
+ run_stellar = 1;
+ break;
+ case 'f':
+ result_filename = optarg;
+ break;
+ case 'c':
+ conf_filename = optarg;
+ break;
+ case 'h':
+ default:
+ fprintf(stderr, usage, argv[0]);
+ exit(-1);
+ }
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (run_unittest) {
+ testing::GTEST_FLAG(filter) = test_filter ? test_filter : "";
+ int test_ret = RUN_ALL_TESTS();
+ if (test_ret != 0) {
+ fprintf(stderr, "Tests failed with return code %d\n", test_ret);
+ return test_ret;
+ }
+ }
+
+ if (run_stellar) {
+ if (result_filename == NULL) {
+ result_filename = "./mail_result.json";
+ }
+ // setenv for mail test module
+ setenv("MAIL_TEST_RESULT_EXPECT", result_filename, 1);
+
+ if (conf_filename == NULL) {
+ conf_filename = "./conf/stellar.toml";
+ }
+
+ struct stellar *st = stellar_new(conf_filename);
+ if (st == NULL) {
+ fprintf(stderr, "Failed to create stellar instance.\n");
+ return -1;
+ }
+
+ stellar_run(st);
+ stellar_free(st);
+ }
+
+ return ::testing::Test::HasFailure() ? -1 : 0;
+}
+
diff --git a/test/decoders/mail/mail_test_module.cpp b/test/decoders/mail/mail_test_module.cpp
new file mode 100644
index 0000000..827d273
--- /dev/null
+++ b/test/decoders/mail/mail_test_module.cpp
@@ -0,0 +1,367 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "stellar/stellar.h"
+#include "stellar/module.h"
+#include "stellar/session.h"
+#include "stellar/utils.h"
+#include "stellar/mail.h"
+
+#include <gtest/gtest.h>
+#include <uthash/uthash.h>
+#include "cjson/cJSON.h"
+
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#define MAIL_TEST_MODULE_NAME "MAIL_TEST_MODULE"
+#define MAIL_TEST_RESULT_EXPECT_ENV "MAIL_TEST_RESULT_EXPECT"
+#define MAIL_TEST_EXDATA_NAME "MAIL_TEST_EXDATA"
+
+#define MAIL_TEST_TRANSACTION_SEQ_MAX 64
+
+#define timeval_delta_ms(start, end) (((end).tv_sec-(start).tv_sec)*1000 + ((end).tv_usec-(start).tv_usec)/1000)
+#define timeval_delta_us(start, end) (((end).tv_sec-(start).tv_sec)*1000*1000 + ((end).tv_usec-(start).tv_usec))
+#define timeval_to_ms(t) ((t).tv_sec*1000+(t).tv_usec/1000)
+
+struct mail_test_sockaddr_in {
+ int family;
+ union {
+ struct sockaddr_in ipv4;
+ struct sockaddr_in6 ipv6;
+ } sockaddr;
+};
+
+struct mail_test_exdata {
+ int callback_times;
+ struct mail_test_module_ctx *mod_ctx_ref;
+};
+
+struct mail_test_result {
+ cJSON *test_json;
+ cJSON *expect_json;
+ int count;
+};
+
+struct mail_test_module_ctx {
+ int exdata_id;
+ struct mail_test_result *result;
+
+ // depends a single session pcap
+ int callback_times;
+ struct module_manager *mod_mgr_ref;
+};
+
+const char *mail_protocol_name[]={"SMTP", "POP3", "IMAP"};
+
+static void mail_test_result_commit(struct mail_test_result *result, cJSON *json)
+{
+ cJSON_AddNumberToObject(json, "test_result", ++result->count);
+ cJSON_AddItemToArray(result->test_json, json);
+}
+
+static void mail_test_result_compare(struct mail_test_result *result)
+{
+ EXPECT_TRUE(result->expect_json != NULL);
+ EXPECT_TRUE(result->test_json != NULL);
+
+ int i, json_compare;
+ int test_result_count, expect_result_count;
+ char *test_str, *expect_str;
+ cJSON *tmp_test, *tmp_expect;
+
+ //expect_str = cJSON_Print(result->expect_json);
+ //test_str = cJSON_Print(result->test_json);
+ //printf("LOAD Raw:\n%s\n", expect_str);
+ //printf("TEST Raw:\n%s\n", test_str);
+
+ test_result_count = cJSON_GetArraySize(result->test_json);
+ expect_result_count = cJSON_GetArraySize(result->expect_json);
+
+ EXPECT_EQ(test_result_count, expect_result_count);
+
+ for (i = 0; i < MIN(test_result_count, expect_result_count); i++) {
+ tmp_test = cJSON_GetArrayItem(result->test_json, i);
+ tmp_expect = cJSON_GetArrayItem(result->expect_json, i);
+ expect_str = cJSON_Print(tmp_expect);
+ test_str = cJSON_Print(tmp_test);
+
+ json_compare = cJSON_Compare(tmp_expect, tmp_test, 0);
+ if (json_compare != 1) {
+ printf("LOAD Diff:\n%s\n", expect_str);
+ printf("TEST Diff:\n%s\n", test_str);
+ }
+
+ free(expect_str);
+ free(test_str);
+
+ EXPECT_EQ(json_compare, 1);
+ break;
+ }
+}
+
+static void mail_test_result_exit(struct mail_test_result *result)
+{
+ if (result->expect_json) {
+ cJSON_Delete(result->expect_json);
+ }
+ if (result->test_json) {
+ cJSON_Delete(result->test_json);
+ }
+ free(result);
+}
+
+static struct mail_test_result * mail_test_result_init(const char *filename)
+{
+ long filesize;
+ char *buffer;
+ FILE *file;
+ struct mail_test_result *result;
+
+ result = (struct mail_test_result *)calloc(1, sizeof(struct mail_test_result));
+
+ file = fopen(filename, "rb");
+ if (file) {
+ fseek(file, 0, SEEK_END);
+ filesize = ftell(file);
+ rewind(file);
+ buffer = (char *)calloc(filesize + 1, 1);
+ fread(buffer, 1, filesize, file);
+
+ result->expect_json = cJSON_Parse(buffer);
+
+ free(buffer);
+ fclose(file);
+ }
+
+ result->test_json = cJSON_CreateArray();
+
+ printf("mail test result expect: %s\n", filename);
+ return result;
+}
+
+static void mail_test_store_packet_dst(struct mail_test_sockaddr_in *sockaddr, const struct packet *pkt) {
+ const struct layer *layer;
+
+ if (sockaddr == NULL) {
+ return;
+ }
+
+ memset(sockaddr, 0, sizeof(struct mail_test_sockaddr_in));
+
+ layer = packet_get_layer_by_idx(pkt, packet_get_layer_count(pkt) - 2);
+ if (layer->proto == LAYER_PROTO_IPV4) {
+ sockaddr->family = AF_INET;
+ sockaddr->sockaddr.ipv4.sin_family = AF_INET;
+ sockaddr->sockaddr.ipv4.sin_addr = layer->hdr.ip4->ip_dst;
+
+ layer = packet_get_layer_by_idx(pkt, packet_get_layer_count(pkt) - 1);
+ if (layer->proto == LAYER_PROTO_TCP) {
+ sockaddr->sockaddr.ipv4.sin_port = htons(layer->hdr.tcp->dest);
+ }
+ if (layer->proto == LAYER_PROTO_UDP) {
+ sockaddr->sockaddr.ipv4.sin_port = htons(layer->hdr.udp->dest);
+ }
+ }
+ if (layer->proto == LAYER_PROTO_IPV6) {
+ sockaddr->family = AF_INET6;
+ sockaddr->sockaddr.ipv6.sin6_family = AF_INET6;
+ memcpy(&sockaddr->sockaddr.ipv6.sin6_addr, &layer->hdr.ip6->ip6_dst, sizeof(struct in6_addr));
+
+ layer = packet_get_layer_by_idx(pkt, packet_get_layer_count(pkt) - 2);
+ if (layer->proto == LAYER_PROTO_TCP) {
+ sockaddr->sockaddr.ipv6.sin6_port = htons(layer->hdr.tcp->dest);
+ }
+ if (layer->proto == LAYER_PROTO_UDP) {
+ sockaddr->sockaddr.ipv6.sin6_port = htons(layer->hdr.udp->dest);
+ }
+ }
+}
+
+static void mail_test_store_packet_src(struct mail_test_sockaddr_in *sockaddr, const struct packet *pkt) {
+ const struct layer *layer;
+
+ if (sockaddr == NULL) {
+ return;
+ }
+
+ memset(sockaddr, 0, sizeof(struct mail_test_sockaddr_in));
+
+ layer = packet_get_layer_by_idx(pkt, packet_get_layer_count(pkt) - 2);
+ if (layer->proto == LAYER_PROTO_IPV4) {
+ sockaddr->family = AF_INET;
+ sockaddr->sockaddr.ipv4.sin_family = AF_INET;
+ sockaddr->sockaddr.ipv4.sin_addr = layer->hdr.ip4->ip_src;
+
+ layer = packet_get_layer_by_idx(pkt, packet_get_layer_count(pkt) - 1);
+ if (layer->proto == LAYER_PROTO_TCP) {
+ sockaddr->sockaddr.ipv4.sin_port = htons(layer->hdr.tcp->source);
+ }
+ if (layer->proto == LAYER_PROTO_UDP) {
+ sockaddr->sockaddr.ipv4.sin_port = htons(layer->hdr.udp->source);
+ }
+ }
+ if (layer->proto == LAYER_PROTO_IPV6) {
+ sockaddr->family = AF_INET6;
+ sockaddr->sockaddr.ipv6.sin6_family = AF_INET6;
+ memcpy(&sockaddr->sockaddr.ipv6.sin6_addr, &layer->hdr.ip6->ip6_src, sizeof(struct in6_addr));
+
+ layer = packet_get_layer_by_idx(pkt, packet_get_layer_count(pkt) - 2);
+ if (layer->proto == LAYER_PROTO_TCP) {
+ sockaddr->sockaddr.ipv6.sin6_port = htons(layer->hdr.tcp->source);
+ }
+ if (layer->proto == LAYER_PROTO_UDP) {
+ sockaddr->sockaddr.ipv6.sin6_port = htons(layer->hdr.udp->source);
+ }
+ }
+}
+
+static int mail_test_compare_packet_src(struct mail_test_sockaddr_in *sockaddr, const struct packet *pkt)
+{
+ struct mail_test_sockaddr_in pkt_src;
+ mail_test_store_packet_src(&pkt_src, pkt);
+ return 0 == memcmp(sockaddr, &pkt_src, sizeof(pkt_src));
+}
+
+void mail_test_exdata_free(struct mail_test_exdata *exdata)
+{
+ if (exdata) {
+ free(exdata);
+ }
+}
+
+struct mail_test_exdata * mail_test_exdata_new(void)
+{
+ struct mail_test_exdata *exdata;
+ exdata = (struct mail_test_exdata *)calloc(1, sizeof(struct mail_test_exdata));
+ return exdata;
+}
+
+void mail_test_command_callback(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ enum MAIL_COMMAND command,
+ char *cmd_param, size_t cmd_param_len,
+ char *cmd_line, size_t cmd_line_len,
+ void *arg)
+{
+ printf("mail command callback\n");
+ printf("%.*s\n", cmd_line_len, cmd_line);
+}
+
+void mail_test_header_callback(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ struct mail_header *header,
+ void *arg)
+{
+ printf("mail header callback\n");
+}
+
+void mail_test_body_callback(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ char *body, size_t body_len, size_t offset, int is_finish,
+ void *arg)
+{
+ printf("mail body callback\n");
+}
+
+void mail_test_attachment_callback(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol, size_t mail_seq,
+ char *name, size_t name_len,
+ char *attachment, size_t attachment_len, size_t offset, int is_finish,
+ void *arg)
+{
+ printf("mail attachment callback\n");
+}
+
+void mail_test_eml_callback(struct session *sess,
+ enum MAIL_PROTOCOL mail_protocol,
+ char *eml, size_t eml_len, size_t offset, int is_finish,
+ void *arg)
+{
+ struct mail_test_exdata *exdata;
+ struct mail_test_module_ctx *mod_ctx = (struct mail_test_module_ctx *)arg;
+
+ exdata = (struct mail_test_exdata *)session_get_exdata(sess, mod_ctx->exdata_id);
+ if (exdata == NULL) {
+ exdata = (struct mail_test_exdata *)calloc(1, sizeof(struct mail_test_exdata));
+ session_set_exdata(sess, mod_ctx->exdata_id, exdata);
+ }
+
+ printf("mail eml callback: %s\n", mail_protocol_name[mail_protocol]);
+
+}
+
+void mail_test_on_exdata_free(int idx, void *ex_ptr, void *arg)
+{
+ (void)(idx);
+ (void)(arg);
+ struct mail_test_exdata *exdata = (struct mail_test_exdata *)ex_ptr;
+
+ if (exdata) {
+ mail_test_exdata_free(exdata);
+ }
+}
+
+extern "C" void mail_test_exit(struct module_manager *mod_mgr, struct module *mod)
+{
+ (void)(mod_mgr);
+ struct mail_test_module_ctx *mod_ctx;
+
+ if (mod) {
+ mod_ctx = (struct mail_test_module_ctx *)module_get_ctx(mod);
+ if (mod_ctx) {
+ mail_test_result_compare(mod_ctx->result);
+ mail_test_result_exit(mod_ctx->result);
+ free(mod_ctx);
+ }
+ module_free(mod);
+ }
+}
+
+extern "C" struct module *mail_test_init(struct module_manager *mod_mgr)
+{
+ int ret;
+ struct module *mod;
+ struct mail_test_module_ctx *mod_ctx;
+ struct session_manager *sess_mgr;
+ struct mail_decoder *decoder;
+
+ mod_ctx = (struct mail_test_module_ctx *)calloc(1, sizeof(struct mail_test_module_ctx));
+ mod_ctx->mod_mgr_ref = mod_mgr;
+ mod = module_new(MAIL_TEST_MODULE_NAME, mod_ctx);
+ sess_mgr = module_to_session_manager(module_manager_get_module(mod_mgr, SESSION_MANAGER_MODULE_NAME));
+
+ if (mod_mgr == NULL || sess_mgr == NULL) {
+ goto exit;
+ }
+
+ mod_ctx->exdata_id = session_manager_new_session_exdata_index(sess_mgr, MAIL_TEST_EXDATA_NAME, mail_test_on_exdata_free, mod_ctx);
+ if (mod_ctx->exdata_id < 0) {
+ goto exit;
+ }
+
+ mod_ctx->result = mail_test_result_init(getenv(MAIL_TEST_RESULT_EXPECT_ENV));
+ if (mod_ctx->result == NULL) {
+ goto exit;
+ }
+
+ decoder = module_to_mail_decoder(module_manager_get_module(mod_mgr, MAIL_MODULE_NAME));
+ ret = mail_subscribe(decoder,
+ mail_test_command_callback,
+ mail_test_header_callback,
+ mail_test_body_callback,
+ mail_test_attachment_callback,
+ mail_test_eml_callback,
+ mod_ctx);
+ if (ret < 0) {
+ goto exit;
+ }
+
+ return mod;
+exit:
+ printf("mail_test module init failed!\n");
+ mail_test_exit(mod_mgr, mod);
+ return NULL;
+}
+
diff --git a/test/decoders/mail/pcap/01-imap-mail-account-in-command-with-base64.pcap b/test/decoders/mail/pcap/01-imap-mail-account-in-command-with-base64.pcap
new file mode 100644
index 0000000..d4344c7
--- /dev/null
+++ b/test/decoders/mail/pcap/01-imap-mail-account-in-command-with-base64.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/02-imap-starttls.pcap b/test/decoders/mail/pcap/02-imap-starttls.pcap
new file mode 100644
index 0000000..2fd3bf8
--- /dev/null
+++ b/test/decoders/mail/pcap/02-imap-starttls.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/03-smtp-s2c-mail.pcap b/test/decoders/mail/pcap/03-smtp-s2c-mail.pcap
new file mode 100644
index 0000000..3549c0d
--- /dev/null
+++ b/test/decoders/mail/pcap/03-smtp-s2c-mail.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/04-smtp-attachment-charset-unknown.pcap b/test/decoders/mail/pcap/04-smtp-attachment-charset-unknown.pcap
new file mode 100644
index 0000000..1cd8cfc
--- /dev/null
+++ b/test/decoders/mail/pcap/04-smtp-attachment-charset-unknown.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/05-smtp-curl-mails.pcap b/test/decoders/mail/pcap/05-smtp-curl-mails.pcap
new file mode 100644
index 0000000..b452fd7
--- /dev/null
+++ b/test/decoders/mail/pcap/05-smtp-curl-mails.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/06-smtp-starttls.pcap b/test/decoders/mail/pcap/06-smtp-starttls.pcap
new file mode 100644
index 0000000..c26ec62
--- /dev/null
+++ b/test/decoders/mail/pcap/06-smtp-starttls.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/07-smtp-auth-xoauth2.pcap b/test/decoders/mail/pcap/07-smtp-auth-xoauth2.pcap
new file mode 100644
index 0000000..b6e3bf8
--- /dev/null
+++ b/test/decoders/mail/pcap/07-smtp-auth-xoauth2.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/08-smtp-mime-header-rfc2047.pcap b/test/decoders/mail/pcap/08-smtp-mime-header-rfc2047.pcap
new file mode 100644
index 0000000..c7754e0
--- /dev/null
+++ b/test/decoders/mail/pcap/08-smtp-mime-header-rfc2047.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/09-pop3-c2s-55325-1100.pcap b/test/decoders/mail/pcap/09-pop3-c2s-55325-1100.pcap
new file mode 100644
index 0000000..3b54a68
--- /dev/null
+++ b/test/decoders/mail/pcap/09-pop3-c2s-55325-1100.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/10-pop3-s2c-1100-55325.pcap b/test/decoders/mail/pcap/10-pop3-s2c-1100-55325.pcap
new file mode 100644
index 0000000..f018536
--- /dev/null
+++ b/test/decoders/mail/pcap/10-pop3-s2c-1100-55325.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/11-pop3-c2s.pcap b/test/decoders/mail/pcap/11-pop3-c2s.pcap
new file mode 100644
index 0000000..dc1c679
--- /dev/null
+++ b/test/decoders/mail/pcap/11-pop3-c2s.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/12-pop3-s2c.pcap b/test/decoders/mail/pcap/12-pop3-s2c.pcap
new file mode 100644
index 0000000..810d885
--- /dev/null
+++ b/test/decoders/mail/pcap/12-pop3-s2c.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/13-pop3-mail.pcap b/test/decoders/mail/pcap/13-pop3-mail.pcap
new file mode 100644
index 0000000..6663699
--- /dev/null
+++ b/test/decoders/mail/pcap/13-pop3-mail.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/14-pop3-starttls.pcap b/test/decoders/mail/pcap/14-pop3-starttls.pcap
new file mode 100644
index 0000000..faae843
--- /dev/null
+++ b/test/decoders/mail/pcap/14-pop3-starttls.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/15-pop3-capa.pcap b/test/decoders/mail/pcap/15-pop3-capa.pcap
new file mode 100644
index 0000000..a2eabc5
--- /dev/null
+++ b/test/decoders/mail/pcap/15-pop3-capa.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/16-ftp-c2s-8569-8021.pcap b/test/decoders/mail/pcap/16-ftp-c2s-8569-8021.pcap
new file mode 100644
index 0000000..f6cbb48
--- /dev/null
+++ b/test/decoders/mail/pcap/16-ftp-c2s-8569-8021.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/17-ftp-c2s-8569-8080.pcap b/test/decoders/mail/pcap/17-ftp-c2s-8569-8080.pcap
new file mode 100644
index 0000000..75dc205
--- /dev/null
+++ b/test/decoders/mail/pcap/17-ftp-c2s-8569-8080.pcap
Binary files differ
diff --git a/test/decoders/mail/pcap/18-http-delete.pcap b/test/decoders/mail/pcap/18-http-delete.pcap
new file mode 100644
index 0000000..08f04bd
--- /dev/null
+++ b/test/decoders/mail/pcap/18-http-delete.pcap
Binary files differ
diff --git a/test/decoders/mail/result/01-imap-mail-account-in-command-with-base64.json b/test/decoders/mail/result/01-imap-mail-account-in-command-with-base64.json
new file mode 100644
index 0000000..4eea170
--- /dev/null
+++ b/test/decoders/mail/result/01-imap-mail-account-in-command-with-base64.json
@@ -0,0 +1,8 @@
+[
+ {
+ "MAIL_STARTTLS_CMD": "1",
+ "Mail_proto": "IMAP",
+ "Tuple4": "192.168.32.123.54643>159.226.251.13.143",
+ "name": "MAIL_RESULT_1"
+ }
+]