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

« back to all changes in this revision

Viewing changes to sendmail.c

  • Committer: Bazaar Package Importer
  • Author(s): Kapil Hari Paranjape
  • Date: 2006-10-20 10:47:30 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061020104730-o7vxbrypwaz932dt
Tags: 3.1.2-1
* New upstream version (3.1.2). Closes: #306486.
  - programs now written in C
  - support for javascript.
* debian/control:
  - added Kapil Hari Paranjape to Uploaders.
  - Build-depends on "libssl-dev", "libmozjs-dev", "libpcre3-dev".
  - Standards-Version to 3.7.2. No changes required.
* debian/rules:
  - add "noopt" feature.
  - set CFLAGS and LIBS.
  - Put $(MAKE) into the build rules.
* debian/copyright: Edited to add the current copyright which
  is GPL with the exception for linking with OpenSSL.
* debian/docs: added "README".
* debian/examples: added "jsrt".

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* sendmail.c
 
2
 * Send mail using the smtp protocol.
 
3
 * Send the contents of a file, or the current edbrowse buffer.
 
4
 * Copyright (c) Karl Dahlke, 2006
 
5
 * This file is part of the edbrowse project, released under GPL.
 
6
 */
 
7
 
 
8
#include "eb.h"
 
9
#include "tcp.h"
 
10
#include <time.h>
 
11
 
 
12
char serverLine[MAXTTYLINE];
 
13
static char spareLine[MAXTTYLINE];
 
14
int mssock;                     /* server socket */
 
15
static bool doSignature;
 
16
static char subjectLine[81];
 
17
 
 
18
static struct ALIAS {
 
19
    char name[16];
 
20
    char email[64];
 
21
} *addressList;
 
22
static int nads;                /* number of addresses */
 
23
static time_t adbooktime;
 
24
 
 
25
/* read and/or refresh the address book */
 
26
bool
 
27
loadAddressBook(void)
 
28
{
 
29
    char *buf, *bufend, *v, *last, *s, *t;
 
30
    bool cmt = false;
 
31
    char state = 0, c;
 
32
    int j, buflen, ln = 1;
 
33
    time_t mtime;
 
34
 
 
35
    if(!addressFile ||
 
36
       (mtime = fileTimeByName(addressFile)) == -1 || mtime <= adbooktime)
 
37
        return true;
 
38
 
 
39
    debugPrint(3, "loading address book");
 
40
    nzFree(addressList);
 
41
    addressList = 0;
 
42
    nads = 0;
 
43
    if(!fileIntoMemory(addressFile, &buf, &buflen))
 
44
        return false;
 
45
    bufend = buf + buflen;
 
46
 
 
47
    for(s = t = last = buf; s < bufend; ++s) {
 
48
        c = *s;
 
49
        if(cmt) {
 
50
            if(c != '\n')
 
51
                continue;
 
52
            cmt = false;
 
53
        }
 
54
        if(c == ':') {          /* delimiter */
 
55
            if(state == 0) {
 
56
                setError("missing alias in address book line %d", ln);
 
57
              freefail:
 
58
                nzFree(buf);
 
59
                return false;
 
60
            }
 
61
            while(t[-1] == ' ' || t[-1] == '\t')
 
62
                --t;
 
63
            if(state == 1) {
 
64
                *t++ = c;
 
65
                state = 2;
 
66
                continue;
 
67
            }
 
68
            c = '#';            /* extra fields are ignored */
 
69
        }                       /* : */
 
70
        if(c == '#') {
 
71
            cmt = true;
 
72
            continue;
 
73
        }
 
74
        if(c == '\n') {
 
75
            ++ln;
 
76
            if(state == 0)
 
77
                continue;
 
78
            if(state == 1) {
 
79
                setError("missing : in address book line %d", ln - 1);
 
80
                goto freefail;
 
81
            }
 
82
            if(state == 3) {
 
83
                ++nads;
 
84
                while(isspaceByte(t[-1]))
 
85
                    --t;
 
86
                *t = 0;
 
87
                v = strchr(last, ':');
 
88
                if(v - last >= 16) {
 
89
                    setError
 
90
                       ("alias in address book line %d is too long, limit 15 characters",
 
91
                       ln - 1);
 
92
                    goto freefail;
 
93
                }
 
94
                ++v;
 
95
                if(t - v >= 64) {
 
96
                    setError
 
97
                       ("email in address book line %d is too long, limit 63 characters",
 
98
                       ln - 1);
 
99
                    goto freefail;
 
100
                }
 
101
                if(!strchr(v, '@')) {
 
102
                    setError
 
103
                       (" email in address book line %d does not have an @",
 
104
                       ln - 1);
 
105
                    goto freefail;
 
106
                }
 
107
                if(strpbrk(v, " \t")) {
 
108
                    setError
 
109
                       ("cannot handle whitespace in email, address book line %d",
 
110
                       ln - 1);
 
111
                    goto freefail;
 
112
                }
 
113
 
 
114
                while(last < t) {
 
115
                    if(!isprintByte(*last)) {
 
116
                        setError
 
117
                           ("unprintable characters in your alias or email, address book line %d",
 
118
                           ln - 1);
 
119
                        goto freefail;
 
120
                    }
 
121
                    ++last;
 
122
                }
 
123
                *t++ = c;
 
124
            } else
 
125
                t = last;       /* back it up */
 
126
            last = t;
 
127
            state = 0;
 
128
            continue;
 
129
        }
 
130
        /* nl */
 
131
        if((c == ' ' || c == '\t') && (state == 0 || state == 2))
 
132
            continue;
 
133
        if(state == 0)
 
134
            state = 1;
 
135
        if(state == 2)
 
136
            state = 3;
 
137
        *t++ = c;
 
138
    }
 
139
 
 
140
    *t = 0;
 
141
    if(state) {
 
142
        setError("last line of your address book is not terminated");
 
143
        goto freefail;
 
144
    }
 
145
 
 
146
    if(nads) {
 
147
        addressList = allocMem(nads * sizeof (struct ALIAS));
 
148
        j = 0;
 
149
        for(s = buf; *s; s = t + 1, ++j) {
 
150
            t = strchr(s, ':');
 
151
            memcpy(addressList[j].name, s, t - s);
 
152
            addressList[j].name[t - s] = 0;
 
153
            s = t + 1;
 
154
            t = strchr(s, '\n');
 
155
            memcpy(addressList[j].email, s, t - s);
 
156
            addressList[j].email[t - s] = 0;
 
157
        }
 
158
    }
 
159
    /* aliases are present */
 
160
    nzFree(buf);
 
161
    adbooktime = mtime;
 
162
    return true;
 
163
}                               /* loadAddressBook */
 
