~ubuntu-branches/ubuntu/jaunty/edbrowse/jaunty-security

« back to all changes in this revision

Viewing changes to fetchmail.c

  • Committer: Bazaar Package Importer
  • Author(s): Kapil Hari Paranjape
  • Date: 2008-04-09 18:55:23 UTC
  • mfrom: (1.1.4 upstream) (3.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080409185523-dqokcloumyn1ibn4
Tags: 3.3.4-1
* New upstream version (3.3.4).
 - Convert between iso8859-1 and utf-8 on the fly.
 - Support reading of pdf using pdftohtml.
 - French translation of html documentation.
 - Old html documentation renamed to usersguide.
 - Additional documentation on philosophy.
* debian/control:
 - Changed homepage to sourcefource site.
 - Moved homepage from description to its own field.
 - Added "poppler-utils | xpdf-utils" to Recommends.
 - Added "www-browser", "mail-reader" and "editor" to Provides. 
 - Removed "XS-" from Vcs-Svn tag.
 - Standards-Version: 3.7.3
* debian/docs: Added new documentation files
  from "doc/" subdirectory.
* debian/watch: Updated to use sourceforge site.
* debian/edbrowse.doc-base:
  - Changed name of upstream provided html documentation from
    "ebdoc.html" to "usersguide.html".
  - Changed section from "net" to "Network/Web Browsing".
* debian/install: Compiled binary is now in "src/".

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* fetchmail.c
2
 
 * Get mail using the pop3 protocol.
3
 
 * Format the mail in ascii, or in html.
4
 
 * Unpack attachments.
5
 
 * Copyright (c) Karl Dahlke, 2006
6
 
 * This file is part of the edbrowse project, released under GPL.
7
 
 */
8
 
 
9
 
#include "eb.h"
10
 
 
11
 
#define MHLINE 120              /* length of a mail header line */
12
 
#define BAD64 1
13
 
#define EXTRA64 2
14
 
struct MHINFO {
15
 
    struct MHINFO *next, *prev;
16
 
    struct listHead components;
17
 
    char *start, *end;
18
 
    char subject[MHLINE];
19
 
    char to[MHLINE];
20
 
    char from[MHLINE];
21
 
    char reply[MHLINE];
22
 
    char date[MHLINE];
23
 
    char boundary[MHLINE];
24
 
    int boundlen;
25
 
    char cfn[MHLINE];           /* content file name */
26
 
    uchar ct, ce;               /* content type, content encoding */
27
 
    bool andOthers;
28
 
    bool doAttach;
29
 
    bool atimage;
30
 
    uchar error64;
31
 
    bool ne;                    /* non english */
32
 
};
33
 
 
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 */
39
 
static int fm_l;
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];
46
 
static int nipblack;
47
 
 
48
 
void
49
 
loadBlacklist(void)
50
 
{
51
 
    char *buf;
52
 
    int buflen;
53
 
    char *s, *t;
54
 
    if(!ipbFile)
55
 
        return;
56
 
    if(!fileIntoMemory(ipbFile, &buf, &buflen))
57
 
        showErrorAbort();
58
 
    s = buf;
59
 
    while(*s) {
60
 
        t = strchr(s, '\n');
61
 
        if(!t)
62
 
            t = s + strlen(s);
63
 
        else
64
 
            *t++ = 0;
65
 
        while(isspaceByte(*s))
66
 
            ++s;
67
 
        if(isdigitByte(*s)) {
68
 
            IP32bit ip;
69
 
            IP32bit ipmask;
70
 
            char dotstop = 0;
71
 
            char *q = strpbrk(s, "/!");
72
 
            if(q)
73
 
                dotstop = *q, *q++ = 0;
74
 
            ip = tcp_dots_ip(s);
75
 
            if(ip != -1) {
76
 
                ipmask = 0xffffffff;
77
 
                if(q) {
78
 
                    int bits;
79
 
                    if(dotstop == '!')
80
 
                        bits = 0;
81
 
                    else {
82
 
                        bits = atoi(q);
83
 
                        q = strchr(q, '!');
84
 
                        if(q)
85
 
                            dotstop = '!';
86
 
                    }
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,
97
 
                            0
98
 
                        };
99
 
                        ipmask = masklist[32 - bits];
100
 
                    }
101
 
                }
102
 
                if(ipmask)
103
 
                    ip &= ipmask;
104
 
                if(nipblack == MAXIPBLACK)
105
 
                    i_printfExit(MSG_ManyIP, MAXIPBLACK);
106
 
                ipblacklist[nipblack] = ip;
107
 
                ipblackmask[nipblack] = ipmask;
108
 
                ipblackcomp[nipblack] = (dotstop == '!');
109
 
                ++nipblack;
110
 
            }
111
 
        }
112
 
        s = t;
113
 
    }
114
 
    nzFree(buf);
115
 
    debugPrint(3, "%d ip addresses in blacklist", nipblack);
116
 
}                               /* loadBlacklist */
117
 
 
118
 
bool
119
 
onBlacklist1(IP32bit tip)
120
 
{
121
 
    IP32bit blip;               /* black ip */
122
 
    IP32bit mask;
123
 
    int j;
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)
129
 
            continue;
130
 
        if(comp)
131
 
            return false;
132
 
        debugPrint(3, "blocked by rule %d", j + 1);
133
 
        return true;
134
 
    }
135
 
    return false;
136
 
}                               /* onBlacklist1 */
137
 
 
138
 
static bool
139
 
onBlacklist(void)
140
 
{
141
 
    IP32bit *ipp = cw->iplist;
142
 
    IP32bit tip;                /* test ip */
143
 
    if(!ipp)
144
 
        return false;
145
 
    while((tip = *ipp++) != NULL_IP)
146
 
        if(onBlacklist1(tip))
147
 
            return true;
148
 
    return false;
149
 
}                               /* onBlacklist */
150
 
 
151
 
static void
152
 
freeMailInfo(struct MHINFO *w)
153
 
{
154
 
    struct MHINFO *v;
155
 
    while(!listIsEmpty(&w->components)) {
156
 
        v = w->components.next;
157
 
        delFromList(v);
158
 
        freeMailInfo(v);
159
 
    }
160
 
    nzFree(w);
161
 
}                               /* freeMailInfo */
162
 
 
163
 
static char *
164
 
getFileName(const char *defname, bool isnew)
165
 
{
166
 
    static char buf[ABSPATH];
167
 
    int l;
168
 
    char *p;
169
 
    while(true) {
170
 
        i_printf(MSG_FileName);
171
 
        if(defname)
172
 
            printf("[%s] ", defname);
173
 
        if(!fgets(buf, sizeof (buf), stdin))
174
 
            exit(0);
175
 
        for(p = buf; isspaceByte(*p); ++p) ;
176
 
        l = strlen(p);
177
 
        while(l && isspaceByte(p[l - 1]))
178
 
            --l;
179
 
        p[l] = 0;
180
 
        if(!l) {
181
 
            if(!defname)
182
 
                continue;
183
 
/* make a copy just to be safe */
184
 
            strcpy(buf, defname);
185
 
            p = buf;
186
 
        } else
187
 
            defname = 0;
188
 
        if(isnew && fileTypeByName(p, false)) {
189
 
            i_printf(MSG_FileExists, p);
190
 
            defname = 0;
191
 
            continue;
192
 
        }
193
 
        return p;
194
 
    }
195
 
}                               /* getFileName */
196
 
 
197
 
