piler/src/parser_utils.c

1065 lines
28 KiB
C
Raw Normal View History

2011-11-14 15:57:52 +01:00
/*
2011-11-16 14:47:47 +01:00
* parser_utils.c
2011-11-14 15:57:52 +01:00
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
2011-11-14 15:57:52 +01:00
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <piler.h>
#include "trans.h"
2015-11-21 23:06:47 +01:00
void init_state(struct parser_state *state){
2011-11-14 15:57:52 +01:00
int i;
state->message_state = MSG_UNDEF;
2011-11-16 14:47:47 +01:00
state->line_num = 0;
2011-11-14 15:57:52 +01:00
2011-11-16 14:47:47 +01:00
state->is_header = 1;
2011-11-22 12:31:54 +01:00
state->is_1st_header = 1;
2011-11-14 15:57:52 +01:00
2011-11-16 14:47:47 +01:00
state->textplain = 1; /* by default we are a text/plain message */
2011-11-14 15:57:52 +01:00
state->texthtml = 0;
state->message_rfc822 = 0;
state->base64 = 0;
state->utf8 = 0;
state->qp = 0;
state->htmltag = 0;
state->style = 0;
state->skip_html = 0;
state->content_type_is_set = 0;
memset(state->message_id, 0, SMALLBUFSIZE);
2014-04-25 21:17:01 +02:00
memset(state->message_id_hash, 0, 2*DIGEST_LENGTH+1);
2011-11-16 14:47:47 +01:00
memset(state->miscbuf, 0, MAX_TOKEN_LEN);
2011-12-07 15:24:52 +01:00
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
2011-11-14 15:57:52 +01:00
2011-11-22 12:31:54 +01:00
memset(state->type, 0, TINYBUFSIZE);
memset(state->attachment_name_buf, 0, SMALLBUFSIZE);
state->anamepos = 0;
2011-11-14 15:57:52 +01:00
state->has_to_dump = 0;
state->has_to_dump_whole_body = 0;
2011-11-14 15:57:52 +01:00
state->fd = -1;
2012-09-07 15:08:50 +02:00
state->b64fd = -1;
2011-11-19 21:25:44 +01:00
state->mfd = -1;
2011-11-14 15:57:52 +01:00
state->realbinary = 0;
2011-11-16 14:47:47 +01:00
state->octetstream = 0;
2011-11-22 12:31:54 +01:00
state->pushed_pointer = 0;
2011-11-19 21:25:44 +01:00
state->saved_size = 0;
2011-11-14 15:57:52 +01:00
2012-08-21 21:57:39 +02:00
state->writebufpos = 0;
state->abufpos = 0;
inithash(state->boundaries);
inithash(state->rcpt);
inithash(state->rcpt_domain);
inithash(state->journal_recipient);
2011-11-14 15:57:52 +01:00
state->n_attachments = 0;
for(i=0; i<MAX_ATTACHMENTS; i++){
state->attachments[i].size = 0;
2012-09-07 15:08:50 +02:00
state->attachments[i].dumped = 0;
2011-11-16 14:47:47 +01:00
memset(state->attachments[i].type, 0, TINYBUFSIZE);
memset(state->attachments[i].shorttype, 0, TINYBUFSIZE);
2012-09-07 15:08:50 +02:00
memset(state->attachments[i].aname, 0, TINYBUFSIZE);
memset(state->attachments[i].filename, 0, SMALLBUFSIZE);
2011-11-19 21:25:44 +01:00
memset(state->attachments[i].internalname, 0, TINYBUFSIZE);
memset(state->attachments[i].digest, 0, 2*DIGEST_LENGTH+1);
2011-11-14 15:57:52 +01:00
}
2012-02-08 23:14:28 +01:00
memset(state->reference, 0, SMALLBUFSIZE);
2011-11-14 15:57:52 +01:00
memset(state->b_from, 0, SMALLBUFSIZE);
memset(state->b_from_domain, 0, SMALLBUFSIZE);
memset(state->b_sender, 0, SMALLBUFSIZE);
memset(state->b_sender_domain, 0, SMALLBUFSIZE);
memset(state->b_to, 0, MAXBUFSIZE);
memset(state->b_to_domain, 0, SMALLBUFSIZE);
2011-11-14 15:57:52 +01:00
memset(state->b_subject, 0, MAXBUFSIZE);
memset(state->b_body, 0, BIGBUFSIZE);
memset(state->b_journal_to, 0, MAXBUFSIZE);
2012-08-21 21:57:39 +02:00
state->tolen = 0;
state->todomainlen = 0;
2012-08-21 21:57:39 +02:00
state->bodylen = 0;
state->journaltolen = 0;
2013-07-31 10:06:05 +02:00
state->retention = 0;
state->found_security_header = 0;
2011-11-14 15:57:52 +01:00
}
long get_local_timezone_offset(){
time_t t = time(NULL);
struct tm lt = {0};
localtime_r(&t, &lt);
return lt.tm_gmtoff;
}
2016-04-05 21:10:09 +02:00
time_t parse_date_header(char *datestr){
int n=0;
long offset=0;
2015-11-02 15:14:52 +01:00
time_t ts=0;
2014-02-09 21:54:29 +01:00
char *p, *q, *r, *tz, s[SMALLBUFSIZE], tzh[4], tzm[3];
2011-11-16 14:47:47 +01:00
struct tm tm;
2011-11-14 15:57:52 +01:00
2012-01-07 00:00:36 +01:00
datestr += 5;
p = datestr;
2011-11-16 14:47:47 +01:00
2012-01-07 00:00:36 +01:00
for(; *datestr; datestr++){
2014-02-09 21:54:29 +01:00
if(isspace(*datestr) || *datestr == '.') *datestr = ' ';
}
datestr = p;
for(; *datestr != '\0' && datestr - p < 16; datestr++){
if(*datestr == '-') *datestr = ' ';
2012-01-07 00:00:36 +01:00
}
2014-02-09 21:54:29 +01:00
datestr = p;
2012-01-07 00:00:36 +01:00
if(*p == ' '){ p++; }
2014-02-09 21:54:29 +01:00
tm.tm_wday = 0;
tm.tm_mday = 0;
tm.tm_mon = -1;
tm.tm_year = 0;
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
tm.tm_isdst = -1;
2012-01-07 00:00:36 +01:00
do {
p = split_str(p, " ", s, sizeof(s)-1);
2014-02-09 21:54:29 +01:00
int len = strlen(s);
2014-02-09 21:54:29 +01:00
if(len > 0){
2012-01-07 00:00:36 +01:00
n++;
2014-02-03 15:38:10 +01:00
/*
* A proper Date: header should look like this:
*
* Date: Mon, 3 Feb 2014 13:21:07 +0100
*
*
* However some email applications provide crap, eg.
*
* Sat, 4 Aug 2007 13:36:52 GMT-0700
* Sat, 4 Aug 07 13:36:52 GMT-0700
* 16 Dec 07 20:45:52
* 03 Jun 06 05:59:00 +0100
* 30.06.2005 17:47:42
2014-02-09 21:54:29 +01:00
* 03-Feb-2014 08:09:10
2015-01-16 22:51:37 +01:00
* 13 Mar 2013 14:56:02 UT
2014-02-03 15:38:10 +01:00
*
* [wday] mday mon year h:m:s offset
*/
2014-02-09 21:54:29 +01:00
q = strchr(s, ','); if(q) *q='\0';
2014-05-23 14:27:02 +02:00
if(strncasecmp(s, "Jan", 3) == 0) tm.tm_mon = 0;
else if(strncasecmp(s, "Feb", 3) == 0) tm.tm_mon = 1;
else if(strncasecmp(s, "Mar", 3) == 0) tm.tm_mon = 2;
else if(strncasecmp(s, "Apr", 3) == 0) tm.tm_mon = 3;
else if(strncasecmp(s, "May", 3) == 0) tm.tm_mon = 4;
else if(strncasecmp(s, "Jun", 3) == 0) tm.tm_mon = 5;
else if(strncasecmp(s, "Jul", 3) == 0) tm.tm_mon = 6;
else if(strncasecmp(s, "Aug", 3) == 0) tm.tm_mon = 7;
else if(strncasecmp(s, "Sep", 3) == 0) tm.tm_mon = 8;
else if(strncasecmp(s, "Oct", 3) == 0) tm.tm_mon = 9;
else if(strncasecmp(s, "Nov", 3) == 0) tm.tm_mon = 10;
else if(strncasecmp(s, "Dec", 3) == 0) tm.tm_mon = 11;
if(strncasecmp(s, "Mon", 3) == 0) tm.tm_wday = 1;
else if(strncasecmp(s, "Tue", 3) == 0) tm.tm_wday = 2;
else if(strncasecmp(s, "Wed", 3) == 0) tm.tm_wday = 3;
else if(strncasecmp(s, "Thu", 3) == 0) tm.tm_wday = 4;
else if(strncasecmp(s, "Fri", 3) == 0) tm.tm_wday = 5;
else if(strncasecmp(s, "Sat", 3) == 0) tm.tm_wday = 6;
else if(strncasecmp(s, "Sun", 3) == 0) tm.tm_wday = 0;
2014-02-09 21:54:29 +01:00
2014-02-09 21:54:29 +01:00
if(len <= 2 && tm.tm_mday == 0){ tm.tm_mday = atoi(s); continue; }
if(len <= 2 && tm.tm_mon == -1){ tm.tm_mon = atoi(s) - 1; continue; }
if(len == 2){ if(atoi(s) >=90) tm.tm_year = atoi(s); else tm.tm_year = atoi(s) + 100; continue; }
2014-05-23 10:52:53 +02:00
if(len == 4 && atoi(s) > 1900){ tm.tm_year = atoi(s) - 1900; continue; }
2014-02-09 21:54:29 +01:00
if(len == 3){
if(strcmp(s, "EDT") == 0) offset = -14400;
else if(strcmp(s, "EST") == 0) offset = -18000;
else if(strcmp(s, "CDT") == 0) offset = -18000;
else if(strcmp(s, "CST") == 0) offset = -21600;
else if(strcmp(s, "MDT") == 0) offset = -21600;
else if(strcmp(s, "MST") == 0) offset = -25200;
else if(strcmp(s, "PDT") == 0) offset = -25200;
else if(strcmp(s, "PST") == 0) offset = -28800;
continue;
2014-02-03 15:38:10 +01:00
}
2014-02-09 21:54:29 +01:00
if((len == 5 && (*s == '+' || *s == '-')) || (len == 8 && (strncmp(s, "GMT+", 4) == 0 || strncmp(s, "GMT-", 4) == 0))){
offset = 0;
tz = strpbrk(s, "+-");
2015-12-28 14:50:37 +01:00
if(tz){
memset(tzh, 0, 4);
memset(tzm, 0, 3);
strncpy(tzh, tz, 3);
strncpy(tzm, tz+3, 2);
offset += atoi(tzh) * 3600;
offset += atoi(tzm) * 60;
}
2014-02-09 21:54:29 +01:00
continue;
2014-02-03 15:38:10 +01:00
}
2012-01-07 00:00:36 +01:00
2014-02-09 21:54:29 +01:00
if(len == 5 || len == 7 || len == 8){
2012-01-07 00:00:36 +01:00
r = &s[0];
2014-02-09 21:54:29 +01:00
q = strchr(r, ':'); if(!q) continue;
*q = '\0'; tm.tm_hour = atoi(r); r = q+1; *q = ':';
2012-01-07 00:00:36 +01:00
2014-02-09 21:54:29 +01:00
if(strlen(r) == 5) {
q = strchr(r, ':'); if(!q) continue;
*q = '\0'; tm.tm_min = atoi(r); r = q+1; *q = ':';
2014-02-03 15:38:10 +01:00
tm.tm_sec = atoi(r);
2014-02-09 21:54:29 +01:00
} else {
tm.tm_min = atoi(r);
2014-02-03 15:38:10 +01:00
}
continue;
2012-01-07 00:00:36 +01:00
}
}
} while(p);
2014-02-09 21:54:29 +01:00
if(tm.tm_mon == -1) tm.tm_mon = 0;
2011-11-16 14:47:47 +01:00
2014-02-09 21:54:29 +01:00
ts = mktime(&tm);
2014-02-09 21:54:29 +01:00
ts += get_local_timezone_offset() - offset;
2011-11-16 14:47:47 +01:00
return ts;
2011-11-14 15:57:52 +01:00
}
2015-11-21 23:06:47 +01:00
int extract_boundary(char *p, struct parser_state *state){
char *q;
2011-11-14 15:57:52 +01:00
p += strlen("boundary");
q = strchr(p, '"');
if(q) *q = ' ';
/*
* I've seen an idiot spammer using the following boundary definition in the header:
*
* Content-Type: multipart/alternative;
* boundary=3D"b1_52b92b01a943615aff28b7f4d2f2d69d"
*/
if(strncmp(p, "=3D", 3) == 0){
*(p+3) = '=';
p += 3;
}
p = strchr(p, '=');
if(p){
p++;
for(; *p; p++){
if(isspace(*p) == 0)
break;
}
2014-08-13 15:09:24 +02:00
char *q2 = strchr(p, ';');
2014-08-13 15:09:24 +02:00
if(q2) *q2 = '\0';
2011-11-14 15:57:52 +01:00
q = strrchr(p, '"');
if(q) *q = '\0';
q = strrchr(p, '\r');
if(q) *q = '\0';
q = strrchr(p, '\n');
if(q) *q = '\0';
addnode(state->boundaries, p);
2011-11-14 15:57:52 +01:00
2014-08-13 15:09:24 +02:00
if(q2) *q2 = ';';
2011-11-14 15:57:52 +01:00
return 1;
}
return 0;
}
2014-02-11 15:34:12 +01:00
void fixupEncodedHeaderLine(char *buf, int buflen){
char *q, *r, *s, *e, *end;
2015-11-21 23:06:47 +01:00
/*
* I thought SMALLBUFSIZE would be enough for v, encoding and tmpbuf(2*),
* but then I saw a 6-7000 byte long subject line, so I've switched to MAXBUFSIZE
*/
char v[MAXBUFSIZE], u[MAXBUFSIZE], puf[MAXBUFSIZE], encoding[MAXBUFSIZE], tmpbuf[2*MAXBUFSIZE];
int need_encoding, ret, prev_encoded=0, n_tokens=0;
2014-02-11 15:34:12 +01:00
if(buflen < 5) return;
2011-11-14 15:57:52 +01:00
2011-11-28 14:21:14 +01:00
memset(puf, 0, sizeof(puf));
2011-11-14 15:57:52 +01:00
q = buf;
do {
2011-11-28 14:21:14 +01:00
q = split_str(q, " ", v, sizeof(v)-1);
char *p = v;
2011-11-14 15:57:52 +01:00
do {
memset(u, 0, sizeof(u));
2011-11-14 15:57:52 +01:00
/*
* We can't use split_str(p, "=?", ...) it will fail with the following pattern
* =?UTF-8?B?SG9neWFuIMOtcmp1bmsgcGFuYXN6bGV2ZWxldD8=?=
*
* Also the below pattern requires special care:
* =?gb2312?B?<something>?==?gb2312?Q?<something else>?=
*
* And we have to check the following cases as well:
* Happy New Year! =?utf-8?q?=F0=9F=8E=86?=
*/
2011-11-14 15:57:52 +01:00
int b64=0, qp=0;
memset(encoding, 0, sizeof(encoding));
r = strstr(p, "=?");
if(r){
p = r + 2;
e = strchr(p, '?');
if(e){
*e = '\0';
snprintf(encoding, sizeof(encoding)-1, "%s", p);
*e = '?';
s = strcasestr(e, "?B?");
if(s){
b64 = 1;
p = s + 3;
}
else {
s = strcasestr(e, "?Q?");
if(s){
qp = 1;
p = s + 3;
}
}
}
end = strstr(p, "?=");
if(end){
*end = '\0';
}
snprintf(u, sizeof(u)-1, "%s", p);
2011-11-14 15:57:52 +01:00
if(end) {
p = end + 2;
}
}
else {
snprintf(u, sizeof(u)-1, "%s", p);
p = NULL;
}
2011-12-05 17:18:03 +01:00
if(u[0] == 0) continue;
2011-11-14 15:57:52 +01:00
n_tokens++;
if(b64 == 1) decodeBase64(u);
else if(qp == 1) decodeQP(u);
2011-11-14 15:57:52 +01:00
/*
* https://www.ietf.org/rfc/rfc2047.txt says that
*
* "When displaying a particular header field that contains multiple
* 'encoded-word's, any 'linear-white-space' that separates a pair of
* adjacent 'encoded-word's is ignored." (6.2)
*/
if(prev_encoded == 1 && (b64 == 1 || qp == 1)) {}
else if(n_tokens > 1){
strncat(puf, " ", sizeof(puf)-strlen(puf)-1);
}
2011-11-28 14:21:14 +01:00
if(b64 == 1 || qp == 1){
prev_encoded = 1;
need_encoding = 0;
ret = ERR;
if(encoding[0] && strcasecmp(encoding, "utf-8")){
need_encoding = 1;
ret = utf8_encode(u, strlen(u), &tmpbuf[0], sizeof(tmpbuf), encoding);
}
if(need_encoding == 1 && ret == OK){
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
strncat(puf, tmpbuf, sizeof(puf)-strlen(puf)-1);
2011-11-14 15:57:52 +01:00
}
else {
strncat(puf, u, sizeof(puf)-strlen(puf)-1);
2011-11-14 15:57:52 +01:00
}
}
2011-11-28 14:21:14 +01:00
else {
strncat(puf, u, sizeof(puf)-strlen(puf)-1);
2011-11-14 15:57:52 +01:00
}
2011-11-28 14:21:14 +01:00
} while(p);
2011-11-14 15:57:52 +01:00
} while(q);
2014-02-11 15:34:12 +01:00
snprintf(buf, buflen-1, "%s", puf);
2011-11-14 15:57:52 +01:00
}
2015-11-21 23:06:47 +01:00
void fixupSoftBreakInQuotedPritableLine(char *buf, struct parser_state *state){
2011-12-07 15:24:52 +01:00
int i=0;
if(strlen(state->qpbuf) > 0){
char puf[MAXBUFSIZE];
memset(puf, 0, sizeof(puf));
snprintf(puf, sizeof(puf)-1, "%s%s", state->qpbuf, buf);
snprintf(buf, MAXBUFSIZE-1, "%s", puf);
2011-12-07 15:24:52 +01:00
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
}
if(buf[strlen(buf)-1] == '='){
buf[strlen(buf)-1] = '\0';
i = 1;
}
if(i == 1){
char *p = strrchr(buf, ' ');
2011-12-07 15:24:52 +01:00
if(p){
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
if(strlen(p) < MAX_TOKEN_LEN-1){
memcpy(&(state->qpbuf[0]), p, MAX_TOKEN_LEN-1);
*p = '\0';
}
}
}
}
2015-11-21 23:06:47 +01:00
void fixupBase64EncodedLine(char *buf, struct parser_state *state){
2011-11-14 15:57:52 +01:00
if(strlen(state->miscbuf) > 0){
char puf[MAXBUFSIZE];
2011-11-16 14:47:47 +01:00
memset(puf, 0, sizeof(puf));
2016-01-02 08:16:38 +01:00
strncpy(puf, state->miscbuf, sizeof(puf)-strlen(puf)-1);
2015-12-28 14:50:37 +01:00
strncat(puf, buf, sizeof(puf)-strlen(puf)-1);
2011-11-14 15:57:52 +01:00
memset(buf, 0, MAXBUFSIZE);
memcpy(buf, puf, MAXBUFSIZE);
memset(state->miscbuf, 0, MAX_TOKEN_LEN);
}
if(buf[strlen(buf)-1] != '\n'){
char *p = strrchr(buf, ' ');
2011-11-14 15:57:52 +01:00
if(p){
2011-11-16 14:47:47 +01:00
memcpy(&(state->miscbuf[0]), p+1, MAX_TOKEN_LEN-1);
2011-11-14 15:57:52 +01:00
*p = '\0';
}
}
}
2015-11-21 23:06:47 +01:00
void markHTML(char *buf, struct parser_state *state){
2011-11-14 15:57:52 +01:00
char *s, puf[MAXBUFSIZE], html[SMALLBUFSIZE];
int k=0, j=0, pos=0;
memset(puf, 0, MAXBUFSIZE);
memset(html, 0, SMALLBUFSIZE);
s = buf;
for(; *s; s++){
if(*s == '<'){
state->htmltag = 1;
puf[k] = ' ';
k++;
memset(html, 0, SMALLBUFSIZE); j=0;
pos = 0;
//printf("start html:%c\n", *s);
}
if(state->htmltag == 1){
2011-11-14 15:57:52 +01:00
if(j == 0 && *s == '!'){
state->skip_html = 1;
//printf("skiphtml=1\n");
}
if(state->skip_html == 0){
2011-11-14 15:57:52 +01:00
if(*s != '>' && *s != '<' && *s != '"'){
//printf("j=%d/%c", j, *s);
html[j] = tolower(*s);
if(j < SMALLBUFSIZE-10) j++;
}
if(isspace(*s)){
if(j > 0){
setStateHTMLStyle(html, pos, state);
2011-11-14 15:57:52 +01:00
memset(html, 0, SMALLBUFSIZE); j=0;
}
pos++;
}
}
}
else {
if(state->style == 0){
puf[k] = *s;
k++;
}
}
if(*s == '>'){
state->htmltag = 0;
state->skip_html = 0;
//printf("skiphtml=0\n");
//printf("end html:%c\n", *s);
//strncat(html, " ", SMALLBUFSIZE-1);
if(j > 0){
strncat(html, " ", SMALLBUFSIZE-1);
setStateHTMLStyle(html, pos, state);
2011-11-14 15:57:52 +01:00
memset(html, 0, SMALLBUFSIZE); j=0;
}
}
}
//printf("append last in line:*%s*, html=+%s+, j=%d\n", puf, html, j);
if(j > 0){ setStateHTMLStyle(html, pos, state); }
2011-11-14 15:57:52 +01:00
strcpy(buf, puf);
}
void setStateHTMLStyle(char *htmlbuf, int pos, struct parser_state *state){
2011-11-14 15:57:52 +01:00
if(pos == 0 && strncmp(htmlbuf, "style ", 6) == 0) state->style = 1;
if(pos == 0 && strncmp(htmlbuf, "/style ", 7) == 0) state->style = 0;
}
2015-11-21 23:06:47 +01:00
void translateLine(unsigned char *p, struct parser_state *state){
2011-11-14 15:57:52 +01:00
int url=0;
2015-03-23 14:09:22 +01:00
int has_url=0;
unsigned char prev=' ';
2012-07-23 13:24:37 +02:00
if(strcasestr((char *)p, "http://") || strcasestr((char *)p, "https://")) has_url = 1;
2011-11-14 15:57:52 +01:00
for(; *p; p++){
if( (state->message_state == MSG_RECEIVED || state->message_state == MSG_FROM || state->message_state == MSG_SENDER || state->message_state == MSG_TO || state->message_state == MSG_CC || state->message_state == MSG_RECIPIENT) && *p == '@'){ continue; }
2011-11-14 15:57:52 +01:00
if(state->message_state == MSG_FROM || state->message_state == MSG_SENDER || state->message_state == MSG_TO || state->message_state == MSG_CC || state->message_state == MSG_RECIPIENT){
2015-03-23 14:09:22 +01:00
/* To fix some unusual addresses, eg.
* "'user@domain'" -> user@domain
* "''user@domain'" -> 'user@domain
* "''user'@domain'" -> 'user'@domain
* "'user'@domain'" -> user'@domain
*/
if(*p == '\'' && prev == '"') { *p = ' '; }
if(*p == '\'' && *(p+1) == '"'){ *p = ' '; }
if(*p == '_' || *p == '\'' || *p == '&' || *p == '+'){ continue; }
2015-03-23 14:09:22 +01:00
prev = *p;
}
2011-11-28 14:21:14 +01:00
if(state->message_state == MSG_SUBJECT && (*p == '%' || *p == '_' || *p == '&') ){ continue; }
2011-11-14 15:57:52 +01:00
if(state->message_state == MSG_CONTENT_TYPE && *p == '_' ){ continue; }
2011-11-16 14:47:47 +01:00
if(*p == '.' || *p == '-'){ continue; }
2011-11-14 15:57:52 +01:00
2012-07-23 13:24:37 +02:00
if(has_url == 1){
if(strncasecmp((char *)p, "http://", 7) == 0){ p += 7; url = 1; continue; }
if(strncasecmp((char *)p, "https://", 8) == 0){ p += 8; url = 1; continue; }
2011-11-14 15:57:52 +01:00
if(url == 1 && (*p == '.' || *p == '-' || *p == '_' || *p == '/' || *p == '%' || *p == '?' || isalnum(*p)) ) continue;
2012-07-23 13:24:37 +02:00
if(url == 1) url = 0;
}
2011-11-14 15:57:52 +01:00
2012-07-23 13:24:37 +02:00
if(delimiter_characters[(unsigned int)*p] != ' ') *p = ' ';
/* we MUSTN'T convert it to lowercase in the 'else' case, because it breaks utf-8 encoding! */
2011-11-14 15:57:52 +01:00
}
}
2011-12-05 17:18:03 +01:00
void fix_email_address_for_sphinx(char *s){
for(; *s; s++){
2012-01-28 20:52:13 +01:00
if(*s == '@' || *s == '.' || *s == '+' || *s == '-' || *s == '_') *s = 'X';
2011-12-05 17:18:03 +01:00
}
}
void split_email_address(char *s){
for(; *s; s++){
if(*s == '@' || *s == '.' || *s == '+' || *s == '-' || *s == '_') *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;
}
void add_recipient(char *email, unsigned int len, struct session_data *sdata, struct parser_state *state, struct data *data, struct config *cfg){
if(findnode(state->rcpt, email) == NULL){
char *q = strchr(email, '@');
/* skip any address matching ...@cfg->hostid, 2013.10.29, SJ */
if(q && strncmp(q+1, cfg->hostid, cfg->hostid_len) == 0){
return;
}
addnode(state->rcpt, email);
memcpy(&(state->b_to[state->tolen]), email, len);
state->tolen += len;
if(len >= MIN_EMAIL_ADDRESS_LEN && does_it_seem_like_an_email_address(email) == 1){
if(is_email_address_on_my_domains(email, data) == 1) sdata->internal_recipient = 1;
else sdata->external_recipient = 1;
if(q){
if(findnode(state->rcpt_domain, q+1) == NULL){
addnode(state->rcpt_domain, q+1);
unsigned int domainlen = strlen(q+1);
if(state->todomainlen < SMALLBUFSIZE-domainlen-1){
memcpy(&(state->b_to_domain[state->todomainlen]), q+1, domainlen);
state->todomainlen += domainlen;
}
}
}
if(state->tolen < MAXBUFSIZE-len-1){
split_email_address(email);
memcpy(&(state->b_to[state->tolen]), email, len);
state->tolen += len;
}
}
}
}
2011-11-14 15:57:52 +01:00
/*
* reassemble 'V i a g r a' to 'Viagra'
*/
void reassembleToken(char *p){
unsigned int i, k=0;
2011-11-14 15:57:52 +01:00
for(i=0; i<strlen(p); i++){
if(isspace(*(p+i-1)) && !isspace(*(p+i)) && isspace(*(p+i+1)) && !isspace(*(p+i+2)) && isspace(*(p+i+3)) && !isspace(*(p+i+4)) && isspace(*(p+i+5)) ){
p[k] = *(p+i); k++;
p[k] = *(p+i+2); k++;
p[k] = *(p+i+4); k++;
i += 5;
}
else {
p[k] = *(p+i);
k++;
}
}
p[k] = '\0';
}
void degenerateToken(unsigned char *p){
int i=1, d=0, dp=0;
unsigned char *s;
/* quit if the string does not end with a punctuation character */
2011-11-14 15:57:52 +01:00
if(!ispunct(*(p+strlen((char *)p)-1)))
return;
s = p;
for(; *p; p++){
if(ispunct(*p)){
d = i;
if(!ispunct(*(p-1)))
dp = d;
}
else
d = dp = i;
i++;
}
*(s+dp) = '\0';
if(*(s+dp-1) == '.' || *(s+dp-1) == '!' || *(s+dp-1) == '?') *(s+dp-1) = '\0';
}
2015-11-27 12:17:19 +01:00
void fixURL(char *buf, int buflen){
2012-07-23 13:24:37 +02:00
int len=0;
2011-11-16 14:47:47 +01:00
char *p, *q, fixed_url[SMALLBUFSIZE];
2011-11-14 15:57:52 +01:00
2015-11-27 12:17:19 +01:00
if(strlen(buf) < 3) return;
2012-07-23 13:24:37 +02:00
2011-11-16 14:47:47 +01:00
memset(fixed_url, 0, sizeof(fixed_url));
2011-11-14 15:57:52 +01:00
2015-11-27 12:17:19 +01:00
p = buf;
2011-11-14 15:57:52 +01:00
2015-11-27 12:17:19 +01:00
if(strncasecmp(buf, "http://", 7) == 0) p += 7;
if(strncasecmp(buf, "https://", 8) == 0) p += 8;
2011-11-14 15:57:52 +01:00
2011-11-16 14:47:47 +01:00
q = strchr(p, '/');
if(q) *q = '\0';
2011-11-14 15:57:52 +01:00
2011-12-07 15:24:52 +01:00
snprintf(fixed_url, sizeof(fixed_url)-1, "__URL__%s ", p);
2012-01-28 20:52:13 +01:00
fix_email_address_for_sphinx(fixed_url+7);
2011-11-14 15:57:52 +01:00
2012-07-23 13:24:37 +02:00
len = strlen(fixed_url);
if(len > 9 && fixed_url[len-2] == 'X'){
fixed_url[len-2] = ' ';
fixed_url[len-1] = '\0';
}
2016-01-02 08:16:38 +01:00
snprintf(buf, buflen, "%s", fixed_url);
2011-11-14 15:57:52 +01:00
}
void extractNameFromHeaderLine(char *s, char *name, char *resultbuf, int resultbuflen){
char buf[SMALLBUFSIZE], *p, *q;
2011-11-14 15:57:52 +01:00
2011-11-16 14:47:47 +01:00
snprintf(buf, sizeof(buf)-1, "%s", s);
2011-11-14 15:57:52 +01:00
memset(resultbuf, 0, resultbuflen);
2011-11-16 14:47:47 +01:00
p = strstr(buf, name);
2011-11-14 15:57:52 +01:00
if(p){
/*
*
* Some examples from http://tools.ietf.org/html/rfc5987:
*
* Non-extended notation, using "token":
*
* foo: bar; title=Economy
*
* Non-extended notation, using "quoted-string":
*
* foo: bar; title="US-$ rates"
*
* Extended notation, using the Unicode character U+00A3 (POUND SIGN):
*
* foo: bar; title*=iso-8859-1'en'%A3%20rates
*
* Extended notation, using the Unicode characters U+00A3 (POUND SIGN) and U+20AC (EURO SIGN):
*
* foo: bar; title*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates
*
* Odd one having two filename definitions, and having a semicolon (;) in the filename:
*
* filename*=utf-8''P;LAN%20Holden%204.docx;filename="P;LAN Holden 4.docx"*
*
*
*/
int extended=0;
2011-11-16 14:47:47 +01:00
p += strlen(name);
if(*p == '*'){
extended = 1;
}
2011-11-16 14:47:47 +01:00
p = strchr(p, '=');
if(p){
p++;
// skip any whitespace after name=, ie. name = "
while(*p==' ' || *p=='\t') p++;
// if there's a double quote after the equal symbol (=), ie. name*="utf-8....
if(*p == '"'){
p++;
q = strchr(p, '"');
if(q) *q = '\0';
2011-11-14 15:57:52 +01:00
}
else {
// no " after =, so split on ;
q = strchr(p, ';');
if(q) *q = '\0';
}
2014-02-11 15:34:12 +01:00
if(extended == 1){
char *encoding = p;
q = strchr(p, '\'');
if(q){
*q = '\0';
p = q + 1;
q = strchr(p, '\'');
if(q) p = q + 1;
}
2014-02-11 15:34:12 +01:00
decodeURL(p);
2014-02-11 15:34:12 +01:00
if(strlen(encoding) > 2 && strcasecmp(encoding, "utf-8"))
utf8_encode(p, strlen(p), resultbuf, resultbuflen-2, encoding);
else
snprintf(resultbuf, resultbuflen-2, "%s", p);
}
else {
char puf[SMALLBUFSIZE];
snprintf(puf, sizeof(puf)-1, "%s", p);
fixupEncodedHeaderLine(puf, sizeof(puf));
snprintf(resultbuf, resultbuflen-2, "%s", puf);
}
2014-02-11 15:34:12 +01:00
2011-11-14 15:57:52 +01:00
}
2011-11-16 14:47:47 +01:00
}
2011-11-14 15:57:52 +01:00
}
2011-12-30 15:52:59 +01:00
char *determine_attachment_type(char *filename, char *type){
if(strncasecmp(type, "text/", strlen("text/")) == 0) return "text,";
if(strncasecmp(type, "image/", strlen("image/")) == 0) return "image,";
if(strncasecmp(type, "audio/", strlen("audio/")) == 0) return "audio,";
if(strncasecmp(type, "video/", strlen("video/")) == 0) return "video,";
if(strncasecmp(type, "text/x-card", strlen("text/x-card")) == 0) return "vcard,";
if(strncasecmp(type, "application/pdf", strlen("application/pdf")) == 0) return "pdf,";
2013-09-11 09:19:29 +02:00
if(strncasecmp(type, "application/ms-tnef", strlen("application/ms-tnef")) == 0) return "tnef,";
2011-12-30 15:52:59 +01:00
if(strncasecmp(type, "application/msword", strlen("application/msword")) == 0) return "word,";
// a .csv file has the same type
2011-12-30 15:52:59 +01:00
if(strncasecmp(type, "application/vnd.ms-excel", strlen("application/vnd.ms-excel")) == 0) return "excel,";
2011-12-30 15:52:59 +01:00
if(strncasecmp(type, "application/vnd.ms-powerpoint", strlen("application/vnd.ms-powerpoint")) == 0) return "powerpoint,";
if(strncasecmp(type, "application/vnd.visio", strlen("application/vnd.visio")) == 0) return "visio,";
if(strncasecmp(type, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", strlen("application/vnd.openxmlformats-officedocument.wordprocessingml.document")) == 0) return "word,";
if(strncasecmp(type, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", strlen("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) == 0) return "excel,";
if(strncasecmp(type, "application/vnd.openxmlformats-officedocument.presentationml.presentation", strlen("application/vnd.openxmlformats-officedocument.presentationml.presentation")) == 0) return "powerpoint,";
2011-12-30 15:52:59 +01:00
if(strncasecmp(type, "application/x-shockwave-flash", strlen("application/x-shockwave-flash")) == 0) return "flash,";
if(strcasestr(type, "opendocument")) return "odf,";
if(strncasecmp(type, "application/", 12) == 0){
char *p = strrchr(filename, '.');
2011-12-30 15:52:59 +01:00
if(p){
p++;
if(strncasecmp(p, "pdf", 3) == 0) return "pdf,";
2011-12-30 15:52:59 +01:00
if(strncasecmp(p, "zip", 3) == 0) return "compressed,";
if(strncasecmp(p, "rar", 3) == 0) return "compressed,";
// tar.gz has the same type
if(strncasecmp(p, "gz", 2) == 0) return "compressed,";
2012-09-14 15:03:00 +02:00
if(strncasecmp(p, "rtf", 3) == 0) return "word,";
2011-12-30 15:52:59 +01:00
if(strncasecmp(p, "doc", 3) == 0) return "word,";
if(strncasecmp(p, "docx", 4) == 0) return "word,";
if(strncasecmp(p, "xls", 3) == 0) return "excel,";
if(strncasecmp(p, "xlsx", 4) == 0) return "excel,";
if(strncasecmp(p, "ppt", 3) == 0) return "powerpoint,";
if(strncasecmp(p, "pptx", 4) == 0) return "powerpoint,";
if(strncasecmp(p, "png", 3) == 0) return "image,";
if(strncasecmp(p, "gif", 3) == 0) return "image,";
if(strncasecmp(p, "jpg", 3) == 0) return "image,";
if(strncasecmp(p, "jpeg", 4) == 0) return "image,";
if(strncasecmp(p, "tiff", 4) == 0) return "image,";
}
2011-12-30 15:52:59 +01:00
}
return "other,";
}
2011-11-14 15:57:52 +01:00
2012-02-08 23:14:28 +01:00
char *get_attachment_extractor_by_filename(char *filename){
char *p;
2013-09-11 09:19:29 +02:00
if(strcasecmp(filename, "winmail.dat") == 0) return "tnef";
p = strrchr(filename, '.');
if(!p) return "other";
if(strcasecmp(p, ".pdf") == 0) return "pdf";
if(strcasecmp(p, ".zip") == 0) return "zip";
if(strcasecmp(p, ".gz") == 0) return "gzip";
if(strcasecmp(p, ".rar") == 0) return "rar";
if(strcasecmp(p, ".odt") == 0) return "odf";
if(strcasecmp(p, ".odp") == 0) return "odf";
if(strcasecmp(p, ".ods") == 0) return "odf";
if(strcasecmp(p, ".doc") == 0) return "doc";
if(strcasecmp(p, ".docx") == 0) return "docx";
if(strcasecmp(p, ".xls") == 0) return "xls";
if(strcasecmp(p, ".xlsx") == 0) return "xlsx";
if(strcasecmp(p, ".ppt") == 0) return "ppt";
if(strcasecmp(p, ".pptx") == 0) return "pptx";
2012-09-14 15:03:00 +02:00
if(strcasecmp(p, ".rtf") == 0) return "rtf";
if(strcasecmp(p, ".txt") == 0) return "text";
if(strcasecmp(p, ".csv") == 0) return "text";
return "other";
}
2015-11-21 23:06:47 +01:00
void parse_reference(struct parser_state *state, char *s){
2012-02-08 23:14:28 +01:00
char puf[SMALLBUFSIZE];
if(strlen(state->reference) > 10) return;
do {
s = split_str(s, " ", puf, sizeof(puf)-1);
int len = strlen(puf);
2012-02-08 23:14:28 +01:00
if(len > 10 && len < SMALLBUFSIZE-1){
memcpy(&(state->reference[strlen(state->reference)]), puf, len);
return;
}
} while(s);
}
2012-09-07 15:08:50 +02:00
2016-04-05 21:10:09 +02:00
int base64_decode_attachment_buffer(char *p, unsigned char *b, int blen){
2012-09-07 15:08:50 +02:00
int b64len=0;
char puf[2*SMALLBUFSIZE];
do {
p = split_str(p, "\n", puf, sizeof(puf)-1);
trimBuffer(puf);
b64len += decode_base64_to_buffer(puf, strlen(puf), b+b64len, blen);
} while(p);
return b64len;
}
void fix_plus_sign_in_email_address(char *puf, char **at_sign, unsigned int *len){
char *r;
r = strchr(puf, '+');
if(r){
int n = strlen(*at_sign);
memmove(r, *at_sign, n);
*(r+n) = '\0';
*len = strlen(puf);
*at_sign = r;
}
}
void fill_attachment_name_buf(struct parser_state *state, char *buf){
char *p = &buf[0];
for(; *p; p++){
if(*p != ' ' && *p != '\t') break;
}
int len = strlen(p);
if(len + state->anamepos < SMALLBUFSIZE-3){
memcpy(&(state->attachment_name_buf[state->anamepos]), p, len);
state->anamepos += len;
// add a trailing separator semicolon to make sure there's separation
// with the next item
state->attachment_name_buf[state->anamepos] = ';';
state->anamepos++;
}
}
int get_first_email_address_from_string(char *str, char *buf, int buflen){
int result;
char *p = str;
do {
memset(buf, 0, buflen);
p = split(p, ' ', buf, buflen-1, &result);
if(*buf == '\0') continue;
if(does_it_seem_like_an_email_address(buf) == 1){ return 1; }
} while(p);
return 0;
}