164
 
 
165
const char *
 
166
reverseAlias(const char *reply)
 
167
{
 
168
    int i;
 
169
    for(i = 0; i < nads; ++i)
 
170
        if(stringEqual(reply, addressList[i].email)) {
 
171
            const char *a = addressList[i].name;
 
172
            if(*a == '!')
 
173
                break;
 
174
            return a;
 
175
        }
 
176
    return 0;                   /* not found */
 
177
}                               /* reverseAlias */
 
178
 
 
179
 
 
180
/*********************************************************************
 
181
Put and get lines from the mail server.
 
182
Print the lines if the debug level calls for it.
 
183
*********************************************************************/
 
184
 
 
185
bool
 
186
serverPutLine(const char *buf)
 
187
{
 
188
    int n, len = strlen(buf);
 
189
    char c;
 
190
    if(debugLevel >= 4) {
 
191
        printf("> ");
 
192
        for(n = 0; n < len; ++n) {
 
193
            c = buf[n];
 
194
            if(c != '\r')
 
195
                putchar(c);
 
196
        }
 
197
    }
 
198
    n = tcp_write(mssock, buf, len);
 
199
    if(n < len) {
 
200
        setError("cannot send data to the mail server");
 
201
        return false;
 
202
    }
 
203
    return true;
 
204
}                               /* serverPutLine */
 
205
 
 
206
bool
 
207
serverGetLine(void)
 
208
{
 
209
    int n, len, slen;
 
210
    char c;
 
211
    char *s;
 
212
    slen = strlen(spareLine);
 
213
    strcpy(serverLine, spareLine);
 
214
    s = strchr(serverLine, '\n');
 
215
    if(!s) {
 
216
        len =
 
217
           tcp_read(mssock, serverLine + slen, sizeof (serverLine) - 1 - slen);
 
218
        if(len <= 0) {
 
219
            setError("cannot read data from the mail server");
 
220
            return false;
 
221
        }
 
222
        slen += len;
 
223
        serverLine[slen] = 0;
 
224
    }
 
225
    s = strchr(serverLine, '\n');
 
226
    if(!s) {
 
227
        setError("line from the mail server is too long, or unterminated");
 
228
        return false;
 
229
    }
 
230
    strcpy(spareLine, s + 1);
 
231
    *s = 0;
 
232
    if(s > serverLine && s[-1] == '\r')
 
233
        *--s = 0;
 
234
    debugPrint(4, "< %s", serverLine);
 
235
    return true;
 
236
}                               /* serverGetLine */
 
237
 
 
238
void
 
239
serverClose(void)
 
240
{
 
241
    serverPutLine("quit\r\n");
 
242
    endhostent();
 
243
    sleep(2);
 
244
    close(mssock);
 
245
}                               /* serverClose */
 
246
 
 
247
/* Connect to the mail server */
 
248
bool
 
249
mailConnect(const char *host, int port)
 
250
{
 
251
    long ip;
 
252
    ip = tcp_name_ip(host);
 
253
    if(ip == -1) {
 
254
        setError(intFlag ? opint : "cannot locate the mail server %s", host);
 
255
        return false;
 
256
    }
 
257
    debugPrint(4, "%s -> %s", host, tcp_ip_dots(ip));
 
258
    mssock = tcp_connect(ip, port, mailTimeout);
 
259
    if(mssock < 0) {
 
260
        setError(intFlag ? opint : "cannot connect to the mail server");
 
261
        return false;
 
262
    }
 
263
    debugPrint(4, "connected to port %d", port);
 
264
    spareLine[0] = 0;
 
265
    return true;
 
266
}                               /* mailConnect */
 
267
 
 
268
static char base64_chars[] =
 
269
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
270
char *
 
271
base64Encode(const char *inbuf, int inlen, bool lines)
 
