2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * Mail -- a mail program
49 * Mail to mail folders and displays.
52 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
53 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
54 * TODO may leave child running if fdopen() fails! */
62 static void onpipe(int signo
);
63 extern void brokpipe(int signo
);
64 static int sendpart(struct message
*zmp
, struct mimepart
*ip
, FILE *obuf
,
65 struct ignoretab
*doign
, char *prefix
, size_t prefixlen
,
66 enum sendaction action
, off_t
*stats
, int level
);
67 static struct mimepart
*parsemsg(struct message
*mp
, enum parseflags pf
);
68 static enum okay
parsepart(struct message
*zmp
, struct mimepart
*ip
,
69 enum parseflags pf
, int level
);
70 static void parsemultipart(struct message
*zmp
, struct mimepart
*ip
,
71 enum parseflags pf
, int level
);
72 static void newpart(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
,
74 static void endpart(struct mimepart
**np
, off_t xoffs
, long lines
);
75 static void parse822(struct message
*zmp
, struct mimepart
*ip
,
76 enum parseflags pf
, int level
);
77 static void parsepkcs7(struct message
*zmp
, struct mimepart
*ip
,
78 enum parseflags pf
, int level
);
79 static size_t out(char *buf
, size_t len
, FILE *fp
,
80 enum conversion convert
, enum sendaction action
,
81 char *prefix
, size_t prefixlen
, off_t
*stats
,
82 char **restp
, size_t *restsizep
);
83 static void addstats(off_t
*stats
, off_t lines
, off_t bytes
);
84 static FILE *newfile(struct mimepart
*ip
, int *ispipe
,
85 sighandler_type
*oldpipe
);
86 static char *getpipecmd(char *content
);
87 static FILE *getpipefile(char *cmd
, FILE **qbuf
, int quote
);
88 static void pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
,
89 char *prefix
, size_t prefixlen
, off_t
*stats
);
90 static void statusput(const struct message
*mp
, FILE *obuf
,
91 char *prefix
, off_t
*stats
);
92 static void xstatusput(const struct message
*mp
, FILE *obuf
,
93 char *prefix
, off_t
*stats
);
94 static void put_from_(FILE *fp
, struct mimepart
*ip
);
96 static sigjmp_buf pipejmp
;
103 siglongjmp(pipejmp
, 1);
107 * Send message described by the passed pointer to the
108 * passed output buffer. Return -1 on error.
109 * Adjust the status: field if need be.
110 * If doign is given, suppress ignored header fields.
111 * prefix is a string to prepend to each output line.
112 * action = data destination (SEND_MBOX,_TOFILE,_TODISP,_QUOTE,_DECRYPT).
113 * stats[0] is line count, stats[1] is character count. stats may be NULL.
114 * Note that stats[0] is valid for SEND_MBOX only.
117 send(struct message
*mp
, FILE *obuf
, struct ignoretab
*doign
,
118 char *prefix
, enum sendaction action
, off_t
*stats
)
122 size_t prefixlen
, sz
;
128 if (mp
== dot
&& action
!= SEND_TOSRCH
&& action
!= SEND_TOFLTR
)
131 stats
[0] = stats
[1] = 0;
133 * Compute the prefix string, without trailing whitespace
135 if (prefix
!= NULL
) {
137 for (cp
= prefix
; *cp
; cp
++)
138 if (!blankchar(*cp
& 0377))
140 prefixlen
= cp2
== 0 ? 0 : cp2
- prefix
+ 1;
144 * First line is the From_ line, so no headers there to worry about.
146 if ((ibuf
= setinput(&mb
, mp
, NEED_BODY
)) == NULL
)
150 if (mp
->m_flag
& MNOFROM
) {
151 if (doign
!= allignore
&& doign
!= fwdignore
&&
152 action
!= SEND_RFC822
)
153 sz
= fprintf(obuf
, "%sFrom %s %s\n",
154 prefix
? prefix
: "",
155 fakefrom(mp
), fakedate(mp
->m_time
));
157 if (prefix
&& doign
!= allignore
&& doign
!= fwdignore
&&
158 action
!= SEND_RFC822
) {
160 sz
+= strlen(prefix
);
162 while (count
&& (c
= getc(ibuf
)) != EOF
) {
163 if (doign
!= allignore
&& doign
!= fwdignore
&&
164 action
!= SEND_RFC822
) {
174 addstats(stats
, 1, sz
);
176 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&& action
!= SEND_SHOW
)
177 pf
|= PARSE_DECRYPT
|PARSE_PARTS
;
178 if ((ip
= parsemsg(mp
, pf
)) == NULL
)
180 return sendpart(mp
, ip
, obuf
, doign
, prefix
, prefixlen
, action
, stats
,
185 sendpart(struct message
*zmp
, struct mimepart
*ip
, FILE *obuf
,
186 struct ignoretab
*doign
, char *prefix
, size_t prefixlen
,
187 enum sendaction action
, off_t
*volatile stats
, int level
)
189 char *line
= NULL
, *cp
, *cp2
, *start
, *tcs
, *pipecmd
= NULL
, *rest
;
190 size_t linesize
= 0, linelen
, count
, len
, restsize
;
191 int dostat
, infld
= 0, ignoring
= 1, isenc
, c
, rt
= 0, eof
, ispipe
= 0;
193 FILE *volatile ibuf
= NULL
, *volatile pbuf
= obuf
,
194 *volatile qbuf
= obuf
, *origobuf
= obuf
;
195 enum conversion
volatile convert
;
196 sighandler_type
volatile oldpipe
= SIG_DFL
;
200 if (ip
->m_mimecontent
== MIME_PKCS7
&& ip
->m_multipart
&&
201 action
!= SEND_MBOX
&& action
!= SEND_RFC822
&&
207 if (!is_ign("status", 6, doign
))
209 if (!is_ign("x-status", 8, doign
))
214 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
217 if (ip
->m_mimecontent
== MIME_DISCARD
)
219 if ((ip
->m_flag
&MNOFROM
) == 0)
220 while (count
&& (c
= getc(ibuf
)) != EOF
) {
226 convert
= action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
227 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
228 action
== SEND_TOSRCH
|| action
== SEND_TOFLTR
?
229 CONV_FROMHDR
: CONV_NONE
;
230 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
232 if (line
[0] == '\n') {
234 * If line is blank, we've reached end of
235 * headers, so force out status: field
236 * and note that we are no longer in header
240 statusput(zmp
, obuf
, prefix
, stats
);
242 xstatusput(zmp
, obuf
, prefix
, stats
);
243 if (doign
!= allignore
)
244 out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
,
245 prefix
, prefixlen
, stats
,
250 if (infld
&& blankchar(line
[0]&0377)) {
252 * If this line is a continuation (via space or tab)
253 * of a previous header field, determine if the start
254 * of the line is a MIME encoded word.
257 for (cp
= line
; blankchar(*cp
&0377); cp
++);
258 if (cp
> line
&& linelen
- (cp
- line
) > 8 &&
259 cp
[0] == '=' && cp
[1] == '?')
264 * Pick up the header field if we have one.
266 for (cp
= line
; (c
= *cp
&0377) && c
!= ':' &&
267 !spacechar(c
); cp
++);
269 while (spacechar(*cp
&0377))
271 if (cp
[0] != ':' && level
== 0 && lineno
== 1) {
273 * Not a header line, force out status:
274 * This happens in uucp style mail where
275 * there are no headers at all.
278 statusput(zmp
, obuf
, prefix
, stats
);
280 xstatusput(zmp
, obuf
, prefix
, stats
);
281 if (doign
!= allignore
)
282 out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
,
283 prefix
, prefixlen
, stats
,
288 * If it is an ignored field and
289 * we care about such things, skip it.
292 *cp2
= 0; /* temporarily null terminate */
293 if (doign
&& is_ign(line
, cp2
- line
, doign
))
295 else if (asccasecmp(line
, "status") == 0) {
297 * If the field is "status," go compute
298 * and print the real Status: field
301 statusput(zmp
, obuf
, prefix
, stats
);
305 } else if (asccasecmp(line
, "x-status") == 0) {
307 * If the field is "status," go compute
308 * and print the real Status: field
311 xstatusput(zmp
, obuf
, prefix
, stats
);
321 * Determine if the end of the line is a MIME encoded word.
324 if (count
&& (c
= getc(ibuf
)) != EOF
) {
326 if (linelen
> 0 && line
[linelen
-1] == '\n')
327 cp
= &line
[linelen
-2];
329 cp
= &line
[linelen
-1];
330 while (cp
>= line
&& whitechar(*cp
&0377))
332 if (cp
- line
> 8 && cp
[0] == '=' &&
341 if (action
== SEND_TODISP
||
342 action
== SEND_TODISP_ALL
||
343 action
== SEND_QUOTE
||
344 action
== SEND_QUOTE_ALL
||
345 action
== SEND_TOSRCH
||
346 action
== SEND_TOFLTR
) {
348 * Strip blank characters if two MIME-encoded
349 * words follow on continuing lines.
352 while (len
>0&&blankchar(*start
&0377)) {
357 if (len
> 0 && start
[len
-1] == '\n')
359 while (len
> 0 && blankchar(start
[len
-1]&0377))
362 out(start
, len
, obuf
, convert
,
363 action
, prefix
, prefixlen
, stats
,
373 skip
: switch (ip
->m_mimecontent
) {
380 case SEND_TODISP_ALL
:
383 put_from_(obuf
, ip
->m_multipart
);
390 put_from_(obuf
, ip
->m_multipart
);
399 if (action
== SEND_TOFLTR
)
403 case MIME_TEXT_PLAIN
:
406 case SEND_TODISP_ALL
:
409 pipecmd
= getpipecmd(ip
->m_ct_type_plain
);
416 if (action
!= SEND_DECRYPT
)
420 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&&
421 action
!= SEND_SHOW
&& ip
->m_multipart
)
427 case SEND_TODISP_ALL
:
430 if ((pipecmd
= getpipecmd(ip
->m_ct_type_plain
)) != NULL
)
432 if (level
== 0 && count
) {
433 cp
= "[Binary content]\n\n";
434 out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
,
435 prefix
, prefixlen
, stats
,
451 case MIME_ALTERNATIVE
:
452 if ((action
== SEND_TODISP
|| action
== SEND_QUOTE
) &&
453 value("print-alternatives") == NULL
)
454 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
)
455 if (np
->m_mimecontent
== MIME_TEXT_PLAIN
) {
456 if (sendpart(zmp
, np
, obuf
,
469 case SEND_TODISP_ALL
:
478 if ((action
== SEND_TODISP
||
479 action
== SEND_TODISP_ALL
) &&
480 ip
->m_multipart
!= NULL
&&
481 ip
->m_multipart
->m_mimecontent
== MIME_DISCARD
&&
482 ip
->m_multipart
->m_nextpart
== NULL
) {
483 cp
= "[Missing multipart boundary - "
484 "use \"show\" to display "
485 "the raw message]\n\n";
486 out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
,
487 prefix
, prefixlen
, stats
,
490 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
) {
491 if (np
->m_mimecontent
== MIME_DISCARD
&&
492 action
!= SEND_DECRYPT
)
496 if (np
->m_partstring
&&
497 strcmp(np
->m_partstring
,
501 if ((obuf
= newfile(np
, &ispipe
,
508 case SEND_TODISP_ALL
:
510 if ((ip
->m_mimecontent
== MIME_MULTI
||
516 len
= strlen(np
->m_partstring
) +
520 "%sPart %s:\n", level
||
521 strcmp(np
->m_partstring
,
525 out(cp
, strlen(cp
), obuf
,
526 CONV_NONE
, SEND_MBOX
,
545 if (sendpart(zmp
, np
, obuf
,
546 doign
, prefix
, prefixlen
,
547 action
, stats
, level
+1) < 0)
549 else if (action
== SEND_QUOTE
)
551 if (action
== SEND_TOFILE
&& obuf
!= origobuf
) {
555 safe_signal(SIGPIPE
, SIG_IGN
);
557 safe_signal(SIGPIPE
, oldpipe
);
569 * Copy out message body
571 if (doign
== allignore
&& level
== 0) /* skip final blank line */
573 switch (ip
->m_mimeenc
) {
583 convert
= CONV_FROMQP
;
586 switch (ip
->m_mimecontent
) {
588 case MIME_TEXT_PLAIN
:
590 convert
= CONV_FROMB64_T
;
593 convert
= CONV_FROMB64
;
599 if (action
== SEND_DECRYPT
|| action
== SEND_MBOX
||
600 action
== SEND_RFC822
|| action
== SEND_SHOW
)
604 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
605 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
606 action
== SEND_TOSRCH
) &&
607 (ip
->m_mimecontent
== MIME_TEXT_PLAIN
||
608 ip
->m_mimecontent
== MIME_TEXT_HTML
||
609 ip
->m_mimecontent
== MIME_TEXT
)) {
610 if (iconvd
!= (iconv_t
)-1)
612 if (asccasecmp(tcs
, ip
->m_charset
) &&
613 asccasecmp(us_ascii
, ip
->m_charset
))
614 iconvd
= iconv_open_ft(tcs
, ip
->m_charset
);
616 iconvd
= (iconv_t
)-1;
618 #endif /* HAVE_ICONV */
619 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
620 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
) &&
623 pbuf
= getpipefile(pipecmd
, (FILE**)&qbuf
,
624 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
);
625 action
= SEND_TOPIPE
;
627 oldpipe
= safe_signal(SIGPIPE
, onpipe
);
628 if (sigsetjmp(pipejmp
, 1))
634 while (!eof
&& foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
636 while (convert
== CONV_FROMQP
&& linelen
>= 2 &&
637 line
[linelen
-2] == '=') {
639 size_t linesize2
, linelen2
;
643 if (foldergets(&line2
, &linesize2
, &count
, &linelen2
,
648 if (linelen
+ linelen2
+ 1 > linesize
)
649 line
= srealloc(line
, linesize
= linelen
+
651 memcpy(&line
[linelen
], line2
, linelen2
+1);
657 out(line
, linelen
, pbuf
, convert
, action
,
658 pbuf
== origobuf
? prefix
: NULL
,
659 pbuf
== origobuf
? prefixlen
: 0,
660 pbuf
== origobuf
? stats
: NULL
,
661 eof
? NULL
: &rest
, eof
? NULL
: &restsize
);
668 memmove(line
, rest
, restsize
);
675 safe_signal(SIGPIPE
, SIG_IGN
);
677 safe_signal(SIGPIPE
, oldpipe
);
679 pipecpy(qbuf
, obuf
, origobuf
, prefix
, prefixlen
, stats
);
682 if (iconvd
!= (iconv_t
)-1) {
684 iconvd
= (iconv_t
)-1;
690 static struct mimepart
*
691 parsemsg(struct message
*mp
, enum parseflags pf
)
695 ip
= csalloc(1, sizeof *ip
);
696 ip
->m_flag
= mp
->m_flag
;
697 ip
->m_have
= mp
->m_have
;
698 ip
->m_block
= mp
->m_block
;
699 ip
->m_offset
= mp
->m_offset
;
700 ip
->m_size
= mp
->m_size
;
701 ip
->m_xsize
= mp
->m_xsize
;
702 ip
->m_lines
= mp
->m_lines
;
703 ip
->m_xlines
= mp
->m_lines
;
704 if (parsepart(mp
, ip
, pf
, 0) != OKAY
)
710 parsepart(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
715 ip
->m_ct_type
= hfield1("content-type", (struct message
*)ip
);
716 if (ip
->m_ct_type
!= NULL
) {
717 ip
->m_ct_type_plain
= savestr(ip
->m_ct_type
);
718 if ((cp
= strchr(ip
->m_ct_type_plain
, ';')) != NULL
)
720 } else if (ip
->m_parent
&& ip
->m_parent
->m_mimecontent
== MIME_DIGEST
)
721 ip
->m_ct_type_plain
= "message/rfc822";
723 ip
->m_ct_type_plain
= "text/plain";
724 ip
->m_mimecontent
= mime_getcontent(ip
->m_ct_type_plain
);
726 ip
->m_charset
= mime_getparam("charset", ip
->m_ct_type
);
727 if (ip
->m_charset
== NULL
)
728 ip
->m_charset
= us_ascii
;
729 ip
->m_ct_transfer_enc
= hfield1("content-transfer-encoding",
730 (struct message
*)ip
);
731 ip
->m_mimeenc
= ip
->m_ct_transfer_enc
?
732 mime_getenc(ip
->m_ct_transfer_enc
) : MIME_7B
;
733 if ((cp
= hfield1("content-disposition", (struct message
*)ip
)) == 0 ||
734 (ip
->m_filename
= mime_getparam("filename", cp
)) == 0)
735 if (ip
->m_ct_type
!= NULL
)
736 ip
->m_filename
= mime_getparam("name", ip
->m_ct_type
);
737 if (pf
& PARSE_PARTS
) {
739 fprintf(stderr
, "MIME content too deeply nested.\n");
742 switch (ip
->m_mimecontent
) {
744 if (pf
& PARSE_DECRYPT
) {
745 parsepkcs7(zmp
, ip
, pf
, level
);
752 case MIME_ALTERNATIVE
:
754 parsemultipart(zmp
, ip
, pf
, level
);
757 parse822(zmp
, ip
, pf
, level
);
765 parsemultipart(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
770 size_t linesize
= 0, linelen
, count
, boundlen
;
772 struct mimepart
*np
= NULL
;
777 if ((boundary
= mime_getboundary(ip
->m_ct_type
)) == NULL
)
779 boundlen
= strlen(boundary
);
780 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
783 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
))
787 newpart(ip
, &np
, offs
, NULL
);
788 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
789 if ((lines
> 0 || part
== 0) && linelen
>= boundlen
+ 1 &&
790 strncmp(line
, boundary
, boundlen
) == 0) {
791 if (line
[boundlen
] == '\n') {
794 endpart(&np
, offs
-boundlen
-2, lines
);
795 newpart(ip
, &np
, offs
-boundlen
-2, NULL
);
797 endpart(&np
, offs
, 2);
798 newpart(ip
, &np
, offs
, &part
);
800 } else if (line
[boundlen
] == '-' &&
801 line
[boundlen
+1] == '-' &&
802 line
[boundlen
+2] == '\n') {
805 endpart(&np
, offs
-boundlen
-4, lines
);
806 newpart(ip
, &np
, offs
-boundlen
-4, NULL
);
808 endpart(&np
, offs
+count
, 2);
817 endpart(&np
, offs
, lines
);
819 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
)
820 if (np
->m_mimecontent
!= MIME_DISCARD
)
821 parsepart(zmp
, np
, pf
, level
+1);
826 newpart(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
, int *part
)
831 *np
= csalloc(1, sizeof **np
);
832 (*np
)->m_flag
= MNOFROM
;
833 (*np
)->m_have
= HAVE_HEADER
|HAVE_BODY
;
834 (*np
)->m_block
= mailx_blockof(offs
);
835 (*np
)->m_offset
= mailx_offsetof(offs
);
838 sz
= ip
->m_partstring
? strlen(ip
->m_partstring
) : 0;
840 (*np
)->m_partstring
= salloc(sz
);
841 if (ip
->m_partstring
)
842 snprintf((*np
)->m_partstring
, sz
, "%s.%u",
843 ip
->m_partstring
, *part
);
845 snprintf((*np
)->m_partstring
, sz
, "%u", *part
);
847 (*np
)->m_mimecontent
= MIME_DISCARD
;
848 (*np
)->m_parent
= ip
;
849 if (ip
->m_multipart
) {
850 for (pp
= ip
->m_multipart
; pp
->m_nextpart
; pp
= pp
->m_nextpart
);
851 pp
->m_nextpart
= *np
;
853 ip
->m_multipart
= *np
;
857 endpart(struct mimepart
**np
, off_t xoffs
, long lines
)
861 offs
= mailx_positionof((*np
)->m_block
, (*np
)->m_offset
);
862 (*np
)->m_size
= (*np
)->m_xsize
= xoffs
- offs
;
863 (*np
)->m_lines
= (*np
)->m_xlines
= lines
;
868 parse822(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
878 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
882 while (count
&& ((c
= getc(ibuf
)) != EOF
)) {
892 np
= csalloc(1, sizeof *np
);
893 np
->m_flag
= MNOFROM
;
894 np
->m_have
= HAVE_HEADER
|HAVE_BODY
;
895 np
->m_block
= mailx_blockof(offs
);
896 np
->m_offset
= mailx_offsetof(offs
);
897 np
->m_size
= np
->m_xsize
= count
;
898 np
->m_lines
= np
->m_xlines
= lines
;
899 np
->m_partstring
= ip
->m_partstring
;
901 ip
->m_multipart
= np
;
902 substdate((struct message
*)np
);
903 np
->m_from
= fakefrom((struct message
*)np
);
904 parsepart(zmp
, np
, pf
, level
+1);
908 parsepkcs7(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
911 struct message m
, *xmp
;
915 memcpy(&m
, ip
, sizeof m
);
916 to
= hfield1("to", zmp
);
917 cc
= hfield1("cc", zmp
);
918 if ((xmp
= smime_decrypt(&m
, to
, cc
, 0)) != NULL
) {
919 np
= csalloc(1, sizeof *np
);
920 np
->m_flag
= xmp
->m_flag
;
921 np
->m_have
= xmp
->m_have
;
922 np
->m_block
= xmp
->m_block
;
923 np
->m_offset
= xmp
->m_offset
;
924 np
->m_size
= xmp
->m_size
;
925 np
->m_xsize
= xmp
->m_xsize
;
926 np
->m_lines
= xmp
->m_lines
;
927 np
->m_xlines
= xmp
->m_xlines
;
928 np
->m_partstring
= ip
->m_partstring
;
929 if (parsepart(zmp
, np
, pf
, level
+1) == OKAY
) {
931 ip
->m_multipart
= np
;
937 out(char *buf
, size_t len
, FILE *fp
,
938 enum conversion convert
, enum sendaction action
,
939 char *prefix
, size_t prefixlen
, off_t
*stats
,
940 char **restp
, size_t *restsizep
)
947 if (action
== SEND_MBOX
|| action
== SEND_DECRYPT
) {
950 while (n
&& cp
[0] == '>')
952 if (n
>= 5 && cp
[0] == 'F' && cp
[1] == 'r' && cp
[2] == 'o' &&
953 cp
[3] == 'm' && cp
[4] == ' ') {
958 sz
+= mime_write(buf
, len
, fp
,
959 action
== SEND_MBOX
? CONV_NONE
: convert
,
960 action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
961 action
== SEND_QUOTE
||
962 action
== SEND_QUOTE_ALL
?
964 action
== SEND_TOSRCH
|| action
== SEND_TOPIPE
?
966 action
== SEND_TOFLTR
?
968 action
== SEND_SHOW
?
973 if (stats
&& stats
[0] != -1) {
974 for (cp
= buf
; cp
< &buf
[sz
]; cp
++)
978 addstats(stats
, lines
, sz
);
983 addstats(off_t
*stats
, off_t lines
, off_t bytes
)
993 * Get a file for an attachment.
996 newfile(struct mimepart
*ip
, int *ispipe
, sighandler_type
*oldpipe
)
998 char *f
= ip
->m_filename
;
1003 if (f
!= NULL
&& f
!= (char *)-1) {
1006 mime_fromhdr(&in
, &out
, TD_ISPR
);
1007 memcpy(f
, out
.s
, out
.l
);
1008 *(f
+ out
.l
) = '\0';
1012 if (value("interactive") != NULL
) {
1014 jgetname
: (void)printf(tr(278, "Enter filename for part %s (%s)"),
1015 ip
->m_partstring
? ip
->m_partstring
: "?",
1016 ip
->m_ct_type_plain
);
1017 f2
= readtty(": ", f
!= (char *)-1 ? f
: NULL
);
1018 if (f2
== NULL
|| *f2
== '\0') {
1019 fprintf(stderr
, tr(279, "... skipping this\n"));
1021 } else if (*f2
== '|')
1022 /* Pipes are expanded by the shell */
1024 else if ((f3
= expand(f2
)) == NULL
)
1025 /* (Error message written by expand()) */
1030 if (f
== NULL
|| f
== (char *)-1)
1035 cp
= value("SHELL");
1038 fp
= Popen(f
+ 1, "w", cp
, 1);
1043 *oldpipe
= safe_signal(SIGPIPE
, brokpipe
);
1047 if ((fp
= Fopen(f
, "w")) == NULL
)
1048 fprintf(stderr
, tr(176, "Cannot open %s\n"), f
);
1054 getpipecmd(char *content
)
1056 char *penv
, *cp
, *cq
, *pipecmd
;
1058 if (content
== NULL
)
1060 penv
= ac_alloc(strlen(content
) + 6);
1061 strcpy(penv
, "pipe-");
1065 *cp
++ = lowerconv(*cq
& 0377);
1067 pipecmd
= value(penv
);
1073 getpipefile(char *pipecmd
, FILE **qbuf
, int quote
)
1078 if (pipecmd
!= NULL
) {
1082 if ((*qbuf
= Ftemp(&tempPipe
, "Rp", "w+", 0600, 1))
1084 perror(catgets(catd
, CATSET
, 173, "tmpfile"));
1090 if ((shell
= value("SHELL")) == NULL
)
1092 if ((rbuf
= Popen(pipecmd
, "W", shell
, fileno(*qbuf
)))
1097 if (*qbuf
!= stdout
)
1105 pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
,
1106 char *prefix
, size_t prefixlen
, off_t
*stats
)
1109 size_t linesize
= 0, linelen
, sz
, count
;
1113 count
= fsize(pipebuf
);
1114 while (fgetline(&line
, &linesize
, &count
, &linelen
, pipebuf
, 0)
1116 sz
= prefixwrite(line
, sizeof *line
, linelen
, outbuf
,
1118 if (outbuf
== origobuf
)
1119 addstats(stats
, 1, sz
);
1127 * Output a reasonable looking status field.
1130 statusput(const struct message
*mp
, FILE *obuf
, char *prefix
, off_t
*stats
)
1135 if (mp
->m_flag
& MREAD
)
1137 if ((mp
->m_flag
& MNEW
) == 0)
1141 fprintf(obuf
, "%sStatus: %s\n",
1142 prefix
== NULL
? "" : prefix
, statout
);
1143 addstats(stats
, 1, (prefix
? strlen(prefix
) : 0) + 9 + cp
- statout
);
1147 xstatusput(const struct message
*mp
, FILE *obuf
, char *prefix
, off_t
*stats
)
1150 char *xp
= xstatout
;
1152 if (mp
->m_flag
& MFLAGGED
)
1154 if (mp
->m_flag
& MANSWERED
)
1156 if (mp
->m_flag
& MDRAFTED
)
1160 fprintf(obuf
, "%sX-Status: %s\n",
1161 prefix
== NULL
? "" : prefix
, xstatout
);
1162 addstats(stats
, 1, (prefix
? strlen(prefix
) : 0) + 11 + xp
- xstatout
);
1166 put_from_(FILE *fp
, struct mimepart
*ip
)
1170 if (ip
&& ip
->m_from
)
1171 fprintf(fp
, "From %s %s\n", ip
->m_from
, fakedate(ip
->m_time
));
1174 fprintf(fp
, "From %s %s", myname
, ctime(&now
));
1179 * This is fgetline for mbox lines.
1182 foldergets(char **s
, size_t *size
, size_t *count
, size_t *llen
, FILE *stream
)
1186 if ((p
= fgetline(s
, size
, count
, llen
, stream
, 0)) == NULL
)
1190 while (*p
== '>') p
++;
1191 if (strncmp(p
, "From ", 5) == 0) {
1192 /* we got a masked From line */