static bool ignoreImages;
198
 
 
199
 
static void
200
 
writeAttachment(struct MHINFO *w)
201
 
{
202
 
    const char *atname;
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);
209
 
        if(w->cfn[0])
210
 
            printf(" %s", w->cfn);
211
 
        nl();
212
 
        atname = "x";
213
 
    } else {
214
 
        i_printf(MSG_Att);
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")) {
218
 
            atname = "x";
219
 
            ignoreImages = true;
220
 
        }
221
 
    }
222
 
    if(!ismc && stringEqual(atname, "e")) {
223
 
        int cx, svcx = context;
224
 
        for(cx = 1; cx < MAXSESSION; ++cx)
225
 
            if(!sessionList[cx].lw)
226
 
                break;
227
 
        if(cx == MAXSESSION) {
228
 
            i_printf(MSG_AttNoBuffer);
229
 
        } else {
230
 
            cxSwitch(cx, false);
231
 
            i_printf(MSG_SessionX, cx);
232
 
            if(!addTextToBuffer((pst) w->start, w->end - w->start, 0))
233
 
                i_printf(MSG_AttNoCopy, cx);
234
 
            else if(w->cfn[0])
235
 
                cw->fileName = cloneString(w->cfn);
236
 
            cxSwitch(svcx, false);      /* back to where we were */
237
 
        }
238
 
    } else if(!stringEqual(atname, "x")) {
239
 
        int fh = open(atname, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666);
240
 
        if(fh < 0) {
241
 
            i_printf(MSG_AttNoSave, atname);
242
 
            if(ismc)
243
 
                exit(1);
244
 
        } else {
245
 
            int nb = w->end - w->start;
246
 
            if(write(fh, w->start, nb) < nb) {
247
 
                i_printf(MSG_AttNoWrite, atname);
248
 
                if(ismc)
249
 
                    exit(1);
250
 
            }
251
 
            close(fh);
252
 
        }
253
 
    }
254
 
}                               /* writeAttachment */
255
 
 
256
 
static void
257
 
writeAttachments(struct MHINFO *w)
258
 
{
259
 
    struct MHINFO *v;
260
 
    if(w->doAttach) {
261
 
        writeAttachment(w);
262
 
    } else {
263
 
        foreach(v, w->components)
264
 
           writeAttachments(v);
265
 
    }
266
 
}                               /* writeAttachments */
267
 
 
268
 
static void
269
 
serverPutGet(const char *line)
270
 
{
271
 
    if(!serverPutLine(line))
272
 
        showErrorAbort();
273
 
    if(!serverGetLine())
274
 
        showErrorAbort();
275
 
}                               /* serverPutGet */
276
 
 
277
 
void
278
 
fetchMail(int account)
279
 
{
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;
284
 
    int nmsgs, m, j, k;
285
 
 
286
 
    if(!isInteractive) {
287
 
        if(!zapMail)
288
 
            i_printfExit(MSG_FetchNotBackgnd);
289
 
    }
290
 
 
291
 
    if(!mailDir)
292
 
        i_printfExit(MSG_NoMailDir);
293
 
    if(chdir(mailDir))
294
 
        i_printfExit(MSG_NoDirChange, mailDir);
295
 
 
296
 
    if(!loadAddressBook())
297
 
        showErrorAbort();
298
 
 
299
 
    if(!mailConnect(host, a->inport))
300
 
        showErrorAbort();
301
 
    if(!serverGetLine())
302
 
        showErrorAbort();
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);
310
 
    }                           /* password */
311
 
    if(memcmp(serverLine, "+OK", 3))
312
 
        i_printfExit(MSG_PopNotComplete, serverLine);
313
 
 
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);
319
 
    if(!nmsgs) {
320
 
        i_puts(MSG_NoMail);
321
 
        serverClose();
322
 
        exit(0);
323
 
    }
324
 
 
325
 
    if(!(zapMail | passMail)) {
326
 
/* We look up a lot of hosts; make a tcp connect */
327
 
        sethostent(1);
328
 
/* the conect will drop when the program exits */
329
 
    }
330
 
 
331
 
    i_printf(MSG_MessagesX, nmsgs);
332
 
    if(zapMail && nmsgs > 300)
333
 
        nmsgs = 300;
