~ubuntu-branches/ubuntu/karmic/edbrowse/karmic-security

« back to all changes in this revision

Viewing changes to src/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, 2008
 
6
 * This file is part of the edbrowse project, released under GPL.
 
7
 */
 
8
 
 
9
#include "eb.h"
 
10
 
 
11
#define MHLINE 200              /* 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 + 4];
 
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 *tolist, *cclist;
 
26
    int tolen, cclen;
 
27
    char mid[MHLINE];           /* message id */
 
28
    char ref[MHLINE];           /* references */
 
29
    char cfn[MHLINE];           /* content file name */
 
30
    uchar ct, ce;               /* content type, content encoding */
 
31
    bool andOthers;
 
32
    bool doAttach;
 
33
    bool atimage;
 
34
    uchar error64;
 
35
    bool ne;                    /* non english */
 
36
};
 
37
 
 
38
static int nattach;             /* number of attachments */
 
39
static int nimages;             /* number of attached images */
 
40
static char *firstAttach;       /* name of first file */
 
41
static bool mailIsHtml, mailIsSpam, mailIsBlack;
 
42
static char *fm;                /* formatted mail string */
 
43
static int fm_l;
 
44
static struct MHINFO *lastMailInfo;
 
45
static char *lastMailText;
 
46
#define MAXIPBLACK 3000
 
47
static IP32bit ipblacklist[MAXIPBLACK];
 
48
static IP32bit ipblackmask[MAXIPBLACK];
 
49
static bool ipblackcomp[MAXIPBLACK];
 
50
static int nipblack;
 
51
 
 
52
void
 
53
loadBlacklist(void)
 
54
{
 
55
    char *buf;
 
56
    int buflen;
 
57
    char *s, *t;
 
58
    if(!ipbFile)
 
59
        return;
 
60
    if(!fileIntoMemory(ipbFile, &buf, &buflen))
 
61
        showErrorAbort();
 
62
    s = buf;
 
63
    while(*s) {
 
64
        t = strchr(s, '\n');
 
65
        if(!t)
 
66
            t = s + strlen(s);
 
67
        else
 
68
            *t++ = 0;
 
69
        while(isspaceByte(*s))
 
70
            ++s;
 
71
        if(isdigitByte(*s)) {
 
72
            IP32bit ip;
 
73
            IP32bit ipmask;
 
74
            char dotstop = 0;
 
75
            char *q = strpbrk(s, "/!");
 
76
            if(q)
 
77
                dotstop = *q, *q++ = 0;
 
78
            ip = tcp_dots_ip(s);
 
79
            if(ip != -1) {
 
80
                ipmask = 0xffffffff;
 
81
                if(q) {
 
82
                    int bits;
 
83
                    if(dotstop == '!')
 
84
                        bits = 0;
 
85
                    else {
 
86
                        bits = atoi(q);
 
87
                        q = strchr(q, '!');
 
88
                        if(q)
 
89
                            dotstop = '!';
 
90
                    }
 
91
                    if(bits > 0 && bits < 32) {
 
92
                        static const IP32bit masklist[] = {
 
93
                            0xffffffff, 0xfeffffff, 0xfcffffff, 0xf8ffffff,
 
94
                            0xf0ffffff, 0xe0ffffff, 0xc0ffffff, 0x80ffffff,
 
95
                            0x00ffffff, 0x00feffff, 0x00fcffff, 0x00f8ffff,
 
96
                            0x00f0ffff, 0x00e0ffff, 0x00c0ffff, 0x0080ffff,
 
97
                            0x0000ffff, 0x0000feff, 0x0000fcff, 0x0000f8ff,
 
98
                            0x0000f0ff, 0x0000e0ff, 0x0000c0ff, 0x000080ff,
 
99
                            0x000000ff, 0x000000fe, 0x000000fc, 0x000000f8,
 
100
                            0x000000f0, 0x000000e0, 0x000000c0, 0x00000080,
 
101
                            0
 
102
                        };
 
103
                        ipmask = masklist[32 - bits];
 
104
                    }
 
105
                }
 
106
                if(ipmask)
 
107
                    ip &= ipmask;
 
108
                if(nipblack == MAXIPBLACK)
 
109
                    i_printfExit(MSG_ManyIP, MAXIPBLACK);
 
110
                ipblacklist[nipblack] = ip;
 
111
                ipblackmask[nipblack] = ipmask;
 
112
                ipblackcomp[nipblack] = (dotstop == '!');
 
113
                ++nipblack;
 
114
            }
 
115
        }
 
116
        s = t;
 
117
    }
 
118
    nzFree(buf);
 
119
    debugPrint(3, "%d ip addresses in blacklist", nipblack);
 
120
}                               /* loadBlacklist */
 
121
 
 
122
bool
 
123
onBlacklist1(IP32bit tip)
 
124
{
 
125
    IP32bit blip;               /* black ip */
 
126
    IP32bit mask;
 
127
    int j;
 
128
    for(j = 0; j < nipblack; ++j) {
 
129
        bool comp = ipblackcomp[j];
 
130
        blip = ipblacklist[j];
 
131
        mask = ipblackmask[j];
 
132
        if((tip ^ blip) & mask)
 
133
            continue;
 
134
        if(comp)
 
135
            return false;
 
136
        debugPrint(3, "blocked by rule %d", j + 1);
 
137
        return true;
 
138
    }
 
139
    return false;
 
140
}                               /* onBlacklist1 */
 
141
 
 
142
static bool
 
143
onBlacklist(void)
 
144
{
 
145
    IP32bit *ipp = cw->iplist;
 
146
    IP32bit tip;                /* test ip */
 
147
    if(!ipp)
 
148
        return false;
 
149
    while((tip = *ipp++) != NULL_IP)
 
150
        if(onBlacklist1(tip))
 
151
            return true;
 
152
    return false;
 
153
}                               /* onBlacklist */
 
154
 
 
155
static void
 
156
freeMailInfo(struct MHINFO *w)
 
157
{
 
158
    struct MHINFO *v;
 
159
    while(!listIsEmpty(&w->components)) {
 
160
        v = w->components.next;
 
161
        delFromList(v);
 
162
        freeMailInfo(v);
 
163
    }
 
164
    nzFree(w->tolist);
 
165
    nzFree(w->cclist);
 
166
    nzFree(w);
 
167
}                               /* freeMailInfo */
 
168
 
 
169
static char *
 
170
getFileName(const char *defname, bool isnew)
 
171
{
 
172
    static char buf[ABSPATH];
 
173
    int l;
 
174
    char *p;
 
175
    while(true) {
 
176
        i_printf(MSG_FileName);
 
177
        if(defname)
 
178
            printf("[%s] ", defname);
 
179
        if(!fgets(buf, sizeof (buf), stdin))
 
180
            exit(0);
 
181
        for(p = buf; isspaceByte(*p); ++p) ;
 
182
        l = strlen(p);
 
183
        while(l && isspaceByte(p[l - 1]))
 
184
            --l;
 
185
        p[l] = 0;
 
186
        if(!l) {
 
187
            if(!defname)
 
188
                continue;
 
189
/* make a copy just to be safe */
 
190
            strcpy(buf, defname);
 
191
            p = buf;
 
192
        } else
 
193
            defname = 0;
 
194
        if(isnew && fileTypeByName(p, false)) {
 
195
            i_printf(MSG_FileExists, p);
 
196
            defname = 0;
 
197
            continue;
 
198
        }
 
199
        return p;
 
200
    }
 
201
}                               /* getFileName */
 
202
 
 
203
static bool ignoreImages;
 
204
 
 
205
static void
 
206
writeAttachment(struct MHINFO *w)
 
207
{
 
208
    const char *atname;
 
209
    if((ismc | ignoreImages) && w->atimage)
 
210
        return;                 /* image ignored */
 
211
    if(w->error64 == BAD64)
 
212
        i_printf(MSG_Abbreviated);
 
213
    if(w->start == w->end) {
 
214
        i_printf(MSG_AttEmpty);
 
215
        if(w->cfn[0])
 
216
            printf(" %s", w->cfn);
 
217
        nl();
 
218
        atname = "x";
 
219
    } else {
 
220
        i_printf(MSG_Att);
 
221
        atname = getFileName((w->cfn[0] ? w->cfn : 0), true);
 
222
/* X is like x, but deletes all future images */
 
223
        if(stringEqual(atname, "X")) {
 
224
            atname = "x";
 
225
            ignoreImages = true;
 
226
        }
 
227
    }
 
228
    if(!ismc && stringEqual(atname, "e")) {
 
229
        int cx, svcx = context;
 
230
        for(cx = 1; cx < MAXSESSION; ++cx)
 
231
            if(!sessionList[cx].lw)
 
232
                break;
 
233
        if(cx == MAXSESSION) {
 
234
            i_printf(MSG_AttNoBuffer);
 
235
        } else {
 
236
            cxSwitch(cx, false);
 
237
            i_printf(MSG_SessionX, cx);
 
238
            if(!addTextToBuffer((pst) w->start, w->end - w->start, 0))
 
239
                i_printf(MSG_AttNoCopy, cx);
 
240
            else if(w->cfn[0])
 
241
                cw->fileName = cloneString(w->cfn);
 
242
            cxSwitch(svcx, false);      /* back to where we were */
 
243
        }
 
244
    } else if(!stringEqual(atname, "x")) {
 
245
        int fh = open(atname, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666);
 
246
        if(fh < 0) {
 
247
            i_printf(MSG_AttNoSave, atname);
 
248
            if(ismc)
 
249
                exit(1);
 
250
        } else {
 
251
            int nb = w->end - w->start;
 
252
            if(write(fh, w->start, nb) < nb) {
 
253
                i_printf(MSG_AttNoWrite, atname);
 
254
                if(ismc)
 
255
                    exit(1);
 
256
            }
 
257
            close(fh);
 
258
        }
 
259
    }
 
260
}                               /* writeAttachment */
 