272
{
 
273
    char *out, *outstr;
 
274
    uchar *in = (uchar *) inbuf;
 
275
    int colno;
 
276
    int outlen = ((inlen / 3) + 1) * 4;
 
277
    ++outlen;                   /* zero on the end */
 
278
    if(lines)
 
279
        outlen += (inlen / 54) + 1;
 
280
    outstr = out = allocMem(outlen);
 
281
    colno = 0;
 
282
    while(inlen >= 3) {
 
283
        *out++ = base64_chars[(int)(*in >> 2)];
 
284
        *out++ = base64_chars[(int)((*in << 4 | *(in + 1) >> 4) & 63)];
 
285
        *out++ = base64_chars[(int)((*(in + 1) << 2 | *(in + 2) >> 6) & 63)];
 
286
        *out++ = base64_chars[(int)(*(in + 2) & 63)];
 
287
        inlen -= 3;
 
288
        in += 3;
 
289
        if(!lines)
 
290
            continue;
 
291
        colno += 4;
 
292
        if(colno < 72)
 
293
            continue;
 
294
        *out++ = '\n';
 
295
        colno = 0;
 
296
    }
 
297
    if(inlen == 1) {
 
298
        *out++ = base64_chars[(int)(*in >> 2)];
 
299
        *out++ = base64_chars[(int)(*in << 4 & 63)];
 
300
        *out++ = '=';
 
301
        *out++ = '=';
 
302
        colno += 4;
 
303
    }
 
304
    if(inlen == 2) {
 
305
        *out++ = base64_chars[(int)(*in >> 2)];
 
306
        *out++ = base64_chars[(int)((*in << 4 | *(in + 1) >> 4) & 63)];
 
307
        *out++ = base64_chars[(int)((*(in + 1) << 2) & 63)];
 
308
        *out++ = '=';
 
309
        colno += 4;
 
310
    }
 
311
/* finish the last line */
 
312
    if(lines && colno)
 
313
        *out++ = '\n';
 
314
    *out = 0;
 
315
    return outstr;
 
316
}                               /* base64Encode */
 
317
 
 
318
/* Read a file into memory, mime encode it,
 
319
 * and return the type of encoding and the encoded data.
 
320
 * Last three parameters are result parameters.
 
321
 * If ismail is nonzero, the file is the mail, not an attachment.
 
322
 * In fact ismail indicates the line that holds the subject.
 
323
 * If ismail is negative, then -ismail indicates the subject line,
 
324
 * and the string file is not the filename, but rather, the mail to send. */
 
325
bool
 
326
encodeAttachment(const char *file, int ismail,
 
327
   const char **type_p, const char **enc_p, char **data_p)
 
