2
* Get mail using the pop3 protocol.
3
* Format the mail in ascii, or in html.
5
* Copyright (c) Karl Dahlke, 2006
6
* This file is part of the edbrowse project, released under GPL.
11
#define MHLINE 120 /* length of a mail header line */
15
struct MHINFO *next, *prev;
16
struct listHead components;
23
char boundary[MHLINE];
25
char cfn[MHLINE]; /* content file name */
26
uchar ct, ce; /* content type, content encoding */
31
bool ne; /* non english */
34
static int nattach; /* number of attachments */
35
static int nimages; /* number of attached images */
36
static char *firstAttach; /* name of first file */
37
static bool mailIsHtml, mailIsSpam, mailIsBlack;
38
static char *fm; /* formatted mail string */
40
static struct MHINFO *lastMailInfo;
41
static char *lastMailText;
42
#define MAXIPBLACK 3000
43
static IP32bit ipblacklist[MAXIPBLACK];
44
static IP32bit ipblackmask[MAXIPBLACK];
45
static bool ipblackcomp[MAXIPBLACK];
56
if(!fileIntoMemory(ipbFile, &buf, &buflen))
65
while(isspaceByte(*s))
71
char *q = strpbrk(s, "/!");
73
dotstop = *q, *q++ = 0;
87
if(bits > 0 && bits < 32) {
88
static const IP32bit masklist[] = {
89
0xffffffff, 0xfeffffff, 0xfcffffff, 0xf8ffffff,
90
0xf0ffffff, 0xe0ffffff, 0xc0ffffff, 0x80ffffff,
91
0x00ffffff, 0x00feffff, 0x00fcffff, 0x00f8ffff,
92
0x00f0ffff, 0x00e0ffff, 0x00c0ffff, 0x0080ffff,
93
0x0000ffff, 0x0000feff, 0x0000fcff, 0x0000f8ff,
94
0x0000f0ff, 0x0000e0ff, 0x0000c0ff, 0x000080ff,
95
0x000000ff, 0x000000fe, 0x000000fc, 0x000000f8,
96
0x000000f0, 0x000000e0, 0x000000c0, 0x00000080,
99
ipmask = masklist[32 - bits];
104
if(nipblack == MAXIPBLACK)
105
i_printfExit(MSG_ManyIP, MAXIPBLACK);
106
ipblacklist[nipblack] = ip;
107
ipblackmask[nipblack] = ipmask;
108
ipblackcomp[nipblack] = (dotstop == '!');
115
debugPrint(3, "%d ip addresses in blacklist", nipblack);
116
} /* loadBlacklist */
119
onBlacklist1(IP32bit tip)
121
IP32bit blip; /* black ip */
124
for(j = 0; j < nipblack; ++j) {
125
bool comp = ipblackcomp[j];
126
blip = ipblacklist[j];
127
mask = ipblackmask[j];
128
if((tip ^ blip) & mask)
132
debugPrint(3, "blocked by rule %d", j + 1);
141
IP32bit *ipp = cw->iplist;
142
IP32bit tip; /* test ip */
145
while((tip = *ipp++) != NULL_IP)
146
if(onBlacklist1(tip))
152
freeMailInfo(struct MHINFO *w)
155
while(!listIsEmpty(&w->components)) {
156
v = w->components.next;
164
getFileName(const char *defname, bool isnew)
166
static char buf[ABSPATH];
170
i_printf(MSG_FileName);
172
printf("[%s] ", defname);
173
if(!fgets(buf, sizeof (buf), stdin))
175
for(p = buf; isspaceByte(*p); ++p) ;
177
while(l && isspaceByte(p[l - 1]))
183
/* make a copy just to be safe */
184
strcpy(buf, defname);
188
if(isnew && fileTypeByName(p, false)) {
189
i_printf(MSG_FileExists, p);
197
static bool ignoreImages;
200
writeAttachment(struct MHINFO *w)
203
if((ismc | ignoreImages) && w->atimage)
204
return; /* image ignored */
205
if(w->error64 == BAD64)
206
i_printf(MSG_Abbreviated);
207
if(w->start == w->end) {
208
i_printf(MSG_AttEmpty);
210
printf(" %s", w->cfn);
215
atname = getFileName((w->cfn[0] ? w->cfn : 0), true);
216
/* X is like x, but deletes all future images */
217
if(stringEqual(atname, "X")) {
222
if(!ismc && stringEqual(atname, "e")) {
223
int cx, svcx = context;
224
for(cx = 1; cx < MAXSESSION; ++cx)
225
if(!sessionList[cx].lw)
227
if(cx == MAXSESSION) {
228
i_printf(MSG_AttNoBuffer);
231
i_printf(MSG_SessionX, cx);
232
if(!addTextToBuffer((pst) w->start, w->end - w->start, 0))
233
i_printf(MSG_AttNoCopy, cx);
235
cw->fileName = cloneString(w->cfn);
236
cxSwitch(svcx, false); /* back to where we were */
238
} else if(!stringEqual(atname, "x")) {
239
int fh = open(atname, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666);
241
i_printf(MSG_AttNoSave, atname);
245
int nb = w->end - w->start;
246
if(write(fh, w->start, nb) < nb) {
247
i_printf(MSG_AttNoWrite, atname);
254
} /* writeAttachment */
257
writeAttachments(struct MHINFO *w)
263
foreach(v, w->components)
266
} /* writeAttachments */
269
serverPutGet(const char *line)
271
if(!serverPutLine(line))
278
fetchMail(int account)
280
const struct MACCOUNT *a = accounts + account - 1;
281
const char *login = a->login;
282
const char *pass = a->password;
283
const char *host = a->inurl;
288
i_printfExit(MSG_FetchNotBackgnd);
292
i_printfExit(MSG_NoMailDir);
294
i_printfExit(MSG_NoDirChange, mailDir);
296
if(!loadAddressBook())
299
if(!mailConnect(host, a->inport))
303
if(memcmp(serverLine, "+OK ", 4))
304
i_printfExit(MSG_BadPopIntro, serverLine);
305
sprintf(serverLine, "user %s%s", login, eol);
306
serverPutGet(serverLine);
307
if(pass) { /* I think this is always required */
308
sprintf(serverLine, "pass %s%s", pass, eol);
309
serverPutGet(serverLine);
311
if(memcmp(serverLine, "+OK", 3))
312
i_printfExit(MSG_PopNotComplete, serverLine);
314
/* How many mail messages? */
315
serverPutGet("stat\r\n");
316
if(memcmp(serverLine, "+OK ", 4))
317
i_printfExit(MSG_NoStatusMailBox, serverLine);
318
nmsgs = atoi(serverLine + 4);
325
if(!(zapMail | passMail)) {
326
/* We look up a lot of hosts; make a tcp connect */
328
/* the conect will drop when the program exits */
331
i_printf(MSG_MessagesX, nmsgs);
332
if(zapMail && nmsgs > 300)
335
for(m = 1; m <= nmsgs; ++m) {
336
const char *redirect = 0; /* send mail elsewhere */
338
const char *atname; /* name of attachment */
339
bool delflag = false; /* delete this mail */
341
char *exact = initString(&exact_l); /* the exact message */
346
else { /* read the next message */
349
/* We need to clear out the editor, from the last message,
350
* then read in this one, in its entirety.
351
* This is probably a waste, since the user might delete a megabyte message
352
* after seeing only the first paragraph, or even the subject,
353
* but as for programming, it's easier to read the whole thing in right now. */
355
freeMailInfo(lastMailInfo);
357
nzFree(lastMailText);
359
if(sessionList[1].lw)
364
/* Now grab the entire message */
365
sprintf(serverLine, "retr %d%s", m, eol);
366
if(!serverPutLine(serverLine))
370
int nr = tcp_read(mssock, retrbuf, sizeof (retrbuf));
372
i_printfExit(MSG_ErrorReadMess, errno);
374
/* add null, to make it easy to print the error message, if necessary */
375
if(nr < sizeof (retrbuf))
377
if(memcmp(retrbuf, "+OK", 3))
378
i_printfExit(MSG_ErrorFetchMess, m, retrbuf);
380
while(retrbuf[j] != '\n')
384
memcpy(retrbuf, retrbuf + j, nr);
388
stringAndBytes(&exact, &exact_l, retrbuf, nr);
389
/* . by itself on a line ends the transmission */
396
if(j >= 0 && exact[j] == '\r')
404
if(exact[j - 1] == '\n')
409
/* get rid of the dos returns */
410
for(j = k = 0; j < exact_l; ++j) {
411
if(exact[j] == '\r' && j < exact_l - 1 && exact[j + 1] == '\n')
413
exact[k++] = exact[j];
418
if(!addTextToBuffer((pst) exact, exact_l, 0))
421
browseCurrentBuffer();
423
mailIsBlack = onBlacklist();
424
redirect = mailRedirect(lastMailInfo->to,
425
lastMailInfo->from, lastMailInfo->reply,
426
lastMailInfo->subject);
431
++redirect, key = 'u';
432
if(stringEqual(redirect, "x"))
435
printf("> %s\n", redirect);
436
} else if((mailIsSpam | mailIsBlack) && spamCan) {
440
if(lastMailInfo->from[0]) {
442
printf("%s", lastMailInfo->from);
443
} else if(lastMailInfo->reply[0]) {
445
printf("%s", lastMailInfo->reply);
448
} else if(!nattach && /* drop empty mail message */
450
(lastMailInfo->subject != 0) -
451
(lastMailInfo->from != 0) -
452
(lastMailInfo->reply != 0) <= 1) {
460
/* display the next page of mail and get a command from the keyboard */
463
if(!delflag) { /* show next page */
465
if(displine <= cw->dol) {
466
for(j = 0; j < 20 && displine <= cw->dol;
468
char *showline = (char *)fetchLine(displine, 1);
469
k = pstLength((pst) showline);
471
printf("%s\n", showline);
477
/* get key command */
480
/* interactive prompt depends on whether there is more text or not */
481
printf("%c ", displine > cw->dol ? '?' : '*');
483
key = getLetter("qx? nwkudijJ");
501
i_puts(MSG_IPDelete);
502
if(!cw->iplist || cw->iplist[0] == -1) {
506
i_puts(ipbFile ? MSG_None :
510
for(k = 0; (addr = cw->iplist[k]) != NULL_IP;
512
puts(tcp_ip_dots(addr));
513
if(nipblack == MAXIPBLACK)
515
ipblacklist[nipblack] = addr;
516
ipblackmask[nipblack] = 0xffffffff;
517
ipblackcomp[nipblack] = false;
526
if(!junkSubject(lastMailInfo->subject, key))
531
if(displine > cw->dol)
532
i_puts(MSG_EndMessage);
535
i_puts(MSG_MailHelp);
548
/* At this point we're saving the mail somewhere. */
554
atname = getFileName(0, false);
555
if(!stringEqual(atname, "x")) {
556
char exists = fileTypeByName(atname, false);
557
int fsize; /* file size */
559
open(atname, O_WRONLY | O_TEXT | O_CREAT | O_APPEND,
562
i_printf(MSG_NoCreate, atname);
567
"======================================================================\n",
569
if(key == 'u' || unformatMail) {
570
if(write(fh, exact, exact_l) < exact_l) {
572
i_printf(MSG_NoWrite, atname);
580
for(j = 1; j <= cw->dol; ++j) {
581
char *showline = (char *)fetchLine(j, 1);
582
int len = pstLength((pst) showline);
583
if(write(fh, showline, len) < len)
587
} /* loop over lines */
590
writeAttachments(lastMailInfo);
591
} /* unformat or format */
592
if(atname != spamCan) {
593
i_printf(MSG_MailSaved, fsize);
595
i_printf(MSG_Appended);
598
} /* saving to a real file */
602
} /* paging through the message */
603
} /* interactive or zap */
607
/* Remember, it isn't really gone until you quit the session.
608
* So if you didn't mean to delete, type x to exit abruptly,
609
* then fetch your mail again. */
610
sprintf(serverLine, "dele %d%s", m, eol);
611
if(!serverPutLine(serverLine))
614
i_printfExit(MSG_MailTimeOver);
615
if(memcmp(serverLine, "+OK", 3))
616
i_printfExit(MSG_UnableDelMail, serverLine);
620
} /* loop over mail messages */
623
printf("%d\n", nmsgs);
628
/* Here are the common keywords for mail header lines.
629
* These are in alphabetical order, so you can stick more in as you find them.
630
* The more words we have, the more accurate the test. */
631
static const char *const mhwords[] = {
636
"content-transfer-encoding:",
644
"last-attempt-date:",
664
"x-mailman-version:",
665
"x-mdaemon-deliver-to:",
668
"x-ms-tnef-correlator:",
669
"x-msmail-priority:",
673
"X-Spam-Checker-Version:",
676
"X-SPAM-Msg-Sniffer-Result:",
683
/* Before we render a mail message, let's make sure it looks like email.
684
* This is similar to htmlTest() in html.c. */
690
/* This is a very simple test - hopefully not too simple.
691
* The first 20 non-indented lines have to look like mail header lines,
692
* with at least half the keywords recognized. */
693
for(i = 1, j = k = 0; i <= cw->dol && j < 20; ++i) {
695
char *p = (char *)fetchLine(i, -1);
697
if(first == '\n' || first == '\r' && p[1] == '\n')
699
if(first == ' ' || first == '\t')
701
++j; /* nonindented line */
702
for(q = p; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
707
/* X-Whatever is a mail header word */
708
if(q - p >= 8 && p[1] == '-' && toupper(p[0]) == 'X') {
711
for(n = 0; mhwords[n]; ++n)
712
if(memEqualCI(mhwords[n], p, q - p))
717
if(k >= 4 && k * 2 >= j)
719
} /* loop over lines */
730
return c - ('a' - 26);
732
return c - ('0' - 52);
737
return 64; /* error */
741
unpack64(struct MHINFO *w)
743
uchar val, leftover, mod;
746
/* Since this is a copy, and the unpacked version is always
747
* smaller, just unpack it inline. */
750
for(q = r = w->start; q < w->end; ++q) {
757
runningError(MSG_AttAfterChars);
758
w->error64 = EXTRA64;
767
runningError(MSG_AttBad64);
773
} else if(mod == 1) {
774
*r++ = (leftover | (val >> 4));
776
} else if(mod == 2) {
777
*r++ = (leftover | (val >> 2));
780
*r++ = (leftover | val);
789
unpackQP(struct MHINFO *w)
793
for(q = r = w->start; q < w->end; ++q) {
803
if(isxdigit(c) && isxdigit(d)) {
818
/* Look for the name of the attachment and boundary */
820
ctExtras(struct MHINFO *w, const char *s, const char *t)
823
const char *q, *al, *ar;
825
if(w->ct < CT_MULTI) {
827
for(q = s + 1; q < t; ++q) {
828
if(isalnumByte(q[-1]))
830
/* could be name= or filename= */
831
if(memEqualCI(q, "file", 4))
833
if(!memEqualCI(q, "name=", 5))
840
for(al = q; q < t; ++q) {
845
if(strchr(",; \t", *q))
849
if(ar - al >= MHLINE)
850
ar = al + MHLINE - 1;
851
strncpy(w->cfn, al, ar - al);
856
if(w->ct >= CT_MULTI) {
858
for(q = s + 1; q < t; ++q) {
859
if(isalnumByte(q[-1]))
861
if(!memEqualCI(q, "boundary=", 9))
868
for(al = q; q < t; ++q) {
873
if(strchr(",; \t", *q))
877
w->boundlen = ar - al;
878
strncpy(w->boundary, al, ar - al);
884
/* Now that we know it's mail, see what information we can
885
* glean from the headers.
886
* Returns a pointer to an allocated MHINFO structure.
887
* This routine is recursive. */
888
static struct MHINFO *
889
headerGlean(char *start, char *end)
892
char *vl, *vr; /* value left and value right */
898
w = allocZeroMem(sizeof (struct MHINFO));
899
initList(&w->components);
902
w->andOthers = false;
903
w->start = start, w->end = end;
905
for(s = start; s < end; s = t + 1) {
910
t = end - 1; /* should never happen */
912
break; /* empty line */
914
if(first == ' ' || first == '\t') {
920
/* find the lead word */
921
for(q = s; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
923
continue; /* should never happen */
925
continue; /* should never happen */
926
for(vl = q; *vl == ' ' || *vl == '\t'; ++vl) ;
927
for(vr = t; vr > vl && (vr[-1] == ' ' || vr[-1] == '\t'); --vr) ;
929
continue; /* empty */
930
if(vr - vl > MHLINE - 1)
931
vr = vl + MHLINE - 1;
933
/* This is sort of a switch statement on the word */
934
if(memEqualCI(s, "subject:", q - s)) {
938
/* get rid of forward/reply prefixes */
939
for(q = vl; q < vr; ++q) {
940
static const char *const prefix[] = {
945
if(q > vl && isalnumByte(q[-1]))
947
for(j = 0; prefix[j]; ++j)
948
if(memEqualCI(q, prefix[j], strlen(prefix[j])))
952
j = strlen(prefix[j]);
953
if(!strchr(":-,;", q[j]))
956
while(q + j < vr && q[j] == ' ')
958
memcpy(q, q + j, vr - q - j);
962
strncpy(w->subject, vl, vr - vl);
963
/* If the subject is really long, spreads onto the next line,
964
* I'll just use ... */
965
if(t < end - 1 && (t[1] == ' ' || t[1] == '\t'))
966
strcat(w->subject, "...");
970
if(memEqualCI(s, "reply-to:", q - s)) {
973
strncpy(w->reply, vl, vr - vl);
977
if(memEqualCI(s, "from:", q - s)) {
980
strncpy(w->from, vl, vr - vl);
984
if(memEqualCI(s, "date:", q - s) || memEqualCI(s, "sent:", q - s)) {
988
/* don't need the weekday, seconds, or timezone */
990
isalphaByte(vl[0]) && isalphaByte(vl[1]) && isalphaByte(vl[2]) &&
991
vl[3] == ',' && vl[4] == ' ')
993
strncpy(w->date, vl, vr - vl);
994
q = strrchr(w->date, ':');
1000
if(memEqualCI(s, "to:", q - s)) {
1004
strncpy(w->to, vl, vr - vl);
1005
/* Only retain the first recipient */
1007
for(q = w->to; *q; ++q) {
1008
if(*q == ',' && !quote) {
1009
w->andOthers = true;
1015
else if(quote == *q)
1030
*q = 0; /* cut it off at the comma */
1034
if(memEqualCI(s, "cc:", q - s) || memEqualCI(s, "bcc:", q - s)) {
1035
w->andOthers = true;
1039
if(memEqualCI(s, "content-type:", q - s)) {
1041
if(memEqualCI(vl, "text", 4))
1043
if(memEqualCI(vl, "text/html", 9))
1045
if(memEqualCI(vl, "text/plain", 10))
1047
if(memEqualCI(vl, "application", 11))
1049
if(memEqualCI(vl, "multipart", 9))
1051
if(memEqualCI(vl, "multipart/alternative", 21))
1058
if(memEqualCI(s, "content-transfer-encoding:", q - s)) {
1060
if(memEqualCI(vl, "quoted-printable", 16))
1062
if(memEqualCI(vl, "7bit", 4))
1064
if(memEqualCI(vl, "8bit", 4))
1066
if(memEqualCI(vl, "base64", 6))
1070
/* content transfer encoding */
1072
} /* loop over lines */
1074
w->start = start = s + 1;
1076
/* Fix up reply and from lines.
1077
* From should be the name, reply the address. */
1079
strcpy(w->from, w->reply);
1081
strcpy(w->reply, w->from);
1082
if(w->from[0] == '"') {
1083
strcpy(w->from, w->from + 1);
1084
q = strchr(w->from, '"');
1088
vl = strchr(w->from, '<');
1089
vr = strchr(w->from, '>');
1090
if(vl && vr && vl < vr) {
1091
while(vl > w->from && vl[-1] == ' ')
1095
vl = strchr(w->reply, '<');
1096
vr = strchr(w->reply, '>');
1097
if(vl && vr && vl < vr) {
1099
strcpy(w->reply, vl + 1);
1101
/* get rid of (name) comment */
1102
vl = strchr(w->reply, '(');
1103
vr = strchr(w->reply, ')');
1104
if(vl && vr && vl < vr) {
1105
while(vl > w->reply && vl[-1] == ' ')
1109
/* no @ means it's not an email address */
1110
if(!strchr(w->reply, '@'))
1112
if(stringEqual(w->reply, w->from))
1114
vl = strchr(w->to, '<');
1115
vr = strchr(w->to, '>');
1116
if(vl && vr && vl < vr) {
1118
strcpy(w->to, vl + 1);
1121
if(debugLevel >= 5) {
1122
puts("mail header analyzed");
1123
printf("subject: %s\n", w->subject);
1124
printf("from: %s\n", w->from);
1125
printf("date: %s\n", w->date);
1126
printf("reply: %s\n", w->reply);
1127
printf("boundary: %d|%s\n", w->boundlen, w->boundary);
1128
printf("filename: %s\n", w->cfn);
1129
printf("content %d/%d\n", w->ct, w->ce);
1136
if(w->ce == CE_64 && w->ct == CT_OTHER || w->ct == CT_APPLIC || w->cfn[0]) {
1140
if(*q) { /* name present */
1141
if(stringEqual(q, "winmail.dat")) {
1144
} else if((q = strrchr(q, '.'))) {
1145
static const char *const imagelist[] = {
1146
"gif", "jpg", "tif", "bmp", "asc", "png", 0
1148
/* the asc isn't an image, it's a signature card. */
1149
/* Similarly for the winmail.dat */
1150
if(stringInListCI(imagelist, q + 1) >= 0) {
1155
if(!w->atimage && nattach == nimages + 1)
1156
firstAttach = w->cfn;
1161
/* loop over the mime components */
1162
if(w->ct == CT_MULTI || w->ct == CT_ALT) {
1163
char *lastbound = 0;
1164
bool endmode = false;
1165
struct MHINFO *child;
1166
/* We really need the -1 here, because sometimes the boundary will
1167
* be the very first thing in the message body. */
1169
while(!endmode && (t = strstr(s, "\n--")) && t < end) {
1170
if(memcmp(t + 3, w->boundary, w->boundlen)) {
1174
q = t + 3 + w->boundlen;
1176
endmode = true, ++q;
1179
debugPrint(5, "boundary found at offset %d", t - w->start);
1181
child = headerGlean(lastbound, t);
1182
addToListBack(&w->components, child);
1186
w->start = w->end = 0;
1191
/* Scan through, we might have a mail message included inline */
1192
vl = 0; /* first mail header keyword line */
1193
for(s = start; s < end; s = t + 1) {
1195
t = strchr(s, '\n');
1197
t = end - 1; /* should never happen */
1198
if(t == s) { /* empty line */
1201
/* Do we have enough for a mail header? */
1202
if(k >= 4 && k * 2 >= j) {
1203
struct MHINFO *child = headerGlean(vl, end);
1204
addToListBack(&w->components, child);
1207
} /* found mail message inside */
1210
if(first == ' ' || first == '\t')
1211
continue; /* indented */
1212
for(q = s; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
1213
if(q == s || *q != ':') {
1217
/* looks like header: stuff */
1223
for(n = 0; mhwords[n]; ++n)
1224
if(memEqualCI(mhwords[n], s, q - s))
1228
} /* loop over lines */
1230
/* Header could be at the very end */
1231
if(vl && k >= 4 && k * 2 >= j) {
1232
struct MHINFO *child = headerGlean(vl, end);
1233
addToListBack(&w->components, child);
1238
/* Any additional processing of the text, from start to end, can go here. */
1239
/* Remove leading blank lines or lines with useless words */
1240
for(s = start; s < end; s = t + 1) {
1241
t = strchr(s, '\n');
1243
t = end - 1; /* should never happen */
1245
if(vr - vl >= 4 && memEqualCI(vr - 4, "<br>", 4))
1248
if(isalnumByte(*vl))
1253
if(isalnumByte(vr[-1]))
1258
continue; /* empty */
1259
if(memEqualCI(vl, "forwarded message", vr - vl))
1261
if(memEqualCI(vl, "original message", vr - vl))
1263
break; /* something real */
1265
w->start = start = s;
1273
headerShow(struct MHINFO *w, bool top)
1275
static char buf[(MHLINE + 30) * 4];
1276
static char lastsubject[MHLINE];
1281
if(!(w->subject[0] | w->from[0] | w->reply[0]))
1285
strcpy(buf, "Message");
1288
strcat(buf, " from ");
1289
strcat(buf, w->from);
1292
if(stringEqual(w->subject, lastsubject)) {
1293
strcat(buf, " with the same subject");
1295
strcat(buf, " with subject: ");
1296
strcat(buf, w->subject);
1299
strcat(buf, " with no subject");
1300
if(mailIsHtml) { /* trash & < > */
1301
for(s = buf; *s; ++s) {
1302
/* This is quick and stupid */
1311
/* need a dot at the end? */
1312
s = buf + strlen(buf);
1313
if(isalnumByte(s[-1]))
1315
strcpy(s, mailIsHtml ? "\n<br>" : "\n");
1317
strcat(buf, "Sent ");
1318
strcat(buf, w->date);
1322
strcat(buf, "From ");
1324
strcat(buf, " from ");
1325
strcat(buf, w->reply);
1327
if(w->date[0] | w->reply[0]) { /* second line */
1332
/* This is at the top of the file */
1334
sprintf(buf, "Subject: %s\n", w->subject);
1337
if(nattach && ismc) {
1339
if(lines & mailIsHtml)
1340
strcat(buf, "<br>");
1343
sprintf(atbuf, "%d images\n", nimages);
1345
strcpy(atbuf, "1 image");
1347
if(nattach > nimages)
1350
if(nattach == nimages + 1) {
1351
strcat(buf, "1 attachment");
1352
if(firstAttach && firstAttach[0]) {
1354
strcat(buf, firstAttach);
1357
if(nattach > nimages + 1) {
1358
sprintf(atbuf, "%d attachments\n", nattach - nimages);
1364
if(w->to[0] && !ismc) {
1365
if(lines & mailIsHtml)
1366
strcat(buf, "<br>");
1371
strcat(buf, " and others");
1375
if(lines & mailIsHtml)
1376
strcat(buf, "<br>");
1378
strcat(buf, "From ");
1379
strcat(buf, w->from);
1382
if(w->date[0] && !ismc) {
1383
if(lines & mailIsHtml)
1384
strcat(buf, "<br>");
1386
strcat(buf, "Mail sent ");
1387
strcat(buf, w->date);
1391
if(lines & mailIsHtml)
1392
strcat(buf, "<br>");
1394
strcat(buf, "Reply to ");
1395
strcat(buf, w->reply);
1401
strcat(buf, mailIsHtml ? "<P>\n" : "\n");
1402
strcpy(lastsubject, w->subject);
1406
/* Depth first block of text determines the type */
1408
mailTextType(struct MHINFO *w)
1411
int texttype = CT_OTHER, rc;
1416
/* jump right into the hard part, multi/alt */
1417
if(w->ct >= CT_MULTI) {
1418
foreach(v, w->components) {
1419
rc = mailTextType(v);
1424
if(w->ct == CT_MULTI)
1432
/* If there is no text, return nothing */
1433
if(w->start == w->end)
1435
/* I don't know if this is right, but I override the type,
1436
* and make it html, if we start out with <html> */
1437
if(memEqualCI(w->start, "<html>", 6))
1439
return w->ct == CT_HTML ? CT_HTML : CT_TEXT;
1440
} /* mailTextType */
1443
formatMail(struct MHINFO *w, bool top)
1451
debugPrint(5, "format headers for content %d subject %s", ct, w->subject);
1452
stringAndString(&fm, &fm_l, headerShow(w, top));
1455
char *start = w->start;
1458
/* If mail is not in html, reformat it */
1462
if(breakLine(start, end - start, &newlen)) {
1463
start = replaceLine;
1464
end = start + newlen;
1467
if(mailIsHtml && ct != CT_HTML)
1468
stringAndString(&fm, &fm_l, "<pre>");
1469
stringAndBytes(&fm, &fm_l, start, end - start);
1470
if(mailIsHtml && ct != CT_HTML)
1471
stringAndString(&fm, &fm_l, "</pre>\n");
1475
/* There could be a mail message inline */
1476
foreach(v, w->components) {
1478
stringAndString(&fm, &fm_l, mailIsHtml ? "<P>\n" : "\n");
1479
formatMail(v, false);
1485
if(ct == CT_MULTI) {
1486
foreach(v, w->components)
1487
formatMail(v, false);
1491
/* alternate presentations, pick the best one */
1493
foreach(v, w->components) {
1494
int subtype = mailTextType(v);
1496
if(subtype != CT_OTHER)
1498
if(mailIsHtml && subtype == CT_HTML ||
1499
!mailIsHtml && subtype == CT_TEXT)
1506
foreach(v, w->components) {
1510
formatMail(v, false);
1515
/* Browse the email file. */
1517
emailParse(char *buf)
1519
struct MHINFO *w, *v;
1520
nattach = nimages = 0;
1522
mailIsHtml = mailIsSpam = ignoreImages = false;
1523
fm = initString(&fm_l);
1524
w = headerGlean(buf, buf + strlen(buf));
1525
mailIsHtml = (mailTextType(w) == CT_HTML);
1528
else if(w->ct == CT_ALT) {
1529
foreach(v, w->components)
1534
stringAndString(&fm, &fm_l, "<html>\n");
1535
formatMail(w, true);
1536
/* Remember, we always need a nonzero buffer */
1537
if(!fm_l || fm[fm_l - 1] != '\n')
1538
stringAndChar(&fm, &fm_l, '\n');
1540
writeAttachments(w);