334
 
 
335
 
    for(m = 1; m <= nmsgs; ++m) {
336
 
        const char *redirect = 0;       /* send mail elsewhere */
337
 
        char key;
338
 
        const char *atname;     /* name of attachment */
339
 
        bool delflag = false;   /* delete this mail */
340
 
        int exact_l;
341
 
        char *exact = initString(&exact_l);     /* the exact message */
342
 
        int displine;
343
 
 
344
 
        if(zapMail)
345
 
            delflag = true;
346
 
        else {                  /* read the next message */
347
 
            char retrbuf[5000];
348
 
            bool retr1;
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. */
354
 
            if(lastMailInfo)
355
 
                freeMailInfo(lastMailInfo);
356
 
            lastMailInfo = 0;
357
 
            nzFree(lastMailText);
358
 
            lastMailText = 0;
359
 
            if(sessionList[1].lw)
360
 
                cxQuit(1, 2);
361
 
            cs = 0;
362
 
            linesReset();
363
 
            cxSwitch(1, false);
364
 
/* Now grab the entire message */
365
 
            sprintf(serverLine, "retr %d%s", m, eol);
366
 
            if(!serverPutLine(serverLine))
367
 
                showErrorAbort();
368
 
            retr1 = true;
369
 
            while(true) {
370
 
                int nr = tcp_read(mssock, retrbuf, sizeof (retrbuf));
371
 
                if(nr <= 0)
372
 
                    i_printfExit(MSG_ErrorReadMess, errno);
373
 
                if(retr1) {
374
 
/* add null, to make it easy to print the error message, if necessary */
375
 
                    if(nr < sizeof (retrbuf))
376
 
                        retrbuf[nr] = 0;
377
 
                    if(memcmp(retrbuf, "+OK", 3))
378
 
                        i_printfExit(MSG_ErrorFetchMess, m, retrbuf);
379
 
                    j = 3;
380
 
                    while(retrbuf[j] != '\n')
381
 
                        ++j;
382
 
                    ++j;
383
 
                    nr -= j;
384
 
                    memcpy(retrbuf, retrbuf + j, nr);
385
 
                    retr1 = false;
386
 
                }
387
 
                if(nr)
388
 
                    stringAndBytes(&exact, &exact_l, retrbuf, nr);
389
 
/* . by itself on a line ends the transmission */
390
 
                j = exact_l - 1;
391
 
                if(j < 0)
392
 
                    continue;
393
 
                if(exact[j] != '\n')
394
 
                    continue;
395
 
                --j;
396
 
                if(j >= 0 && exact[j] == '\r')
397
 
                    --j;
398
 
                if(j < 0)
399
 
                    continue;
400
 
                if(exact[j] != '.')
401
 
                    continue;
402
 
                if(!j)
403
 
                    break;
404
 
                if(exact[j - 1] == '\n')
405
 
                    break;
406
 
            }
407
 
            exact_l = j;
408
 
 
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')
412
 
                    continue;
413
 
                exact[k++] = exact[j];
414
 
            }
415
 
            exact_l = k;
416
 
            exact[k] = 0;
417
 
 
418
 
            if(!addTextToBuffer((pst) exact, exact_l, 0))
419
 
                showErrorAbort();
420
 
            if(!unformatMail) {
421
 
                browseCurrentBuffer();
422
 
                if(!passMail) {
423
 
                    mailIsBlack = onBlacklist();
424
 
                    redirect = mailRedirect(lastMailInfo->to,
425
 
                       lastMailInfo->from, lastMailInfo->reply,
426
 
                       lastMailInfo->subject);
427
 
                }
428
 
                if(redirect) {
429
 
                    key = 'w';
430
 
                    if(*redirect == '-')
431
 
                        ++redirect, key = 'u';
432
 
                    if(stringEqual(redirect, "x"))
433
 
                        i_puts(MSG_Junk);
434
 
                    else
435
 
                        printf("> %s\n", redirect);
436
 
                } else if((mailIsSpam | mailIsBlack) && spamCan) {
437
 
                    redirect = spamCan;
438
 
                    key = 'u';
439
 
                    i_printf(MSG_Spam);
440
 
                    if(lastMailInfo->from[0]) {
441
 
                        i_printf(MSG_From);
442
 
                        printf("%s", lastMailInfo->from);
443
 
                    } else if(lastMailInfo->reply[0]) {
444
 
                        i_printf(MSG_From);
445
 
                        printf("%s", lastMailInfo->reply);
446
 
                    }
447
 
                    nl();
448
 
                } else if(!nattach &&   /* drop empty mail message */
449
 
                   cw->dol -
450
 
                   (lastMailInfo->subject != 0) -
451
 
                   (lastMailInfo->from != 0) -
452
 
                   (lastMailInfo->reply != 0) <= 1) {
453
 
                    redirect = "x";
454
 
                    i_puts(MSG_Empty);
455
 
                }
456
 
            }
457
 
            if(redirect)
458
 
                delflag = true;
459
 
 
460
 
/* display the next page of mail and get a command from the keyboard */
461
 
            displine = 1;
462
 
            while(true) {
463
 
                if(!delflag) {  /* show next page */
464
 
                  nextpage:
465
 
                    if(displine <= cw->dol) {
466
 
                        for(j = 0; j < 20 && displine <= cw->dol;
467
 
                           ++j, ++displine) {
468
 
                            char *showline = (char *)fetchLine(displine, 1);
469
 
                            k = pstLength((pst) showline);
470
 
                            showline[--k] = 0;
471
 
                            printf("%s\n", showline);
472
 
                            nzFree(showline);
473
 
                        }
474
 
                    }
475
 
                }
476
 
 
477
 
/* get key command */
478
 
                while(true) {
479
 
                    if(!delflag) {
480
 
/* interactive prompt depends on whether there is more text or not */
481
 
                        printf("%c ", displine > cw->dol ? '?' : '*');
482
 
                        fflush(stdout);
483
 
                        key = getLetter("qx? nwkudijJ");
484
 
                        printf("\b\b\b");
485
 
                        fflush(stdout);
486
 
 
487
 
                        switch (key) {
488
 
                        case 'q':
489
 
                            i_puts(MSG_Quit);
490
 
                            serverClose();
491
 
                        case 'x':
492
 
                            exit(0);
493
 
                        case 'n':
494
 
                            i_puts(MSG_Next);
495
 
                            goto afterinput;
496
 
                        case 'd':
497
 
                            i_puts(MSG_Delete);
498
 
                            delflag = true;
499
 
                            goto afterinput;
500
 
                        case 'i':
501
 
                            i_puts(MSG_IPDelete);
502
 
                            if(!cw->iplist || cw->iplist[0] == -1) {
503
 
                                if(passMail)
504
 
                                    i_puts(MSG_POption);
505
 
                                else
506
 
                                    i_puts(ipbFile ? MSG_None :
507
 
                                       MSG_NoBlackList);
508
 
                            } else {
509
 
                                IP32bit addr;
510
 
                                for(k = 0; (addr = cw->iplist[k]) != NULL_IP;
511
 
                                   ++k) {
512
 
                                    puts(tcp_ip_dots(addr));
513
 
                                    if(nipblack == MAXIPBLACK)
514
 
                                        continue;
515
 
                                    ipblacklist[nipblack] = addr;
516
 
                                    ipblackmask[nipblack] = 0xffffffff;
517
 
                                    ipblackcomp[nipblack] = false;
518
 
                                    ++nipblack;
519
 
                                }
520
 
                            }
521
 
                            delflag = true;
522
 
                            goto afterinput;
523
 
                        case 'j':
524
 
                        case 'J':
525
 
                            i_puts(MSG_Junk);
526
 
                            if(!junkSubject(lastMailInfo->subject, key))
527
 
                                continue;
528
 
                            delflag = true;
529
 
                            goto afterinput;
530
 
                        case ' ':
531
 
                            if(displine > cw->dol)
532
 
                                i_puts(MSG_EndMessage);
533
 
                            goto nextpage;
534
 
                        case '?':
535
 
                            i_puts(MSG_MailHelp);
536
 
                            continue;
537
 
                        case 'k':
538
 
                        case 'w':
539
 
                        case 'u':
540
 
                            break;
541
 
                        default:
542
 
                            i_puts(MSG_NYI);
543
 
                            continue;
544
 
                        }       /* switch */
545
 
                    }
546
 
 
547
 
                    /* delflag or not */
548
 
                    /* At this point we're saving the mail somewhere. */
549
 
                    if(key != 'k')
550
 
                        delflag = true;
551
 
                    atname = redirect;
552
 
                  savemail:
553
 
                    if(!atname)
554
 
                        atname = getFileName(0, false);
555
 
                    if(!stringEqual(atname, "x")) {
556
 
                        char exists = fileTypeByName(atname, false);
557
 
                        int fsize;      /* file size */
558
 
                        int fh =
559
 
                           open(atname, O_WRONLY | O_TEXT | O_CREAT | O_APPEND,
560
 
                           0666);
561
 
                        if(fh < 0) {
562
 
                            i_printf(MSG_NoCreate, atname);
563
 
                            goto savemail;
564
 
                        }
565
 
                        if(exists)
566
 
                            write(fh,
567
 
                               "======================================================================\n",
568
 
                               71);
569
 
                        if(key == 'u' || unformatMail) {
570
 
                            if(write(fh, exact, exact_l) < exact_l) {
571
 
                              badsave:
572
 
                                i_printf(MSG_NoWrite, atname);
573
 
                                close(fh);
574
 
                                goto savemail;
575
 
                            }
576
 
                            close(fh);
577
 
                            fsize = exact_l;
578
 
                        } else {
579
 
                            fsize = 0;
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)
584
 
                                    goto badsave;
585
 
                                nzFree(showline);
586
 
                                fsize += len;
587
 
                            }   /* loop over lines */
588
 
                            close(fh);
589
 
                            if(nattach)
590
 
                                writeAttachments(lastMailInfo);
591
 
                        }       /* unformat or format */
592
 
                        if(atname != spamCan) {
593
 
                            i_printf(MSG_MailSaved, fsize);
594
 
                            if(exists)
595
 
                                i_printf(MSG_Appended);
596
 
                            nl();
597
 
                        }
598
 
                    }           /* saving to a real file */
599
 
                    goto afterinput;
600
 
 
601
 
                }               /* key commands */
