Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/collect.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2006 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
* Collect input from standard input, handling ~ escapes.
73
*/
74
75
#include "mailx.h"
76
77
#define CODE_64 (1<<0)
78
#define CODE_QP (1<<1)
79
#define CODE_TEXT (1<<2)
80
81
#define PART_INIT 0
82
#define PART_MAIN 1
83
#define PART_DATA 2
84
85
/*
86
* On interrupt, come here to save the partial message in ~/dead.letter.
87
* Then jump out of the collection loop.
88
*/
89
/*ARGSUSED*/
90
static void
91
collint(int sig)
92
{
93
/*
94
* the control flow is subtle, because we can be called from ~q.
95
*/
96
if (!state.collect.hadintr) {
97
if (state.var.ignore) {
98
puts("@");
99
fflush(stdout);
100
clearerr(stdin);
101
return;
102
}
103
state.collect.hadintr = 1;
104
longjmp(state.collect.work, sig);
105
}
106
rewind(state.collect.fp);
107
if (state.var.save)
108
savedeadletter(state.collect.fp);
109
longjmp(state.collect.abort, sig);
110
}
111
112
/*ARGSUSED*/
113
static void
114
collhup(int sig)
115
{
116
rewind(state.collect.fp);
117
savedeadletter(state.collect.fp);
118
/*
119
* Let's pretend nobody else wants to clean up,
120
* a true statement at this time.
121
*/
122
exit(1);
123
}
124
125
/*
126
* Print (continue) when continued after ^Z.
127
*/
128
/*ARGSUSED*/
129
static void
130
collstop(int sig)
131
{
132
sig_t old_action = signal(sig, SIG_DFL);
133
134
kill(getpid(), sig);
135
signal(sig, old_action);
136
if (state.collect.working) {
137
state.collect.working = 0;
138
state.collect.hadintr = 0;
139
longjmp(state.collect.work, sig);
140
}
141
}
142
143
/*
144
* Write a file, ex-like if f set.
145
*/
146
static int
147
exwrite(char* name, FILE* fp, int f)
148
{
149
register FILE* of;
150
off_t cc;
151
off_t lc;
152
153
if (f)
154
note(PROMPT, "\"%s\" ", name);
155
if (isreg(name)) {
156
if (!f)
157
note(PROMPT, "\"%s\" ", name);
158
note(0, "[File exists]");
159
return -1;
160
}
161
if (!(of = fileopen(name, "Ew")))
162
return -1;
163
if (filecopy(NiL, fp, name, of, NiL, (off_t)0, &lc, &cc, 0)) {
164
fileclose(of);
165
return -1;
166
}
167
fileclose(of);
168
note(0, "%ld/%ld", (long)lc, (long)cc);
169
return 0;
170
}
171
172
/*
173
* Edit the message on state.collect.fp.
174
* On return, make the edit file the new temp file.
175
*/
176
static void
177
editmessage(struct header* hp, int c)
178
{
179
sig_t sigint;
180
FILE* fp;
181
182
sigint = signal(SIGINT, SIG_IGN);
183
if (fp = run_editor(state.collect.fp, (off_t)-1, hp, c, 0)) {
184
fseek(fp, (off_t)0, SEEK_END);
185
fileclose(state.collect.fp);
186
state.collect.fp = fp;
187
}
188
signal(SIGINT, sigint);
189
}
190
191
/*
192
* Pipe the message through the command.
193
* Old message is on stdin of command;
194
* New message collected from stdout.
195
* Sh -c must return 0 to accept the new message.
196
*/
197
static void
198
pipemessage(FILE* fp, char* cmd)
199
{
200
FILE *nf;
201
sig_t sigint = signal(SIGINT, SIG_IGN);
202
203
if (!(nf = fileopen(state.tmp.edit, "Ew+")))
204
goto out;
205
remove(state.tmp.edit);
206
/*
207
* stdin = current message.
208
* stdout = new message.
209
*/
210
if (run_command(state.var.shell, 0, fileno(fp), fileno(nf), "-c", cmd, NiL) < 0) {
211
fileclose(nf);
212
goto out;
213
}
214
if (!filesize(nf)) {
215
note(0, "No bytes from \"%s\" !?", cmd);
216
fileclose(nf);
217
goto out;
218
}
219
/*
220
* Take new files.
221
*/
222
fseek(nf, (off_t)0, SEEK_END);
223
state.collect.fp = nf;
224
fileclose(fp);
225
out:
226
signal(SIGINT, sigint);
227
}
228
229
/*
230
* Return the name of the dead.letter file.
231
*/
232
static char*
233
deadletter(void)
234
{
235
register char* s;
236
237
if ((s = expand(state.var.dead, 1)) && *s != '/') {
238
sfprintf(state.path.temp, "~/%s", s);
239
s = expand(struse(state.path.temp), 1);
240
}
241
return s;
242
}
243
244
/*
245
* Interpolate the named messages into the current
246
* message, preceding each line with a tab.
247
* Return a count of the number of characters now in
248
* the message, or -1 if an error is encountered writing
249
* the message temporary. The flag argument is 'm' if we
250
* should shift over and 'f' if not.
251
*/
252
static int
253
interpolate(char* ms, FILE* fp, int f, int followup)
254
{
255
register struct msg* mp;
256
register struct msg* ip;
257
Dt_t** ignore;
258
char* prefix;
259
unsigned long flags;
260
261
if (getmsglist(ms, 0) < 0)
262
return 0;
263
if (!state.msg.list->m_index) {
264
if (!(state.msg.list->m_index = first(0, MMNORM))) {
265
note(0, "No appropriate messages");
266
return 0;
267
}
268
(state.msg.list + 1)->m_index = 0;
269
}
270
flags = 0;
271
if (f == 'f' || f == 'F') {
272
if (f == 'f')
273
flags |= GINTERPOLATE|GMIME;
274
prefix = 0;
275
}
276
else if (!(prefix = state.var.indentprefix))
277
prefix = "\t";
278
ignore = isupper(f) ? (Dt_t**)0 : &state.ignore;
279
if (!followup)
280
printf("Interpolating:");
281
for (ip = state.msg.list; ip->m_index; ip++) {
282
mp = state.msg.list + ip->m_index - 1;
283
touchmsg(mp);
284
if (followup)
285
flags |= GREFERENCES;
286
else
287
printf(" %d", ip->m_index);
288
if (copy(mp, fp, ignore, prefix, flags) < 0) {
289
note(SYSTEM, "%s", state.tmp.mail);
290
return -1;
291
}
292
}
293
if (!followup)
294
printf("\n");
295
return 0;
296
}
297
298
/*
299
* Generate multipart boundary.
300
*/
301
void
302
boundary(void)
303
{
304
if (!state.part.out.multi) {
305
state.part.out.multi = 1;
306
state.part.out.boundlen = sfsprintf(state.part.out.boundary, sizeof(state.part.out.boundary), "=_=_=_=_=%s==%04X==%08X==", state.var.user, getpid(), time(NiL));
307
}
308
}
309
310
/*
311
* Ouput multipart header.
312
*/
313
static void
314
part(FILE* fp, char* name, char* type, int code)
315
{
316
char* s;
317
318
if (name && (s = strrchr(name, '/')))
319
name = s + 1;
320
boundary();
321
fprintf(fp, "\n--%s\n", state.part.out.boundary);
322
if (!type)
323
type = (code & CODE_64) ? "application/octet-stream" : "text/plain";
324
fprintf(fp, "Content-Type: %s", type);
325
if (!mimecmp("text", type, NiL))
326
fprintf(fp, "; charset=us-ascii");
327
if (name)
328
fprintf(fp, "; name=\"%s\"", name);
329
fprintf(fp, "\nContent-Transfer-Encoding: %s\n", (code & CODE_64) ? "base64" : (code & CODE_QP) ? "quoted-printable" : "7bit");
330
if (name)
331
fprintf(fp, "Content-Disposition: attachment; filename=\"%s\"\n", name);
332
fprintf(fp, "\n");
333
}
334
335
/*
336
* Collect input from standard input, handling ~ escapes.
337
*/
338
FILE*
339
collect(struct header* hp, unsigned long flags)
340
{
341
register char* s;
342
register int n;
343
int ask;
344
int c;
345
int eofcount;
346
int escape;
347
int headers;
348
int code;
349
int sig;
350
int g;
351
long cc;
352
long lc;
353
long tc;
354
char* t;
355
char* e;
356
FILE* fp;
357
char* av[2];
358
struct parse pp;
359
360
ask = 0;
361
state.collect.fp = 0;
362
state.part.out.multi = 0;
363
headers = 0;
364
/*
365
* Start catching signals from here, but we'll still die on interrupts
366
* until we're in the main loop.
367
*/
368
#ifdef SIGTSTP
369
state.collect.sigtstp = signal(SIGTSTP, collstop);
370
#endif
371
#ifdef SIGTTOU
372
state.collect.sigttou = signal(SIGTTOU, collstop);
373
#endif
374
#ifdef SIGTTIN
375
state.collect.sigttin = signal(SIGTTIN, collstop);
376
#endif
377
if ((sig = setjmp(state.collect.abort)) || (sig = setjmp(state.collect.work))) {
378
resume(sig);
379
rm(state.tmp.mail);
380
goto err;
381
}
382
if ((state.collect.sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
383
signal(SIGINT, collint);
384
if ((state.collect.sighup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
385
signal(SIGHUP, collhup);
386
state.noreset++;
387
if (!(state.collect.fp = fileopen(state.tmp.mail, "Ew+")))
388
goto err;
389
remove(state.tmp.mail);
390
391
pp.fp = stdin;
392
if ((flags & INTERPOLATE) && interpolate("", state.collect.fp, 'm', 1) < 0)
393
goto err;
394
if ((flags & INTERPOLATE) && state.var.interactive) {
395
rewind(state.collect.fp);
396
editmessage(hp, 'v');
397
note(0, "(continue)");
398
}
399
else {
400
g = GEDIT|GNL;
401
if (state.var.sendheaders) {
402
headers = 1;
403
flags |= HEADERS;
404
g &= ~GNL;
405
}
406
else if (state.var.interactive && !hp->h_subject && (state.askheaders & GSUB)) {
407
ask |= GSUB;
408
g &= ~GNL;
409
}
410
if (flags & HEADERS) {
411
if (flags & (FOLLOWUP|INTERPOLATE))
412
g |= GREFERENCES;
413
headout(stdout, hp, g);
414
fflush(stdout);
415
}
416
if (headers)
417
headset(&pp, NiL, pp.fp, hp, NiL, g|GDISPLAY);
418
}
419
escape = state.var.escape ? *state.var.escape : 0;
420
eofcount = 0;
421
state.collect.hadintr = 0;
422
if (sig = setjmp(state.collect.work)) {
423
/*
424
* Come here for printing the after-signal message.
425
* Duplicate messages won't be printed because
426
* the write is aborted if we get a SIGTTOU.
427
*/
428
resume(sig);
429
cont:
430
if (state.collect.hadintr)
431
note(0, "\n(Interrupt -- one more to kill letter)");
432
else
433
note(0, "(continue)");
434
}
435
else if (ask)
436
grabedit(hp, ask);
437
for (;;) {
438
state.collect.working = 1;
439
if (headers) {
440
if (headget(&pp))
441
continue;
442
headers = 0;
443
}
444
c = readline(pp.fp, pp.buf, sizeof(pp.buf));
445
state.collect.working = 0;
446
if (c < 0) {
447
if (state.var.interactive &&
448
state.var.ignoreeof && ++eofcount < 32) {
449
note(0, "Use \".\" to terminate letter");
450
continue;
451
}
452
break;
453
}
454
eofcount = 0;
455
state.collect.hadintr = 0;
456
if (pp.buf[0] == '.' && pp.buf[1] == 0 &&
457
state.var.interactive &&
458
(state.var.dot || state.var.ignoreeof))
459
break;
460
if (pp.buf[0] != escape || !state.var.interactive) {
461
if (putline(state.collect.fp, pp.buf) < 0)
462
goto err;
463
continue;
464
}
465
c = pp.buf[1];
466
switch (c) {
467
case '!':
468
/*
469
* Shell escape, send the balance of the
470
* line to sh -c.
471
*/
472
shell(&pp.buf[2]);
473
break;
474
case '.':
475
/*
476
* Simulate end of file on input.
477
*/
478
goto out;
479
case ':':
480
case '_':
481
/*
482
* Escape to command mode, but be nice!
483
*/
484
execute(&pp.buf[2], 1);
485
goto cont;
486
case '?':
487
pp.buf[1] = *state.var.escape;
488
av[0] = &pp.buf[1];
489
av[1] = 0;
490
help(av);
491
break;
492
case 'A':
493
case 'a':
494
/*
495
* Sign letter.
496
*/
497
s = c == 'a' ? state.var.sign : state.var.Sign;
498
if (s)
499
goto outstr;
500
if (state.var.signature && (fp = fileopen(state.var.signature, "EXr"))) {
501
filecopy(state.var.signature, fp, state.var.signature, state.collect.fp, stdout, (off_t)0, NiL, NiL, 0);
502
fileclose(fp);
503
}
504
goto cont;
505
case 'b':
506
/*
507
* Add stuff to blind carbon copies list.
508
*/
509
extract(hp, GBCC|GMETOO, &pp.buf[2]);
510
break;
511
case 'c':
512
/*
513
* Add to the CC list.
514
*/
515
extract(hp, GCC|GMETOO, &pp.buf[2]);
516
break;
517
case 'd':
518
strncopy(pp.buf + 2, deadletter(), sizeof(pp.buf) - 2);
519
/* fall into . . . */
520
case 'g':
521
case 'r':
522
case '<':
523
/*
524
* Invoke a file:
525
* Search for the file name,
526
* then open it and copy the contents
527
* to state.collect.fp.
528
*/
529
for (s = &pp.buf[2]; isspace(*s); s++);
530
if (!*s || !(s = expand(s, 1))) {
531
note(0, "Read what file !?");
532
break;
533
}
534
if (c == 'g') {
535
if (!(fp = fileopen(s, "ERr")))
536
break;
537
code = 0;
538
if (mime(1)) {
539
t = mimetype(state.part.mime, fp, s, &state.openstat);
540
if (!mimecmp("text", t, NiL))
541
code |= CODE_TEXT;
542
}
543
else
544
t = 0;
545
cc = tc = 0;
546
while ((lc = getc(fp)) != EOF) {
547
tc++;
548
if ((iscntrl(lc) || !isprint(lc)) && !isspace(lc))
549
cc++;
550
}
551
code |= cc ? ((cc < tc / 80) ? CODE_QP : CODE_64) : CODE_TEXT;
552
part(state.collect.fp, s, t, code);
553
if (!(code & (CODE_64|CODE_QP)))
554
rewind(fp);
555
else {
556
fileclose(fp);
557
if (t) {
558
if (e = mimeview(state.part.mime, "compose", state.tmp.mail, t, NiL)) {
559
sfprintf(state.path.temp, "%s<%s", e, s);
560
if (state.part.disc.flags & MIME_PIPE) {
561
sfprintf(state.path.temp, "|");
562
t = 0;
563
}
564
else {
565
sfprintf(state.path.temp, ";");
566
t = state.tmp.mail;
567
}
568
}
569
else
570
t = s;
571
}
572
else
573
t = s;
574
sfprintf(state.path.temp, "uuencode -h -x %s", (code & CODE_QP) ? "quoted-printable " : "base64 ");
575
if (t)
576
shquote(state.path.temp, t);
577
sfputc(state.path.temp, ' ');
578
shquote(state.path.temp, s);
579
if (t == state.tmp.mail)
580
{
581
sfprintf(state.path.temp, "; rm -f ");
582
shquote(state.path.temp, t);
583
}
584
if (!(fp = pipeopen(struse(state.path.temp), "r")))
585
break;
586
}
587
}
588
else if (!(fp = iscmd(s) ? pipeopen(s + 1, "r") : fileopen(s, "ERr")))
589
break;
590
note(PROMPT, "\"%s\" ", s);
591
lc = 0;
592
cc = 0;
593
while (readline(fp, pp.buf, LINESIZE) >= 0) {
594
lc++;
595
if ((n = putline(state.collect.fp, pp.buf)) < 0) {
596
fileclose(fp);
597
goto err;
598
}
599
cc += n;
600
}
601
fileclose(fp);
602
if (c == 'g')
603
fprintf(state.collect.fp, "--%s--\n\n", state.part.out.boundary);
604
note(0, "%d/%d", lc, cc);
605
break;
606
case 'e':
607
case 'v':
608
/*
609
* Edit the current message.
610
* 'e' means to use EDITOR
611
* 'v' means to use VISUAL
612
*/
613
rewind(state.collect.fp);
614
editmessage(hp, c);
615
goto cont;
616
case 'f':
617
case 'F':
618
case 'm':
619
case 'M':
620
/*
621
* Interpolate the named messages, if we
622
* are in receiving mail mode. Does the
623
* standard list processing garbage.
624
* If ~f is given, we don't shift over.
625
*/
626
if (interpolate(pp.buf + 2, state.collect.fp, c, 0) < 0)
627
goto err;
628
goto cont;
629
case 'h':
630
/*
631
* Grab the standard headers.
632
*/
633
grabedit(hp, GSTD);
634
goto cont;
635
case 'i':
636
/*
637
* Insert variable value.
638
*/
639
for (s = pp.buf + 2; isspace(*s); s++);
640
s = varget(s);
641
if (s) {
642
outstr:
643
putline(state.collect.fp, s);
644
if (state.var.interactive)
645
putline(stdout, s);
646
}
647
goto cont;
648
case 'p':
649
/*
650
* Print out the current state of the
651
* message without altering anything.
652
*/
653
rewind(state.collect.fp);
654
fp = stdout;
655
if (sig = setjmp(state.jump.sigpipe))
656
resume(sig);
657
else {
658
if (!state.more.discipline && state.var.interactive && (lc = state.var.crt)) {
659
lc -= 5;
660
while ((c = getc(state.collect.fp)) != EOF)
661
if (c == '\n' && --lc <= 0) {
662
if (!(fp = pipeopen(state.var.pager, "Jw")))
663
fp = stdout;
664
break;
665
}
666
rewind(state.collect.fp);
667
}
668
fprintf(fp, "-------\nMessage contains:\n");
669
headout(fp, hp, GEDIT|GNL);
670
filecopy(NiL, state.collect.fp, NiL, fp, NiL, (off_t)0, NiL, NiL, 0);
671
}
672
fileclose(fp);
673
goto cont;
674
case 's':
675
/*
676
* Set the Subject list.
677
*/
678
s = &pp.buf[2];
679
while (isspace(*s))
680
s++;
681
if (hp->h_subject = savestr(s))
682
hp->h_flags |= GSUB;
683
break;
684
case 't':
685
/*
686
* Add to the To list.
687
*/
688
extract(hp, GTO|GMETOO, &pp.buf[2]);
689
break;
690
case 'w':
691
/*
692
* Write the message on a file.
693
*/
694
s = &pp.buf[2];
695
while (*s == ' ' || *s == '\t')
696
s++;
697
if (*s == 0) {
698
note(0, "Write what file !?");
699
break;
700
}
701
if (!(s = expand(s, 1)))
702
break;
703
rewind(state.collect.fp);
704
exwrite(s, state.collect.fp, 1);
705
break;
706
case 'x':
707
state.var.save = 0;
708
/*FALLTHROUGH*/
709
case 'q':
710
/*
711
* Force a quit of sending mail.
712
* Act like an interrupt happened.
713
*/
714
state.collect.hadintr++;
715
collint(SIGINT);
716
exit(1);
717
case '|':
718
/*
719
* Pipe message through command.
720
* Collect output as new message.
721
*/
722
rewind(state.collect.fp);
723
pipemessage(state.collect.fp, &pp.buf[2]);
724
goto cont;
725
default:
726
/*
727
* On double escape, just send the single one.
728
* Otherwise, it's an error.
729
*/
730
if (c == escape) {
731
if (putline(state.collect.fp, &pp.buf[1]) < 0)
732
goto err;
733
else
734
break;
735
}
736
if (isprint(c))
737
note(0, "\"%c%c\": unknown escape command", escape, c);
738
else
739
note(0, "\"%c\\%03o\": unknown escape command", escape, c);
740
break;
741
}
742
}
743
goto out;
744
err:
745
if (state.collect.fp) {
746
fileclose(state.collect.fp);
747
state.collect.fp = 0;
748
}
749
out:
750
if (state.collect.fp) {
751
rewind(state.collect.fp);
752
if (state.part.out.multi) {
753
/*
754
* Copy to a temp file adding the mime boundaries.
755
*/
756
if (fp = fileopen(state.tmp.edit, "EMw+")) {
757
fprintf(fp, "This is a multipart message in MIME format.\n");
758
n = PART_INIT;
759
while (s = fgets(state.path.path, sizeof(state.path.path), state.collect.fp)) {
760
if (s[0] == '-' && s[1] == '-' && !strncmp(s + 2, state.part.out.boundary, state.part.out.boundlen)) {
761
if (n == PART_INIT)
762
putc('\n', fp);
763
t = s + state.part.out.boundlen + 2;
764
if (*t == '\n' || *t == '\r' && *(t + 1) == '\n')
765
n = PART_DATA;
766
else if (*t++ == '-' && *t++ == '-' && (*t == '\n' || *t == '\r' && *(t + 1) == '\n')) {
767
n = PART_INIT;
768
continue;
769
}
770
}
771
else if (n == PART_INIT) {
772
if (*s == '\n' || *s == '\r' && *(s + 1) == '\n')
773
continue;
774
n = PART_MAIN;
775
part(fp, NiL, NiL, 0);
776
}
777
fputs(s, fp);
778
}
779
fprintf(fp, "\n--%s--\n\n", state.part.out.boundary);
780
fileclose(state.collect.fp);
781
remove(state.tmp.edit);
782
state.collect.fp = fp;
783
rewind(state.collect.fp);
784
}
785
}
786
}
787
state.noreset--;
788
#ifdef SIGTSTP
789
signal(SIGTSTP, state.collect.sigtstp);
790
#endif
791
#ifdef SIGTTOU
792
signal(SIGTTOU, state.collect.sigttou);
793
#endif
794
#ifdef SIGTTIN
795
signal(SIGTTIN, state.collect.sigttin);
796
#endif
797
signal(SIGINT, state.collect.sigint);
798
signal(SIGHUP, state.collect.sighup);
799
return state.collect.fp;
800
}
801
802
/*
803
* Save fp in ${DEAD}.
804
*/
805
void
806
savedeadletter(FILE* fp)
807
{
808
FILE* dp;
809
char* s;
810
811
if (filesize(fp) && (s = deadletter()) && (dp = fileopen(s, "Ma"))) {
812
filecopy(NiL, fp, s, dp, NiL, (off_t)0, NiL, NiL, 0);
813
fileclose(dp);
814
rewind(fp);
815
}
816
}
817
818