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.
58 static void onpipe(int signo
);
59 extern void brokpipe(int signo
);
60 static int sendpart(struct message
*zmp
, struct mimepart
*ip
, FILE *obuf
,
61 struct ignoretab
*doign
, char *prefix
, size_t prefixlen
,
62 enum sendaction action
, off_t
*stats
, int level
);
63 static struct mimepart
*parsemsg(struct message
*mp
, enum parseflags pf
);
64 static enum okay
parsepart(struct message
*zmp
, struct mimepart
*ip
,
65 enum parseflags pf
, int level
);
66 static void parsemultipart(struct message
*zmp
, struct mimepart
*ip
,
67 enum parseflags pf
, int level
);
68 static void newpart(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
,
70 static void endpart(struct mimepart
**np
, off_t xoffs
, long lines
);
71 static void parse822(struct message
*zmp
, struct mimepart
*ip
,
72 enum parseflags pf
, int level
);
73 static void parsepkcs7(struct message
*zmp
, struct mimepart
*ip
,
74 enum parseflags pf
, int level
);
75 static size_t out(char *buf
, size_t len
, FILE *fp
,
76 enum conversion convert
, enum sendaction action
,
77 char *prefix
, size_t prefixlen
, off_t
*stats
,
78 char **restp
, size_t *restsizep
);
79 static void addstats(off_t
*stats
, off_t lines
, off_t bytes
);
80 static FILE *newfile(struct mimepart
*ip
, int *ispipe
,
81 sighandler_type
volatile*oldpipe
);
82 static char *getpipecmd(char *content
);
83 static FILE *getpipefile(char *cmd
, FILE **qbuf
, int quote
);
84 static void pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
,
85 char *prefix
, size_t prefixlen
, off_t
*stats
);
86 static void statusput(const struct message
*mp
, FILE *obuf
,
87 char *prefix
, size_t prefixlen
, off_t
*stats
);
88 static void xstatusput(const struct message
*mp
, FILE *obuf
,
89 char *prefix
, size_t prefixlen
, off_t
*stats
);
90 static void put_from_(FILE *fp
, struct mimepart
*ip
, off_t
*stats
);
92 static sigjmp_buf pipejmp
;
99 siglongjmp(pipejmp
, 1);
103 * Send message described by the passed pointer to the
104 * passed output buffer. Return -1 on error.
105 * Adjust the status: field if need be.
106 * If doign is given, suppress ignored header fields.
107 * prefix is a string to prepend to each output line.
108 * action = data destination (SEND_MBOX,_TOFILE,_TODISP,_QUOTE,_DECRYPT).
109 * stats[0] is line count, stats[1] is character count. stats may be NULL.
110 * Note that stats[0] is valid for SEND_MBOX only.
113 send(struct message
*mp
, FILE *obuf
, struct ignoretab
*doign
,
114 char *prefix
, enum sendaction action
, off_t
*stats
)
116 size_t prefixlen
, count
, sz
, i
;
122 if (mp
== dot
&& action
!= SEND_TOSRCH
&& action
!= SEND_TOFLTR
)
125 stats
[0] = stats
[1] = 0;
126 prefixlen
= (prefix
!= NULL
) ? strlen(prefix
) : 0;
129 * First line is the From_ line, so no headers there to worry about.
131 if ((ibuf
= setinput(&mb
, mp
, NEED_BODY
)) == NULL
)
135 if (mp
->m_flag
& MNOFROM
) {
136 if (doign
!= allignore
&& doign
!= fwdignore
&&
137 action
!= SEND_RFC822
)
138 sz
= fprintf(obuf
, "%.*sFrom %s %s\n",
139 (int)prefixlen
, prefixlen
? prefix
:"",
140 fakefrom(mp
), fakedate(mp
->m_time
));
142 if (prefixlen
&& doign
!= allignore
&& doign
!= fwdignore
&&
143 action
!= SEND_RFC822
) {
144 i
= fwrite(prefix
, sizeof *prefix
, prefixlen
, obuf
);
149 while (count
&& (c
= getc(ibuf
)) != EOF
) {
150 if (doign
!= allignore
&& doign
!= fwdignore
&&
151 action
!= SEND_RFC822
) {
161 addstats(stats
, 1, sz
);
163 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&& action
!= SEND_SHOW
)
164 pf
|= PARSE_DECRYPT
|PARSE_PARTS
;
165 if ((ip
= parsemsg(mp
, pf
)) == NULL
)
167 return (sendpart(mp
, ip
, obuf
, doign
, prefix
, prefixlen
, action
, stats
,
172 sendpart(struct message
*zmp
, struct mimepart
*ip
, FILE *obuf
,
173 struct ignoretab
*doign
, char *prefix
, size_t prefixlen
,
174 enum sendaction action
, off_t
*volatile stats
, int level
)
176 char *line
= NULL
, *cp
, *cp2
, *start
, *tcs
, *pipecmd
= NULL
, *rest
;
177 size_t linesize
= 0, linelen
, count
, len
, restsize
;
178 int dostat
, infld
= 0, ignoring
= 1, isenc
, c
, rt
= 0, eof
, ispipe
= 0;
180 FILE *volatile ibuf
= NULL
, *volatile pbuf
= obuf
,
181 *volatile qbuf
= obuf
, *origobuf
= obuf
;
182 enum conversion
volatile convert
;
183 sighandler_type
volatile oldpipe
= SIG_DFL
;
186 if (ip
->m_mimecontent
== MIME_PKCS7
&& ip
->m_multipart
&&
187 action
!= SEND_MBOX
&& action
!= SEND_RFC822
&&
193 if (!is_ign("status", 6, doign
))
195 if (!is_ign("x-status", 8, doign
))
200 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
203 if (ip
->m_mimecontent
== MIME_DISCARD
)
205 if ((ip
->m_flag
&MNOFROM
) == 0)
206 while (count
&& (c
= getc(ibuf
)) != EOF
) {
212 convert
= action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
213 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
214 action
== SEND_TOSRCH
|| action
== SEND_TOFLTR
?
215 CONV_FROMHDR
: CONV_NONE
;
218 * Normally headers included in "Content-Type: message/rfc822" messages
219 * will not show up in replies to the encapsulating envelope.
220 * This is nail(1) specific and thus may be configured differently.
222 if (ip
->m_mimecontent
== MIME_TEXT_PLAIN
&& ip
->m_parent
!= NULL
&&
223 ip
->m_parent
->m_mimecontent
== MIME_822
&&
224 value("rfc822-show-all"))
227 /* Work the headers */
228 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
230 if (line
[0] == '\n') {
232 * If line is blank, we've reached end of
233 * headers, so force out status: field
234 * and note that we are no longer in header
238 statusput(zmp
, obuf
, prefix
, prefixlen
, stats
);
240 xstatusput(zmp
, obuf
, prefix
, prefixlen
, stats
);
241 if (doign
!= allignore
)
242 out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
,
243 prefix
, prefixlen
, stats
,
248 if (infld
&& blankchar(line
[0]&0377)) {
250 * If this line is a continuation (via space or tab)
251 * of a previous header field, determine if the start
252 * of the line is a MIME encoded word.
255 for (cp
= line
; blankchar(*cp
&0377); cp
++);
256 if (cp
> line
&& linelen
- (cp
- line
) > 8 &&
257 cp
[0] == '=' && cp
[1] == '?')
262 * Pick up the header field if we have one.
264 for (cp
= line
; (c
= *cp
&0377) && c
!= ':' &&
265 !spacechar(c
); cp
++);
267 while (spacechar(*cp
&0377))
269 if (cp
[0] != ':' && level
== 0 && lineno
== 1) {
271 * Not a header line, force out status:
272 * This happens in uucp style mail where
273 * there are no headers at all.
276 statusput(zmp
, obuf
, prefix
, prefixlen
,
279 xstatusput(zmp
, obuf
, prefix
,
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
, prefixlen
,
306 } else if (asccasecmp(line
, "x-status") == 0) {
308 * If the field is "status," go compute
309 * and print the real Status: field
312 xstatusput(zmp
, obuf
, prefix
,
323 * Determine if the end of the line is a MIME encoded word.
326 if (count
&& (c
= getc(ibuf
)) != EOF
) {
328 if (linelen
> 0 && line
[linelen
-1] == '\n')
329 cp
= &line
[linelen
-2];
331 cp
= &line
[linelen
-1];
332 while (cp
>= line
&& whitechar(*cp
&0377))
334 if (cp
- line
> 8 && cp
[0] == '=' &&
343 if (action
== SEND_TODISP
||
344 action
== SEND_TODISP_ALL
||
345 action
== SEND_QUOTE
||
346 action
== SEND_QUOTE_ALL
||
347 action
== SEND_TOSRCH
||
348 action
== SEND_TOFLTR
) {
350 * Strip blank characters if two MIME-encoded
351 * words follow on continuing lines.
354 while (len
>0&&blankchar(*start
&0377)) {
359 if (len
> 0 && start
[len
-1] == '\n')
361 while (len
> 0 && blankchar(start
[len
-1]&0377))
364 out(start
, len
, obuf
, convert
,
365 action
, prefix
, prefixlen
, stats
,
376 skip
: switch (ip
->m_mimecontent
) {
383 case SEND_TODISP_ALL
:
386 if (! value("rfc822-no-body-from_")) {
387 if (prefixlen
&& value("rfc822-show-all")) {
388 size_t i
= fwrite(prefix
,
389 sizeof *prefix
, prefixlen
,
392 addstats(stats
, 0, i
);
394 put_from_(obuf
, ip
->m_multipart
, stats
);
402 put_from_(obuf
, ip
->m_multipart
, stats
);
411 if (action
== SEND_TOFLTR
)
415 case MIME_TEXT_PLAIN
:
418 case SEND_TODISP_ALL
:
421 pipecmd
= getpipecmd(ip
->m_ct_type_plain
);
428 if (action
!= SEND_DECRYPT
)
432 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&&
433 action
!= SEND_SHOW
&& ip
->m_multipart
)
439 case SEND_TODISP_ALL
:
442 if ((pipecmd
= getpipecmd(ip
->m_ct_type_plain
)) != NULL
)
444 if (level
== 0 && count
) {
445 cp
= "[Binary content]\n\n";
446 out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
,
447 prefix
, prefixlen
, stats
,
463 case MIME_ALTERNATIVE
:
464 if ((action
== SEND_TODISP
|| action
== SEND_QUOTE
) &&
465 value("print-alternatives") == NULL
)
466 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
)
467 if (np
->m_mimecontent
== MIME_TEXT_PLAIN
) {
468 if (sendpart(zmp
, np
, obuf
,
481 case SEND_TODISP_ALL
:
490 if ((action
== SEND_TODISP
||
491 action
== SEND_TODISP_ALL
) &&
492 ip
->m_multipart
!= NULL
&&
493 ip
->m_multipart
->m_mimecontent
== MIME_DISCARD
&&
494 ip
->m_multipart
->m_nextpart
== NULL
) {
495 cp
= "[Missing multipart boundary - "
496 "use \"show\" to display "
497 "the raw message]\n\n";
498 out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
,
499 prefix
, prefixlen
, stats
,
502 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
) {
503 if (np
->m_mimecontent
== MIME_DISCARD
&&
504 action
!= SEND_DECRYPT
)
508 if (np
->m_partstring
&&
509 strcmp(np
->m_partstring
,
513 if ((obuf
= newfile(np
, &ispipe
,
519 case SEND_TODISP_ALL
:
521 if ((ip
->m_mimecontent
== MIME_MULTI
||
527 len
= strlen(np
->m_partstring
) +
531 "%sPart %s:\n", level
||
532 strcmp(np
->m_partstring
,
536 out(cp
, strlen(cp
), obuf
,
537 CONV_NONE
, SEND_MBOX
,
556 if (sendpart(zmp
, np
, obuf
,
557 doign
, prefix
, prefixlen
,
558 action
, stats
, level
+1) < 0)
560 else if (action
== SEND_QUOTE
)
562 if (action
== SEND_TOFILE
&& obuf
!= origobuf
) {
566 safe_signal(SIGPIPE
, SIG_IGN
);
568 safe_signal(SIGPIPE
, oldpipe
);
580 * Copy out message body
582 if (doign
== allignore
&& level
== 0) /* skip final blank line */
584 switch (ip
->m_mimeenc
) {
594 convert
= CONV_FROMQP
;
597 switch (ip
->m_mimecontent
) {
599 case MIME_TEXT_PLAIN
:
601 convert
= CONV_FROMB64_T
;
604 convert
= CONV_FROMB64
;
610 if (action
== SEND_DECRYPT
|| action
== SEND_MBOX
||
611 action
== SEND_RFC822
|| action
== SEND_SHOW
)
615 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
616 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
617 action
== SEND_TOSRCH
) &&
618 (ip
->m_mimecontent
== MIME_TEXT_PLAIN
||
619 ip
->m_mimecontent
== MIME_TEXT_HTML
||
620 ip
->m_mimecontent
== MIME_TEXT
)) {
621 if (iconvd
!= (iconv_t
)-1)
623 if (asccasecmp(tcs
, ip
->m_charset
) &&
624 asccasecmp(us_ascii
, ip
->m_charset
))
625 iconvd
= iconv_open_ft(tcs
, ip
->m_charset
);
627 iconvd
= (iconv_t
)-1;
629 #endif /* HAVE_ICONV */
630 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
631 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
) &&
634 pbuf
= getpipefile(pipecmd
, (FILE**)&qbuf
,
635 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
);
636 action
= SEND_TOPIPE
;
638 oldpipe
= safe_signal(SIGPIPE
, onpipe
);
639 if (sigsetjmp(pipejmp
, 1))
645 while (!eof
&& foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
647 while (convert
== CONV_FROMQP
&& linelen
>= 2 &&
648 line
[linelen
-2] == '=') {
650 size_t linesize2
, linelen2
;
654 if (foldergets(&line2
, &linesize2
, &count
, &linelen2
,
659 if (linelen
+ linelen2
+ 1 > linesize
)
660 line
= srealloc(line
, linesize
= linelen
+
662 memcpy(&line
[linelen
], line2
, linelen2
+1);
668 out(line
, linelen
, pbuf
, convert
, action
,
669 pbuf
== origobuf
? prefix
: NULL
,
670 pbuf
== origobuf
? prefixlen
: 0,
671 pbuf
== origobuf
? stats
: NULL
,
672 eof
? NULL
: &rest
, eof
? NULL
: &restsize
);
679 memmove(line
, rest
, restsize
);
686 safe_signal(SIGPIPE
, SIG_IGN
);
688 safe_signal(SIGPIPE
, oldpipe
);
690 pipecpy(qbuf
, obuf
, origobuf
, prefix
, prefixlen
, stats
);
693 if (iconvd
!= (iconv_t
)-1) {
695 iconvd
= (iconv_t
)-1;
701 static struct mimepart
*
702 parsemsg(struct message
*mp
, enum parseflags pf
)
706 ip
= csalloc(1, sizeof *ip
);
707 ip
->m_flag
= mp
->m_flag
;
708 ip
->m_have
= mp
->m_have
;
709 ip
->m_block
= mp
->m_block
;
710 ip
->m_offset
= mp
->m_offset
;
711 ip
->m_size
= mp
->m_size
;
712 ip
->m_xsize
= mp
->m_xsize
;
713 ip
->m_lines
= mp
->m_lines
;
714 ip
->m_xlines
= mp
->m_lines
;
715 if (parsepart(mp
, ip
, pf
, 0) != OKAY
)
721 parsepart(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
726 ip
->m_ct_type
= hfield1("content-type", (struct message
*)ip
);
727 if (ip
->m_ct_type
!= NULL
) {
728 ip
->m_ct_type_plain
= savestr(ip
->m_ct_type
);
729 if ((cp
= strchr(ip
->m_ct_type_plain
, ';')) != NULL
)
731 } else if (ip
->m_parent
&& ip
->m_parent
->m_mimecontent
== MIME_DIGEST
)
732 ip
->m_ct_type_plain
= "message/rfc822";
734 ip
->m_ct_type_plain
= "text/plain";
736 ip
->m_mimecontent
= mime_getcontent(ip
->m_ct_type_plain
);
738 ip
->m_charset
= mime_getparam("charset", ip
->m_ct_type
);
739 if (ip
->m_charset
== NULL
)
740 ip
->m_charset
= us_ascii
;
741 ip
->m_ct_transfer_enc
= hfield1("content-transfer-encoding",
742 (struct message
*)ip
);
743 ip
->m_mimeenc
= ip
->m_ct_transfer_enc
?
744 mime_getenc(ip
->m_ct_transfer_enc
) : MIME_7B
;
745 if ((cp
= hfield1("content-disposition", (struct message
*)ip
)) == 0 ||
746 (ip
->m_filename
= mime_getparam("filename", cp
)) == 0)
747 if (ip
->m_ct_type
!= NULL
)
748 ip
->m_filename
= mime_getparam("name", ip
->m_ct_type
);
749 if (pf
& PARSE_PARTS
) {
751 fprintf(stderr
, "MIME content too deeply nested.\n");
754 switch (ip
->m_mimecontent
) {
756 if (pf
& PARSE_DECRYPT
) {
757 parsepkcs7(zmp
, ip
, pf
, level
);
764 case MIME_ALTERNATIVE
:
766 parsemultipart(zmp
, ip
, pf
, level
);
769 parse822(zmp
, ip
, pf
, level
);
777 parsemultipart(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
782 size_t linesize
= 0, linelen
, count
, boundlen
;
784 struct mimepart
*np
= NULL
;
789 if ((boundary
= mime_getboundary(ip
->m_ct_type
)) == NULL
)
791 boundlen
= strlen(boundary
);
792 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
795 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
))
799 newpart(ip
, &np
, offs
, NULL
);
800 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
801 if ((lines
> 0 || part
== 0) && linelen
>= boundlen
+ 1 &&
802 strncmp(line
, boundary
, boundlen
) == 0) {
803 if (line
[boundlen
] == '\n') {
806 endpart(&np
, offs
-boundlen
-2, lines
);
807 newpart(ip
, &np
, offs
-boundlen
-2, NULL
);
809 endpart(&np
, offs
, 2);
810 newpart(ip
, &np
, offs
, &part
);
812 } else if (line
[boundlen
] == '-' &&
813 line
[boundlen
+1] == '-' &&
814 line
[boundlen
+2] == '\n') {
817 endpart(&np
, offs
-boundlen
-4, lines
);
818 newpart(ip
, &np
, offs
-boundlen
-4, NULL
);
820 endpart(&np
, offs
+count
, 2);
829 endpart(&np
, offs
, lines
);
831 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
)
832 if (np
->m_mimecontent
!= MIME_DISCARD
)
833 parsepart(zmp
, np
, pf
, level
+1);
838 newpart(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
, int *part
)
843 *np
= csalloc(1, sizeof **np
);
844 (*np
)->m_flag
= MNOFROM
;
845 (*np
)->m_have
= HAVE_HEADER
|HAVE_BODY
;
846 (*np
)->m_block
= mailx_blockof(offs
);
847 (*np
)->m_offset
= mailx_offsetof(offs
);
850 sz
= ip
->m_partstring
? strlen(ip
->m_partstring
) : 0;
852 (*np
)->m_partstring
= salloc(sz
);
853 if (ip
->m_partstring
)
854 snprintf((*np
)->m_partstring
, sz
, "%s.%u",
855 ip
->m_partstring
, *part
);
857 snprintf((*np
)->m_partstring
, sz
, "%u", *part
);
859 (*np
)->m_mimecontent
= MIME_DISCARD
;
860 (*np
)->m_parent
= ip
;
861 if (ip
->m_multipart
) {
862 for (pp
= ip
->m_multipart
; pp
->m_nextpart
; pp
= pp
->m_nextpart
);
863 pp
->m_nextpart
= *np
;
865 ip
->m_multipart
= *np
;
869 endpart(struct mimepart
**np
, off_t xoffs
, long lines
)
873 offs
= mailx_positionof((*np
)->m_block
, (*np
)->m_offset
);
874 (*np
)->m_size
= (*np
)->m_xsize
= xoffs
- offs
;
875 (*np
)->m_lines
= (*np
)->m_xlines
= lines
;
880 parse822(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
890 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
894 while (count
&& ((c
= getc(ibuf
)) != EOF
)) {
904 np
= csalloc(1, sizeof *np
);
905 np
->m_flag
= MNOFROM
;
906 np
->m_have
= HAVE_HEADER
|HAVE_BODY
;
907 np
->m_block
= mailx_blockof(offs
);
908 np
->m_offset
= mailx_offsetof(offs
);
909 np
->m_size
= np
->m_xsize
= count
;
910 np
->m_lines
= np
->m_xlines
= lines
;
911 np
->m_partstring
= ip
->m_partstring
;
913 ip
->m_multipart
= np
;
914 if (! value("rfc822-no-body-from_")) {
915 substdate((struct message
*)np
);
916 np
->m_from
= fakefrom((struct message
*)np
);
918 parsepart(zmp
, np
, pf
, level
+1);
922 parsepkcs7(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
925 struct message m
, *xmp
;
929 memcpy(&m
, ip
, sizeof m
);
930 to
= hfield1("to", zmp
);
931 cc
= hfield1("cc", zmp
);
932 if ((xmp
= smime_decrypt(&m
, to
, cc
, 0)) != NULL
) {
933 np
= csalloc(1, sizeof *np
);
934 np
->m_flag
= xmp
->m_flag
;
935 np
->m_have
= xmp
->m_have
;
936 np
->m_block
= xmp
->m_block
;
937 np
->m_offset
= xmp
->m_offset
;
938 np
->m_size
= xmp
->m_size
;
939 np
->m_xsize
= xmp
->m_xsize
;
940 np
->m_lines
= xmp
->m_lines
;
941 np
->m_xlines
= xmp
->m_xlines
;
942 np
->m_partstring
= ip
->m_partstring
;
943 if (parsepart(zmp
, np
, pf
, level
+1) == OKAY
) {
945 ip
->m_multipart
= np
;
951 out(char *buf
, size_t len
, FILE *fp
,
952 enum conversion convert
, enum sendaction action
,
953 char *prefix
, size_t prefixlen
, off_t
*stats
,
954 char **restp
, size_t *restsizep
)
961 if (action
== SEND_MBOX
|| action
== SEND_DECRYPT
) {
964 while (n
&& cp
[0] == '>')
966 if (n
>= 5 && cp
[0] == 'F' && cp
[1] == 'r' && cp
[2] == 'o' &&
967 cp
[3] == 'm' && cp
[4] == ' ') {
972 sz
+= mime_write(buf
, len
, fp
,
973 action
== SEND_MBOX
? CONV_NONE
: convert
,
974 action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
975 action
== SEND_QUOTE
||
976 action
== SEND_QUOTE_ALL
?
978 action
== SEND_TOSRCH
|| action
== SEND_TOPIPE
?
980 action
== SEND_TOFLTR
?
982 action
== SEND_SHOW
?
987 if (stats
&& stats
[0] != -1) {
988 for (cp
= buf
; cp
< &buf
[sz
]; cp
++)
992 addstats(stats
, lines
, sz
);
997 addstats(off_t
*stats
, off_t lines
, off_t bytes
)
1007 * Get a file for an attachment.
1010 newfile(struct mimepart
*ip
, int *ispipe
, sighandler_type
volatile*oldpipe
)
1012 char *f
= ip
->m_filename
;
1017 if (f
!= NULL
&& f
!= (char *)-1) {
1020 mime_fromhdr(&in
, &out
, TD_ISPR
);
1021 memcpy(f
, out
.s
, out
.l
);
1022 *(f
+ out
.l
) = '\0';
1026 if (value("interactive") != NULL
) {
1028 jgetname
: (void)printf(tr(278, "Enter filename for part %s (%s)"),
1029 ip
->m_partstring
? ip
->m_partstring
: "?",
1030 ip
->m_ct_type_plain
);
1031 f2
= readtty(": ", f
!= (char *)-1 ? f
: NULL
);
1032 if (f2
== NULL
|| *f2
== '\0') {
1033 fprintf(stderr
, tr(279, "... skipping this\n"));
1035 } else if (*f2
== '|')
1036 /* Pipes are expanded by the shell */
1038 else if ((f3
= file_expand(f2
)) == NULL
)
1039 /* (Error message written by file_expand()) */
1044 if (f
== NULL
|| f
== (char *)-1)
1049 cp
= value("SHELL");
1052 fp
= Popen(f
+ 1, "w", cp
, 1);
1057 *oldpipe
= safe_signal(SIGPIPE
, brokpipe
);
1061 if ((fp
= Fopen(f
, "w")) == NULL
)
1062 fprintf(stderr
, tr(176, "Cannot open %s\n"), f
);
1068 getpipecmd(char *content
)
1070 char *penv
, *cp
, *cq
, *pipecmd
;
1072 if (content
== NULL
)
1074 penv
= ac_alloc(strlen(content
) + 6);
1075 strcpy(penv
, "pipe-");
1079 *cp
++ = lowerconv(*cq
& 0377);
1081 pipecmd
= value(penv
);
1087 getpipefile(char *pipecmd
, FILE **qbuf
, int quote
)
1092 if (pipecmd
!= NULL
) {
1096 if ((*qbuf
= Ftemp(&tempPipe
, "Rp", "w+", 0600, 1))
1098 perror(catgets(catd
, CATSET
, 173, "tmpfile"));
1104 if ((shell
= value("SHELL")) == NULL
)
1106 if ((rbuf
= Popen(pipecmd
, "W", shell
, fileno(*qbuf
)))
1111 if (*qbuf
!= stdout
)
1119 pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
,
1120 char *prefix
, size_t prefixlen
, off_t
*stats
)
1123 size_t linesize
= 0, linelen
, sz
, count
;
1127 count
= fsize(pipebuf
);
1128 while (fgetline(&line
, &linesize
, &count
, &linelen
, pipebuf
, 0)
1130 sz
= prefixwrite(line
, sizeof *line
, linelen
, outbuf
,
1132 if (outbuf
== origobuf
)
1133 addstats(stats
, 1, sz
);
1141 * Output a reasonable looking status field.
1144 statusput(const struct message
*mp
, FILE *obuf
, char *prefix
, size_t prefixlen
,
1150 if (mp
->m_flag
& MREAD
)
1152 if ((mp
->m_flag
& MNEW
) == 0)
1156 fprintf(obuf
, "%.*sStatus: %s\n",
1157 (int)prefixlen
, (prefixlen
? prefix
: ""), statout
);
1158 addstats(stats
, 1, prefixlen
+ 9 + cp
- statout
);
1162 xstatusput(const struct message
*mp
, FILE *obuf
, char *prefix
,
1163 size_t prefixlen
, off_t
*stats
)
1166 char *xp
= xstatout
;
1168 if (mp
->m_flag
& MFLAGGED
)
1170 if (mp
->m_flag
& MANSWERED
)
1172 if (mp
->m_flag
& MDRAFTED
)
1176 fprintf(obuf
, "%.*sX-Status: %s\n",
1177 (int)prefixlen
, (prefixlen
? prefix
: ""), xstatout
);
1178 addstats(stats
, 1, prefixlen
+ 11 + xp
- xstatout
);
1182 put_from_(FILE *fp
, struct mimepart
*ip
, off_t
*stats
)
1185 char const *from
, *date
, *nl
;
1188 if (ip
&& ip
->m_from
) {
1190 date
= fakedate(ip
->m_time
);
1199 i
= fprintf(fp
, "From %s %s%s", from
, date
, nl
);
1201 addstats(stats
, (*nl
!= '\0'), i
);
1205 * This is fgetline for mbox lines.
1208 foldergets(char **s
, size_t *size
, size_t *count
, size_t *llen
, FILE *stream
)
1212 if ((p
= fgetline(s
, size
, count
, llen
, stream
, 0)) == NULL
)
1216 while (*p
== '>') p
++;
1217 if (strncmp(p
, "From ", 5) == 0) {
1218 /* we got a masked From line */