diff --git a/src/digest.c b/src/digest.c index 60030484..a284236c 100644 --- a/src/digest.c +++ b/src/digest.c @@ -113,3 +113,21 @@ void digest_file(char *filename, char *digest){ } +void digest_string(char *s, char *digest){ + int i; + unsigned char md[DIGEST_LENGTH]; + SHA256_CTX context; + + memset(digest, 0, 2*DIGEST_LENGTH+1); + + SHA256_Init(&context); + + SHA256_Update(&context, s, strlen(s)); + + SHA256_Final(md, &context); + + for(i=0;imysql)); if(!stmt){ @@ -204,7 +204,7 @@ int store_recipients(struct session_data *sdata, char *to, uint64 id, struct __c return ERR; } - snprintf(s, sizeof(s)-1, "INSERT INTO %s (`id`,`to`) VALUES('%llu',?)", SQL_RECIPIENT_TABLE, id); + snprintf(s, sizeof(s)-1, "INSERT INTO %s (`id`,`to`,`todomain`) VALUES('%llu',?,?)", SQL_RECIPIENT_TABLE, id); if(mysql_stmt_prepare(stmt, s, strlen(s))){ syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_prepare() error: %s", sdata->ttmpfile, SQL_RECIPIENT_TABLE, mysql_stmt_error(stmt)); @@ -215,7 +215,10 @@ int store_recipients(struct session_data *sdata, char *to, uint64 id, struct __c do { p = split_str(p, " ", puf, sizeof(puf)-1); - if(strlen(puf) > 5){ + q = strchr(puf, '@'); + + if(q && strlen(q) > 3 && does_it_seem_like_an_email_address(puf) == 1){ + q++; memset(bind, 0, sizeof(bind)); @@ -224,6 +227,11 @@ int store_recipients(struct session_data *sdata, char *to, uint64 id, struct __c bind[0].is_null = 0; len[0] = strlen(puf); bind[0].length = &len[0]; + bind[1].buffer_type = MYSQL_TYPE_STRING; + bind[1].buffer = q; + bind[1].is_null = 0; + len[1] = strlen(q); bind[1].length = &len[1]; + if(mysql_stmt_bind_param(stmt, bind)){ syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_bind_param() error: %s", sdata->ttmpfile, SQL_RECIPIENT_TABLE, mysql_stmt_error(stmt)); return ERR; @@ -237,6 +245,9 @@ int store_recipients(struct session_data *sdata, char *to, uint64 id, struct __c ret = ERR; } + } else { + syslog(LOG_PRIORITY, "%s: invalid email address: %s", sdata->ttmpfile, puf); + continue; } } while(p); @@ -245,9 +256,42 @@ int store_recipients(struct session_data *sdata, char *to, uint64 id, struct __c } +int update_verification_code(struct session_data *sdata, struct _state *state, uint64 id, struct __config *cfg){ + char s[SMALLBUFSIZE], vcode[MAXBUFSIZE], digest[2*DIGEST_LENGTH+1]; + MYSQL_RES *res; + MYSQL_ROW row; + + snprintf(vcode, sizeof(vcode)-1, "%llu+%s%s%ld%ld%d%d%d%d%s%s%s+", id, state->b_from, state->message_id, sdata->now, sdata->sent, sdata->tot_len, sdata->hdr_len, sdata->direction, state->n_attachments, sdata->ttmpfile, sdata->digest, sdata->bodydigest); + + if(id > 2){ + snprintf(s, SMALLBUFSIZE-1, "SELECT `vcode` FROM `%s` WHERE `id`=%llu OR `id`=%llu ORDER BY `id` ASC", SQL_METADATA_TABLE, id-1, id-2); + + if(mysql_real_query(&(sdata->mysql), s, strlen(s)) == 0){ + res = mysql_store_result(&(sdata->mysql)); + if(res != NULL){ + while((row = mysql_fetch_row(res))){ + if(row){ + if((char*)row[0]) strncat(vcode, (char*)row[0], sizeof(vcode)-1); + } + } + mysql_free_result(res); + } + } + } + + digest_string(vcode, &digest[0]); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: verification data: %s, digest: %s", sdata->ttmpfile, vcode, digest); + + snprintf(s, SMALLBUFSIZE-1, "UPDATE %s SET `vcode`='%s' WHERE id=%llu", SQL_METADATA_TABLE, digest, id); + + return mysql_real_query(&(sdata->mysql), s, strlen(s)); +} + + int store_meta_data(struct session_data *sdata, struct _state *state, struct __config *cfg){ int rc, ret=ERR; - char *subj, s[MAXBUFSIZE]; + char *subj, *p, s[MAXBUFSIZE]; MYSQL_STMT *stmt; MYSQL_BIND bind[4]; @@ -265,7 +309,7 @@ int store_meta_data(struct session_data *sdata, struct _state *state, struct __c subj = state->b_subject; if(*subj == ' ') subj++; - snprintf(s, MAXBUFSIZE-1, "INSERT INTO %s (`from`,`subject`,`arrived`,`sent`,`size`,`hlen`,`direction`,`attachments`,`piler_id`,`message_id`,`digest`,`bodydigest`) VALUES(?,?,%ld,%ld,%d,%d,%d,%d,'%s',?,'%s','%s')", SQL_METADATA_TABLE, sdata->now, sdata->sent, sdata->tot_len, sdata->hdr_len, sdata->direction, state->n_attachments, sdata->ttmpfile, sdata->digest, sdata->bodydigest); + snprintf(s, MAXBUFSIZE-1, "INSERT INTO %s (`from`,`fromdomain`,`subject`,`arrived`,`sent`,`size`,`hlen`,`direction`,`attachments`,`piler_id`,`message_id`,`digest`,`bodydigest`) VALUES(?,?,?,%ld,%ld,%d,%d,%d,%d,'%s',?,'%s','%s')", SQL_METADATA_TABLE, sdata->now, sdata->sent, sdata->tot_len, sdata->hdr_len, sdata->direction, state->n_attachments, sdata->ttmpfile, sdata->digest, sdata->bodydigest); if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: meta sql: *%s*", sdata->ttmpfile, s); @@ -277,7 +321,7 @@ int store_meta_data(struct session_data *sdata, struct _state *state, struct __c if(strlen(state->b_to) < 5){ - snprintf(state->b_to, SMALLBUFSIZE-1, "undisclosed-recipients"); + snprintf(state->b_to, SMALLBUFSIZE-1, "undisclosed-recipients@no.domain"); } memset(bind, 0, sizeof(bind)); @@ -287,15 +331,24 @@ int store_meta_data(struct session_data *sdata, struct _state *state, struct __c bind[0].is_null = 0; len[0] = strlen(state->b_from); bind[0].length = &len[0]; - bind[1].buffer_type = MYSQL_TYPE_STRING; - bind[1].buffer = subj; - bind[1].is_null = 0; - len[1] = strlen(subj); bind[1].length = &len[1]; + p = strchr(state->b_from, '@'); + if(p && strlen(p) > 3){ + p++; + bind[1].buffer_type = MYSQL_TYPE_STRING; + bind[1].buffer = p; + bind[1].is_null = 0; + len[1] = strlen(p); bind[1].length = &len[1]; + } bind[2].buffer_type = MYSQL_TYPE_STRING; - bind[2].buffer = state->message_id; + bind[2].buffer = subj; bind[2].is_null = 0; - len[2] = strlen(state->message_id); bind[2].length = &len[2]; + len[2] = strlen(subj); bind[2].length = &len[2]; + + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = state->message_id; + bind[3].is_null = 0; + len[3] = strlen(state->message_id); bind[3].length = &len[3]; if(mysql_stmt_bind_param(stmt, bind)){ syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_bind_param() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt)); @@ -311,11 +364,17 @@ int store_meta_data(struct session_data *sdata, struct _state *state, struct __c } else { id = mysql_stmt_insert_id(stmt); + rc = store_recipients(sdata, state->b_to, id, cfg); if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: stored recipients, rc=%d", sdata->ttmpfile, rc); if(rc == OK){ + + rc = update_verification_code(sdata, state, id, cfg); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: updated verification code, rc=%d", sdata->ttmpfile, rc); + rc = store_index_data(sdata, state, id, cfg); if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: stored indexdata, rc=%d", sdata->ttmpfile, rc); diff --git a/src/parser.c b/src/parser.c index 121ca11e..e3895aaf 100644 --- a/src/parser.c +++ b/src/parser.c @@ -398,12 +398,12 @@ int parse_line(char *buf, struct _state *state, struct session_data *sdata, stru len = strlen(puf); - if(state->message_state == MSG_FROM && strchr(puf, '@') && strlen(puf) > 5 && state->is_1st_header == 1 && state->b_from[0] == '\0' && strlen(state->b_from) < SMALLBUFSIZE-len-1){ + if(state->message_state == MSG_FROM && does_it_seem_like_an_email_address(puf) == 1 && state->is_1st_header == 1 && state->b_from[0] == '\0' && strlen(state->b_from) < SMALLBUFSIZE-len-1){ memcpy(&(state->b_from[strlen(state->b_from)]), puf, len); if(is_email_address_on_my_domains(puf, cfg) == 1) sdata->internal_sender = 1; } - else if((state->message_state == MSG_TO || state->message_state == MSG_CC) && state->is_1st_header == 1 && strchr(puf, '@') && strlen(puf) > 5 && strlen(state->b_to) < SMALLBUFSIZE-len-1){ + else if((state->message_state == MSG_TO || state->message_state == MSG_CC) && state->is_1st_header == 1 && does_it_seem_like_an_email_address(puf) == 1 && strlen(state->b_to) < SMALLBUFSIZE-len-1){ if(is_string_on_list(state->rcpt, puf) == 0){ append_list(&(state->rcpt), puf); diff --git a/src/parser.h b/src/parser.h index ba8b4295..90999c5c 100644 --- a/src/parser.h +++ b/src/parser.h @@ -24,6 +24,7 @@ void markHTML(char *buf, struct _state *state); int appendHTMLTag(char *buf, char *htmlbuf, int pos, struct _state *state); void translateLine(unsigned char *p, struct _state *state); void fix_email_address_for_sphinx(char *s); +int does_it_seem_like_an_email_address(char *email); void reassembleToken(char *p); void degenerateToken(unsigned char *p); void fixURL(char *url); diff --git a/src/parser_utils.c b/src/parser_utils.c index d9e07804..16f25658 100644 --- a/src/parser_utils.c +++ b/src/parser_utils.c @@ -498,6 +498,24 @@ void fix_email_address_for_sphinx(char *s){ } +int does_it_seem_like_an_email_address(char *email){ + char *p; + + if(email == NULL) return 0; + + if(strlen(email) < 5) return 0; + + p = strchr(email, '@'); + if(!p) return 0; + + if(strlen(p+1) < 3) return 0; + + if(!strchr(p+1, '.')) return 0; + + return 1; +} + + /* * reassemble 'V i a g r a' to 'Viagra' */ diff --git a/src/piler.h b/src/piler.h index 8b6ed555..c573b39d 100644 --- a/src/piler.h +++ b/src/piler.h @@ -31,6 +31,7 @@ int do_av_check(struct session_data *sdata, char *rcpttoemail, char *fromemail, int make_digests(struct session_data *sdata, struct __config *cfg); void digest_file(char *filename, char *digest); +void digest_string(char *s, char *digest); int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg); diff --git a/util/db-mysql.sql b/util/db-mysql.sql index 63a650bb..0c853560 100644 --- a/util/db-mysql.sql +++ b/util/db-mysql.sql @@ -31,6 +31,7 @@ drop table if exists `metadata`; create table if not exists `metadata` ( `id` bigint unsigned not null auto_increment, `from` char(255) not null, + `fromdomain` char(48) not null, `subject` text(512) default null, `arrived` int not null, `sent` int not null, @@ -43,6 +44,7 @@ create table if not exists `metadata` ( `message_id` char(128) character set 'latin1' not null, `digest` char(64) not null, `bodydigest` char(64) not null, + `vcode` char(64) default null, primary key (`id`), unique(`message_id`) ) Engine=InnoDB; @@ -56,6 +58,7 @@ drop table if exists `rcpt`; create table if not exists `rcpt` ( `id` bigint unsigned not null, `to` char(64) not null, + `todomain` char(48) not null, unique(`id`,`to`) ) Engine=InnoDB; @@ -64,7 +67,7 @@ create index `rcpt_idx2` on `rcpt`(`to`); drop view if exists `messages`; -create view `messages` AS select `metadata`.`id` AS `id`,`metadata`.`piler_id` AS `piler_id`,`metadata`.`from` AS `from`,`rcpt`.`to` AS `to`,`metadata`.`subject` AS `subject`, `metadata`.`size` AS `size`, `metadata`.`direction` AS `direction`, `metadata`.`sent` AS `sent`, `metadata`.`digest` AS `digest`, `metadata`.`bodydigest` AS `bodydigest` from (`metadata` join `rcpt`) where (`metadata`.`id` = `rcpt`.`id`); +create view `messages` AS select `metadata`.`id` AS `id`,`metadata`.`piler_id` AS `piler_id`,`metadata`.`from` AS `from`,`metadata`.`fromdomain` AS `fromdomain`,`rcpt`.`to` AS `to`,`rcpt`.`todomain` AS `todomain`,`metadata`.`subject` AS `subject`, `metadata`.`size` AS `size`, `metadata`.`direction` AS `direction`, `metadata`.`sent` AS `sent`, `metadata`.`digest` AS `digest`, `metadata`.`bodydigest` AS `bodydigest` from (`metadata` join `rcpt`) where (`metadata`.`id` = `rcpt`.`id`); drop table if exists `attachment`;