328
{
 
329
    char *buf;
 
330
    char c;
 
331
    bool longline;
 
332
    char *s, *t, *v;
 
333
    char *ct, *ce;              /* content type, content encoding */
 
334
    int buflen, i, cx;
 
335
    int nacount, nullcount, nlcount;
 
336
 
 
337
    if(ismail < 0) {
 
338
        buf = cloneString(file);
 
339
        buflen = strlen(buf);
 
340
        ismail = -ismail;
 
341
        file = EMPTYSTRING;
 
342
    } else {
 
343
 
 
344
        if(!ismc && (cx = stringIsNum(file)) >= 0) {
 
345
            static char newfilename[16];
 
346
            if(!unfoldBuffer(cx, false, &buf, &buflen))
 
347
                return false;
 
348
            if(!buflen) {
 
349
                setError("buffer %d is empty", cx);
 
350
                goto freefail;
 
351
            }
 
352
            sprintf(newfilename, "<buffer %d>", cx);
 
353
            file = newfilename;
 
354
            if(sessionList[cx].lw->fileName)
 
355
                file = sessionList[cx].lw->fileName;
 
356
        } else {
 
357
            if(!fileIntoMemory(file, &buf, &buflen))
 
358
                return false;
 
359
            if(!buflen) {
 
360
                setError("file %s is empty", file);
 
361
                goto freefail;
 
362
            }
 
363
        }
 
364
    }                           /* ismail negative or normal */
 
365
 
 
366
    if(ismail) {
 
367
/* Put newline at the end.  Yes, the buffer is allocated
 
368
 * with space for newline and null. */
 
369
        if(buf[buflen - 1] != '\n')
 
370
            buf[buflen++] = '\n';
 
371
/* check for subject: line */
 
372
        s = buf;
 
373
        i = ismail;
 
374
        while(--i) {
 
375
            while(*s != '\n')
 
376
                ++s;
 
377
            ++s;
 
378
        }
 
379
        while(*s == ' ' || *s == '\t')
 
380
            ++s;
 
381
        if(!memEqualCI(s, "subject:", 8)) {
 
382
            setError("your email should begin with subject:");
 
383
            goto freefail;
 
384
        }
 
385
        s += 8;
 
386
        while(*s == ' ' || *s == '\t')
 
387
            ++s;
 
388
        t = s;
 
389
        while(*s != '\n')
 
390
            ++s;
 
391
        v = s;
 
392
        while(s > t && isspaceByte(s[-1]))
 
393
            --s;
 
394
        if(s == t) {
 
395
            setError("empty subject line");
 
396
            goto freefail;
 
397
        }
 
398
        if(s - t >= sizeof (subjectLine)) {
 
399
            setError("subject line too long, limit %d characters",
 
400
               sizeof (subjectLine) - 1);
 
401
            goto freefail;
 
402
        }
 
403
        memcpy(subjectLine, t, s - t);
 
404
        subjectLine[s - t] = 0;
 
405
        t = subjectLine + (s - t);
 
406
        for(s = subjectLine; s < t; ++s) {
 
407
            c = *s;
 
408
            if(!isprintByte(c) && c != ' ') {
 
409
                setError
 
410
                   ("invalid characters in the subject line, please use only spaces and printable ascii text");
 
411
                goto freefail;
 
412
            }
 
413
        }
 
414
        debugPrint(6, "subject = %s", subjectLine);
 
415
/* Blank lines after subject are optional, and ignored. */
 
416
        for(t = buf + buflen; v < t; ++v)
 
417
            if(*v != '\r' && *v != '\n')
 
418
                break;
 
419
        buflen -= (v - buf);
 
420
        if(buflen)
 
421
            memcpy(buf, v, buflen);
 
422
        buf[buflen] = 0;
 
423
 
 
424
        if(doSignature) {       /* Append .signature file. */
 
425
            c = fileTypeByName(sigFile, false);
 
426
            if(c != 0) {
 
427
                int fd, n;
 
428
                if(c != 'f') {
 
429
                    setError(".signature is not a regular file");
 
430
                    goto freefail;
 
431
                }
 
432
                n = fileSizeByName(sigFile);
 
433
                if(n > 0) {
 
434
                    buf = reallocMem(buf, buflen + n + 1);
 
435
                    fd = open(sigFile, O_RDONLY);
 
436
                    if(fd < 0) {
 
437
                        setError("cannot access .signature file");
 
438
                        goto freefail;
 
439
                    }
 
440
                    read(fd, buf + buflen, n);
 
441
                    close(fd);
 
442
                    buflen += n;
 
443
                    buf[buflen] = 0;
 
444
                }
 
445
            }
 
446
        }                       /* .signature */
 
447
    }
 
448
 
 
449
    /* primary email message */
 
450
    /* Infer content type from the filename */
 
451
    ct = 0;
 
452
    s = strrchr(file, '.');
 
453
    if(s && s[1]) {
 
454
        ++s;
 
455
        if(stringEqualCI(s, "ps"))
 
456
            ct = "application/PostScript";
 
457
        if(stringEqualCI(s, "jpeg"))
 
458
            ct = "image/jpeg";
 
459
        if(stringEqualCI(s, "gif"))
 
460
            ct = "image/gif";
 
461
        if(stringEqualCI(s, "wav"))
 
462
            ct = "audio/basic";
 
463
        if(stringEqualCI(s, "mpeg"))
 
464
            ct = "video/mpeg";
 
465
        if(stringEqualCI(s, "rtf"))
 
466
            ct = "text/richtext";
 
467
        if(stringEqualCI(s, "htm") ||
 
468
           stringEqualCI(s, "html") ||
 
469
           stringEqualCI(s, "shtm") ||
 
470
           stringEqualCI(s, "shtml") || stringEqualCI(s, "asp"))
 
471
            ct = "text/html";
 
472
    }
 
473
 
 
474
/* Count the nonascii characters */
 
475
    nacount = nullcount = nlcount = 0;
 
476
    longline = false;
 
477
    s = 0;
 
478
    for(i = 0; i < buflen; ++i) {
 
479
        c = buf[i];
 
480
        if(c == '\0')
 
481
            ++nullcount;
 
482
        if(c < 0)
 
483
            ++nacount;
 
484
        if(c != '\n')
 
485
            continue;
 
486
        ++nlcount;
 
487
        t = buf + i;
 
488
        if(s && t - s > 120)
 
489
            longline = true;
 
490
        s = t;
 
491
    }
 
492
    debugPrint(6, "attaching %s length %d nonascii %d nulls %d longline %d",
 
493
       file, buflen, nacount, nullcount, longline);
 
494
    nacount += nullcount;
 
495
 
 
496
    if(buflen > 20 && nacount * 5 > buflen) {   /* binary file */
 
497
        if(ismail) {
 
498
            setError
 
499
               ("cannot mail the binary file %s, perhaps this should be an attachment?",
 
500
               file);
 
501
            goto freefail;
 
502
        }
 
503
 
 
504
        s = base64Encode(buf, buflen, true);
 
505
        nzFree(buf);
 
506
        buf = s;
 
507
        if(!ct)
 
508
            ct = "application/octet-stream";    /* default type */
 
509
        ce = "base64";
 
510
        goto success;
 
511
    }
 
512
    /* binary file */
 
513
    if(!ct)
 
514
        ct = "text/plain";
 
515
 
 
516
/* Switch to unix newlines - we'll switch back to dos later. */
 
517
    v = buf + buflen;
 
518
    for(s = t = buf; s < v; ++s) {
 
519
        c = *s;
 
520
        if(c == '\r' && s < v - 1 && s[1] == '\n')
 
521
            continue;
 
522
        *t++ = c;
 
523
    }
 
524
    buflen = t - buf;
 
525
 
 
526
/* Do we need to use quoted-printable? */
 
527
/* Perhaps this hshould read (nacount > 0) */
 
528
    if(nacount * 20 > buflen || nullcount || longline) {
 
529
        char *newbuf;
 
530
        int l, colno = 0, space = 0;
 
531
 
 
532
        newbuf = initString(&l);
 
533
        v = buf + buflen;
 
534
        for(s = buf; s < v; ++s) {
 
535
            c = *s;
 
536
/* do we have to =expand this character? */
 
537
            if(c < '\n' && c != '\t' ||
 
538
               c == '=' ||
 
539
               c == '\xff' ||
 
540
               (c == ' ' || c == '\t') && s < v - 1 && s[1] == '\n') {
 
541
                char expand[4];
 
542
                sprintf(expand, "=%02X", (uchar) c);
 
543
                stringAndString(&newbuf, &l, expand);
 
544
                colno += 3;
 
545
            } else {
 
546
                stringAndChar(&newbuf, &l, c);
 
547
                ++colno;
 
548
            }
 
549
            if(c == '\n') {
 
550
                colno = space = 0;
 
551
                continue;
 
552
            }
 
553
            if(c == ' ' || c == '\t')
 
554
                space = l;
 
555
            if(colno < 72)
 
556
                continue;
 
557
            if(s == v - 1)
 
558
                continue;
 
559
/* If newline's coming up anyways, don't force another one. */
 
560
            if(s[1] == '\n')
 
561
                continue;
 
562
            i = l;
 
563
            if(!space || space == i) {
 
564
                stringAndString(&newbuf, &l, "=\n");
 
565
                colno = space = 0;
 
566
                continue;
 
567
            }
 
568
            colno = i - space;
 
569
            stringAndString(&newbuf, &l, "**"); /* make room */
 
570
            while(i > space) {
 
571
                newbuf[i + 1] = newbuf[i - 1];
 
572
                --i;
 
573
            }
 
574
            newbuf[space] = '=';
 
575
            newbuf[space + 1] = '\n';
 
576
            space = 0;
 
577
        }                       /* loop over characters */
 
578
 
 
579
        nzFree(buf);
 
580
        buf = newbuf;
 
581
        ce = "quoted-printable";
 
582
        goto success;
 
583
    }
 
584
    /* quoted printable */
 
585
    buf[buflen] = 0;
 
586
    ce = (nacount ? "8bit" : "7bit");
 
587
 
 
588
  success:
 
589
    debugPrint(6, "encoded %s %s length %d", ct, ce, strlen(buf));
 
590
    *enc_p = ce;
 
591
    *type_p = ct;
 
592
    *data_p = buf;
 
593
    return true;
 
594
 
 
595
  freefail:
 
596
    nzFree(buf);
 
597
    return false;
 
598
}                               /* encodeAttachment */
 