261
 
 
262
static void
 
263
writeAttachments(struct MHINFO *w)
 
264
{
 
265
    struct MHINFO *v;
 
266
    if(w->doAttach) {
 
267
        writeAttachment(w);
 
268
    } else {
 
269
        foreach(v, w->components)
 
270
           writeAttachments(v);
 
271
    }
 
272
}                               /* writeAttachments */
 
273
 
 
274
static void
 
275
serverPutGet(const char *line, bool secure)
 
276
{
 
277
    if(!serverPutLine(line, secure))
 
278
        showErrorAbort();
 
279
    if(!serverGetLine(secure))
 
280
        showErrorAbort();
 
281
}                               /* serverPutGet */
 
282
 
 
283
void
 
284
fetchMail(int account)
 
285
{
 
286
    const struct MACCOUNT *a = accounts + account - 1;
 
287
    const char *login = a->login;
 
288
    const char *pass = a->password;
 
289
    const char *host = a->inurl;
 
290
    int nmsgs, m, j, k;
 
291
 
 
292
    if(!isInteractive) {
 
293
        if(!zapMail)
 
294
            i_printfExit(MSG_FetchNotBackgnd);
 
295
    }
 
296
 
 
297
    if(!mailDir)
 
298
        i_printfExit(MSG_NoMailDir);
 
299
    if(chdir(mailDir))
 
300
        i_printfExit(MSG_NoDirChange, mailDir);
 
301
 
 
302
    if(!loadAddressBook())
 
303
        showErrorAbort();
 
304
 
 
305
    if(!mailConnect(host, a->inport, a->inssl))
 
306
        showErrorAbort();
 
307
    if(!serverGetLine(a->inssl))
 
308
        showErrorAbort();
 
309
    if(memcmp(serverLine, "+OK ", 4))
 
310
        i_printfExit(MSG_BadPopIntro, serverLine);
 
311
    sprintf(serverLine, "user %s%s", login, eol);
 
312
    serverPutGet(serverLine, a->inssl);
 
313
    if(pass) {                  /* I think this is always required */
 
314
        sprintf(serverLine, "pass %s%s", pass, eol);
 
315
        serverPutGet(serverLine, a->inssl);
 
316
    }                           /* password */
 
317
    if(memcmp(serverLine, "+OK", 3))
 
318
        i_printfExit(MSG_PopNotComplete, serverLine);
 
319
 
 
320
/* How many mail messages? */
 
321
    serverPutGet("stat\r\n", a->inssl);
 
322
    if(memcmp(serverLine, "+OK ", 4))
 
323
        i_printfExit(MSG_NoStatusMailBox, serverLine);
 
324
    nmsgs = atoi(serverLine + 4);
 
325
    if(!nmsgs) {
 
326
        i_puts(MSG_NoMail);
 
327
        serverClose(a->inssl);
 
328
        exit(0);
 
329
    }
 
330
 
 
331
    if(!(zapMail | passMail)) {
 
332
/* We look up a lot of hosts; make a tcp connect */
 
333
        sethostent(1);
 
334
/* the conect will drop when the program exits */
 
335
    }
 
336
 
 
337
    i_printf(MSG_MessagesX, nmsgs);
 
338
    if(zapMail && nmsgs > 300)
 
339
        nmsgs = 300;
 
340
 
 
341
    for(m = 1; m <= nmsgs; ++m) {
 
342
        const char *redirect = 0;       /* send mail elsewhere */
 
343
        char key;
 
344
        const char *atname;     /* name of attachment */
 
345
        bool delflag = false;   /* delete this mail */
 
346
        int exact_l;
 
347
        char *exact = initString(&exact_l);     /* the exact message */
 
348
        int exactf_l;
 
349
        char *exactf = 0;       /* utf8 formatted */
 
350
        int displine;
 
351
 
 
352
        if(zapMail)
 
353
            delflag = true;
 
354
        else {                  /* read the next message */
 
355
            char retrbuf[5000];
 
356
            bool retr1;
 
357
/* We need to clear out the editor, from the last message,
 
358
 * then read in this one, in its entirety.
 
359
 * This is probably a waste, since the user might delete a megabyte message
 
360
 * after seeing only the first paragraph, or even the subject,
 
361
 * but as for programming, it's easier to read the whole thing in right now. */
 
362
            if(lastMailInfo)
 
363
                freeMailInfo(lastMailInfo);
 
364
            lastMailInfo = 0;
 
365
            nzFree(lastMailText);
 
366
            lastMailText = 0;
 
367
            if(sessionList[1].lw)
 
368
                cxQuit(1, 2);
 
369
            cs = 0;
 
370
            linesReset();
 
371
            cxSwitch(1, false);
 
372
/* Now grab the entire message */
 
373
            sprintf(serverLine, "retr %d%s", m, eol);
 
374
            if(!serverPutLine(serverLine, a->inssl))
 
375
                showErrorAbort();
 
376
            retr1 = true;
 
377
            while(true) {
 
378
                int nr;
 
379
                if(a->inssl)
 
380
                    nr = ssl_read(retrbuf, sizeof (retrbuf));
 
381
                else
 
382
                    nr = tcp_read(mssock, retrbuf, sizeof (retrbuf));
 
383
                if(nr <= 0)
 
384
                    i_printfExit(MSG_ErrorReadMess, errno);
 
385
                if(retr1) {
 
386
/* add null, to make it easy to print the error message, if necessary */
 
387
                    if(nr < sizeof (retrbuf))
 
388
                        retrbuf[nr] = 0;
 
389
                    if(memcmp(retrbuf, "+OK", 3))
 
390
                        i_printfExit(MSG_ErrorFetchMess, m, retrbuf);
 
391
                    j = 3;
 
392
                    while(retrbuf[j] != '\n')
 
393
                        ++j;
 
394
                    ++j;
 
395
                    nr -= j;
 
396
                    memcpy(retrbuf, retrbuf + j, nr);
 
397
                    retr1 = false;
 
398
                }
 
399
                if(nr)
 
400
                    stringAndBytes(&exact, &exact_l, retrbuf, nr);
 
401
/* . by itself on a line ends the transmission */
 
402
                j = exact_l - 1;
 
403
                if(j < 0)
 
404
                    continue;
 
405
                if(exact[j] != '\n')
 
406
                    continue;
 
407
                --j;
 
408
                if(j >= 0 && exact[j] == '\r')
 
409
                    --j;
 
410
                if(j < 0)
 
411
                    continue;
 
412
                if(exact[j] != '.')
 
413
                    continue;
 
414
                if(!j)
 
415
                    break;
 
416
                if(exact[j - 1] == '\n')
 
417
                    break;
 
418
            }
 
419
            exact_l = j;
 
420
 
 
421
/* get rid of the dos returns */
 
422
            for(j = k = 0; j < exact_l; ++j) {
 
423
                if(exact[j] == '\r' && j < exact_l - 1 && exact[j + 1] == '\n')
 
424
                    continue;
 
425
                exact[k++] = exact[j];
 
426
            }
 
427
            exact_l = k;
 
428
            exact[k] = 0;
 
429
 
 
430
            iuReformat(exact, exact_l, &exactf, &exactf_l);
 
431
 
 
432
            if(exactf) {
 
433
                if(!addTextToBuffer((pst) exactf, exactf_l, 0))
 
434
                    showErrorAbort();
 
435
            } else {
 
436
                if(!addTextToBuffer((pst) exact, exact_l, 0))
 
437
                    showErrorAbort();
 
438
            }
 
439
 
 
440
            if(!unformatMail) {
 
441
                browseCurrentBuffer();
 
442
                if(!passMail) {
 
443
                    mailIsBlack = onBlacklist();
 
444
                    redirect = mailRedirect(lastMailInfo->to,
 
445
                       lastMailInfo->from, lastMailInfo->reply,
 
446
                       lastMailInfo->subject);
 
447
                }
 
448
                if(redirect) {
 
449
                    key = 'w';
 
450
                    if(*redirect == '-')
 
451
                        ++redirect, key = 'u';
 
452
                    if(stringEqual(redirect, "x"))
 
453
                        i_puts(MSG_Junk);
 
454
                    else
 
455
                        printf("> %s\n", redirect);
 
456
                } else if((mailIsSpam | mailIsBlack) && spamCan) {
 
457
                    redirect = spamCan;
 
458
                    key = 'u';
 
459
                    i_printf(MSG_Spam);
 
460
                    if(lastMailInfo->from[0]) {
 
461
                        i_printf(MSG_From);
 
462
                        printf("%s", lastMailInfo->from);
 
463
                    } else if(lastMailInfo->reply[0]) {
 
464
                        i_printf(MSG_From);
 
465
                        printf("%s", lastMailInfo->reply);
 
466
                    }
 
467
                    nl();
 
468
                } else if(!nattach &&   /* drop empty mail message */
 
469
                   cw->dol -
 
470
                   (lastMailInfo->subject != 0) -
 
471
                   (lastMailInfo->from != 0) -
 
472
                   (lastMailInfo->reply != 0) <= 1) {
 
473
                    redirect = "x";
 
474
                    i_puts(MSG_Empty);
 
475
                }
 
476
            }
 
477
            if(redirect)
 
478
                delflag = true;
 
479
 
 
480
/* display the next page of mail and get a command from the keyboard */
 
481
            displine = 1;
 
482
            while(true) {
 
483
                if(!delflag) {  /* show next page */
 
484
                  nextpage:
 
485
                    if(displine <= cw->dol) {
 
486
                        for(j = 0; j < 20 && displine <= cw->dol;
 
487
                           ++j, ++displine) {
 
488
                            char *showline = (char *)fetchLine(displine, 1);
 
489
                            k = pstLength((pst) showline);
 
490
                            showline[--k] = 0;
 
491
                            printf("%s\n", showline);
 
492
                            nzFree(showline);
 
493
                        }
 
494
                    }
 
495
                }
 
496
 
 
497
/* get key command */
 
498
                while(true) {
 
499
                    if(!delflag) {
 
500
/* interactive prompt depends on whether there is more text or not */
 
501
                        printf("%c ", displine > cw->dol ? '?' : '*');
 
502
                        fflush(stdout);
 
503
                        key = getLetter("qx? nwkudijJ");
 
504
                        printf("\b\b\b");
 
505
                        fflush(stdout);
 
506
 
 
507
                        switch (key) {
 
508
                        case 'q':
 
509
                            i_puts(MSG_Quit);
 
510
                            serverClose(a->inssl);
 
511
                        case 'x':
 
512
                            exit(0);
 
513
                        case 'n':
 
514
                            i_puts(MSG_Next);
 
515
                            goto afterinput;
 
516
                        case 'd':
 
517
                            i_puts(MSG_Delete);
 
518
                            delflag = true;
 
519
                            goto afterinput;
 
520
                        case 'i':
 
521
                            i_puts(MSG_IPDelete);
 
522
                            if(!cw->iplist || cw->iplist[0] == -1) {
 
523
                                if(passMail)
 
524
                                    i_puts(MSG_POption);
 
525
                                else
 
526
                                    i_puts(ipbFile ? MSG_None :
 
527
                                       MSG_NoBlackList);
 
528
                            } else {
 
529
                                IP32bit addr;
 
530
                                for(k = 0; (addr = cw->iplist[k]) != NULL_IP;
 
531
                                   ++k) {
 
532
                                    puts(tcp_ip_dots(addr));
 
533
                                    if(nipblack == MAXIPBLACK)
 
534
                                        continue;
 
535
                                    ipblacklist[nipblack] = addr;
 
536
                                    ipblackmask[nipblack] = 0xffffffff;
 
537
                                    ipblackcomp[nipblack] = false;
 
538
                                    ++nipblack;
 
539
                                }
 
540
                            }
 
541
                            delflag = true;
 
542
                            goto afterinput;
 
543
                        case 'j':
 
544
                        case 'J':
 
545
                            i_puts(MSG_Junk);
 
546
                            if(!junkSubject(lastMailInfo->subject, key))
 
547
                                continue;
 
548
                            delflag = true;
 
549
                            goto afterinput;
 
550
                        case ' ':
 
551
                            if(displine > cw->dol)
 
552
                                i_puts(MSG_EndMessage);
 
553
                            goto nextpage;
 
554
                        case '?':
 
555
                            i_puts(MSG_MailHelp);
 
556
                            continue;
 
557
                        case 'k':
 
558
                        case 'w':
 
559
                        case 'u':
 
560
                            break;
 
561
                        default:
 
562
                            i_puts(MSG_NYI);
 
563
                            continue;
 
564
                        }       /* switch */
 
565
                    }
 
566
 
 
567
                    /* delflag or not */
 
568
                    /* At this point we're saving the mail somewhere. */
 
569
                    if(key != 'k')
 
570
                        delflag = true;
 
571
                    atname = redirect;
 
572
                  savemail:
 
573
                    if(!atname)
 
574
                        atname = getFileName(0, false);
 
575
                    if(!stringEqual(atname, "x")) {
 
576
                        char exists = fileTypeByName(atname, false);
 
577
                        int fsize;      /* file size */
 
578
                        int fh =
 
579
                           open(atname, O_WRONLY | O_TEXT | O_CREAT | O_APPEND,
 
580
                           0666);
 
581
                        if(fh < 0) {
 
582
                            i_printf(MSG_NoCreate, atname);
 
583
                            goto savemail;
 
584
                        }
 
585
                        if(exists)
 
586
                            write(fh,
 
587
                               "======================================================================\n",
 
588
                               71);
 
589
                        if(key == 'u' || unformatMail) {
 
590
                            if(write(fh, exact, exact_l) < exact_l) {
 
591
                              badsave:
 
592
                                i_printf(MSG_NoWrite, atname);
 
593
                                close(fh);
 
594
                                goto savemail;
 
595
                            }
 
596
                            close(fh);
 
597
                            fsize = exact_l;
 
598
                        } else {
 
599
                            fsize = 0;
 
600
                            for(j = 1; j <= cw->dol; ++j) {
 
601
                                char *showline = (char *)fetchLine(j, 1);
 
602
                                int len = pstLength((pst) showline);
 
603
                                if(write(fh, showline, len) < len)
 
604
                                    goto badsave;
 
605
                                nzFree(showline);
 
606
                                fsize += len;
 
607
                            }   /* loop over lines */
 
608
                            close(fh);
 
609
                            if(nattach)
 
610
                                writeAttachments(lastMailInfo);
 
611
                        }       /* unformat or format */
 
612
                        if(atname != spamCan) {
 
613
                            i_printf(MSG_MailSaved, fsize);
 
614
                            if(exists)
 
615
                                i_printf(MSG_Appended);
 
616
                            nl();
 
617
                        }
 
618
                    }           /* saving to a real file */
 
619
                    goto afterinput;
 
620
 
 
621
                }               /* key commands */
 
622
            }                   /* paging through the message */
 
623
        }                       /* interactive or zap */
 
624
      afterinput:
 
625
 
 
626
        if(delflag) {
 
627
/* Remember, it isn't really gone until you quit the session.
 
628
 * So if you didn't mean to delete, type x to exit abruptly,
 
629
 * then fetch your mail again. */
 
630
            sprintf(serverLine, "dele %d%s", m, eol);
 
631
            if(!serverPutLine(serverLine, a->inssl))
 
632
                showErrorAbort();
 
633
            if(!serverGetLine(a->inssl))
 
634
                i_printfExit(MSG_MailTimeOver);
 
635
            if(memcmp(serverLine, "+OK", 3))
 
636
                i_printfExit(MSG_UnableDelMail, serverLine);
 
637
        }
 
638
        /* deleted */
 
639
        nzFree(exact);
 
640
        nzFree(exactf);
 
641
    }                           /* loop over mail messages */
 
642
 
 
643
    if(zapMail)
 
644
        printf("%d\n", nmsgs);
 
645
    serverClose(a->inssl);
 
646
    exit(0);
 
647
}                               /* fetchMail */
 