602
 
            }                   /* paging through the message */
603
 
        }                       /* interactive or zap */
604
 
      afterinput:
605
 
 
606
 
        if(delflag) {
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))
612
 
                showErrorAbort();
613
 
            if(!serverGetLine())
614
 
                i_printfExit(MSG_MailTimeOver);
615
 
            if(memcmp(serverLine, "+OK", 3))
616
 
                i_printfExit(MSG_UnableDelMail, serverLine);
617
 
        }
618
 
        /* deleted */
619
 
        nzFree(exact);
620
 
    }                           /* loop over mail messages */
621
 
 
622
 
    if(zapMail)
623
 
        printf("%d\n", nmsgs);
624
 
    serverClose();
625
 
    exit(0);
626
 
}                               /* fetchMail */
627
 
 
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[] = {
632
 
    "action:",
633
 
    "arrival-date:",
634
 
    "bcc:",
635
 
    "cc:",
636
 
    "content-transfer-encoding:",
637
 
    "content-type:",
638
 
    "date:",
639
 
    "delivered-to:",
640
 
    "errors-to:",
641
 
    "final-recipient:",
642
 
    "from:",
643
 
    "importance:",
644
 
    "last-attempt-date:",
645
 
    "list-id:",
646
 
    "mailing-list:",
647
 
    "message-id:",
648
 
    "mime-version:",
649
 
    "precedence:",
650
 
    "received:",
651
 
    "remote-mta:",
652
 
    "reply-to:",
653
 
    "reporting-mta:",
654
 
    "return-path:",
655
 
    "sender:",
656
 
    "sent:",
657
 
    "status:",
658
 
    "subject:",
659
 
    "to:",
660
 
    "x-beenthere:",
661
 
    "x-comment:",
662
 
    "x-loop:",
663
 
    "x-mailer:",
664
 
    "x-mailman-version:",
665
 
    "x-mdaemon-deliver-to:",
666
 
    "x-mdremoteip:",
667
 
    "x-mimeole:",
668
 
    "x-ms-tnef-correlator:",
669
 
    "x-msmail-priority:",
670
 
    "x-originating-ip:",
671
 
    "x-priority:",
672
 
    "x-return-path:",
673
 
    "X-Spam-Checker-Version:",
674
 
    "x-spam-level:",
675
 
    "x-spam-msg-id:",
676
 
    "X-SPAM-Msg-Sniffer-Result:",
677
 
    "x-spam-processed:",
678
 
    "x-spam-status:",
679
 
    "x-uidl:",
680
 
    0
681
 
};
682
 
 
683
 
/* Before we render a mail message, let's make sure it looks like email.
684
 
 * This is similar to htmlTest() in html.c. */
685
 
bool
686
 
emailTest(void)
687
 
{
688
 
    int i, j, k, n;
689
 
 
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) {
694
 
        char *q;
695
 
        char *p = (char *)fetchLine(i, -1);
696
 
        char first = *p;
697
 
        if(first == '\n' || first == '\r' && p[1] == '\n')
698
 
            break;
699
 
        if(first == ' ' || first == '\t')
700
 
            continue;
701
 
        ++j;                    /* nonindented line */
702
 
        for(q = p; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
703
 
        if(q == p)
704
 
            continue;
705
 
        if(*q++ != ':')
706
 
            continue;
707
 
/* X-Whatever is a mail header word */
708
 
        if(q - p >= 8 && p[1] == '-' && toupper(p[0]) == 'X') {
709
 
            ++k;
710
 
        } else {
711
 
            for(n = 0; mhwords[n]; ++n)
712
 
                if(memEqualCI(mhwords[n], p, q - p))
713
 
                    break;
714
 
            if(mhwords[n])
715
 
                ++k;
716
 
        }
717
 
        if(k >= 4 && k * 2 >= j)
718
 
            return true;
719
 
    }                           /* loop over lines */
720
 
 
721
 
    return false;
722
 
}                               /* emailTest */
723
 
 
724
 
static uchar
725
 
unb64(char c)
726
 
{
727
 
    if(isupperByte(c))
728
 
        return c - 'A';
729
 
    if(islowerByte(c))
730
 
        return c - ('a' - 26);
731
 
    if(isdigitByte(c))
732
 
        return c - ('0' - 52);
733
 
    if(c == '+')
734
 
        return 62;
735
 
    if(c == '/')
736
 
        return 63;
737
 
    return 64;                  /* error */
738
 
}                               /* unb64 */
739
 
 
740
 
static void
741
 
unpack64(struct MHINFO *w)
742
 
{
743
 
    uchar val, leftover, mod;
744
 
    bool equals;
745
 
    char c, *q, *r;
746
 
/* Since this is a copy, and the unpacked version is always
747
 
 * smaller, just unpack it inline. */
748
 
    mod = 0;
749
 
    equals = false;
750
 
    for(q = r = w->start; q < w->end; ++q) {
751
 
        c = *q;
752
 
        if(isspaceByte(c))
753
 
            continue;
754
 
        if(equals) {
755
 
            if(c == '=')
756
 
                continue;
757
 
            runningError(MSG_AttAfterChars);
758
 
            w->error64 = EXTRA64;
759
 
            break;
760
 
        }
761
 
        if(c == '=') {
762
 
            equals = true;
763
 
            continue;
764
 
        }
765
 
        val = unb64(c);
766
 
        if(val & 64) {
767
 
            runningError(MSG_AttBad64);
768
 
            w->error64 = BAD64;
769
 
            break;
770
 
        }
771
 
        if(mod == 0) {
772
 
            leftover = val << 2;
773
 
        } else if(mod == 1) {
774
 
            *r++ = (leftover | (val >> 4));
775
 
            leftover = val << 4;
776
 
        } else if(mod == 2) {
777
 
            *r++ = (leftover | (val >> 2));
778
 
            leftover = val << 6;
779
 
        } else {
780
 
            *r++ = (leftover | val);
781
 
        }
782
 
        ++mod;
783
 
        mod &= 3;
784
 
    }
785
 
    w->end = r;
786
 
}                               /* unpack64 */
787
 
 
788
 