599
 
 
600
static char *
 
601
mailTimeString(void)
 
602
{
 
603
    static char buf[48];
 
604
    static const char wday[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
 
605
    static const char month[] =
 
606
       "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
 
607
    struct tm *cur_tm;
 
608
    long now;
 
609
    time(&now);
 
610
    cur_tm = localtime(&now);
 
611
    sprintf(buf, "%s, %02d %s %d %02d:%02d:%02d",
 
612
       wday + cur_tm->tm_wday * 4,
 
613
       cur_tm->tm_mday,
 
614
       month + cur_tm->tm_mon * 4,
 
615
       cur_tm->tm_year + 1900, cur_tm->tm_hour, cur_tm->tm_min, cur_tm->tm_sec);
 
616
    return buf;
 
617
}                               /* mailTimeString */
 
618
 
 
619
static char *
 
620
messageTimeID(void)
 
621
{
 
622
    static char buf[48];
 
623
    struct tm *cur_tm;
 
624
    long now;
 
625
    time(&now);
 
626
    cur_tm = localtime(&now);
 
627
    sprintf(buf, "%04d%02d%02d%02d%02d%02d",
 
628
       cur_tm->tm_year + 1900, cur_tm->tm_mon, cur_tm->tm_mday,
 
629
       cur_tm->tm_hour, cur_tm->tm_min, cur_tm->tm_sec);
 
630
    return buf;
 
631
}                               /* messageTimeID */
 
632
 
 
633
static void
 
634
appendAttachment(const char *s, char **out, int *l)
 
635
{
 
636
    const char *t;
 
637
    int n;
 
638
    while(*s) {                 /* another line */
 
639
        t = strchr(s, '\n');
 
640
        if(!t)
 
641
            t = s + strlen(s);
 
642
        n = t - s;
 
643
        if(t[-1] == '\r')
 
644
            --n;
 
645
        if(n)
 
646
            memcpy(serverLine, s, n);
 
647
        serverLine[n] = 0;
 
648
        if(n == 1 && serverLine[0] == '.')      /* can't allow this */
 
649
            strcpy(serverLine, " .");
 
650
        strcat(serverLine, eol);
 
651
        stringAndString(out, l, serverLine);
 
652
        if(*t)
 
653
            ++t;
 
654
        s = t;
 
655
    }
 
656
/* Small bug here - an attachment that is not base64 encoded,
 
657
 * and had no newline at the end, now has one. */
 
658
}                               /* appendAttachment */
 
659
 
 
660
char *
 
661
makeBoundary(void)
 
662
{
 
663
    static char boundary[60];
 
664
    sprintf(boundary, "nextpart-eb-%06d", rand() % 1000000);
 
665
    return boundary;
 
666
}                               /* makeBoundary */
 
667
 
 
668
/* Send mail to the smtp server. */
 
669
bool
 
670
sendMail(int account, const char **recipients, const char *body,
 
671
   int subjat, const char **attachments, int nalt, bool dosig)
 
672
{
 
673
    const char *from, *reply, *login, *smlogin, *pass;
 
674
    const struct MACCOUNT *a;
 
675
    const char *s, *boundary;
 
676
    char *t;
 
677
    int nat, cx, i, j;
 
678
    char *out = 0;
 
679
    bool mustmime = false;
 
680
    const char *ct, *ce;
 
681
    char *encoded = 0;
 
682
    struct MACCOUNT *localMail;
 
683
 
 
684
    if(!validAccount(account))
 
685
        return false;
 
686
    localMail = accounts + localAccount - 1;
 
687
 
 
688
    a = accounts + account - 1;
 
689
    from = a->from;
 
690
    reply = a->reply;
 
691
    login = localMail->login;
 
692
    smlogin = strchr(login, '\\');
 
693
    if(smlogin)
 
694
        ++smlogin;
 
695
    else
 
696
        smlogin = login;
 
697
    pass = localMail->password;
 
698
    doSignature = dosig;
 
699
 
 
700
    nat = 0;                    /* number of attachments */
 
701
    if(attachments) {
 
702
        while(attachments[nat])
 
703
            ++nat;
 
704
    }
 
705
    if(nat)
 
706
        mustmime = true;
 
707
    if(nalt && nalt < nat) {
 
708
        setError
 
709
           ("either none or all of the attachments must be declared \"alternative\"");
 
710
        return false;
 
711
    }
 
712
 
 
713
    if(!loadAddressBook())
 
714
        return false;
 
715
 
 
716
/* Look up aliases in the address book */
 
717
    for(j = 0; s = recipients[j]; ++j) {
 
718
        if(strchr(s, '@'))
 
719
            continue;
 
720
        t = 0;
 
721
        for(i = 0; i < nads; ++i) {
 
722
            const char *a = addressList[i].name;
 
723
            if(*a == '-' || *a == '!')
 
724
                ++a;
 
725
            if(!stringEqual(s, a))
 
726
                continue;
 
727
            t = addressList[i].email;
 
728
            debugPrint(3, " %s becomes %s", s, t);
 
729
            break;
 
730
        }
 
731
        if(t) {
 
732
            recipients[j] = t;
 
733
            continue;
 
734
        }
 
735
        if(!addressFile) {
 
736
            setError
 
737
               ("No address book specified, please check your .ebrc config file");
 
738
            return false;
 
739
        }
 
740
        setError("alias %s not found in your address book", s);
 
741
        return false;
 
742
    }                           /* recipients */
 
743
    if(!j) {
 
744
        setError("no recipients specified");
 
745
        return false;
 
746
    }
 
747
 
 
748
/* verify attachments are readable */
 
749
    for(j = 0; s = attachments[j]; ++j) {
 
750
        if(!ismc && (cx = stringIsNum(s)) >= 0) {
 
751
            if(!cxCompare(cx) || !cxActive(cx))
 
752
                return false;
 
753
            if(!sessionList[cx].lw->dol) {
 
754
                setError("session %d is empty, cannot atach", cx);
 
755
                return false;
 
756
            }
 
757
        } else {
 
758
            char ftype = fileTypeByName(s, false);
 
759
            if(!ftype) {
 
760
                setError("cannot access attachment %s", s);
 
761
                return false;
 
762
            }
 
763
            if(ftype != 'f') {
 
764
                setError("file %s is not a regular file, cannot attach", s);
 
765
                return false;
 
766
            }
 
767
            if(!fileSizeByName(s)) {
 
768
                setError("file %s is empty, cannot attach", s);
 
769
                return false;
 
770
            }
 
771
        }
 
772
    }                           /* loop over attachments */
 
773
 
 
774
    if(!encodeAttachment(body, subjat, &ct, &ce, &encoded))
 
775
        return false;
 
776
    if(ce[0] == 'q')
 
777
        mustmime = true;
 
778
 
 
779
    boundary = makeBoundary();
 
780
 
 
781
    if(!mailConnect(localMail->outurl, localMail->outport)) {
 
782
        nzFree(encoded);
 
783
        return false;
 
784
    }
 
785
    if(!serverGetLine())
 
786
        goto mailfail;
 
787
    while(memEqualCI(serverLine, "220-", 4)) {
 
788
        if(!serverGetLine())
 
789
            goto mailfail;
 
790
    }
 
791
    if(!memEqualCI(serverLine, "220 ", 4)) {
 
792
        setError
 
793
           ("unexpected prompt \"%s\" at the start of the sendmail session",
 
794
           serverLine);
 
795
        goto mailfail;
 
796
    }
 
797
 
 
798
    sprintf(serverLine, "helo %s%s", smlogin, eol);
 
799
    if(!serverPutLine(serverLine))
 
800
        goto mailfail;
 
801
    if(!serverGetLine())
 
802
        goto mailfail;
 
803
    if(!memEqualCI(serverLine, "250 ", 4)) {
 
804
        setError("mail server doesn't recognize %s", login);
 
805
        goto mailfail;
 
806
    }
 
807
 
 
808
    sprintf(serverLine, "mail from: <%s>%s", reply, eol);
 
809
    if(!serverPutLine(serverLine))
 
810
        goto mailfail;
 
811
    if(!serverGetLine())
 
812
        goto mailfail;
 
813
    if(!memEqualCI(serverLine, "250 ", 4)) {
 
814
        setError("mail server rejected %s", reply);
 
815
        goto mailfail;
 
816
    }
 
817
 
 
818
    for(j = 0; s = recipients[j]; ++j) {
 
819
        sprintf(serverLine, "rcpt to: <%s>%s", s, eol);
 
820
        if(!serverPutLine(serverLine))
 
821
            goto mailfail;
 
822
        if(!serverGetLine())
 
823
            goto mailfail;
 
824
        if(!memEqualCI(serverLine, "250 ", 4)) {
 
825
            setError("mail server rejected %s", s);
 
826
            goto mailfail;
 
827
        }
 
828
    }
 
829
 
 
830
    if(!serverPutLine("data\r\n"))
 
831
        goto mailfail;
 
832
    if(!serverGetLine())
 
833
        goto mailfail;
 
834
    if(!memEqualCI(serverLine, "354 ", 4)) {
 
835
        setError("mail server is not ready to receive data, %s", serverLine);
 
836
        goto mailfail;
 
837
    }
 
838
 
 
839
/* Build the outgoing mail, and send it in one go, as one string. */
 
840
    out = initString(&j);
 
841
    for(i = 0; s = recipients[i]; ++i) {
 
842
        stringAndString(&out, &j, i ? "  " : "To: ");
 
843
        stringAndString(&out, &j, s);
 
844
        if(recipients[i + 1])
 
845
            stringAndChar(&out, &j, ',');
 
846
        stringAndString(&out, &j, eol);
 
847
    }
 
848
    sprintf(serverLine, "From: %s <%s>%s", from, reply, eol);
 
849
    stringAndString(&out, &j, serverLine);
 
850
    sprintf(serverLine, "Reply-to: %s <%s>%s", from, reply, eol);
 
851
    stringAndString(&out, &j, serverLine);
 
852
    sprintf(serverLine, "Subject: %s%s", subjectLine, eol);
 
853
    stringAndString(&out, &j, serverLine);
 
854
    sprintf(serverLine, "Date: %s%sMessage-ID: <%s.%s>%sMime-Version: 1.0%s",
 
855
       mailTimeString(), eol, messageTimeID(), reply, eol, eol);
 
856
    stringAndString(&out, &j, serverLine);
 
857
 
 
858
    if(!mustmime) {
 
859
/* no mime components required, we can just send the mail. */
 
860
        sprintf(serverLine,
 
861
           "Content-type: %s%sContent-Transfer-Encoding: %s%s%s", ct, eol, ce,
 
862
           eol, eol);
 
863
        stringAndString(&out, &j, serverLine);
 
864
    } else {
 
865
        sprintf(serverLine,
 
866
           "Content-Type: multipart/%s; boundary=%s%sContent-Transfer-Encoding: 7bit%s%s",
 
867
           nalt ? "alternative" : "mixed", boundary, eol, eol, eol);
 
868
        stringAndString(&out, &j, serverLine);
 
869
        stringAndString(&out, &j,
 
870
           "This message is in MIME format. Since your mail reader does not understand\r\n\
 
871
this format, some or all of this message may not be legible.\r\n\r\n--");
 
872
        stringAndString(&out, &j, boundary);
 
873
        sprintf(serverLine,
 
874
           "%sContent-type: %s%sContent-Transfer-Encoding: %s%s%s", eol, ct,
 
875
           eol, ce, eol, eol);
 
876
        stringAndString(&out, &j, serverLine);
 
877
    }
 
878
 
 
879
/* Now send the body, line by line. */
 
880
    appendAttachment(encoded, &out, &j);
 
881
    nzFree(encoded);
 
882
    encoded = 0;
 
883
 
 
884
    if(mustmime) {
 
885
        for(i = 0; s = attachments[i]; ++i) {
 
886
            if(!encodeAttachment(s, 0, &ct, &ce, &encoded))
 
887
                return false;
 
888
            sprintf(serverLine, "%s--%s%sContent-Type: %s", eol, boundary, eol,
 
889
               ct);
 
890
            stringAndString(&out, &j, serverLine);
 
891
/* If the filename has a quote in it, forget it. */
 
892
/* Also, suppress filename if this is an alternate presentation. */
 
893
            if(!nalt && !strchr(s, '"') && (ismc || stringIsNum(s) < 0)) {
 
894
                sprintf(serverLine, "; name=\"%s\"", s);
 
895
                stringAndString(&out, &j, serverLine);
 
896
            }
 
897
            sprintf(serverLine, "%sContent-Transfer-Encoding: %s%s%s", eol, ce,
 
898
               eol, eol);
 
899
            stringAndString(&out, &j, serverLine);
 
900
            appendAttachment(encoded, &out, &j);
 
901
            nzFree(encoded);
 
902
            encoded = 0;
 
903
        }                       /* loop over attachments */
 
904
 
 
905
/* The last boundary */
 
906
        sprintf(serverLine, "%s--%s--%s", eol, boundary, eol);
 
907
        stringAndString(&out, &j, serverLine);
 
908
    }
 
909
 
 
910
    /* mime format */
 
911
    /* A dot alone ends the transmission */
 
912
    stringAndString(&out, &j, ".\r\n");
 
913
    if(!serverPutLine(out))
 
914
        goto mailfail;
 
915
    nzFree(out);
 
916
    out = 0;
 
917
 
 
918
    if(!serverGetLine())
 
919
        goto mailfail;
 
920
    if(!memEqualCI(serverLine, "250 ", 4) &&
 
921
/* do these next two lines make any sense? */
 
922
       !strstrCI(serverLine, "message accepted") &&
 
923
       !strstrCI(serverLine, "message received")) {
 
924
        setError("could not send mail message, %s", serverLine);
 
925
        goto mailfail;
 
926
    }
 
927
 
 
928
    serverClose();
 
929
    return true;
 
930
 
 
931
  mailfail:
 
932
    nzFree(encoded);
 
933
    nzFree(out);
 
934
    close(mssock);
 
935
    return false;
 
936
}                               /* sendMail */
 
937
 
 
938
bool
 
939
validAccount(int n)
 
940
{
 
941
    if(!maxAccount) {
 
942
        setError("no mail accounts specified, plese check your config file");
 
943
        return false;
 
944
    }
 
945
    if(n <= 0 || n > maxAccount) {
 
946
        setError("invalid account number %d, please use 1 through %d", n,
 
947
           maxAccount);
 
948
        return false;
 
949
    }
 
950
    return true;
 
951
}                               /* validAccount */
 
952
 
 
953
#define MAXRECAT 100            /* max number of recipients or attachments */
 
954
bool
 
955
sendMailCurrent(int sm_account, bool dosig)
 
956
{
 
957
    const char *reclist[MAXRECAT + 1];
 
958
    char *recmem;
 
959
    const char *atlist[MAXRECAT + 1];
 
960
    char *atmem;
 
961
    char *s, *t;
 
962
    char cxbuf[4];
 
963
    int lr, la, ln;
 
964
    int nrec = 0, nat = 0, nalt = 0;
 
965
    int account = localAccount;
 
966
    int j;
 
967
    bool rc = false;
 
968
    bool subj = false;
 
969
 
 
970
    if(cw->browseMode) {
 
971
        setError("cannot send mail while in browse mode");
 
972
        return false;
 
973
    }
 
974
    if(cw->dirMode) {
 
975
        setError("cannot send mail from directory mode");
 
976
        return false;
 
977
    }
 
978
    if(cw->binMode) {
 
979
        setError("cannot mail binary data, should this be an attachment?");
 
980
        return false;
 
981
    }
 
982
    if(!cw->dol) {
 
983
        setError("cannot mail an empty file");
 
984
        return false;
 
985
    }
 
986
 
 
987
    if(!validAccount(account))
 
988
        return false;
 
989
 
 
990
    recmem = initString(&lr);
 
991
    atmem = initString(&la);
 
992
 
 
993
/* Gather recipients and attachments, until we reach subject: */
 
994
    for(ln = 1; ln <= cw->dol; ++ln) {
 
995
        char *line = (char *)fetchLine(ln, -1);
 
996
        if(memEqualCI(line, "to:", 3) ||
 
997
           memEqualCI(line, "mailto:", 7) ||
 
998
           memEqualCI(line, "reply to:", 9) ||
 
999
           memEqualCI(line, "reply to ", 9)) {
 
1000
            if(toupper(line[0]) == 'R')
 
1001
                line += 9;
 
1002
            else if(toupper(line[0]) == 'M')
 
1003
                line += 7;
 
1004
            else
 
1005
                line += 3;
 
1006
            while(*line == ' ' || *line == '\t')
 
1007
                ++line;
 
1008
            if(*line == '\n') {
 
1009
                setError("no recipient at line %d", ln);
 
1010
                goto done;
 
1011
            }
 
1012
            if(nrec == MAXRECAT) {
 
1013
                setError("too many recipients, limit %d", MAXRECAT);
 
1014
                goto done;
 
1015
            }
 
1016
            ++nrec;
 
1017
            for(t = line; *t != '\n'; ++t) ;
 
1018
            stringAndBytes(&recmem, &lr, line, t + 1 - line);
 
1019
            continue;
 
1020
        }
 
1021
        if(memEqualCI(line, "attach:", 7) || memEqualCI(line, "alt:", 4)) {
 
1022
            if(toupper(line[1]) == 'T')
 
1023
                line += 7;
 
1024
            else
 
1025
                line += 4, ++nalt;
 
1026
            while(*line == ' ' || *line == '\t')
 
1027
                ++line;
 
1028
            if(*line == '\n') {
 
1029
                setError("no attachment at line %d", ln);
 
1030
                goto done;
 
1031
            }
 
1032
            if(nat == MAXRECAT) {
 
1033
                setError("too many recipients, limit %d", MAXRECAT);
 
1034
                goto done;
 
1035
            }
 
1036
            ++nat;
 
1037
            for(t = line; *t != '\n'; ++t) ;
 
1038
            stringAndBytes(&atmem, &la, line, t + 1 - line);
 
1039
            continue;
 
1040
        }
 
1041
        if(memEqualCI(line, "account:", 8)) {
 
1042
            line += 8;
 
1043
            while(*line == ' ' || *line == '\t')
 
1044
                ++line;
 
1045
            if(!isdigitByte(*line) ||
 
1046
               (account = strtol(line, &line, 10)) == 0 ||
 
1047
               account > maxAccount || *line != '\n') {
 
1048
                setError("invalid account number at line %d", ln);
 
1049
                goto done;
 
1050
            }
 
1051
            continue;
 
1052
        }
 
1053
        if(memEqualCI(line, "subject:", 8)) {
 
1054
            while(*line == ' ' || *line == '\t')
 
1055
                ++line;
 
1056
            if(*line == '\n') {
 
1057
                setError("empty subject");
 
1058
                goto done;
 
1059
            }
 
1060
            subj = true;
 
1061
        }
 
1062
        break;
 
1063
    }                           /* loop over lines */
 
1064
 
 
1065
    if(sm_account)
 
1066
        account = sm_account;
 
1067
    if(!subj) {
 
1068
        setError(ln > cw->dol ?
 
1069
           "there is no subject line" :
 
1070
           "line %d should begin with to: attach: alt: account: or subject:",
 
1071
           ln);
 
1072
        goto done;
 
1073
    }
 
1074
 
 
1075
    if(nrec == 0) {
 
1076
        setError
 
1077
           ("no recipients specified, place to: emailaddress at the top of youre file");
 
1078
        goto done;
 
1079
    }
 
1080
 
 
1081
    for(s = recmem, j = 0; *s; s = t + 1, ++j) {
 
1082
        t = strchr(s, '\n');
 
1083
        *t = 0;
 
1084
        reclist[j] = s;
 
1085
    }
 
1086
    reclist[j] = 0;
 
1087
    for(s = atmem, j = 0; *s; s = t + 1, ++j) {
 
1088
        t = strchr(s, '\n');
 
1089
        *t = 0;
 
1090
        atlist[j] = s;
 
1091
    }
 
1092
    atlist[j] = 0;
 
1093
 
 
1094
    sprintf(cxbuf, "%d", context);
 
1095
    rc = sendMail(account, reclist, cxbuf, ln, atlist, nalt, dosig);
 
1096
 
 
1097
  done:
 
1098
    nzFree(recmem);
 
1099
    nzFree(atmem);
 
1100
    if(!rc && intFlag)
 
1101
        setError(opint);
 
1102
    if(rc)
 
1103
        puts("ok");
 
1104
    return rc;
 
1105
}                               /* sendMailCurrent */