648
 
 
649
/* Here are the common keywords for mail header lines.
 
650
 * These are in alphabetical order, so you can stick more in as you find them.
 
651
 * The more words we have, the more accurate the test. */
 
652
static const char *const mhwords[] = {
 
653
    "action:",
 
654
    "arrival-date:",
 
655
    "bcc:",
 
656
    "cc:",
 
657
    "content-transfer-encoding:",
 
658
    "content-type:",
 
659
    "date:",
 
660
    "delivered-to:",
 
661
    "errors-to:",
 
662
    "final-recipient:",
 
663
    "from:",
 
664
    "importance:",
 
665
    "last-attempt-date:",
 
666
    "list-id:",
 
667
    "mailing-list:",
 
668
    "message-id:",
 
669
    "mime-version:",
 
670
    "precedence:",
 
671
    "received:",
 
672
    "remote-mta:",
 
673
    "reply-to:",
 
674
    "reporting-mta:",
 
675
    "return-path:",
 
676
    "sender:",
 
677
    "sent:",
 
678
    "status:",
 
679
    "subject:",
 
680
    "to:",
 
681
    "user-agent:",
 
682
    "x-beenthere:",
 
683
    "x-comment:",
 
684
    "x-loop:",
 
685
    "x-mailer:",
 
686
    "x-mailman-version:",
 
687
    "x-mdaemon-deliver-to:",
 
688
    "x-mdremoteip:",
 
689
    "x-mimeole:",
 
690
    "x-ms-tnef-correlator:",
 
691
    "x-msmail-priority:",
 
692
    "x-originating-ip:",
 
693
    "x-priority:",
 
694
    "x-return-path:",
 
695
    "X-Spam-Checker-Version:",
 
696
    "x-spam-level:",
 
697
    "x-spam-msg-id:",
 
698
    "X-SPAM-Msg-Sniffer-Result:",
 
699
    "x-spam-processed:",
 
700
    "x-spam-status:",
 
701
    "x-uidl:",
 
702
    0
 
703
};
 
704
 
 
705
/* Before we render a mail message, let's make sure it looks like email.
 
706
 * This is similar to htmlTest() in html.c. */
 
707
bool
 
708
emailTest(void)
 