static void
789
 
unpackQP(struct MHINFO *w)
790
 
{
791
 
    uchar val;
792
 
    char c, d, *q, *r;
793
 
    for(q = r = w->start; q < w->end; ++q) {
794
 
        c = *q;
795
 
        if(c != '=') {
796
 
            *r++ = c;
797
 
            continue;
798
 
        }
799
 
        c = *++q;
800
 
        if(c == '\n')
801
 
            continue;
802
 
        d = q[1];
803
 
        if(isxdigit(c) && isxdigit(d)) {
804
 
            d = fromHex(c, d);
805
 
            if(d == 0)
806
 
                d = ' ';
807
 
            *r++ = d;
808
 
            ++q;
809
 
            continue;
810
 
        }
811
 
        --q;
812
 
        *r++ = '=';
813
 
    }
814
 
    w->end = r;
815
 
    *r = 0;
816
 
}                               /* unpackQP */
817
 
 
818
 
/* Look for the name of the attachment and boundary */
819
 
static void
820
 
ctExtras(struct MHINFO *w, const char *s, const char *t)
821
 
{
822
 
    char quote;
823
 
    const char *q, *al, *ar;
824
 
 
825
 
    if(w->ct < CT_MULTI) {
826
 
        quote = 0;
827
 
        for(q = s + 1; q < t; ++q) {
828
 
            if(isalnumByte(q[-1]))
829
 
                continue;
830
 
/* could be name= or filename= */
831
 
            if(memEqualCI(q, "file", 4))
832
 
                q += 4;
833
 
            if(!memEqualCI(q, "name=", 5))
834
 
                continue;
835
 
            q += 5;
836
 
            if(*q == '"') {
837
 
                quote = *q;
838
 
                ++q;
839
 
            }
840
 
            for(al = q; q < t; ++q) {
841
 
                if(*q == '"')
842
 
                    break;
843
 
                if(quote)
844
 
                    continue;
845
 
                if(strchr(",; \t", *q))
846
 
                    break;
847
 
            }
848
 
            ar = q;
849
 
            if(ar - al >= MHLINE)
850
 
                ar = al + MHLINE - 1;
851
 
            strncpy(w->cfn, al, ar - al);
852
 
            break;
853
 
        }
854
 
    }
855
 
    /* regular file */
856
 
    if(w->ct >= CT_MULTI) {
857
 
        quote = 0;
858
 
        for(q = s + 1; q < t; ++q) {
859
 
            if(isalnumByte(q[-1]))
860
 
                continue;
861
 
            if(!memEqualCI(q, "boundary=", 9))
862
 
                continue;
863
 
            q += 9;
864
 
            if(*q == '"') {
865
 
                quote = *q;
866
 
                ++q;
867
 
            }
868
 
            for(al = q; q < t; ++q) {
869
 
                if(*q == '"')
870
 
                    break;
871
 
                if(quote)
872
 
                    continue;
873
 
                if(strchr(",; \t", *q))
874
 
                    break;
875
 
            }
876
 
            ar = q;
877
 
            w->boundlen = ar - al;
878
 
            strncpy(w->boundary, al, ar - al);
879
 
            break;
880
 
        }
881
 
    }                           /* multi or alt */
882
 
}                               /* ctExtras */
883
 
 
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)
890
 
