Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/send.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2011 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* Mail -- a mail program
71
*
72
* Mail to others.
73
*/
74
75
#include "mailx.h"
76
77
struct letter {
78
FILE* fp;
79
struct header* hp;
80
off_t head;
81
off_t body;
82
};
83
84
/*
85
* Ouput message part to op.
86
*/
87
static int
88
part(register struct parse* pp, FILE* op, struct part* ap, off_t size, char* prefix, int prefixlen, int emptylen, unsigned long flags)
89
{
90
register int n = 0;
91
register int i;
92
register FILE* ip;
93
int r = -1;
94
char* s;
95
FILE* tp;
96
int skip;
97
int line;
98
99
ip = pp->fp;
100
if (*ap->code && !isdigit(*ap->code)) {
101
if (!(tp = fileopen(state.tmp.more, "Ew")))
102
return -1;
103
filecopy(NiL, ip, NiL, tp, NiL, size, NiL, NiL, 0);
104
fileclose(tp);
105
sfprintf(state.path.temp, "uudecode -h -t -o - -x %s %s", ap->code, state.tmp.more);
106
if (!(ap->flags & PART_text) &&
107
!mimecmp("text", ap->type, NiL) &&
108
mime(1) &&
109
(s = mimeview(state.part.mime, NiL, "", ap->type, NiL)))
110
sfprintf(state.path.temp, " | %s", s);
111
if (!(ip = pipeopen(struse(state.path.temp), "r")))
112
goto bad;
113
}
114
else if (!(ap->flags & PART_text) &&
115
!mimecmp("text", ap->type, NiL) &&
116
mime(1) &&
117
(s = mimeview(state.part.mime, NiL, state.tmp.more, ap->type, NiL))) {
118
if (!(tp = fileopen(state.tmp.more, "Ew")))
119
return -1;
120
filecopy(NiL, ip, NiL, tp, NiL, size, NiL, NiL, 0);
121
fileclose(tp);
122
if (!(ip = pipeopen(s, "r")))
123
goto bad;
124
}
125
if (prefix) {
126
skip = 1;
127
line = 0;
128
while (size > 0) {
129
if (!fgets(pp->buf, sizeof(pp->buf), ip)) {
130
n = 0;
131
break;
132
}
133
size -= n = strlen(pp->buf);
134
if (pp->buf[0] == '\n' || pp->buf[0] == '\r' && pp->buf[1] == '\n') {
135
if (!skip)
136
line++;
137
continue;
138
}
139
else
140
skip = 0;
141
#if 0
142
if ((flags & GREFERENCES) && pp->buf[0] == '-' && pp->buf[1] == '-' && isspace(pp->buf[2]))
143
break;
144
#endif
145
if (line) {
146
line = 0;
147
fputc('\n', op);
148
}
149
i = n > 1 ? prefixlen : emptylen;
150
if (fwrite(prefix, 1, i, op) != i || fwrite(pp->buf, 1, n, op) != n)
151
goto bad;
152
}
153
}
154
else {
155
while (size > 0) {
156
n = size < sizeof(pp->buf) ? size : sizeof(pp->buf);
157
if ((n = fread(pp->buf, 1, n, ip)) <= 0)
158
break;
159
size -= n;
160
if (fwrite(pp->buf, 1, n, op) != n)
161
goto bad;
162
}
163
}
164
if (n > 0 && pp->buf[n - 1] != '\n')
165
if ((n = getc(ip)) != EOF && putc(n, op) == EOF)
166
goto bad;
167
r = 0;
168
bad:
169
if (ip != pp->fp) {
170
if (ip)
171
fileclose(ip);
172
remove(state.tmp.more);
173
}
174
return r;
175
}
176
177
/*
178
* Show the message described by the passed pointer to the
179
* passed output buffer. Return -1 on error.
180
* Adjust the status: field if need be.
181
* If ignore is given, suppress ignored header fields.
182
* prefix is a string to prepend to each output line.
183
*/
184
int
185
copy(register struct msg* mp, FILE* op, Dt_t** ignore, char* prefix, unsigned long flags)
186
{
187
register char* s;
188
register int i;
189
char* date;
190
char* head;
191
char* from;
192
int emptylen;
193
int prefixlen;
194
long n;
195
struct part* ap;
196
struct parse pp;
197
198
if (state.folder == FIMAP)
199
return imap_copy(mp, op, ignore, prefix, flags);
200
201
/*
202
* Compute the prefix string, without trailing whitespace
203
*/
204
205
if (prefix) {
206
prefixlen = strlen(prefix);
207
s = prefix + prefixlen;
208
while (--s >= prefix && isspace(*s));
209
emptylen = (s + 1) - prefix;
210
}
211
flags |= GDISPLAY|GNL;
212
if (flags & GREFERENCES) {
213
if (headset(&pp, mp, NiL, NiL, NiL, 0)) {
214
date = 0;
215
from = 0;
216
head = savestr(pp.data);
217
while (headget(&pp)) {
218
if (!strcasecmp(pp.name, "date"))
219
date = savestr(pp.data);
220
else if (!strcasecmp(pp.name, "from"))
221
from = savestr(pp.data);
222
}
223
if (from) {
224
if (s = strchr(from, '<')) {
225
while (s > from && isspace(*(s - 1)))
226
s--;
227
*s = 0;
228
}
229
if (*from == '"' && *++from && *(s = from + strlen(from) - 1) == '"')
230
*s = 0;
231
}
232
else {
233
from = head;
234
if (s = strchr(head, ' ')) {
235
*s++ = 0;
236
if (!date)
237
date = s;
238
}
239
}
240
if (date || (date = strchr(head, ' ')) && ++date)
241
fprintf(op, "On %s ", date);
242
fprintf(op, "%s wrote:\n", from);
243
}
244
}
245
else {
246
if (!ignore || !ignored(ignore, "status"))
247
flags |= GSTATUS;
248
if (headset(&pp, mp, NiL, NiL, ignore, flags)) {
249
i = pp.length > 1 ? prefixlen : emptylen;
250
do {
251
if (prefix && fwrite(prefix, 1, i, op) != i ||
252
fwrite(pp.buf, 1, pp.length, op) != pp.length)
253
return -1;
254
} while (headget(&pp));
255
}
256
fputc('\n', op);
257
}
258
259
/*
260
* Copy out the message body
261
*/
262
263
if (ap = state.part.in.head) {
264
n = 0;
265
do {
266
if (flags & GINTERPOLATE) {
267
boundary();
268
note(DEBUG, "interpolate boundary=%s offset=%ld size=%ld", state.part.out.boundary, (long)ap->raw.offset, (long)ap->raw.size);
269
fprintf(op, "\n--%s\n", state.part.out.boundary);
270
fseek(pp.fp, ap->raw.offset, SEEK_SET);
271
if (part(&pp, op, &state.part.global, ap->raw.size, prefix, prefixlen, emptylen, flags))
272
return -1;
273
n += ap->raw.size;
274
}
275
else if ((ap->flags & PART_body) || (flags & GMIME) && ap->count == 1 && !n && ap->next && !ap->next->lines && mime(1) && (s = mimeview(state.part.mime, NiL, NiL, ap->type, ap->opts))) {
276
note(DEBUG, "copy part text offset=%ld size=%ld lines=%ld", (long)ap->offset, (long)ap->size, (long)ap->lines);
277
fseek(pp.fp, ap->offset, SEEK_SET);
278
if (part(&pp, op, ap, ap->size, prefix, prefixlen, emptylen, flags))
279
return -1;
280
n += ap->lines;
281
}
282
else
283
fprintf(op, "(attachment %2d %s %20s \"%s\")\n\n", ap->count, counts(1, ap->lines, ap->size), ap->type, ap->name);
284
} while (ap = ap->next);
285
}
286
else if (part(&pp, op, &state.part.global, pp.count, prefix, prefixlen, emptylen, flags))
287
return -1;
288
return 0;
289
}
290
291
/*
292
* Prepend headers in front of the collected stuff.
293
*/
294
static int
295
prepend(struct letter* lp)
296
{
297
register FILE* nfi;
298
register FILE* nfo;
299
time_t now;
300
301
lp->head = lp->body = 0;
302
if (!(nfo = fileopen(state.tmp.mail, "Ew")))
303
return -1;
304
if (!(nfi = fileopen(state.tmp.mail, "Er"))) {
305
fileclose(nfo);
306
return -1;
307
}
308
rm(state.tmp.mail);
309
time(&now);
310
fprintf(nfo, "From %s %s", state.var.user, ctime(&now));
311
lp->head = ftell(nfo);
312
headout(nfo, lp->hp, GSEND|GEXTERN|GNL|GCOMMA);
313
lp->body = ftell(nfo);
314
if (filecopy("message body", lp->fp, state.tmp.mail, nfo, NiL, (off_t)0, NiL, NiL, 0)) {
315
fileclose(nfo);
316
fileclose(nfi);
317
lp->head = lp->body = 0;
318
rewind(lp->fp);
319
return -1;
320
}
321
fileclose(nfo);
322
fileclose(lp->fp);
323
lp->fp = nfi;
324
rewind(nfi);
325
return 0;
326
}
327
328
/*
329
* Save the outgoing mail on the passed file.
330
*/
331
static void
332
savemail(struct letter* lp, char* name)
333
{
334
register FILE* fp;
335
336
if (fp = fileopen(name, "Ea")) {
337
filecopy(NiL, lp->fp, name, fp, NiL, (off_t)0, NiL, NiL, GNL);
338
fileclose(fp);
339
rewind(lp->fp);
340
}
341
}
342
343
/*
344
* Pack ACTIVE args for execution.
345
*/
346
static int
347
packargs(Dt_t* dt, void* object, void* context)
348
{
349
if (!(((struct name*)object)->flags & GDONE))
350
addarg((struct argvec*)context, ((struct name*)object)->name);
351
return 0;
352
}
353
354
/*
355
* Determine if the passed address is a local "send to file" address.
356
* If any of the network metacharacters precedes any slashes, it can't
357
* be a filename. We cheat with .'s to allow path names like ./...
358
* As a final check the dir prefix must exist.
359
*/
360
static int
361
isfileaddr(char* name)
362
{
363
register char* cp;
364
register char* sp;
365
int r;
366
367
if (*name == '+' || *name == '~')
368
return 1;
369
sp = 0;
370
for (cp = name; *cp; cp++) {
371
if (*cp == '!' || *cp == '%' || *cp == '@')
372
break;
373
if (*cp == '/')
374
sp = cp;
375
}
376
if (!sp)
377
return 0;
378
*sp = 0;
379
r = access(name, F_OK) >= 0;
380
*sp = '/';
381
return r;
382
}
383
384
/*
385
* For each recipient in the header name list with a /
386
* in the name, append the message to the end of the named file
387
* and remove him from the recipient list.
388
*
389
* Recipients whose name begins with | are piped through the given
390
* program and removed.
391
*/
392
static int
393
special(Dt_t* dt, void* object, void* context)
394
{
395
register struct name* np = (struct name*)object;
396
register struct letter* lp = (struct letter*)context;
397
register char* name;
398
FILE* fp;
399
char* cmd;
400
int n;
401
402
if ((cmd = iscmd(np->name)) || isfileaddr(np->name)) {
403
name = cmd ? cmd : expand(np->name, 1);
404
if (state.var.debug) {
405
note(DEBUG, "mail to %s: \"%s\"", cmd ? "pipe" : "file", name);
406
goto cant;
407
}
408
409
/*
410
* Now either copy the letter to the desired file
411
* or give it as the standard input to the desired
412
* program as appropriate.
413
*/
414
415
if (cmd) {
416
if (!(fp = fileopen(state.tmp.edit, "Ew+"))) {
417
state.senderr++;
418
goto cant;
419
}
420
remove(state.tmp.edit);
421
filecopy(NiL, lp->fp, state.tmp.edit, fp, NiL, (off_t)0, NiL, NiL, GNL);
422
rewind(lp->fp);
423
n = start_command(state.var.shell, SIG_REG_EXEC, fileno(fp), -1, "-c", name, NiL);
424
fileclose(fp);
425
if (n < 0) {
426
state.senderr++;
427
goto cant;
428
}
429
free_command(n);
430
}
431
else {
432
if (!(fp = fileopen(name, "Ea"))) {
433
state.senderr++;
434
goto cant;
435
}
436
filecopy(NiL, lp->fp, name, fp, NiL, (off_t)0, NiL, NiL, GNL);
437
rewind(lp->fp);
438
fileclose(fp);
439
}
440
cant:
441
np->flags |= GDONE;
442
}
443
return 0;
444
}
445
446
/*
447
* Mail a message on standard input to the people indicated
448
* in the passed header.
449
*/
450
void
451
sendmail(struct header* hp, unsigned long flags)
452
{
453
register char* s;
454
char** p;
455
int type;
456
int pid;
457
struct argvec args;
458
struct letter letter;
459
460
/*
461
* Collect user's mail from standard input.
462
* Get the result as letter.fp.
463
*/
464
if (!(letter.fp = collect(hp, flags)))
465
return;
466
letter.hp = hp;
467
if (state.var.interactive) {
468
if (type = (state.askheaders & (GBCC|GCC)))
469
grabedit(letter.hp, type);
470
else
471
note(0, "EOT");
472
}
473
if (!filesize(letter.fp))
474
if (letter.hp->h_subject)
475
note(0, "Null message body; hope that's ok");
476
else
477
note(0, "No message, no subject; hope that's ok");
478
/*
479
* Now, take the user names from the combined
480
* to and cc lists and do all the alias processing.
481
*/
482
if (!usermap(letter.hp, 0)) {
483
note(0, "No recipients specified");
484
state.senderr++;
485
}
486
if (prepend(&letter))
487
state.senderr++;
488
dictwalk(&letter.hp->h_names, special, &letter);
489
if (state.senderr) {
490
fseek(letter.fp, letter.body, SEEK_SET);
491
savedeadletter(letter.fp);
492
}
493
initargs(&args);
494
getargs(&args, state.var.sendmail);
495
if (p = letter.hp->h_options)
496
while (s = *p++)
497
addarg(&args, s);
498
p = args.argp;
499
dictwalk(&letter.hp->h_names, packargs, &args);
500
endargs(&args);
501
if (args.argp != p) {
502
s = record(letter.hp->h_first, flags);
503
if (state.var.debug) {
504
note(DEBUG|PROMPT, "sendmail command:");
505
for (p = args.argv; *p; p++)
506
printf(" \"%s\"", *p);
507
printf("\n");
508
if (s)
509
note(DEBUG, "record mail in \"%s\"", s);
510
note(DEBUG, "message contents:");
511
filecopy(NiL, letter.fp, NiL, stdout, NiL, (off_t)0, NiL, NiL, 0);
512
}
513
else {
514
if (s)
515
savemail(&letter, s);
516
/*
517
* Set up the temporary mail file as standard input for "mail"
518
* and exec with the user list we generated far above.
519
*/
520
endargs(&args);
521
fseek(letter.fp, letter.head, SEEK_SET);
522
fflush(letter.fp);
523
s = args.argv[0];
524
if (strneq(s, "smtp://", 7)) {
525
if (!*(s += 7))
526
s = state.var.smtp;
527
if (sendsmtp(letter.fp, s, args.argv + 1, (off_t)0)) {
528
fseek(letter.fp, letter.body, SEEK_SET);
529
savedeadletter(letter.fp);
530
}
531
}
532
else if ((pid = start_command(s, SIG_REG_EXEC|SIG_REG_TERM, fileno(letter.fp), -1, NiL, (char*)args.argv, NiL)) < 0) {
533
fseek(letter.fp, letter.body, SEEK_SET);
534
savedeadletter(letter.fp);
535
}
536
else if (state.var.sendwait)
537
wait_command(pid);
538
else
539
free_command(pid);
540
}
541
}
542
fileclose(letter.fp);
543
}
544
545
/*
546
* Low level for fmt().
547
*/
548
549
typedef struct
550
{
551
int col;
552
int comma;
553
int flags;
554
const char* label;
555
FILE* fp;
556
} Format_t;
557
558
static int
559
format(Dt_t* dt, void* object, void* context)
560
{
561
register struct name* np = (struct name*)object;
562
register Format_t* fs = (Format_t*)context;
563
register int n;
564
565
if (fs->flags & np->flags) {
566
n = strlen(np->name);
567
if (fs->label) {
568
if (fs->col = strlen(fs->label))
569
fputs(fs->label, fs->fp);
570
fs->label = 0;
571
}
572
else if ((fs->col + fs->comma + n + 1) > MARGIN) {
573
if (fs->comma)
574
putc(',', fs->fp);
575
fputs("\n ", fs->fp);
576
fs->col = 4;
577
}
578
else {
579
if (fs->comma) {
580
putc(',', fs->fp);
581
fs->col++;
582
}
583
putc(' ', fs->fp);
584
fs->col++;
585
}
586
fputs(np->name, fs->fp);
587
fs->col += n;
588
}
589
return 0;
590
}
591
592
/*
593
* Format the given header line to not exceed MARGIN characters.
594
*/
595
static void
596
fmt(FILE* fp, struct header* hp, const char* label, unsigned long flags, int comma)
597
{
598
Format_t fs;
599
600
fs.col = 0;
601
fs.comma = comma;
602
fs.flags = flags;
603
fs.label = label;
604
fs.fp = fp;
605
dictwalk(&hp->h_names, format, &fs);
606
if (!fs.label)
607
putc('\n', fp);
608
}
609
610
/*
611
* Dump the to, subject, cc header to fp.
612
*/
613
int
614
headout(FILE* fp, struct header* hp, register unsigned long flags)
615
{
616
register const struct lab* lp;
617
register struct list* x;
618
int comma;
619
int force;
620
int gotcha;
621
622
comma = (flags & GCOMMA);
623
gotcha = 0;
624
flags &= hp->h_flags|GSEND|GCOMMA|GRULE|GNL;
625
force = (flags & GRULE) ? state.editheaders : 0;
626
if (flags & GMISC) {
627
gotcha = 1;
628
for (x = hp->h_misc.head; x; x = x->next)
629
fprintf(fp, "%s\n", x->name);
630
}
631
if (flags & GSEND) {
632
gotcha = 1;
633
if (state.var.fixedheaders)
634
fprintf(fp, "%s\n", state.var.fixedheaders);
635
fprintf(fp, "X-Mailer: %s\n", state.version);
636
fprintf(fp, "Mime-Version: 1.0\n");
637
if (state.part.out.multi)
638
fprintf(fp, "Content-Type: multipart/mixed; boundary=\"%s\"\n", state.part.out.boundary);
639
else {
640
fprintf(fp, "Content-Type: text/plain; charset=us-ascii\n");
641
fprintf(fp, "Content-Transfer-Encoding: 7bit\n");
642
}
643
if (hp->h_flags & GMESSAGEID) {
644
fprintf(fp, "References: ");
645
if (hp->h_flags & GREFERENCES) {
646
char* s;
647
int m;
648
int n;
649
650
m = REFLEN - strlen(hp->h_messageid) - 12;
651
s = hp->h_references;
652
if ((n = strlen(s)) > m) {
653
for (s += n - m; *s && !isspace(*s); s++);
654
for (; isspace(*s); s++);
655
}
656
fprintf(fp, "%s ", s);
657
}
658
fprintf(fp, "%s\n", hp->h_messageid);
659
hp->h_flags &= ~(GMESSAGEID|GREFERENCES);
660
}
661
}
662
for (lp = state.hdrtab; lp->name; lp++)
663
if (flags & lp->type) {
664
gotcha = 1;
665
if (lp->type & GSUB)
666
fprintf(fp, "%s%s\n", lp->name, hp->h_subject);
667
else
668
fmt(fp, hp, lp->name, lp->type, comma);
669
}
670
else if (force & lp->type)
671
fprintf(fp, "%s\n", lp->name);
672
if (gotcha && (flags & GNL)) {
673
if ((flags & GRULE) && state.var.rule && *state.var.rule)
674
fprintf(fp, "%s\n\n", state.var.rule);
675
else
676
fputc('\n', fp);
677
}
678
return 0;
679
}
680
681