709
{
 
710
    int i, j, k, n;
 
711
 
 
712
/* This is a very simple test - hopefully not too simple.
 
713
 * The first 20 non-indented lines have to look like mail header lines,
 
714
 * with at least half the keywords recognized. */
 
715
    for(i = 1, j = k = 0; i <= cw->dol && j < 20; ++i) {
 
716
        char *q;
 
717
        char *p = (char *)fetchLine(i, -1);
 
718
        char first = *p;
 
719
        if(first == '\n' || first == '\r' && p[1] == '\n')
 
720
            break;
 
721
        if(first == ' ' || first == '\t')
 
722
            continue;
 
723
        ++j;                    /* nonindented line */
 
724
        for(q = p; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
 
725
        if(q == p)
 
726
            continue;
 
727
        if(*q++ != ':')
 
728
            continue;
 
729
/* X-Whatever is a mail header word */
 
730
        if(q - p >= 8 && p[1] == '-' && toupper(p[0]) == 'X') {
 
731
            ++k;
 
732
        } else {
 
733
            for(n = 0; mhwords[n]; ++n)
 
734
                if(memEqualCI(mhwords[n], p, q - p))
 
735
                    break;
 
736
            if(mhwords[n])
 
737
                ++k;
 
738
        }
 
739
        if(k >= 4 && k * 2 >= j)
 
740
            return true;
 
741
    }                           /* loop over lines */
 
742
 
 
743
    return false;
 
744
}                               /* emailTest */
 
745
 
 
746
static uchar
 
747
unb64(char c)
 
748
{
 
749
    if(isupperByte(c))
 
750
        return c - 'A';
 
751
    if(islowerByte(c))
 
752
        return c - ('a' - 26);
 
753
    if(isdigitByte(c))
 
754
        return c - ('0' - 52);
 
755
    if(c == '+')
 
756
        return 62;
 
757
    if(c == '/')
 
758
        return 63;
 
759
    return 64;                  /* error */
 
760
}                               /* unb64 */
 
761
 
 
762
static void
 
763
unpack64(struct MHINFO *w)
 
764
{
 
765
    uchar val, leftover, mod;
 
766
    bool equals;
 
767
    char c, *q, *r;
 
768
/* Since this is a copy, and the unpacked version is always
 
769
 * smaller, just unpack it inline. */
 
770
    mod = 0;
 
771
    equals = false;
 
772
    for(q = r = w->start; q < w->end; ++q) {
 
773
        c = *q;
 
774
        if(isspaceByte(c))
 
775
            continue;
 
776
        if(equals) {
 
777
            if(c == '=')
 
778
                continue;
 
779
            runningError(MSG_AttAfterChars);
 
780
            w->error64 = EXTRA64;
 
781
            break;
 
782
        }
 
783
        if(c == '=') {
 
784
            equals = true;
 
785
            continue;
 
786
        }
 
787
        val = unb64(c);
 
788
        if(val & 64) {
 
789
            runningError(MSG_AttBad64);
 
790
            w->error64 = BAD64;
 
791
            break;
 
792
        }
 
793
        if(mod == 0) {
 
794
            leftover = val << 2;
 
795
        } else if(mod == 1) {
 
796
            *r++ = (leftover | (val >> 4));
 
797
            leftover = val << 4;
 
798
        } else if(mod == 2) {
 
799
            *r++ = (leftover | (val >> 2));
 
800
            leftover = val << 6;
 
801
        } else {
 
802
            *r++ = (leftover | val);
 
803
        }
 
804
        ++mod;
 
805
        mod &= 3;
 
806
    }
 
807
    w->end = r;
 
808
}                               /* unpack64 */
 
809
 
 
810
static void
 
811
unpackQP(struct MHINFO *w)
 
812
{
 
813
    uchar val;
 
814
    char c, d, *q, *r;
 
815
    for(q = r = w->start; q < w->end; ++q) {
 
816
        c = *q;
 
817
        if(c != '=') {
 
818
            *r++ = c;
 
819
            continue;
 
820
        }
 
821
        c = *++q;
 
822
        if(c == '\n')
 
823
            continue;
 
824
        d = q[1];
 
825
        if(isxdigit(c) && isxdigit(d)) {
 
826
            d = fromHex(c, d);
 
827
            if(d == 0)
 
828
                d = ' ';
 
829
            *r++ = d;
 
830
            ++q;
 
831
            continue;
 
832
        }
 
833
        --q;
 
834
        *r++ = '=';
 
835
    }
 
836
    w->end = r;
 
837
    *r = 0;
 
838
}                               /* unpackQP */
 
839
 
 
840
/* Look for the name of the attachment and boundary */
 
841
static void
 
842
ctExtras(struct MHINFO *w, const char *s, const char *t)
 
843
{
 
844
    char quote;
 
845
    const char *q, *al, *ar;
 
846
 
 
847
    if(w->ct < CT_MULTI) {
 
848
        quote = 0;
 
849
        for(q = s + 1; q < t; ++q) {
 
850
            if(isalnumByte(q[-1]))
 
851
                continue;
 
852
/* could be name= or filename= */
 
853
            if(memEqualCI(q, "file", 4))
 
854
                q += 4;
 
855
            if(!memEqualCI(q, "name=", 5))
 
856
                continue;
 
857
            q += 5;
 
858
            if(*q == '"') {
 
859
                quote = *q;
 
860
                ++q;
 
861
            }
 
862
            for(al = q; q < t; ++q) {
 
863
                if(*q == '"')
 
864
                    break;
 
865
                if(quote)
 
866
                    continue;
 
867
                if(strchr(",; \t", *q))
 
868
                    break;
 
869
            }
 
870
            ar = q;
 
871
            if(ar - al >= MHLINE)
 
872
                ar = al + MHLINE - 1;
 
873
            strncpy(w->cfn, al, ar - al);
 
874
            break;
 
875
        }
 
876
    }
 
877
    /* regular file */
 
878
    if(w->ct >= CT_MULTI) {
 
879
        quote = 0;
 
880
        for(q = s + 1; q < t; ++q) {
 
881
            if(isalnumByte(q[-1]))
 
882
                continue;
 
883
            if(!memEqualCI(q, "boundary=", 9))
 
884
                continue;
 
885
            q += 9;
 
886
            if(*q == '"') {
 
887
                quote = *q;
 
888
                ++q;
 
889
            }
 
890
            for(al = q; q < t; ++q) {
 
891
                if(*q == '"')
 
892
                    break;
 
893
                if(quote)
 
894
                    continue;
 
895
                if(strchr(",; \t", *q))
 
896
                    break;
 
897
            }
 
898
            ar = q;
 
899
            w->boundlen = ar - al;
 
900
            strncpy(w->boundary, al, ar - al);
 
901
            break;
 
902
        }
 
903
    }                           /* multi or alt */
 
904
}                               /* ctExtras */
 
905
 
 
906
static void
 
907
isoDecode(char *vl, char **vrp)
 
908
{
 
909
    char *vr = *vrp;
 
910
    char *start, *end;          /* section being decoded */
 
911
    char *s, *t, c, d, code;
 
912
    int len;
 
913
    uchar val, leftover, mod;
 
914
 
 
915
    start = vl;
 
916
  restart:
 
917
    start = strstr(start, "=?");
 
918
    if(!start || start >= vr)
 
919
        goto finish;
 
920
    start += 2;
 
921
    if(!memEqualCI(start, "iso-", 4) &&
 
922
       !memEqualCI(start, "utf-", 4) &&
 
923
       !memEqualCI(start, "gb", 2) && !memEqualCI(start, "windows-", 8))
 
924
        goto restart;
 
925
    s = strchr(start, '?');
 
926
    if(!s || s > vr - 5 || s[2] != '?')
 
927
        goto restart;
 
928
    code = s[1];
 
929
    code = toupper(code);
 
930
    if(code != 'Q' && code != 'B')
 
931
        goto restart;
 
932
    s += 3;
 
933
    end = strstr(s, "?=");
 
934
    if(!end || end > vr - 2)
 
935
        goto restart;
 
936
 
 
937
    t = start - 2;
 
938
 
 
939
    if(code == 'Q') {
 
940
        while(s < end) {
 
941
            c = *s++;
 
942
            if(c == '=') {
 
943
                c = *s;
 
944
                d = s[1];
 
945
                if(isxdigit(c) && isxdigit(d)) {
 
946
                    d = fromHex(c, d);
 
947
                    *t++ = d;
 
948
                    s += 2;
 
949
                    continue;
 
950
                }
 
951
                c = '=';
 
952
            }
 
953
            *t++ = c;
 
954
        }
 
955
        goto copy;
 
956
    }
 
957
 
 
958
/* base64 */
 
959
    mod = 0;
 
960
    for(; s < end; ++s) {
 
961
        c = *s;
 
962
        if(isspaceByte(c))
 
963
            continue;
 
964
        if(c == '=')
 
965
            continue;
 
966
        val = unb64(c);
 
967
        if(val & 64)
 
968
            val = 0;            /* ignore errors here */
 
969
        if(mod == 0) {
 
970
            leftover = val << 2;
 
971
        } else if(mod == 1) {
 
972
            *t++ = (leftover | (val >> 4));
 
973
            leftover = val << 4;
 
974
        } else if(mod == 2) {
 
975
            *t++ = (leftover | (val >> 2));
 
976
            leftover = val << 6;
 
977
        } else {
 
978
            *t++ = (leftover | val);
 
979
        }
 
980
        ++mod;
 
981
        mod &= 3;
 
982
    }
 
983
 
 
984
  copy:
 
985
    s += 2;
 
986
    start = t;
 
987
    len = vr - s;
 
988
    if(len)
 
989
        memcpy(t, s, len);
 
990
    vr = t + len;
 
991
    goto restart;
 
992
 
 
993
  finish:
 
994
    for(s = vl; s < vr; ++s) {
 
995
        c = *s;
 
996
        if(c == 0 || c == '\t')
 
997
            *s = ' ';
 
998
    }
 
999
 
 
1000
    *vrp = vr;
 
1001
}                               /* isoDecode */
 
1002
 
 
1003
/* mail header reformat, to/from utf8 */
 
1004
static void
 
1005
mhReformat(char *line)
 
1006
{
 
1007
    char *tbuf;
 
1008
    int tlen = strlen(line);
 
1009
    iuReformat(line, tlen, &tbuf, &tlen);
 
1010
    if(!tbuf)
 
1011
        return;
 
1012
    if(tlen >= MHLINE)
 
1013
        tbuf[MHLINE - 1] = 0;
 
1014
    strcpy(line, tbuf);
 
1015
    nzFree(tbuf);
 
1016
}                               /* mhReformat */
 
1017
 
 
1018
static void
 
1019
extractLessGreater(char *s)
 
1020
{
 
1021
    char *vl, *vr;
 
1022
    vl = strchr(s, '<');
 
1023
    vr = strchr(s, '>');
 
1024
    if(vl && vr && vl < vr) {
 
1025
        *vr = 0;
 
1026
        strcpy(s, vl + 1);
 
1027
    }
 
1028
}                               /* extractLessGreater */
 
1029
 
 
1030
/* Now that we know it's mail, see what information we can
 
1031
 * glean from the headers.
 
1032
 * Returns a pointer to an allocated MHINFO structure.
 
1033
 * This routine is recursive. */
 
1034
static struct MHINFO *
 
1035
headerGlean(char *start, char *end)
 
1036
{
 
1037
    char *s, *t, *q;
 
1038
    char *vl, *vr;              /* value left and value right */
 
1039
    struct MHINFO *w;
 
1040
    int j, k, n;
 
1041
    char linetype = 0;
 
1042
 
 
1043
/* defaults */
 
1044
    w = allocZeroMem(sizeof (struct MHINFO));
 
1045
    initList(&w->components);
 
1046
    w->ct = CT_OTHER;
 
1047
    w->ce = CE_8BIT;
 
1048
    w->andOthers = false;
 
1049
    w->tolist = initString(&w->tolen);
 
1050
    w->cclist = initString(&w->cclen);
 
1051
    w->start = start, w->end = end;
 
1052
 
 
1053
    for(s = start; s < end; s = t + 1) {
 
1054
        char quote;
 
1055
        char first = *s;
 
1056
        t = strchr(s, '\n');
 
1057
        if(!t)
 
1058
            t = end - 1;        /* should never happen */
 
1059
        if(t == s)
 
1060
            break;              /* empty line */
 
1061
 
 
1062
        if(first == ' ' || first == '\t') {
 
1063
            if(linetype == 'c')
 
1064
                ctExtras(w, s, t);
 
1065
            if(linetype == 't')
 
1066
                stringAndBytes(&w->tolist, &w->tolen, s, t - s);
 
1067
            if(linetype == 'y')
 
1068
                stringAndBytes(&w->cclist, &w->cclen, s, t - s);
 
1069
            continue;
 
1070
        }
 
1071
 
 
1072
/* find the lead word */
 
1073
        for(q = s; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
 
1074
        if(q == s)
 
1075
            continue;           /* should never happen */
 
1076
        if(*q++ != ':')
 
1077
            continue;           /* should never happen */
 
1078
        for(vl = q; *vl == ' ' || *vl == '\t'; ++vl) ;
 
1079
        for(vr = t; vr > vl && (vr[-1] == ' ' || vr[-1] == '\t'); --vr) ;
 
1080
        if(vr == vl)
 
1081
            continue;           /* empty */
 
1082
 
 
1083
/* too long? */
 
1084
        if(vr - vl > MHLINE - 1)
 
1085
            vr = vl + MHLINE - 1;
 
1086
 
 
1087
/* This is sort of a switch statement on the word */
 
1088
        if(memEqualCI(s, "subject:", q - s)) {
 
1089
            linetype = 's';
 
1090
            if(w->subject[0])
 
1091
                continue;
 
1092
/* get rid of forward/reply prefixes */
 
1093
            for(q = vl; q < vr; ++q) {
 
1094
                static const char *const prefix[] = {
 
1095
                    "re", "sv", "fwd", 0
 
1096
                };
 
1097
                if(!isalphaByte(*q))
 
1098
                    continue;
 
1099
                if(q > vl && isalnumByte(q[-1]))
 
1100
                    continue;
 
1101
                for(j = 0; prefix[j]; ++j)
 
1102
                    if(memEqualCI(q, prefix[j], strlen(prefix[j])))
 
1103
                        break;
 
1104
                if(!prefix[j])
 
1105
                    continue;
 
1106
                j = strlen(prefix[j]);
 
1107
                if(!strchr(":-,;", q[j]))
 
1108
                    continue;
 
1109
                ++j;
 
1110
                while(q + j < vr && q[j] == ' ')
 
1111
                    ++j;
 
1112
                memcpy(q, q + j, vr - q - j);
 
1113
                vr -= j;
 
1114
                --q;            /* try again */
 
1115
            }
 
1116
            isoDecode(vl, &vr);
 
1117
            strncpy(w->subject, vl, vr - vl);
 
1118
/* If the subject is really long, spreads onto the next line,
 
1119
 * I'll just use ... */
 
1120
            if(t < end - 1 && (t[1] == ' ' || t[1] == '\t'))
 
1121
                strcat(w->subject, "...");
 
1122
            mhReformat(w->subject);
 
1123
            continue;
 
1124
        }
 
1125
 
 
1126
        if(memEqualCI(s, "reply-to:", q - s)) {
 
1127
            linetype = 'r';
 
1128
            if(!w->reply[0])
 
1129
                strncpy(w->reply, vl, vr - vl);
 
1130
            continue;
 
1131
        }
 
1132
 
 
1133
        if(memEqualCI(s, "message-id:", q - s)) {
 
1134
            linetype = 'm';
 
1135
            if(!w->mid[0])
 
1136
                strncpy(w->mid, vl, vr - vl);
 
1137
            continue;
 
1138
        }
 
1139
 
 
1140
        if(memEqualCI(s, "references:", q - s)) {
 
1141
            linetype = 'e';
 
1142
            if(!w->ref[0])
 
1143
                strncpy(w->ref, vl, vr - vl);
 
1144
            continue;
 
1145
        }
 
1146
 
 
1147
        if(memEqualCI(s, "from:", q - s)) {
 
1148
            linetype = 'f';
 
1149
            if(w->from[0])
 
1150
                continue;
 
1151
            isoDecode(vl, &vr);
 
1152
            strncpy(w->from, vl, vr - vl);
 
1153
            mhReformat(w->from);
 
1154
            continue;
 
1155
        }
 
1156
 
 
1157
        if(memEqualCI(s, "date:", q - s) || memEqualCI(s, "sent:", q - s)) {
 
1158
            linetype = 'd';
 
1159
            if(w->date[0])
 
1160
                continue;
 
1161
/* don't need the weekday, seconds, or timezone */
 
1162
            if(vr - vl > 5 &&
 
1163
               isalphaByte(vl[0]) && isalphaByte(vl[1]) && isalphaByte(vl[2]) &&
 
1164
               vl[3] == ',' && vl[4] == ' ')
 
1165
                vl += 5;
 
1166
            strncpy(w->date, vl, vr - vl);
 
1167
            q = strrchr(w->date, ':');
 
1168
            if(q)
 
1169
                *q = 0;
 
1170
            continue;
 
1171
        }
 
1172
 
 
1173
        if(memEqualCI(s, "to:", q - s)) {
 
1174
            linetype = 't';
 
1175
            if(w->tolen)
 
1176
                stringAndChar(&w->tolist, &w->tolen, ',');
 
1177
            stringAndBytes(&w->tolist, &w->tolen, q, vr - q);
 
1178
            if(w->to[0])
 
1179
                continue;
 
1180
            strncpy(w->to, vl, vr - vl);
 
1181
/* Only retain the first recipient */
 
1182
            quote = 0;
 
1183
            for(q = w->to; *q; ++q) {
 
1184
                if(*q == ',' && !quote) {
 
1185
                    w->andOthers = true;
 
1186
                    break;
 
1187
                }
 
1188
                if(*q == '"') {
 
1189
                    if(!quote)
 
1190
                        quote = *q;
 
1191
                    else if(quote == *q)
 
1192
                        quote = 0;
 
1193
                    continue;
 
1194
                }
 
1195
                if(*q == '<') {
 
1196
                    if(!quote)
 
1197
                        quote = *q;
 
1198
                    continue;
 
1199
                }
 
1200
                if(*q == '>') {
 
1201
                    if(quote == '<')
 
1202
                        quote = 0;
 
1203
                    continue;
 
1204
                }
 
1205
            }
 
1206
            *q = 0;             /* cut it off at the comma */
 
1207
            continue;
 
1208
        }
 
1209
 
 
1210
        if(memEqualCI(s, "cc:", q - s)) {
 
1211
            linetype = 'y';
 
1212
            if(w->cclen)
 
1213
                stringAndChar(&w->cclist, &w->cclen, ',');
 
1214
            stringAndBytes(&w->cclist, &w->cclen, q, vr - q);
 
1215
            w->andOthers = true;
 
1216
            continue;
 
1217
        }
 
1218
 
 
1219
        if(memEqualCI(s, "content-type:", q - s)) {
 
1220
            linetype = 'c';
 
1221
            if(memEqualCI(vl, "text", 4))
 
1222
                w->ct = CT_RICH;
 
1223
            if(memEqualCI(vl, "text/html", 9))
 
1224
                w->ct = CT_HTML;
 
1225
            if(memEqualCI(vl, "text/plain", 10))
 
1226
                w->ct = CT_TEXT;
 
1227
            if(memEqualCI(vl, "application", 11))
 
1228
                w->ct = CT_APPLIC;
 
1229
            if(memEqualCI(vl, "multipart", 9))
 
1230
                w->ct = CT_MULTI;
 
1231
            if(memEqualCI(vl, "multipart/alternative", 21))
 
1232
                w->ct = CT_ALT;
 
1233
 
 
1234
            ctExtras(w, s, t);
 
1235
            continue;
 
1236
        }
 
1237
 
 
1238
        if(memEqualCI(s, "content-transfer-encoding:", q - s)) {
 
1239
            linetype = 'e';
 
1240
            if(memEqualCI(vl, "quoted-printable", 16))
 
1241
                w->ce = CE_QP;
 
1242
            if(memEqualCI(vl, "7bit", 4))
 
1243
                w->ce = CE_7BIT;
 
1244
            if(memEqualCI(vl, "8bit", 4))
 
1245
                w->ce = CE_8BIT;
 
1246
            if(memEqualCI(vl, "base64", 6))
 
1247
                w->ce = CE_64;
 
1248
            continue;
 
1249
        }
 
1250
 
 
1251
        linetype = 0;
 
1252
    }                           /* loop over lines */
 
1253
 
 
1254
/* make sure there's room for a final nl */
 
1255
    stringAndChar(&w->tolist, &w->tolen, ' ');
 
1256
    stringAndChar(&w->cclist, &w->cclen, ' ');
 
1257
    extractEmailAddresses(w->tolist);
 
1258
    extractEmailAddresses(w->cclist);
 
1259
 
 
1260
    w->start = start = s + 1;
 
1261
 
 
1262
/* Fix up reply and from lines.
 
1263
 * From should be the name, reply the address. */
 
1264
    if(!w->from[0])
 
1265
        strcpy(w->from, w->reply);
 
1266
    if(!w->reply[0])
 
1267
        strcpy(w->reply, w->from);
 
1268
    if(w->from[0] == '"') {
 
1269
        strcpy(w->from, w->from + 1);
 
1270
        q = strchr(w->from, '"');
 
1271
        if(q)
 
1272
            *q = 0;
 
1273
    }
 
1274
    vl = strchr(w->from, '<');
 
1275
    vr = strchr(w->from, '>');
 
1276
    if(vl && vr && vl < vr) {
 
1277
        while(vl > w->from && vl[-1] == ' ')
 
1278
            --vl;
 
1279
        *vl = 0;
 
1280
    }
 
1281
    extractLessGreater(w->reply);
 
1282
/* get rid of (name) comment */
 
1283
    vl = strchr(w->reply, '(');
 
1284
    vr = strchr(w->reply, ')');
 
1285
    if(vl && vr && vl < vr) {
 
1286
        while(vl > w->reply && vl[-1] == ' ')
 
1287
            --vl;
 
1288
        *vl = 0;
 
1289
    }
 
1290
/* no @ means it's not an email address */
 
1291
    if(!strchr(w->reply, '@'))
 
1292
        w->reply[0] = 0;
 
1293
    if(stringEqual(w->reply, w->from))
 
1294
        w->from[0] = 0;
 
1295
    extractLessGreater(w->to);
 
1296
    extractLessGreater(w->mid);
 
1297
    extractLessGreater(w->ref);
 
1298
 
 
1299
    cutDuplicateEmails(w->tolist, w->cclist, w->reply);
 
1300
    if(debugLevel >= 5) {
 
1301
        puts("mail header analyzed");
 
1302
        printf("subject: %s\n", w->subject);
 
1303
        printf("from: %s\n", w->from);
 
1304
        printf("date: %s\n", w->date);
 
1305
        printf("reply: %s\n", w->reply);
 
1306
        printf("tolist: %s\n", w->tolist);
 
1307
        printf("cclist: %s\n", w->cclist);
 
1308
        printf("reference: %s\n", w->ref);
 
1309
        printf("message: %s\n", w->mid);
 
1310
        printf("boundary: %d|%s\n", w->boundlen, w->boundary);
 
1311
        printf("filename: %s\n", w->cfn);
 
1312
        printf("content %d/%d\n", w->ct, w->ce);
 
1313
    }
 
1314
 
 
1315
    if(w->ce == CE_QP)
 
1316
        unpackQP(w);
 
1317
    if(w->ce == CE_64)
 
1318
        unpack64(w);
 
1319
    if(w->ce == CE_64 && w->ct == CT_OTHER || w->ct == CT_APPLIC || w->cfn[0]) {
 
1320
        w->doAttach = true;
 
1321
        ++nattach;
 
1322
        q = w->cfn;
 
1323
        if(*q) {                /* name present */
 
1324
            if(stringEqual(q, "winmail.dat")) {
 
1325
                w->atimage = true;
 
1326
                ++nimages;
 
1327
            } else if((q = strrchr(q, '.'))) {
 
1328
                static const char *const imagelist[] = {
 
1329
                    "gif", "jpg", "tif", "bmp", "asc", "png", 0
 
1330
                };
 
1331
/* the asc isn't an image, it's a signature card. */
 
1332
/* Similarly for the winmail.dat */
 
1333
                if(stringInListCI(imagelist, q + 1) >= 0) {
 
1334
                    w->atimage = true;
 
1335
                    ++nimages;
 
1336
                }
 
1337
            }
 
1338
            if(!w->atimage && nattach == nimages + 1)
 
1339
                firstAttach = w->cfn;
 
1340
        }
 
1341
        return w;
 
1342
    }
 
1343
 
 
1344
/* loop over the mime components */
 
1345
    if(w->ct == CT_MULTI || w->ct == CT_ALT) {
 
1346
        char *lastbound = 0;
 
1347
        bool endmode = false;
 
1348
        struct MHINFO *child;
 
1349
/* We really need the -1 here, because sometimes the boundary will
 
1350
 * be the very first thing in the message body. */
 
1351
        s = w->start - 1;
 
1352
        while(!endmode && (t = strstr(s, "\n--")) && t < end) {
 
1353
            if(memcmp(t + 3, w->boundary, w->boundlen)) {
 
1354
                s = t + 3;
 
1355
                continue;
 
1356
            }
 
1357
            q = t + 3 + w->boundlen;
 
1358
            while(*q == '-')
 
1359
                endmode = true, ++q;
 
1360
            if(*q == '\n')
 
1361
                ++q;
 
1362
            debugPrint(5, "boundary found at offset %d", t - w->start);
 
1363
            if(lastbound) {
 
1364
                child = headerGlean(lastbound, t);
 
1365
                addToListBack(&w->components, child);
 
1366
            }
 
1367
            s = lastbound = q;
 
1368
        }
 
1369
        w->start = w->end = 0;
 
1370
        return w;
 
1371
    }
 
1372
 
 
1373
    /* mime or alt */
 
1374
    /* Scan through, we might have a mail message included inline */
 
1375
    vl = 0;                     /* first mail header keyword line */
 
1376
    for(s = start; s < end; s = t + 1) {
 
1377
        char first = *s;
 
1378
        t = strchr(s, '\n');
 
1379
        if(!t)
 
1380
            t = end - 1;        /* should never happen */
 
1381
        if(t == s) {            /* empty line */
 
1382
            if(!vl)
 
1383
                continue;
 
1384
/* Do we have enough for a mail header? */
 
1385
            if(k >= 4 && k * 2 >= j) {
 
1386
                struct MHINFO *child = headerGlean(vl, end);
 
1387
                addToListBack(&w->components, child);
 
1388
                w->end = end = vl;
 
1389
                goto textonly;
 
1390
            }                   /* found mail message inside */
 
1391
            vl = 0;
 
1392
        }                       /* empty line */
 
1393
        if(first == ' ' || first == '\t')
 
1394
            continue;           /* indented */
 
1395
        for(q = s; isalnumByte(*q) || *q == '_' || *q == '-'; ++q) ;
 
1396
        if(q == s || *q != ':') {
 
1397
            vl = 0;
 
1398
            continue;
 
1399
        }
 
1400
/* looks like header: stuff */
 
1401
        if(!vl) {
 
1402
            vl = s;
 
1403
            j = k = 0;
 
1404
        }
 
1405
        ++j;
 
1406
        for(n = 0; mhwords[n]; ++n)
 
1407
            if(memEqualCI(mhwords[n], s, q - s))
 
1408
                break;
 
1409
        if(mhwords[n])
 
1410
            ++k;
 
1411
    }                           /* loop over lines */
 
1412
 
 
1413
/* Header could be at the very end */
 
1414
    if(vl && k >= 4 && k * 2 >= j) {
 
1415
        struct MHINFO *child = headerGlean(vl, end);
 
1416
        addToListBack(&w->components, child);
 
1417
        w->end = end = vl;
 
1418
    }
 
1419
 
 
1420
  textonly:
 
1421
/* Any additional processing of the text, from start to end, can go here. */
 
1422
/* Remove leading blank lines or lines with useless words */
 
1423
    for(s = start; s < end; s = t + 1) {
 
1424
        t = strchr(s, '\n');
 
1425
        if(!t)
 
1426
            t = end - 1;        /* should never happen */
 
1427
        vl = s, vr = t;
 
1428
        if(vr - vl >= 4 && memEqualCI(vr - 4, "<br>", 4))
 
1429
            vr -= 4;
 
1430
        while(vl < vr) {
 
1431
            if(isalnumByte(*vl))
 
1432
                break;
 
1433
            ++vl;
 
1434
        }
 
1435
        while(vl < vr) {
 
1436
            if(isalnumByte(vr[-1]))
 
1437
                break;
 
1438
            --vr;
 
1439
        }
 
1440
        if(vl == vr)
 
1441
            continue;           /* empty */
 
1442
        if(memEqualCI(vl, "forwarded message", vr - vl))
 
1443
            continue;
 
1444
        if(memEqualCI(vl, "original message", vr - vl))
 
1445
            continue;
 
1446
        break;                  /* something real */
 
1447
    }
 
1448
    w->start = start = s;
 
1449
 
 
1450
    w->ne = false;
 
1451
 
 
1452
    return w;
 
1453
}                               /* headerGlean */
 
1454
 
 
1455
static char *
 
1456
headerShow(struct MHINFO *w, bool top)
 
1457
{
 
1458
    static char buf[(MHLINE + 30) * 4];
 
1459
    static char lastsubject[MHLINE];
 
1460
    char *s;
 
1461
    bool lines = false;
 
1462
    buf[0] = 0;
 
1463
 
 
1464
    if(!(w->subject[0] | w->from[0] | w->reply[0]))
 
1465
        return buf;
 
1466
 
 
1467
    if(!top) {
 
1468
        strcpy(buf, "Message");
 
1469
        lines = true;
 
1470
        if(w->from[0]) {
 
1471
            strcat(buf, " from ");
 
1472
            strcat(buf, w->from);
 
1473
        }
 
1474
        if(w->subject[0]) {
 
1475
            if(stringEqual(w->subject, lastsubject)) {
 
1476
                strcat(buf, " with the same subject");
 
1477
            } else {
 
1478
                strcat(buf, " with subject: ");
 
1479
                strcat(buf, w->subject);
 
1480
            }
 
1481
        } else
 
1482
            strcat(buf, " with no subject");
 
1483
        if(mailIsHtml) {        /* trash & < > */
 
1484
            for(s = buf; *s; ++s) {
 
1485
/* This is quick and stupid */
 
1486
                if(*s == '<')
 
1487
                    *s = '(';
 
1488
                if(*s == '>')
 
1489
                    *s = ')';
 
1490
                if(*s == '&')
 
1491
                    *s = '*';
 
1492
            }
 
1493
        }
 
1494
/* need a dot at the end? */
 
1495
        s = buf + strlen(buf);
 
1496
        if(isalnumByte(s[-1]))
 
1497
            *s++ = '.';
 
1498
        strcpy(s, mailIsHtml ? "\n<br>" : "\n");
 
1499
        if(w->date[0]) {
 
1500
            strcat(buf, "Sent ");
 
1501
            strcat(buf, w->date);
 
1502
        }
 
1503
        if(w->reply) {
 
1504
            if(!w->date[0])
 
1505
                strcat(buf, "From ");
 
1506
            else
 
1507
                strcat(buf, " from ");
 
1508
            strcat(buf, w->reply);
 
1509
        }
 
1510
        if(w->date[0] | w->reply[0]) {  /* second line */
 
1511
            strcat(buf, "\n");
 
1512
        }
 
1513
    } else {
 
1514
 
 
1515
/* This is at the top of the file */
 
1516
        if(w->subject[0]) {
 
1517
            sprintf(buf, "Subject: %s\n", w->subject);
 
1518
            lines = true;
 
1519
        }
 
1520
        if(nattach && ismc) {
 
1521
            char atbuf[20];
 
1522
            if(lines & mailIsHtml)
 
1523
                strcat(buf, "<br>");
 
1524
            lines = true;
 
1525
            if(nimages) {
 
1526
                sprintf(atbuf, "%d images\n", nimages);
 
1527
                if(nimages == 1)
 
1528
                    strcpy(atbuf, "1 image");
 
1529
                strcat(buf, atbuf);
 
1530
                if(nattach > nimages)
 
1531
                    strcat(buf, " + ");
 
1532
            }
 
1533
            if(nattach == nimages + 1) {
 
1534
                strcat(buf, "1 attachment");
 
1535
                if(firstAttach && firstAttach[0]) {
 
1536
                    strcat(buf, " ");
 
1537
                    strcat(buf, firstAttach);
 
1538
                }
 
1539
            }
 
1540
            if(nattach > nimages + 1) {
 
1541
                sprintf(atbuf, "%d attachments\n", nattach - nimages);
 
1542
                strcat(buf, atbuf);
 
1543
            }
 
1544
            strcat(buf, "\n");
 
1545
        }
 
1546
        /* attachments */
 
1547
        if(w->to[0] && !ismc) {
 
1548
            if(lines & mailIsHtml)
 
1549
                strcat(buf, "<br>");
 
1550
            lines = true;
 
1551
            strcat(buf, "To ");
 
1552
            strcat(buf, w->to);
 
1553
            if(w->andOthers)
 
1554
                strcat(buf, " and others");
 
1555
            strcat(buf, "\n");
 
1556
        }
 
1557
        if(w->from[0]) {
 
1558
            if(lines & mailIsHtml)
 
1559
                strcat(buf, "<br>");
 
1560
            lines = true;
 
1561
            strcat(buf, "From ");
 
1562
            strcat(buf, w->from);
 
1563
            strcat(buf, "\n");
 
1564
        }
 
1565
        if(w->date[0] && !ismc) {
 
1566
            if(lines & mailIsHtml)
 
1567
                strcat(buf, "<br>");
 
1568
            lines = true;
 
1569
            strcat(buf, "Mail sent ");
 
1570
            strcat(buf, w->date);
 
1571
            strcat(buf, "\n");
 
1572
        }
 
1573
        if(w->reply[0]) {
 
1574
            if(lines & mailIsHtml)
 
1575
                strcat(buf, "<br>");
 
1576
            lines = true;
 
1577
            strcat(buf, "Reply to ");
 
1578
            strcat(buf, w->reply);
 
1579
            strcat(buf, "\n");
 
1580
        }
 
1581
    }
 
1582
 
 
1583
    if(lines)
 
1584
        strcat(buf, mailIsHtml ? "<P>\n" : "\n");
 
1585
    strcpy(lastsubject, w->subject);
 
1586
    return buf;
 
1587
}                               /* headerShow */
 
1588
 
 
1589
/* Depth first block of text determines the type */
 
1590
static int
 
1591
mailTextType(struct MHINFO *w)
 
1592
{
 
1593
    struct MHINFO *v;
 
1594
    int texttype = CT_OTHER, rc;
 
1595
 
 
1596
    if(w->doAttach)
 
1597
        return CT_OTHER;
 
1598
 
 
1599
/* jump right into the hard part, multi/alt */
 
1600
    if(w->ct >= CT_MULTI) {
 
1601
        foreach(v, w->components) {
 
1602
            rc = mailTextType(v);
 
1603
            if(rc == CT_HTML)
 
1604
                return rc;
 
1605
            if(rc == CT_OTHER)
 
1606
                continue;
 
1607
            if(w->ct == CT_MULTI)
 
1608
                return rc;
 
1609
            texttype = rc;
 
1610
        }
 
1611
        return texttype;
 
1612
    }
 
1613
 
 
1614
    /* multi */
 
1615
    /* If there is no text, return nothing */
 
1616
    if(w->start == w->end)
 
1617
        return CT_OTHER;
 
1618
/* I don't know if this is right, but I override the type,
 
1619
 * and make it html, if we start out with <html> */
 
1620
    if(memEqualCI(w->start, "<html>", 6))
 
1621
        return CT_HTML;
 
1622
    return w->ct == CT_HTML ? CT_HTML : CT_TEXT;
 
1623
}                               /* mailTextType */
 
1624
 
 
1625
static void
 
1626
formatMail(struct MHINFO *w, bool top)
 
1627
{
 
1628
    struct MHINFO *v;
 
1629
    int ct = w->ct;
 
1630
    int j, best;
 
1631
 
 
1632
    if(w->doAttach)
 
1633
        return;
 
1634
    debugPrint(5, "format headers for content %d subject %s", ct, w->subject);
 
1635
    stringAndString(&fm, &fm_l, headerShow(w, top));
 
1636
 
 
1637
    if(ct < CT_MULTI) {
 
1638
        char *start = w->start;
 
1639
        char *end = w->end;
 
1640
        int newlen;
 
1641
/* If mail is not in html, reformat it */
 
1642
        if(start < end) {
 
1643
            if(ct == CT_TEXT) {
 
1644
                breakLineSetup();
 
1645
                if(breakLine(start, end - start, &newlen)) {
 
1646
                    start = replaceLine;
 
1647
                    end = start + newlen;
 
1648
                }
 
1649
            }
 
1650
            if(mailIsHtml && ct != CT_HTML)
 
1651
                stringAndString(&fm, &fm_l, "<pre>");
 
1652
            stringAndBytes(&fm, &fm_l, start, end - start);
 
1653
            if(mailIsHtml && ct != CT_HTML)
 
1654
                stringAndString(&fm, &fm_l, "</pre>\n");
 
1655
        }
 
1656
 
 
1657
        /* text present */
 
1658
        /* There could be a mail message inline */
 
1659
        foreach(v, w->components) {
 
1660
            if(end > start)
 
1661
                stringAndString(&fm, &fm_l, mailIsHtml ? "<P>\n" : "\n");
 
1662
            formatMail(v, false);
 
1663
        }
 
1664
 
 
1665
        return;
 
1666
    }
 
1667
 
 
1668
    if(ct == CT_MULTI) {
 
1669
        foreach(v, w->components)
 
1670
           formatMail(v, false);
 
1671
        return;
 
1672
    }
 
1673
 
 
1674
/* alternate presentations, pick the best one */
 
1675
    best = j = 0;
 
1676
    foreach(v, w->components) {
 
1677
        int subtype = mailTextType(v);
 
1678
        ++j;
 
1679
        if(subtype != CT_OTHER)
 
1680
            best = j;
 
1681
        if(mailIsHtml && subtype == CT_HTML ||
 
1682
           !mailIsHtml && subtype == CT_TEXT)
 
1683
            break;
 
1684
    }
 
1685
 
 
1686
    if(!best)
 
1687
        best = 1;
 
1688
    j = 0;
 
1689
    foreach(v, w->components) {
 
1690
        ++j;
 
1691
        if(j != best)
 
1692
            continue;
 
1693
        formatMail(v, false);
 
1694
        break;
 
1695
    }
 
1696
}                               /* formatMail */
 
1697
 
 
1698
/* Browse the email file. */
 
1699
char *
 
1700
emailParse(char *buf)
 
1701
{
 
1702
    struct MHINFO *w, *v;
 
1703
    nattach = nimages = 0;
 
1704
    firstAttach = 0;
 
1705
    mailIsHtml = mailIsSpam = ignoreImages = false;
 
1706
    fm = initString(&fm_l);
 
1707
    w = headerGlean(buf, buf + strlen(buf));
 
1708
    mailIsHtml = (mailTextType(w) == CT_HTML);
 
1709
    if(w->ne)
 
1710
        mailIsSpam = true;
 
1711
    else if(w->ct == CT_ALT) {
 
1712
        foreach(v, w->components)
 
1713
           if(v->ne)
 
1714
            mailIsSpam = true;
 
1715
    }
 
1716
    if(mailIsHtml)
 
1717
        stringAndString(&fm, &fm_l, "<html>\n");
 
1718
    formatMail(w, true);
 
1719
/* Remember, we always need a nonzero buffer */
 
1720
    if(!fm_l || fm[fm_l - 1] != '\n')
 
1721
        stringAndChar(&fm, &fm_l, '\n');
 
1722
    if(!ismc) {
 
1723
        writeAttachments(w);
 
1724
        cw->mailInfo =
 
1725
           allocMem(strlen(w->ref) + strlen(w->mid) + strlen(w->tolist) +
 
1726
           strlen(w->cclist) + strlen(w->reply) + 6);
 
1727
        sprintf(cw->mailInfo, "%s>%s>%s>%s>%s>",
 
1728
           w->reply, w->tolist, w->cclist, w->ref, w->mid);
 
1729
        freeMailInfo(w);
 
1730
        nzFree(buf);
 
1731
        debugPrint(5, "mailInfo: %s", cw->mailInfo);
 
1732
    } else {
 
1733
        lastMailInfo = w;
 
1734
        lastMailText = buf;
 
1735
    }
 
1736
    return fm;
 
1737
}                               /* emailParse */
 
1738
 
 
1739
 
 
1740
/*********************************************************************
 
1741
Set up for a reply.
 
1742
This looks at the first 5 lines, which could contain
 
1743
subject
 
1744
to
 
1745
reply to
 
1746
from
 
1747
mail send
 
1748
in no particular order.
 
1749
Move replyt to the top and get rid of the others.
 
1750
Then, if you have browsed a mail file,
 
1751
grab the message id and reference it.
 
1752
Also, if mailing to all, stick in the other recipients.
 
1753
*********************************************************************/
 
1754
 
 
1755
bool
 
1756
setupReply(bool all)
 
1757
{
 
1758
    int subln, repln;
 
1759
    char linetype[8];
 
1760
    int j;
 
1761
    char *out, *s, *t;
 
1762
    bool rc;
 
1763
 
 
1764
/* basic sanity */
 
1765
    if(cw->dirMode) {
 
1766
        setError(MSG_ReDir);
 
1767
        return false;
 
1768
    }
 
1769
 
 
1770
    if(cw->sqlMode) {
 
1771
        setError(MSG_ReDB);
 
1772
        return false;
 
1773
    }
 
1774
 
 
1775
    if(!cw->dol) {
 
1776
        setError(MSG_ReEmpty);
 
1777
        return false;
 
1778
    }
 
1779
 
 
1780
    if(cw->binMode) {
 
1781
        setError(MSG_ReBinary);
 
1782
        return false;
 
1783
    }
 
1784
 
 
1785
    subln = repln = 0;
 
1786
    strcpy(linetype, " xxxxxx");
 
1787
    for(j = 1; j <= 6; ++j) {
 
1788
        if(j > cw->dol)
 
1789
            break;
 
1790
 
 
1791
        char *p = (char *)fetchLine(j, -1);
 
1792
 
 
1793
        if(memEqualCI(p, "subject:", 8)) {
 
1794
            linetype[j] = 's';
 
1795
            subln = j;
 
1796
            continue;
 
1797
        }
 
1798
 
 
1799
        if(memEqualCI(p, "to ", 3)) {
 
1800
            linetype[j] = 't';
 
1801
            continue;
 
1802
        }
 
1803
 
 
1804
        if(memEqualCI(p, "from ", 5)) {
 
1805
            linetype[j] = 'f';
 
1806
            continue;
 
1807
        }
 
1808
 
 
1809
        if(memEqualCI(p, "mail sent ", 10)) {
 
1810
            linetype[j] = 'w';
 
1811
            continue;
 
1812
        }
 
1813
 
 
1814
        if(memEqualCI(p, "reply to ", 9)) {
 
1815
            linetype[j] = 'r';
 
1816
            repln = j;
 
1817
            continue;
 
1818
        }
 
1819
 
 
1820
/* This one has to be last. */
 
1821
        while(isdigitByte(*p))
 
1822
            ++p;
 
1823
        if(memEqualCI(p, " attachment", 11) || memEqualCI(p, " image", 6)) {
 
1824
            linetype[j] = 'a';
 
1825
            continue;
 
1826
        }
 
1827
 
 
1828
        break;
 
1829
    }
 
1830
 
 
1831
    if(!subln || !repln) {
 
1832
        setError(MSG_ReSubjectReply);
 
1833
        return false;
 
1834
    }
 
1835
 
 
1836
/* delete the lines we don't need */
 
1837
    for(j = 6; j >= 1; --j) {
 
1838
        if(!strchr("wfta", linetype[j]))
 
1839
            continue;
 
1840
        delText(j, j);
 
1841
        strcpy(linetype + j, linetype + j + 1);
 
1842
    }
 
1843
 
 
1844
/* move reply to 1, if it isn't already there */
 
1845
    if(linetype[1] != 'r') {
 
1846
        char *map = cw->map;
 
1847
        char swap[LNWIDTH];
 
1848
        char *q1 = map + LNWIDTH;
 
1849
        char *q2 = map + LNWIDTH * 2;
 
1850
        memcpy(swap, q1, LNWIDTH);
 
1851
        memcpy(q1, q2, LNWIDTH);
 
1852
        memcpy(q2, swap, LNWIDTH);
 
1853
    }
 
1854
 
 
1855
    if(!cw->mailInfo) {
 
1856
        cw->browseMode = false;
 
1857
        if(all) {
 
1858
            setError(MSG_ReNoInfo);
 
1859
            return false;
 
1860
        }
 
1861
        return true;            /* that's all we can do */
 
1862
    }
 
1863
 
 
1864
/* Build the header lines and put them in the buffer */
 
1865
    out = initString(&j);
 
1866
/* step through the to list */
 
1867
    s = strchr(cw->mailInfo, '>') + 1;
 
1868
    while(*s != '>') {
 
1869
        t = strchr(s, ',');
 
1870
        if(all) {
 
1871
            stringAndString(&out, &j, "to: ");
 
1872
            stringAndBytes(&out, &j, s, t - s);
 
1873
            stringAndChar(&out, &j, '\n');
 
1874
        }
 
1875
        s = t + 1;
 
1876
    }
 
1877
 
 
1878
/* step through the cc list */
 
1879
    ++s;
 
1880
    while(*s != '>') {
 
1881
        t = strchr(s, ',');
 
1882
        if(all) {
 
1883
            stringAndString(&out, &j, "cc: ");
 
1884
            stringAndBytes(&out, &j, s, t - s);
 
1885
            stringAndChar(&out, &j, '\n');
 
1886
        }
 
1887
        s = t + 1;
 
1888
    }
 
1889
 
 
1890
    ++s;
 
1891
    t = strchr(s, '>');
 
1892
    if(t[1] == '>') {
 
1893
        i_puts(MSG_ReNoID);
 
1894
    } else {
 
1895
        stringAndString(&out, &j, "references: <");
 
1896
        if(*s != '>') {
 
1897
            stringAndBytes(&out, &j, s, t - s);
 
1898
            stringAndString(&out, &j, "> <");
 
1899
        }
 
1900
        stringAndString(&out, &j, t + 1);
 
1901
        stringAndChar(&out, &j, '\n');
 
1902
    }
 
1903
 
 
1904
    rc = true;
 
1905
    if(j)
 
1906
        rc = addTextToBuffer((unsigned char *)out, j, 1);
 
1907
    nzFree(out);
 
1908
    cw->browseMode = false;
 
1909
    return rc;
 
1910
}                               /* setupReply */