{
891
 
    char *s, *t, *q;
892
 
    char *vl, *vr;              /* value left and value right */
893
 
    struct MHINFO *w;
894
 
    int j, k, n;
895
 
    char linetype = 0;
896
 
 
897
 
/* defaults */
898
 
    w = allocZeroMem(sizeof (struct MHINFO));
899
 
    initList(&w->components);
900
 
    w->ct = CT_OTHER;
901
 
    w->ce = CE_8BIT;
902
 
    w->andOthers = false;
903
 
    w->start = start, w->end = end;
904
 
 
905
 
    for(s = start; s < end; s = t + 1) {
906
 
        char quote;
907
 
        char first = *s;
908
 
        t = strchr(s, '\n');
909
 
        if(!t)
910
 
            t = end - 1;        /* should never happen */
911
 
        if(t == s)
912
 
            break;              /* empty line */
913
 
 
914
 
        if(first == ' ' || first == '\t') {
915
 
            if(linetype == 'c')
916
 
                ctExtras(w, s, t);
917
 
            continue;
918
 
        }
919
 
 
920
 
/* find the lead word */
921
 
        for(q = s; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
922
 
        if(q == s)
923
 
            continue;           /* should never happen */
924
 
        if(*q++ != ':')
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) ;
928
 
        if(vr == vl)
929
 
            continue;           /* empty */
930
 
        if(vr - vl > MHLINE - 1)
931
 
            vr = vl + MHLINE - 1;
932
 
 
933
 
/* This is sort of a switch statement on the word */
934
 
        if(memEqualCI(s, "subject:", q - s)) {
935
 
            linetype = 's';
936
 
            if(w->subject[0])
937
 
                continue;
938
 
/* get rid of forward/reply prefixes */
939
 
            for(q = vl; q < vr; ++q) {
940
 
                static const char *const prefix[] = {
941
 
                    "re", "sv", "fwd", 0
942
 
                };
943
 
                if(!isalphaByte(*q))
944
 
                    continue;
945
 
                if(q > vl && isalnumByte(q[-1]))
946
 
                    continue;
947
 
                for(j = 0; prefix[j]; ++j)
948
 
                    if(memEqualCI(q, prefix[j], strlen(prefix[j])))
949
 
                        break;
950
 
                if(!prefix[j])
951
 
                    continue;
952
 
                j = strlen(prefix[j]);
953
 
                if(!strchr(":-,;", q[j]))
954
 
                    continue;
955
 
                ++j;
956
 
                while(q + j < vr && q[j] == ' ')
957
 
                    ++j;
958
 
                memcpy(q, q + j, vr - q - j);
959
 
                vr -= j;
960
 
                --q;            /* try again */
961
 
            }
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, "...");
967
 
            continue;
968
 
        }
969
 
        /* subject */
970
 
        if(memEqualCI(s, "reply-to:", q - s)) {
971
 
            linetype = 'r';
972
 
            if(!w->reply[0])
973
 
                strncpy(w->reply, vl, vr - vl);
974
 
            continue;
975
 
        }
976
 
        /* reply */
977
 
        if(memEqualCI(s, "from:", q - s)) {
978
 
            linetype = 'f';
979
 
            if(!w->from[0])
980
 
                strncpy(w->from, vl, vr - vl);
981
 
            continue;
982
 
        }
983
 
        /* from */
984
 
        if(memEqualCI(s, "date:", q - s) || memEqualCI(s, "sent:", q - s)) {
985
 
            linetype = 'd';
986
 
            if(w->date[0])
987
 
                continue;
988
 
/* don't need the weekday, seconds, or timezone */
989
 
            if(vr - vl > 5 &&
990
 
               isalphaByte(vl[0]) && isalphaByte(vl[1]) && isalphaByte(vl[2]) &&
991
 
               vl[3] == ',' && vl[4] == ' ')
992
 
                vl += 5;
993
 
            strncpy(w->date, vl, vr - vl);
994
 
            q = strrchr(w->date, ':');
995
 
            if(q)
996
 
                *q = 0;
997
 
            continue;
998
 
        }
999
 
        /* date */
1000
 
        if(memEqualCI(s, "to:", q - s)) {
1001
 
            linetype = 't';
1002
 
            if(w->to[0])
1003
 
                continue;
1004
 
            strncpy(w->to, vl, vr - vl);
1005
 
/* Only retain the first recipient */
1006
 
            quote = 0;
1007
 
            for(q = w->to; *q; ++q) {
1008
 
                if(*q == ',' && !quote) {
1009
 
                    w->andOthers = true;
1010
 
                    break;
1011
 
                }
1012
 
                if(*q == '"') {
1013
 
                    if(!quote)
1014
 
                        quote = *q;
1015
 
                    else if(quote == *q)
1016
 
                        quote = 0;
1017
 
                    continue;
1018
 
                }
1019
 
                if(*q == '<') {
1020
 
                    if(!quote)
1021
 
                        quote = *q;
1022
 
                    continue;
1023
 
                }
1024
 
                if(*q == '>') {
1025
 
                    if(quote == '<')
1026
 
                        quote = 0;
1027
 
                    continue;
1028
 
                }
1029
 
            }
1030
 
            *q = 0;             /* cut it off at the comma */
1031
 
            continue;
1032
 
        }
1033
 
        /* to */
1034
 
        if(memEqualCI(s, "cc:", q - s) || memEqualCI(s, "bcc:", q - s)) {
1035
 
            w->andOthers = true;
1036
 
            continue;
1037
 
        }
1038
 
        /* carbon copy */
1039
 
        if(memEqualCI(s, "content-type:", q - s)) {
1040
 
            linetype = 'c';
1041
 
            if(memEqualCI(vl, "text", 4))
1042
 
                w->ct = CT_RICH;
1043
 
            if(memEqualCI(vl, "text/html", 9))
1044
 
                w->ct = CT_HTML;
1045
 
            if(memEqualCI(vl, "text/plain", 10))
1046
 
                w->ct = CT_TEXT;
1047
 
            if(memEqualCI(vl, "application", 11))
1048
 
                w->ct = CT_APPLIC;
1049
 
            if(memEqualCI(vl, "multipart", 9))
1050
 
                w->ct = CT_MULTI;
1051
 
            if(memEqualCI(vl, "multipart/alternative", 21))
1052
 
                w->ct = CT_ALT;
1053
 
 
1054
 
            ctExtras(w, s, t);
1055
 
            continue;
1056
 
        }
1057
 
        /* content type */
1058
 
        if(memEqualCI(s, "content-transfer-encoding:", q - s)) {
1059
 
            linetype = 'e';
1060
 
            if(memEqualCI(vl, "quoted-printable", 16))
1061
 
                w->ce = CE_QP;
1062
 
            if(memEqualCI(vl, "7bit", 4))
1063
 
                w->ce = CE_7BIT;
1064
 
            if(memEqualCI(vl, "8bit", 4))
1065
 
                w->ce = CE_8BIT;
1066
 
            if(memEqualCI(vl, "base64", 6))
1067
 
                w->ce = CE_64;
1068
 
            continue;
1069
 
        }
1070
 
        /* content transfer encoding */
1071
 
        linetype = 0;
1072
 
    }                           /* loop over lines */
1073
 
 
1074
 
    w->start = start = s + 1;
1075
 
 
1076
 
/* Fix up reply and from lines.
1077
 
 * From should be the name, reply the address. */
1078
 
    if(!w->from[0])
1079
 
        strcpy(w->from, w->reply);
1080
 
    if(!w->reply[0])
1081
 
        strcpy(w->reply, w->from);
1082
 
    if(w->from[0] == '"') {
1083
 
        strcpy(w->from, w->from + 1);
1084
 
        q = strchr(w->from, '"');
1085
 
        if(q)
1086
 
            *q = 0;
1087
 
    }
1088
 
    vl = strchr(w->from, '<');
1089
 
    vr = strchr(w->from, '>');
1090
 
    if(vl && vr && vl < vr) {
1091
 
        while(vl > w->from && vl[-1] == ' ')
1092
 
            --vl;
1093
 
        *vl = 0;
1094
 
    }
1095
 
    vl = strchr(w->reply, '<');
1096
 
    vr = strchr(w->reply, '>');
1097
 
    if(vl && vr && vl < vr) {
1098
 
        *vr = 0;
1099
 
        strcpy(w->reply, vl + 1);
1100
 
    }
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] == ' ')
1106
 
            --vl;
1107
 
        *vl = 0;
1108
 
    }
1109
 
/* no @ means it's not an email address */
1110
 
    if(!strchr(w->reply, '@'))
1111
 
        w->reply[0] = 0;
1112
 
    if(stringEqual(w->reply, w->from))
1113
 
        w->from[0] = 0;
1114
 
    vl = strchr(w->to, '<');
1115
 
    vr = strchr(w->to, '>');
1116
 
    if(vl && vr && vl < vr) {
1117
 
        *vr = 0;
1118
 
        strcpy(w->to, vl + 1);
1119
 
    }
1120
 
 
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);
1130
 
    }
1131
 
    /* debug */
1132
 
    if(w->ce == CE_QP)
1133
 
        unpackQP(w);
1134
 
    if(w->ce == CE_64)
1135
 
        unpack64(w);
1136
 
    if(w->ce == CE_64 && w->ct == CT_OTHER || w->ct == CT_APPLIC || w->cfn[0]) {
1137
 
        w->doAttach = true;
1138
 
        ++nattach;
1139
 
        q = w->cfn;
1140
 
        if(*q) {                /* name present */
1141
 
            if(stringEqual(q, "winmail.dat")) {
1142
 
                w->atimage = true;
1143
 
                ++nimages;
1144
 
            } else if((q = strrchr(q, '.'))) {
1145
 
                static const char *const imagelist[] = {
1146
 
                    "gif", "jpg", "tif", "bmp", "asc", "png", 0
1147
 
                };
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) {
1151
 
                    w->atimage = true;
1152
 
                    ++nimages;
1153
 
                }
1154
 
            }
1155
 
            if(!w->atimage && nattach == nimages + 1)
1156
 
                firstAttach = w->cfn;
1157
 
        }
1158
 
        return w;
1159
 
    }
