2 * Heirloom mailx - 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
42 static char sccsid
[] = "@(#)send.c 2.86 (gritter) 2/4/08";
53 * Mail -- a mail program
55 * Mail to mail folders and displays.
58 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
59 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
60 * TODO may leave child running if fdopen() fails! */
68 static void onpipe(int signo
);
69 extern void brokpipe(int signo
);
70 static int sendpart(struct message
*zmp
, struct mimepart
*ip
, FILE *obuf
,
71 struct ignoretab
*doign
, char *prefix
, size_t prefixlen
,
72 enum sendaction action
, off_t
*stats
, int level
);
73 static struct mimepart
*parsemsg(struct message
*mp
, enum parseflags pf
);
74 static enum okay
parsepart(struct message
*zmp
, struct mimepart
*ip
,
75 enum parseflags pf
, int level
);
76 static void parsemultipart(struct message
*zmp
, struct mimepart
*ip
,
77 enum parseflags pf
, int level
);
78 static void newpart(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
,
80 static void endpart(struct mimepart
**np
, off_t xoffs
, long lines
);
81 static void parse822(struct message
*zmp
, struct mimepart
*ip
,
82 enum parseflags pf
, int level
);
83 static void parsepkcs7(struct message
*zmp
, struct mimepart
*ip
,
84 enum parseflags pf
, int level
);
85 static size_t out(char *buf
, size_t len
, FILE *fp
,
86 enum conversion convert
, enum sendaction action
,
87 char *prefix
, size_t prefixlen
, off_t
*stats
,
88 char **restp
, size_t *restsizep
);
89 static void addstats(off_t
*stats
, off_t lines
, off_t bytes
);
90 static FILE *newfile(struct mimepart
*ip
, int *ispipe
,
91 sighandler_type
*oldpipe
);
92 static char *getpipecmd(char *content
);
93 static FILE *getpipefile(char *cmd
, FILE **qbuf
, int quote
);
94 static void pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
,
95 char *prefix
, size_t prefixlen
, off_t
*stats
);
96 static void statusput(const struct message
*mp
, FILE *obuf
,
97 char *prefix
, off_t
*stats
);
98 static void xstatusput(const struct message
*mp
, FILE *obuf
,
99 char *prefix
, off_t
*stats
);
100 static void put_from_(FILE *fp
, struct mimepart
*ip
);
102 static sigjmp_buf pipejmp
;
109 siglongjmp(pipejmp
, 1);
113 * Send message described by the passed pointer to the
114 * passed output buffer. Return -1 on error.
115 * Adjust the status: field if need be.
116 * If doign is given, suppress ignored header fields.
117 * prefix is a string to prepend to each output line.
118 * action = data destination (SEND_MBOX,_TOFILE,_TODISP,_QUOTE,_DECRYPT).
119 * stats[0] is line count, stats[1] is character count. stats may be NULL.
120 * Note that stats[0] is valid for SEND_MBOX only.
123 send(struct message
*mp
, FILE *obuf
, struct ignoretab
*doign
,
124 char *prefix
, enum sendaction action
, off_t
*stats
)
128 size_t prefixlen
, sz
;
134 if (mp
== dot
&& action
!= SEND_TOSRCH
&& action
!= SEND_TOFLTR
)
137 stats
[0] = stats
[1] = 0;
139 * Compute the prefix string, without trailing whitespace
141 if (prefix
!= NULL
) {
143 for (cp
= prefix
; *cp
; cp
++)
144 if (!blankchar(*cp
& 0377))
146 prefixlen
= cp2
== 0 ? 0 : cp2
- prefix
+ 1;
150 * First line is the From_ line, so no headers there to worry about.
152 if ((ibuf
= setinput(&mb
, mp
, NEED_BODY
)) == NULL
)
156 if (mp
->m_flag
& MNOFROM
) {
157 if (doign
!= allignore
&& doign
!= fwdignore
&&
158 action
!= SEND_RFC822
)
159 sz
= fprintf(obuf
, "%sFrom %s %s\n",
160 prefix
? prefix
: "",
161 fakefrom(mp
), fakedate(mp
->m_time
));
163 if (prefix
&& doign
!= allignore
&& doign
!= fwdignore
&&
164 action
!= SEND_RFC822
) {
166 sz
+= strlen(prefix
);
168 while (count
&& (c
= getc(ibuf
)) != EOF
) {
169 if (doign
!= allignore
&& doign
!= fwdignore
&&
170 action
!= SEND_RFC822
) {
180 addstats(stats
, 1, sz
);
182 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&& action
!= SEND_SHOW
)
183 pf
|= PARSE_DECRYPT
|PARSE_PARTS
;
184 if ((ip
= parsemsg(mp
, pf
)) == NULL
)
186 return sendpart(mp
, ip
, obuf
, doign
, prefix
, prefixlen
, action
, stats
,
191 sendpart(struct message
*zmp
, struct mimepart
*ip
, FILE *obuf
,
192 struct ignoretab
*doign
, char *prefix
, size_t prefixlen
,
193 enum sendaction action
, off_t
*stats
, int level
)
196 size_t linesize
= 0, linelen
, count
, len
;
197 int dostat
, infld
= 0, ignoring
= 1, isenc
;
198 char *cp
, *cp2
, *start
;
201 FILE *ibuf
= NULL
, *pbuf
= obuf
, *qbuf
= obuf
, *origobuf
= obuf
;
202 char *tcs
, *pipecmd
= NULL
;
203 enum conversion convert
;
204 sighandler_type oldpipe
= SIG_DFL
;
220 if (ip
->m_mimecontent
== MIME_PKCS7
&& ip
->m_multipart
&&
221 action
!= SEND_MBOX
&& action
!= SEND_RFC822
&&
227 if (!is_ign("status", 6, doign
))
229 if (!is_ign("x-status", 8, doign
))
234 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
237 if (ip
->m_mimecontent
== MIME_DISCARD
)
239 if ((ip
->m_flag
&MNOFROM
) == 0)
240 while (count
&& (c
= getc(ibuf
)) != EOF
) {
246 convert
= action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
247 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
248 action
== SEND_TOSRCH
|| action
== SEND_TOFLTR
?
249 CONV_FROMHDR
: CONV_NONE
;
250 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
252 if (line
[0] == '\n') {
254 * If line is blank, we've reached end of
255 * headers, so force out status: field
256 * and note that we are no longer in header
260 statusput(zmp
, obuf
, prefix
, stats
);
262 xstatusput(zmp
, obuf
, prefix
, stats
);
263 if (doign
!= allignore
)
264 out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
,
265 prefix
, prefixlen
, stats
,
270 if (infld
&& blankchar(line
[0]&0377)) {
272 * If this line is a continuation (via space or tab)
273 * of a previous header field, determine if the start
274 * of the line is a MIME encoded word.
277 for (cp
= line
; blankchar(*cp
&0377); cp
++);
278 if (cp
> line
&& linelen
- (cp
- line
) > 8 &&
279 cp
[0] == '=' && cp
[1] == '?')
284 * Pick up the header field if we have one.
286 for (cp
= line
; (c
= *cp
&0377) && c
!= ':' &&
287 !spacechar(c
); cp
++);
289 while (spacechar(*cp
&0377))
291 if (cp
[0] != ':' && level
== 0 && lineno
== 1) {
293 * Not a header line, force out status:
294 * This happens in uucp style mail where
295 * there are no headers at all.
298 statusput(zmp
, obuf
, prefix
, stats
);
300 xstatusput(zmp
, obuf
, prefix
, stats
);
301 if (doign
!= allignore
)
302 out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
,
303 prefix
, prefixlen
, stats
,
308 * If it is an ignored field and
309 * we care about such things, skip it.
312 *cp2
= 0; /* temporarily null terminate */
313 if (doign
&& is_ign(line
, cp2
- line
, doign
))
315 else if (asccasecmp(line
, "status") == 0) {
317 * If the field is "status," go compute
318 * and print the real Status: field
321 statusput(zmp
, obuf
, prefix
, stats
);
325 } else if (asccasecmp(line
, "x-status") == 0) {
327 * If the field is "status," go compute
328 * and print the real Status: field
331 xstatusput(zmp
, obuf
, prefix
, stats
);
341 * Determine if the end of the line is a MIME encoded word.
344 if (count
&& (c
= getc(ibuf
)) != EOF
) {
346 if (linelen
> 0 && line
[linelen
-1] == '\n')
347 cp
= &line
[linelen
-2];
349 cp
= &line
[linelen
-1];
350 while (cp
>= line
&& whitechar(*cp
&0377))
352 if (cp
- line
> 8 && cp
[0] == '=' &&
361 if (action
== SEND_TODISP
||
362 action
== SEND_TODISP_ALL
||
363 action
== SEND_QUOTE
||
364 action
== SEND_QUOTE_ALL
||
365 action
== SEND_TOSRCH
||
366 action
== SEND_TOFLTR
) {
368 * Strip blank characters if two MIME-encoded
369 * words follow on continuing lines.
372 while (len
>0&&blankchar(*start
&0377)) {
377 if (len
> 0 && start
[len
-1] == '\n')
379 while (len
> 0 && blankchar(start
[len
-1]&0377))
382 out(start
, len
, obuf
, convert
,
383 action
, prefix
, prefixlen
, stats
,
393 skip
: switch (ip
->m_mimecontent
) {
400 case SEND_TODISP_ALL
:
403 put_from_(obuf
, ip
->m_multipart
);
410 put_from_(obuf
, ip
->m_multipart
);
419 if (action
== SEND_TOFLTR
)
423 case MIME_TEXT_PLAIN
:
426 case SEND_TODISP_ALL
:
429 pipecmd
= getpipecmd(ip
->m_ct_type_plain
);
436 if (action
!= SEND_DECRYPT
)
440 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&&
441 action
!= SEND_SHOW
&& ip
->m_multipart
)
447 case SEND_TODISP_ALL
:
450 if ((pipecmd
= getpipecmd(ip
->m_ct_type_plain
)) != NULL
)
452 if (level
== 0 && count
) {
453 cp
= "[Binary content]\n\n";
454 out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
,
455 prefix
, prefixlen
, stats
,
471 case MIME_ALTERNATIVE
:
472 if ((action
== SEND_TODISP
|| action
== SEND_QUOTE
) &&
473 value("print-alternatives") == NULL
)
474 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
)
475 if (np
->m_mimecontent
== MIME_TEXT_PLAIN
) {
476 if (sendpart(zmp
, np
, obuf
,
489 case SEND_TODISP_ALL
:
498 if ((action
== SEND_TODISP
||
499 action
== SEND_TODISP_ALL
) &&
500 ip
->m_multipart
!= NULL
&&
501 ip
->m_multipart
->m_mimecontent
== MIME_DISCARD
&&
502 ip
->m_multipart
->m_nextpart
== NULL
) {
503 cp
= "[Missing multipart boundary - "
504 "use \"show\" to display "
505 "the raw message]\n\n";
506 out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
,
507 prefix
, prefixlen
, stats
,
510 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
) {
511 if (np
->m_mimecontent
== MIME_DISCARD
&&
512 action
!= SEND_DECRYPT
)
516 if (np
->m_partstring
&&
517 strcmp(np
->m_partstring
,
521 if ((obuf
= newfile(np
, &ispipe
,
526 case SEND_TODISP_ALL
:
528 if ((ip
->m_mimecontent
== MIME_MULTI
||
534 len
= strlen(np
->m_partstring
) +
538 "%sPart %s:\n", level
||
539 strcmp(np
->m_partstring
,
543 out(cp
, strlen(cp
), obuf
,
544 CONV_NONE
, SEND_MBOX
,
563 if (sendpart(zmp
, np
, obuf
,
564 doign
, prefix
, prefixlen
,
565 action
, stats
, level
+1) < 0)
567 else if (action
== SEND_QUOTE
)
569 if (action
== SEND_TOFILE
&& obuf
!= origobuf
) {
573 safe_signal(SIGPIPE
, SIG_IGN
);
575 safe_signal(SIGPIPE
, oldpipe
);
587 * Copy out message body
589 if (doign
== allignore
&& level
== 0) /* skip final blank line */
591 switch (ip
->m_mimeenc
) {
601 convert
= CONV_FROMQP
;
604 switch (ip
->m_mimecontent
) {
606 case MIME_TEXT_PLAIN
:
608 convert
= CONV_FROMB64_T
;
611 convert
= CONV_FROMB64
;
617 if (action
== SEND_DECRYPT
|| action
== SEND_MBOX
||
618 action
== SEND_RFC822
|| action
== SEND_SHOW
)
622 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
623 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
624 action
== SEND_TOSRCH
) &&
625 (ip
->m_mimecontent
== MIME_TEXT_PLAIN
||
626 ip
->m_mimecontent
== MIME_TEXT_HTML
||
627 ip
->m_mimecontent
== MIME_TEXT
)) {
628 if (iconvd
!= (iconv_t
)-1)
630 if (asccasecmp(tcs
, ip
->m_charset
) &&
631 asccasecmp(us_ascii
, ip
->m_charset
))
632 iconvd
= iconv_open_ft(tcs
, ip
->m_charset
);
634 iconvd
= (iconv_t
)-1;
636 #endif /* HAVE_ICONV */
637 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
638 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
) &&
641 pbuf
= getpipefile(pipecmd
, &qbuf
,
642 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
);
643 action
= SEND_TOPIPE
;
645 oldpipe
= safe_signal(SIGPIPE
, onpipe
);
646 if (sigsetjmp(pipejmp
, 1))
652 while (!eof
&& foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
654 while (convert
== CONV_FROMQP
&& linelen
>= 2 &&
655 line
[linelen
-2] == '=') {
657 size_t linesize2
, linelen2
;
661 if (foldergets(&line2
, &linesize2
, &count
, &linelen2
,
666 if (linelen
+ linelen2
+ 1 > linesize
)
667 line
= srealloc(line
, linesize
= linelen
+
669 memcpy(&line
[linelen
], line2
, linelen2
+1);
675 out(line
, linelen
, pbuf
, convert
, action
,
676 pbuf
== origobuf
? prefix
: NULL
,
677 pbuf
== origobuf
? prefixlen
: 0,
678 pbuf
== origobuf
? stats
: NULL
,
679 eof
? NULL
: &rest
, eof
? NULL
: &restsize
);
686 memmove(line
, rest
, restsize
);
693 safe_signal(SIGPIPE
, SIG_IGN
);
695 safe_signal(SIGPIPE
, oldpipe
);
697 pipecpy(qbuf
, obuf
, origobuf
, prefix
, prefixlen
, stats
);
700 if (iconvd
!= (iconv_t
)-1) {
702 iconvd
= (iconv_t
)-1;
708 static struct mimepart
*
709 parsemsg(struct message
*mp
, enum parseflags pf
)
713 ip
= csalloc(1, sizeof *ip
);
714 ip
->m_flag
= mp
->m_flag
;
715 ip
->m_have
= mp
->m_have
;
716 ip
->m_block
= mp
->m_block
;
717 ip
->m_offset
= mp
->m_offset
;
718 ip
->m_size
= mp
->m_size
;
719 ip
->m_xsize
= mp
->m_xsize
;
720 ip
->m_lines
= mp
->m_lines
;
721 ip
->m_xlines
= mp
->m_lines
;
722 if (parsepart(mp
, ip
, pf
, 0) != OKAY
)
728 parsepart(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
733 ip
->m_ct_type
= hfield("content-type", (struct message
*)ip
);
734 if (ip
->m_ct_type
!= NULL
) {
735 ip
->m_ct_type_plain
= savestr(ip
->m_ct_type
);
736 if ((cp
= strchr(ip
->m_ct_type_plain
, ';')) != NULL
)
738 } else if (ip
->m_parent
&& ip
->m_parent
->m_mimecontent
== MIME_DIGEST
)
739 ip
->m_ct_type_plain
= "message/rfc822";
741 ip
->m_ct_type_plain
= "text/plain";
742 ip
->m_mimecontent
= mime_getcontent(ip
->m_ct_type_plain
);
744 ip
->m_charset
= mime_getparam("charset", ip
->m_ct_type
);
745 if (ip
->m_charset
== NULL
)
746 ip
->m_charset
= us_ascii
;
747 ip
->m_ct_transfer_enc
= hfield("content-transfer-encoding",
748 (struct message
*)ip
);
749 ip
->m_mimeenc
= ip
->m_ct_transfer_enc
?
750 mime_getenc(ip
->m_ct_transfer_enc
) : MIME_7B
;
751 if ((cp
= hfield("content-disposition", (struct message
*)ip
)) == 0 ||
752 (ip
->m_filename
= mime_getparam("filename", cp
)) == 0)
753 if (ip
->m_ct_type
!= NULL
)
754 ip
->m_filename
= mime_getparam("name", ip
->m_ct_type
);
755 if (pf
& PARSE_PARTS
) {
757 fprintf(stderr
, "MIME content too deeply nested.\n");
760 switch (ip
->m_mimecontent
) {
762 if (pf
& PARSE_DECRYPT
) {
763 parsepkcs7(zmp
, ip
, pf
, level
);
770 case MIME_ALTERNATIVE
:
772 parsemultipart(zmp
, ip
, pf
, level
);
775 parse822(zmp
, ip
, pf
, level
);
783 parsemultipart(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
788 size_t linesize
= 0, linelen
, count
, boundlen
;
790 struct mimepart
*np
= NULL
;
795 if ((boundary
= mime_getboundary(ip
->m_ct_type
)) == NULL
)
797 boundlen
= strlen(boundary
);
798 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
801 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
))
805 newpart(ip
, &np
, offs
, NULL
);
806 while (foldergets(&line
, &linesize
, &count
, &linelen
, ibuf
)) {
807 if ((lines
> 0 || part
== 0) && linelen
>= boundlen
+ 1 &&
808 strncmp(line
, boundary
, boundlen
) == 0) {
809 if (line
[boundlen
] == '\n') {
812 endpart(&np
, offs
-boundlen
-2, lines
);
813 newpart(ip
, &np
, offs
-boundlen
-2, NULL
);
815 endpart(&np
, offs
, 2);
816 newpart(ip
, &np
, offs
, &part
);
818 } else if (line
[boundlen
] == '-' &&
819 line
[boundlen
+1] == '-' &&
820 line
[boundlen
+2] == '\n') {
823 endpart(&np
, offs
-boundlen
-4, lines
);
824 newpart(ip
, &np
, offs
-boundlen
-4, NULL
);
826 endpart(&np
, offs
+count
, 2);
835 endpart(&np
, offs
, lines
);
837 for (np
= ip
->m_multipart
; np
; np
= np
->m_nextpart
)
838 if (np
->m_mimecontent
!= MIME_DISCARD
)
839 parsepart(zmp
, np
, pf
, level
+1);
844 newpart(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
, int *part
)
849 *np
= csalloc(1, sizeof **np
);
850 (*np
)->m_flag
= MNOFROM
;
851 (*np
)->m_have
= HAVE_HEADER
|HAVE_BODY
;
852 (*np
)->m_block
= mailx_blockof(offs
);
853 (*np
)->m_offset
= mailx_offsetof(offs
);
856 sz
= ip
->m_partstring
? strlen(ip
->m_partstring
) : 0;
858 (*np
)->m_partstring
= salloc(sz
);
859 if (ip
->m_partstring
)
860 snprintf((*np
)->m_partstring
, sz
, "%s.%u",
861 ip
->m_partstring
, *part
);
863 snprintf((*np
)->m_partstring
, sz
, "%u", *part
);
865 (*np
)->m_mimecontent
= MIME_DISCARD
;
866 (*np
)->m_parent
= ip
;
867 if (ip
->m_multipart
) {
868 for (pp
= ip
->m_multipart
; pp
->m_nextpart
; pp
= pp
->m_nextpart
);
869 pp
->m_nextpart
= *np
;
871 ip
->m_multipart
= *np
;
875 endpart(struct mimepart
**np
, off_t xoffs
, long lines
)
879 offs
= mailx_positionof((*np
)->m_block
, (*np
)->m_offset
);
880 (*np
)->m_size
= (*np
)->m_xsize
= xoffs
- offs
;
881 (*np
)->m_lines
= (*np
)->m_xlines
= lines
;
886 parse822(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
896 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
900 while (count
&& ((c
= getc(ibuf
)) != EOF
)) {
910 np
= csalloc(1, sizeof *np
);
911 np
->m_flag
= MNOFROM
;
912 np
->m_have
= HAVE_HEADER
|HAVE_BODY
;
913 np
->m_block
= mailx_blockof(offs
);
914 np
->m_offset
= mailx_offsetof(offs
);
915 np
->m_size
= np
->m_xsize
= count
;
916 np
->m_lines
= np
->m_xlines
= lines
;
917 np
->m_partstring
= ip
->m_partstring
;
919 ip
->m_multipart
= np
;
920 substdate((struct message
*)np
);
921 np
->m_from
= fakefrom((struct message
*)np
);
922 parsepart(zmp
, np
, pf
, level
+1);
926 parsepkcs7(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
929 struct message m
, *xmp
;
933 memcpy(&m
, ip
, sizeof m
);
934 to
= hfield("to", zmp
);
935 cc
= hfield("cc", zmp
);
936 if ((xmp
= smime_decrypt(&m
, to
, cc
, 0)) != NULL
) {
937 np
= csalloc(1, sizeof *np
);
938 np
->m_flag
= xmp
->m_flag
;
939 np
->m_have
= xmp
->m_have
;
940 np
->m_block
= xmp
->m_block
;
941 np
->m_offset
= xmp
->m_offset
;
942 np
->m_size
= xmp
->m_size
;
943 np
->m_xsize
= xmp
->m_xsize
;
944 np
->m_lines
= xmp
->m_lines
;
945 np
->m_xlines
= xmp
->m_xlines
;
946 np
->m_partstring
= ip
->m_partstring
;
947 if (parsepart(zmp
, np
, pf
, level
+1) == OKAY
) {
949 ip
->m_multipart
= np
;
955 out(char *buf
, size_t len
, FILE *fp
,
956 enum conversion convert
, enum sendaction action
,
957 char *prefix
, size_t prefixlen
, off_t
*stats
,
958 char **restp
, size_t *restsizep
)
965 if (action
== SEND_MBOX
|| action
== SEND_DECRYPT
) {
968 while (n
&& cp
[0] == '>')
970 if (n
>= 5 && cp
[0] == 'F' && cp
[1] == 'r' && cp
[2] == 'o' &&
971 cp
[3] == 'm' && cp
[4] == ' ') {
976 sz
+= mime_write(buf
, len
, fp
,
977 action
== SEND_MBOX
? CONV_NONE
: convert
,
978 action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
979 action
== SEND_QUOTE
||
980 action
== SEND_QUOTE_ALL
?
982 action
== SEND_TOSRCH
|| action
== SEND_TOPIPE
?
984 action
== SEND_TOFLTR
?
986 action
== SEND_SHOW
?
991 if (stats
&& stats
[0] != -1) {
992 for (cp
= buf
; cp
< &buf
[sz
]; cp
++)
996 addstats(stats
, lines
, sz
);
1001 addstats(off_t
*stats
, off_t lines
, off_t bytes
)
1011 * Get a file for an attachment.
1014 newfile(struct mimepart
*ip
, int *ispipe
, sighandler_type
*oldpipe
)
1016 char *f
= ip
->m_filename
;
1021 if (f
!= NULL
&& f
!= (char *)-1) {
1024 mime_fromhdr(&in
, &out
, TD_ISPR
);
1025 memcpy(f
, out
.s
, out
.l
);
1026 *(f
+ out
.l
) = '\0';
1029 if (value("interactive") != NULL
) {
1030 printf("Enter filename for part %s (%s)",
1031 ip
->m_partstring
? ip
->m_partstring
: "?",
1032 ip
->m_ct_type_plain
);
1033 f
= readtty(catgets(catd
, CATSET
, 173, ": "),
1034 f
!= (char *)-1 ? f
: NULL
);
1036 if (f
== NULL
|| f
== (char *)-1)
1041 cp
= value("SHELL");
1044 fp
= Popen(f
+1, "w", cp
, 1);
1049 *oldpipe
= safe_signal(SIGPIPE
, brokpipe
);
1053 if ((fp
= Fopen(f
, "w")) == NULL
)
1054 fprintf(stderr
, "Cannot open %s\n", f
);
1060 getpipecmd(char *content
)
1062 char *penv
, *cp
, *cq
, *pipecmd
;
1064 if (content
== NULL
)
1066 penv
= ac_alloc(strlen(content
) + 6);
1067 strcpy(penv
, "pipe-");
1071 *cp
++ = lowerconv(*cq
& 0377);
1073 pipecmd
= value(penv
);
1079 getpipefile(char *pipecmd
, FILE **qbuf
, int quote
)
1084 if (pipecmd
!= NULL
) {
1088 if ((*qbuf
= Ftemp(&tempPipe
, "Rp", "w+", 0600, 1))
1090 perror(catgets(catd
, CATSET
, 173, "tmpfile"));
1096 if ((shell
= value("SHELL")) == NULL
)
1098 if ((rbuf
= Popen(pipecmd
, "W", shell
, fileno(*qbuf
)))
1103 if (*qbuf
!= stdout
)
1111 pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
,
1112 char *prefix
, size_t prefixlen
, off_t
*stats
)
1115 size_t linesize
= 0, linelen
, sz
, count
;
1119 count
= fsize(pipebuf
);
1120 while (fgetline(&line
, &linesize
, &count
, &linelen
, pipebuf
, 0)
1122 sz
= prefixwrite(line
, sizeof *line
, linelen
, outbuf
,
1124 if (outbuf
== origobuf
)
1125 addstats(stats
, 1, sz
);
1133 * Output a reasonable looking status field.
1136 statusput(const struct message
*mp
, FILE *obuf
, char *prefix
, off_t
*stats
)
1141 if (mp
->m_flag
& MREAD
)
1143 if ((mp
->m_flag
& MNEW
) == 0)
1147 fprintf(obuf
, "%sStatus: %s\n",
1148 prefix
== NULL
? "" : prefix
, statout
);
1149 addstats(stats
, 1, (prefix
? strlen(prefix
) : 0) + 9 + cp
- statout
);
1153 xstatusput(const struct message
*mp
, FILE *obuf
, char *prefix
, off_t
*stats
)
1156 char *xp
= xstatout
;
1158 if (mp
->m_flag
& MFLAGGED
)
1160 if (mp
->m_flag
& MANSWERED
)
1162 if (mp
->m_flag
& MDRAFTED
)
1166 fprintf(obuf
, "%sX-Status: %s\n",
1167 prefix
== NULL
? "" : prefix
, xstatout
);
1168 addstats(stats
, 1, (prefix
? strlen(prefix
) : 0) + 11 + xp
- xstatout
);
1172 put_from_(FILE *fp
, struct mimepart
*ip
)
1176 if (ip
&& ip
->m_from
)
1177 fprintf(fp
, "From %s %s\n", ip
->m_from
, fakedate(ip
->m_time
));
1180 fprintf(fp
, "From %s %s", myname
, ctime(&now
));
1185 * This is fgetline for mbox lines.
1188 foldergets(char **s
, size_t *size
, size_t *count
, size_t *llen
, FILE *stream
)
1192 if ((p
= fgetline(s
, size
, count
, llen
, stream
, 0)) == NULL
)
1196 while (*p
== '>') p
++;
1197 if (strncmp(p
, "From ", 5) == 0) {
1198 /* we got a masked From line */