1160
 
 
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. */
1168
 
        s = w->start - 1;
1169
 
        while(!endmode && (t = strstr(s, "\n--")) && t < end) {
1170
 
            if(memcmp(t + 3, w->boundary, w->boundlen)) {
1171
 
                s = t + 3;
1172
 
                continue;
1173
 
            }
1174
 
            q = t + 3 + w->boundlen;
1175
 
            while(*q == '-')
1176
 
                endmode = true, ++q;
1177
 
            if(*q == '\n')
1178
 
                ++q;
1179
 
            debugPrint(5, "boundary found at offset %d", t - w->start);
1180
 
            if(lastbound) {
1181
 
                child = headerGlean(lastbound, t);
1182
 
                addToListBack(&w->components, child);
1183
 
            }
1184
 
            s = lastbound = q;
1185
 
        }
1186
 
        w->start = w->end = 0;
1187
 
        return w;
1188
 
    }
1189
 
 
1190
 
    /* mime or alt */
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) {
1194
 
        char first = *s;
1195
 
        t = strchr(s, '\n');
1196
 
        if(!t)
1197
 
            t = end - 1;        /* should never happen */
1198
 
        if(t == s) {            /* empty line */
1199
 
            if(!vl)
1200
 
                continue;
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);
1205
 
                w->end = end = vl;
1206
 
                goto textonly;
1207
 
            }                   /* found mail message inside */
1208
 
            vl = 0;
1209
 
        }                       /* empty line */
1210
 
        if(first == ' ' || first == '\t')
1211
 
            continue;           /* indented */
1212
 
        for(q = s; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
1213
 
        if(q == s || *q != ':') {
1214
 
            vl = 0;
1215
 
            continue;
1216
 
        }
1217
 
/* looks like header: stuff */
1218
 
        if(!vl) {
1219
 
            vl = s;
1220
 
            j = k = 0;
1221
 
        }
1222
 
        ++j;
1223
 
        for(n = 0; mhwords[n]; ++n)
1224
 
            if(memEqualCI(mhwords[n], s, q - s))
1225
 
                break;
1226
 
        if(mhwords[n])
1227
 
            ++k;
1228
 
    }                           /* loop over lines */
1229
 
 
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);
1234
 
        w->end = end = vl;
1235
 
    }
1236
 
 
1237
 
  textonly:
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');
1242
 
        if(!t)
1243
 
            t = end - 1;        /* should never happen */
1244
 
        vl = s, vr = t;
1245
 
        if(vr - vl >= 4 && memEqualCI(vr - 4, "<br>", 4))
1246
 
            vr -= 4;
1247
 
        while(vl < vr) {
1248
 
            if(isalnumByte(*vl))
1249
 
                break;
1250
 
            ++vl;
1251
 
        }
1252
 
        while(vl < vr) {
1253
 
            if(isalnumByte(vr[-1]))
1254
 
                break;
1255
 
            --vr;
1256
 
        }
1257
 
        if(vl == vr)
1258
 
            continue;           /* empty */
1259
 
        if(memEqualCI(vl, "forwarded message", vr - vl))
1260
 
            continue;
1261
 
        if(memEqualCI(vl, "original message", vr - vl))
1262
 
            continue;
1263
 
        break;                  /* something real */
1264
 
    }
1265
 
    w->start = start = s;
1266
 
 
1267
 
    w->ne = false;
1268
 
 
1269
 
    return w;
1270
 
}                               /* headerGlean */
1271
 
 
1272
 
static char *
1273
 
headerShow(struct MHINFO *w, bool top)
1274
 
{
1275
 
    static char buf[(MHLINE + 30) * 4];
1276
 
    static char lastsubject[MHLINE];
1277
 
    char *s;
1278
 
    bool lines = false;
1279
 
    buf[0] = 0;
1280
 
 
1281
 
    if(!(w->subject[0] | w->from[0] | w->reply[0]))
1282
 
        return buf;
1283
 
 
1284
 
    if(!top) {
1285
 
        strcpy(buf, "Message");
1286
 
        lines = true;
1287
 
        if(w->from[0]) {
1288
 
            strcat(buf, " from ");
1289
 
            strcat(buf, w->from);
1290
 
        }
1291
 
        if(w->subject[0]) {
1292
 
            if(stringEqual(w->subject, lastsubject)) {
1293
 
                strcat(buf, " with the same subject");
1294
 
            } else {
1295
 
                strcat(buf, " with subject: ");
1296
 
                strcat(buf, w->subject);
1297
 
            }
1298
 
        } else
1299
 
            strcat(buf, " with no subject");
1300
 
        if(mailIsHtml) {        /* trash & < > */
1301
 
            for(s = buf; *s; ++s) {
1302
 
/* This is quick and stupid */
1303
 
                if(*s == '<')
1304
 
                    *s = '(';
1305
 
                if(*s == '>')
1306
 
                    *s = ')';
1307
 
                if(*s == '&')
1308
 
                    *s = '*';
1309
 
            }
1310
 
        }
1311
 
/* need a dot at the end? */
1312
 
        s = buf + strlen(buf);
1313
 
        if(isalnumByte(s[-1]))
1314
 
            *s++ = '.';
1315
 
        strcpy(s, mailIsHtml ? "\n<br>" : "\n");
1316
 
        if(w->date[0]) {
1317
 
            strcat(buf, "Sent ");
1318
 
            strcat(buf, w->date);
1319
 
        }
1320
 
        if(w->reply) {
1321
 
            if(!w->date[0])
1322
 
                strcat(buf, "From ");
1323
 
            else
1324
 
                strcat(buf, " from ");
1325
 
            strcat(buf, w->reply);
1326
 
        }
1327
 
        if(w->date[0] | w->reply[0]) {  /* second line */
1328
 
            strcat(buf, "\n");
1329
 
        }
1330
 
    } else {
1331
 
 
1332
 
/* This is at the top of the file */
1333
 
        if(w->subject[0]) {
1334
 
            sprintf(buf, "Subject: %s\n", w->subject);
1335
 
            lines = true;
1336
 
        }
1337
 
        if(nattach && ismc) {
1338
 
            char atbuf[20];
1339
 
            if(lines & mailIsHtml)
1340
 
                strcat(buf, "<br>");
1341
 
            lines = true;
1342
 
            if(nimages) {
1343
 
                sprintf(atbuf, "%d images\n", nimages);
1344
 
                if(nimages == 1)
1345
 
                    strcpy(atbuf, "1 image");
1346
 
                strcat(buf, atbuf);
1347
 
                if(nattach > nimages)
1348
 
                    strcat(buf, " + ");
1349
 
            }
1350
 
            if(nattach == nimages + 1) {
1351
 
                strcat(buf, "1 attachment");
1352
 
                if(firstAttach && firstAttach[0]) {
1353
 
                    strcat(buf, " ");
1354
 
                    strcat(buf, firstAttach);
1355
 
                }
1356
 
            }
1357
 
            if(nattach > nimages + 1) {
1358
 
                sprintf(atbuf, "%d attachments\n", nattach - nimages);
1359
 
                strcat(buf, atbuf);
1360
 
            }
1361
 
            strcat(buf, "\n");
1362
 
        }
1363
 
        /* attachments */
1364
 
        if(w->to[0] && !ismc) {
1365
 
            if(lines & mailIsHtml)
1366
 
                strcat(buf, "<br>");
1367
 
            lines = true;
1368
 
            strcat(buf, "To ");
1369
 
            strcat(buf, w->to);
1370
 
            if(w->andOthers)
1371
 
                strcat(buf, " and others");
1372
 
            strcat(buf, "\n");
1373
 
        }
1374
 
        if(w->from[0]) {
1375
 
            if(lines & mailIsHtml)
1376
 
                strcat(buf, "<br>");
1377
 
            lines = true;
1378
 
            strcat(buf, "From ");
1379
 
            strcat(buf, w->from);
1380
 
            strcat(buf, "\n");
1381
 
        }
1382
 
        if(w->date[0] && !ismc) {
1383
 
            if(lines & mailIsHtml)
1384
 
                strcat(buf, "<br>");
1385
 
            lines = true;
1386
 
            strcat(buf, "Mail sent ");
1387
 
            strcat(buf, w->date);
1388
 
            strcat(buf, "\n");
1389
 
        }
1390
 
        if(w->reply[0]) {
1391
 
            if(lines & mailIsHtml)
1392
 
                strcat(buf, "<br>");
1393
 
            lines = true;
1394
 
            strcat(buf, "Reply to ");
1395
 
            strcat(buf, w->reply);
1396
 
            strcat(buf, "\n");
1397
 
        }
1398
 
    }
1399
 
 
1400
 
    if(lines)
1401
 
        strcat(buf, mailIsHtml ? "<P>\n" : "\n");
1402
 
    strcpy(lastsubject, w->subject);
1403
 
    return buf;
1404
 
}                               /* headerShow */
1405
 
 
1406
 
/* Depth first block of text determines the type */
1407
 
static int
1408
 
mailTextType(struct MHINFO *w)
1409
 
{
1410
 
    struct MHINFO *v;
1411
 
    int texttype = CT_OTHER, rc;
1412
 
 
1413
 
    if(w->doAttach)
1414
 
        return CT_OTHER;
1415
 
 
1416
 
/* jump right into the hard part, multi/alt */
1417
 
    if(w->ct >= CT_MULTI) {
1418
 
        foreach(v, w->components) {
1419
 
            rc = mailTextType(v);
1420
 
            if(rc == CT_HTML)
1421
 
                return rc;
1422
 
            if(rc == CT_OTHER)
1423
 
                continue;
1424
 
            if(w->ct == CT_MULTI)
1425
 
                return rc;
1426
 
            texttype = rc;
1427
 
        }
1428
 
        return texttype;
1429
 
    }
1430
 
 
1431
 
    /* multi */
1432
 
    /* If there is no text, return nothing */
1433
 
    if(w->start == w->end)
1434
 
        return CT_OTHER;
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))
1438
 
        return CT_HTML;
1439
 
    return w->ct == CT_HTML ? CT_HTML : CT_TEXT;
1440
 
}                               /* mailTextType */
1441
 
 
1442
 
static void
1443
 
formatMail(struct MHINFO *w, bool top)
1444
 
{
1445
 
    struct MHINFO *v;
1446
 
    int ct = w->ct;
1447
 
    int j, best;
1448
 
 
1449
 
    if(w->doAttach)
1450
 
        return;
1451
 
    debugPrint(5, "format headers for content %d subject %s", ct, w->subject);
1452
 
    stringAndString(&fm, &fm_l, headerShow(w, top));
1453
 
 
1454
 
    if(ct < CT_MULTI) {
1455
 
        char *start = w->start;
1456
 
        char *end = w->end;
1457
 
        int newlen;
1458
 
/* If mail is not in html, reformat it */
1459
 
        if(start < end) {
1460
 
            if(ct == CT_TEXT) {
1461
 
                breakLineSetup();
1462
 
                if(breakLine(start, end - start, &newlen)) {
1463
 
                    start = replaceLine;
1464
 
                    end = start + newlen;
1465
 
                }
1466
 
            }
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");
1472
 
        }
1473
 
 
1474
 
        /* text present */
1475
 
        /* There could be a mail message inline */
1476
 
        foreach(v, w->components) {
1477
 
            if(end > start)
1478
 
                stringAndString(&fm, &fm_l, mailIsHtml ? "<P>\n" : "\n");
1479
 
            formatMail(v, false);
1480
 
        }
1481
 
 
1482
 
        return;
1483
 
    }
1484
 
 
1485
 
    if(ct == CT_MULTI) {
1486
 
        foreach(v, w->components)
1487
 
           formatMail(v, false);
1488
 
        return;
1489
 
    }
1490
 
 
1491
 
/* alternate presentations, pick the best one */
1492
 
    best = j = 0;
1493
 
    foreach(v, w->components) {
1494
 
        int subtype = mailTextType(v);
1495
 
        ++j;
1496
 
        if(subtype != CT_OTHER)
1497
 
            best = j;
1498
 
        if(mailIsHtml && subtype == CT_HTML ||
1499
 
           !mailIsHtml && subtype == CT_TEXT)
1500
 
            break;
1501
 
    }
1502
 
 
1503
 
    if(!best)
1504
 
        best = 1;
1505
 
    j = 0;
1506
 
    foreach(v, w->components) {
1507
 
        ++j;
1508
 
        if(j != best)
1509
 
            continue;
1510
 
        formatMail(v, false);
1511
 
        break;
1512
 
    }
1513
 
}                               /* formatMail */
1514
 
 
1515
 
/* Browse the email file. */
1516
 
char *
1517
 
emailParse(char *buf)
1518
 
{
1519
 
    struct MHINFO *w, *v;
1520
 
    nattach = nimages = 0;
1521
 
    firstAttach = 0;
1522
 
    mailIsHtml = mailIsSpam = ignoreImages = false;
1523
 
    fm = initString(&fm_l);
1524
 
    w = headerGlean(buf, buf + strlen(buf));
1525
 
    mailIsHtml = (mailTextType(w) == CT_HTML);
1526
 
    if(w->ne)
1527
 
        mailIsSpam = true;
1528
 
    else if(w->ct == CT_ALT) {
1529
 
        foreach(v, w->components)
1530
 
           if(v->ne)
1531
 
            mailIsSpam = true;
1532
 
    }
1533
 
    if(mailIsHtml)
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');
1539
 
    if(!ismc) {
1540
 
        writeAttachments(w);
1541
 
        freeMailInfo(w);
1542
 
        nzFree(buf);
1543
 
    } else {
1544
 
        lastMailInfo = w;
1545
 
        lastMailText = buf;
1546
 
    }
1547
 
    return fm;
1548
 
}                               /* emailParse */