1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Message content preparation (sendmp()).
3 *
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36 #undef su_FILE
37 #define su_FILE send
38 #define mx_SOURCE
39
40 #ifndef mx_HAVE_AMALGAMATION
41 # include "mx/nail.h"
42 #endif
43
44 #include <su/cs.h>
45 #include <su/mem.h>
46
47 #include "mx/child.h"
48 #include "mx/colour.h"
49 #include "mx/file-streams.h"
50 /* TODO but only for creating chain! */
51 #include "mx/filter-quote.h"
52 #include "mx/iconv.h"
53 #include "mx/mime-type.h"
54 #include "mx/random.h"
55 #include "mx/sigs.h"
56 #include "mx/tty.h"
57 #include "mx/ui-str.h"
58
59 /* TODO fake */
60 #include "su/code-in.h"
61
62 static sigjmp_buf _send_pipejmp;
63
64 /* Going for user display, print Part: info string */
65 static void _print_part_info(FILE *obuf, struct mimepart const *mpp,
66 struct n_ignore const *doitp, int level,
67 struct quoteflt *qf, u64 *stats);
68
69 /* Create a pipe; if mpp is not NULL, place some n_PIPEENV_* environment
70 * variables accordingly */
71 static FILE *a_send_pipefile(enum sendaction action,
72 struct mx_mimetype_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
73 char const *tmpname, int term_infd);
74
75 /* Call mime_write() as appropriate and adjust statistics */
76 su_SINLINE sz _out(char const *buf, uz len, FILE *fp,
77 enum conversion convert, enum sendaction action, struct quoteflt *qf,
78 u64 *stats, struct str *outrest, struct str *inrest);
79
80 /* Simply (!) print out a LF (via qf if not NIL) */
81 static boole a_send_out_nl(FILE *fp, struct quoteflt *qf, u64 *stats);
82
83 /* SIGPIPE handler */
84 static void _send_onpipe(int signo);
85
86 /* Send one part */
87 static int sendpart(struct message *zmp, struct mimepart *ip,
88 FILE *obuf, struct n_ignore const *doitp,
89 struct quoteflt *qf, enum sendaction action,
90 char **linedat, uz *linesize,
91 u64 *stats, int level, boole *anyoutput/* XXX fake*/);
92
93 /* Dependent on *mime-alternative-favour-rich* (favour_rich) do a tree walk
94 * and check whether there are any such down mpp, which is a .m_multipart of
95 * an /alternative container.. */
96 static boole _send_al7ive_have_better(struct mimepart *mpp,
97 enum sendaction action, boole want_rich);
98
99 /* Get a file for an attachment */
100 static FILE * newfile(struct mimepart *ip, boole volatile *ispipe);
101
102 static boole a_send_pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
103 struct quoteflt *qf, u64 *stats);
104
105 /* Output a reasonable looking status field */
106 static void statusput(const struct message *mp, FILE *obuf,
107 struct quoteflt *qf, u64 *stats);
108 static void xstatusput(const struct message *mp, FILE *obuf,
109 struct quoteflt *qf, u64 *stats);
110
111 static void put_from_(FILE *fp, struct mimepart *ip, u64 *stats);
112
113 su_SINLINE sz
_out(char const * buf,uz len,FILE * fp,enum conversion convert,enum sendaction action,struct quoteflt * qf,u64 * stats,struct str * outrest,struct str * inrest)114 _out(char const *buf, uz len, FILE *fp, enum conversion convert, enum
115 sendaction action, struct quoteflt *qf, u64 *stats, struct str *outrest,
116 struct str *inrest)
117 {
118 sz size = 0, n;
119 int flags;
120 NYD_IN;
121
122 /* TODO We should not need is_head() here, i think in v15 the actual Mailbox
123 * TODO subclass should detect From_ cases and either re-encode the part
124 * TODO in question, or perform From_ quoting as necessary!?!?!? How?!? */
125 /* C99 */{
126 boole from_;
127
128 if((action == SEND_MBOX || action == SEND_DECRYPT) &&
129 (from_ = is_head(buf, len, TRU1))){
130 if(from_ != TRUM1 || (mb.mb_active & MB_BAD_FROM_) ||
131 ok_blook(mbox_rfc4155)){
132 putc('>', fp);
133 ++size;
134 }
135 }
136 }
137
138 flags = ((int)action & _TD_EOF);
139 action &= ~_TD_EOF;
140 n = mime_write(buf, len, fp,
141 action == SEND_MBOX ? CONV_NONE : convert,
142 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
143 action == SEND_TODISP_PARTS ||
144 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
145 ? TD_ISPR | TD_ICONV
146 : (action == SEND_TOSRCH || action == SEND_TOPIPE ||
147 action == SEND_TOFILE)
148 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
149 qf, outrest, inrest);
150 if (n < 0)
151 size = n;
152 else if (n > 0) {
153 size += n;
154 if (stats != NULL)
155 *stats += size;
156 }
157 NYD_OU;
158 return size;
159 }
160
161 static void
_print_part_info(FILE * obuf,struct mimepart const * mpp,struct n_ignore const * doitp,int level,struct quoteflt * qf,u64 * stats)162 _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */
163 struct n_ignore const *doitp, int level, struct quoteflt *qf, u64 *stats)
164 {
165 char buf[64];
166 struct str ti, to;
167 boole want_ct, needsep;
168 struct str const *cpre, *csuf;
169 char const *cp;
170 NYD2_IN;
171
172 cpre = csuf = NULL;
173 #ifdef mx_HAVE_COLOUR
174 if(mx_COLOUR_IS_ACTIVE()){
175 struct mx_colour_pen *cpen;
176
177 cpen = mx_colour_pen_create(mx_COLOUR_ID_VIEW_PARTINFO, NULL);
178 if((cpre = mx_colour_pen_to_str(cpen)) != NIL)
179 csuf = mx_colour_reset_to_str();
180 }
181 #endif
182
183 /* Take care of "99.99", i.e., 5 */
184 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
185 cp = n_qm;
186 if (level || (cp[0] != '1' && cp[1] == '\0') || (cp[0] == '1' && /* TODO */
187 cp[1] == '.' && cp[2] != '1')) /* TODO code should not look like so */
188 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
189
190 /* Part id, content-type, encoding, charset */
191 if (cpre != NULL)
192 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
193 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
194 _out(cp, su_cs_len(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
195
196 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
197 (uz)mpp->m_lines, (uz)mpp->m_size);
198 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
199
200 needsep = FAL0;
201
202 if((cp = mpp->m_ct_type_usr_ovwr) != NULL){
203 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
204 want_ct = TRU1;
205 }else if((want_ct = n_ignore_is_ign(doitp,
206 "content-type", sizeof("content-type") -1)))
207 cp = mpp->m_ct_type_plain;
208 if (want_ct && (to.l = su_cs_len(cp)) > 30 &&
209 su_cs_starts_with_case(cp, "application/")) {
210 uz const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
211 uz i = to.l - fl;
212 char *x = n_autorec_alloc(al + i +1);
213
214 su_mem_copy(x, "appl../", al);
215 su_mem_copy(x + al, cp + fl, i +1);
216 cp = x;
217 to.l = al + i;
218 }
219 if(cp != NULL){
220 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
221 needsep = TRU1;
222 }
223
224 if(mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL &&
225 (!su_cs_cmp_case(cp, "7bit") ||
226 n_ignore_is_ign(doitp, "content-transfer-encoding",
227 sizeof("content-transfer-encoding") -1))){
228 if(needsep)
229 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
230 if (to.l > 25 && !su_cs_cmp_case(cp, "quoted-printable"))
231 cp = "qu.-pr.";
232 _out(cp, su_cs_len(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats,
233 NULL,NULL);
234 needsep = TRU1;
235 }
236
237 if (want_ct && mpp->m_multipart == NULL/* TODO */ &&
238 (cp = mpp->m_charset) != NULL) {
239 if(needsep)
240 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
241 _out(cp, su_cs_len(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats,
242 NULL,NULL);
243 }
244
245 needsep = !needsep;
246 _out(&" --]"[su_S(su_u8,needsep)], 4 - needsep,
247 obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
248 if (csuf != NULL)
249 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
250 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
251
252 /* */
253 if (mpp->m_content_info & CI_MIME_ERRORS) {
254 if (cpre != NULL)
255 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
256 NULL, NULL);
257 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
258
259 ti.l = su_cs_len(ti.s = n_UNCONST(_("Defective MIME structure")));
260 makeprint(&ti, &to);
261 to.l = delctrl(to.s, to.l);
262 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
263 n_free(to.s);
264
265 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
266 if (csuf != NULL)
267 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
268 NULL, NULL);
269 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
270 }
271
272 /* Content-Description */
273 if (n_ignore_is_ign(doitp, "content-description", 19) &&
274 (cp = mpp->m_content_description) != NULL && *cp != '\0') {
275 if (cpre != NULL)
276 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
277 NULL, NULL);
278 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
279
280 ti.l = su_cs_len(ti.s = n_UNCONST(mpp->m_content_description));
281 mime_fromhdr(&ti, &to, TD_ISPR | TD_ICONV);
282 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
283 n_free(to.s);
284
285 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
286 if (csuf != NULL)
287 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
288 NULL, NULL);
289 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
290 }
291
292 /* Filename */
293 if (n_ignore_is_ign(doitp, "content-disposition", 19) &&
294 mpp->m_filename != NULL && *mpp->m_filename != '\0') {
295 if (cpre != NULL)
296 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
297 NULL, NULL);
298 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
299
300 ti.l = su_cs_len(ti.s = mpp->m_filename);
301 makeprint(&ti, &to);
302 to.l = delctrl(to.s, to.l);
303 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
304 n_free(to.s);
305
306 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
307 if (csuf != NULL)
308 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
309 NULL, NULL);
310 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
311 }
312 NYD2_OU;
313 }
314
315 static FILE *
a_send_pipefile(enum sendaction action,struct mx_mimetype_handler * mthp,struct mimepart const * mpp,FILE ** qbuf,char const * tmpname,int term_infd)316 a_send_pipefile(enum sendaction action, struct mx_mimetype_handler *mthp,
317 struct mimepart const *mpp, FILE **qbuf, char const *tmpname,
318 int term_infd)
319 {
320 static u32 reprocnt;
321 struct str s;
322 char const *env_addon[9 +8/*v15*/], *cp, *sh;
323 uz i;
324 FILE *rbuf;
325 NYD_IN;
326
327 rbuf = *qbuf;
328
329 if(action == SEND_QUOTE || action == SEND_QUOTE_ALL){
330 if((*qbuf = mx_fs_tmp_open("sendp", (mx_FS_O_RDWR | mx_FS_O_UNLINK |
331 mx_FS_O_REGISTER), NIL)) == NIL){
332 n_perr(_("tmpfile"), 0);
333 *qbuf = rbuf;
334 }
335 }
336
337 if((mthp->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK) == mx_MIMETYPE_HDL_PTF){
338 union {int (*ptf)(void); char const *sh;} u;
339
340 fflush(*qbuf);
341 if (*qbuf != n_stdout) /* xxx never? v15: it'll be a filter anyway */
342 fflush(n_stdout);
343
344 u.ptf = mthp->mth_ptf;
345 if((rbuf = mx_fs_pipe_open(R(char*,-1), "W", u.sh, NIL, fileno(*qbuf))
346 ) == NIL)
347 goto jerror;
348 goto jleave;
349 }
350
351 i = 0;
352
353 /* MAILX_FILENAME */
354 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
355 cp = n_empty;
356 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_FILENAME, "=", cp, NULL)->s;
357 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME", "=", cp, NULL)->s;/*v15*/
358
359 /* MAILX_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
360 * TODO a file wherever he wants! *Do* create a zero-size temporary file
361 * TODO and give *that* path as MAILX_FILENAME_TEMPORARY, clean it up once
362 * TODO the pipe returns? Like this we *can* verify path/name issues! */
363 cp = mx_random_create_cp(MIN(NAME_MAX - 3, 16), &reprocnt);
364 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_FILENAME_GENERATED, "=", cp,
365 NULL)->s;
366 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME_GENERATED", "=", cp,/*v15*/
367 NULL)->s;
368
369 /* MAILX_CONTENT{,_EVIDENCE} */
370 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
371 cp = n_empty;
372 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_CONTENT, "=", cp, NULL)->s;
373 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT", "=", cp, NULL)->s;/*v15*/
374
375 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
376 cp = mpp->m_ct_type_usr_ovwr;
377 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_CONTENT_EVIDENCE, "=", cp,
378 NULL)->s;
379 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT_EVIDENCE", "=", cp,/* v15 */
380 NULL)->s;
381
382 /* message/external-body, access-type=url */
383 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_EXTERNAL_BODY_URL, "=",
384 ((mpp != NULL && (cp = mpp->m_external_body_url) != NULL
385 ) ? cp : n_empty), NULL)->s;
386
387 /* MAILX_FILENAME_TEMPORARY? */
388 if (tmpname != NULL) {
389 env_addon[i++] = str_concat_csvl(&s,
390 n_PIPEENV_FILENAME_TEMPORARY, "=", tmpname, NULL)->s;
391 env_addon[i++] = str_concat_csvl(&s,
392 "NAIL_FILENAME_TEMPORARY", "=", tmpname, NULL)->s;/* v15 */
393 }
394
395 /* TODO we should include header information, especially From:, so
396 * TODO that same-origin can be tested for e.g. external-body!!! */
397
398 env_addon[i] = NULL;
399 sh = ok_vlook(SHELL);
400
401 if(mthp->mth_flags & mx_MIMETYPE_HDL_NEEDSTERM){
402 struct mx_child_ctx cc;
403 sigset_t nset;
404
405 sigemptyset(&nset);
406 mx_child_ctx_setup(&cc);
407 cc.cc_flags = mx_CHILD_RUN_WAIT_LIFE;
408 cc.cc_mask = &nset;
409 cc.cc_fds[mx_CHILD_FD_IN] = term_infd;
410 cc.cc_cmd = sh;
411 cc.cc_args[0] = "-c";
412 cc.cc_args[1] = mthp->mth_shell_cmd;
413 cc.cc_env_addon = env_addon;
414
415 rbuf = !mx_child_run(&cc) ? NIL : R(FILE*,-1);
416 }else{
417 rbuf = mx_fs_pipe_open(mthp->mth_shell_cmd, "W", sh, env_addon,
418 (mthp->mth_flags & mx_MIMETYPE_HDL_ASYNC ? mx_CHILD_FD_NULL
419 : fileno(*qbuf)));
420 jerror:
421 if(rbuf == NIL)
422 n_err(_("Cannot run MIME type handler: %s: %s\n"),
423 mthp->mth_msg, su_err_doc(su_err_no()));
424 else{
425 fflush(*qbuf);
426 if(*qbuf != n_stdout)
427 fflush(n_stdout);
428 }
429 }
430 jleave:
431 NYD_OU;
432 return rbuf;
433 }
434
435 static boole
a_send_out_nl(FILE * fp,struct quoteflt * qf,u64 * stats)436 a_send_out_nl(FILE *fp, struct quoteflt *qf, u64 *stats){
437 boole rv;
438 NYD2_IN;
439
440 if(qf == NIL)
441 qf = quoteflt_dummy();
442
443 quoteflt_reset(qf, fp);
444 rv = (_out("\n", 1, fp, CONV_NONE, SEND_MBOX, qf, stats, NIL,NIL) > 0);
445 quoteflt_flush(qf);
446 NYD2_OU;
447 return rv;
448 }
449
450 static void
_send_onpipe(int signo)451 _send_onpipe(int signo)
452 {
453 NYD; /* Signal handler */
454 UNUSED(signo);
455 siglongjmp(_send_pipejmp, 1);
456 }
457
458 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
459 static int __sendp_sig; /* TODO someday.. */
460 static n_sighdl_t __sendp_opipe;
461 static void
__sendp_onsig(int sig)462 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
463 {
464 NYD; /* Signal handler */
465 __sendp_sig = sig;
466 siglongjmp(__sendp_actjmp, 1);
467 }
468
469 static int
sendpart(struct message * zmp,struct mimepart * ip,FILE * volatile obuf,struct n_ignore const * doitp,struct quoteflt * qf,enum sendaction volatile action,char ** linedat,uz * linesize,u64 * volatile stats,int volatile level,boole * anyoutput)470 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
471 struct n_ignore const *doitp, struct quoteflt *qf,
472 enum sendaction volatile action,
473 char **linedat, uz *linesize, u64 * volatile stats, int volatile level,
474 boole *anyoutput)
475 {
476 int volatile rv = 0;
477 struct mx_mimetype_handler mth_stack, * volatile mthp;
478 struct str outrest, inrest;
479 boole hany, hign;
480 enum sendaction oaction;
481 char *cp;
482 char const * volatile tmpname = NULL;
483 uz linelen, cnt;
484 int volatile dostat, term_infd;
485 int c;
486 struct mimepart * volatile np;
487 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
488 * volatile qbuf = obuf, *origobuf = obuf;
489 enum conversion volatile convert;
490 n_sighdl_t volatile oldpipe = SIG_DFL;
491 NYD_IN;
492
493 UNINIT(term_infd, 0);
494 UNINIT(cnt, 0);
495 oaction = action;
496 hany = hign = FAL0;
497
498 quoteflt_reset(qf, obuf);
499
500 if((ibuf = setinput(&mb, R(struct message*,ip), NEED_BODY)) == NIL){
501 rv = -1;
502 goto jleave;
503 }
504
505 cnt = ip->m_size;
506 dostat = 0;
507
508 if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
509 action == SEND_TODISP_PARTS ||
510 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
511 action == SEND_TOSRCH){
512 dostat |= 4;
513
514 if(ip->m_mimetype != mx_MIMETYPE_DISCARD && level != 0 &&
515 action != SEND_QUOTE && action != SEND_QUOTE_ALL){
516 _print_part_info(obuf, ip, doitp, level, qf, stats);
517 hany = TRU1;
518 }
519 if(ip->m_parent != NIL && ip->m_parent->m_mimetype == mx_MIMETYPE_822){
520 ASSERT(ip->m_flag & MNOFROM);
521 hign = TRU1;
522 }
523 }
524
525 if(ip->m_mimetype == mx_MIMETYPE_DISCARD)
526 goto jheaders_skip;
527
528 if (ip->m_mimetype == mx_MIMETYPE_PKCS7) {
529 if (ip->m_multipart &&
530 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
531 goto jheaders_skip;
532 }
533
534 if(!(ip->m_flag & MNOFROM))
535 while(cnt > 0 && (c = getc(ibuf)) != EOF){
536 --cnt;
537 if(c == '\n')
538 break;
539 }
540
541 if(!hign && level == 0 && action != SEND_TODISP_PARTS){
542 if(doitp != NIL){
543 if(!n_ignore_is_ign(doitp, "status", 6))
544 dostat |= 1;
545 if(!n_ignore_is_ign(doitp, "x-status", 8))
546 dostat |= 2;
547 }else
548 dostat |= 3;
549 }
550
551 jhdr_redo:
552 convert = (dostat & 4) ? CONV_FROMHDR : CONV_NONE;
553
554 /* Work the headers */
555 /* C99 */{
556 struct n_string hl, *hlp;
557 uz lineno;
558 boole hstop;
559
560 hlp = n_string_creat_auto(&hl); /* TODO pool [or, v15: filter!] */
561 /* Reserve three lines, still not enough for references and DKIM etc. */
562 hlp = n_string_reserve(hlp, MAX(MIME_LINELEN, MIME_LINELEN_RFC2047) * 3);
563 lineno = 0;
564
565 for(hstop = FAL0; !hstop;){
566 uz lcnt;
567
568 lcnt = cnt;
569 if(fgetline(linedat, linesize, &cnt, &linelen, ibuf, FAL0) == NIL)
570 break;
571 ++lineno;
572 if (linelen == 0 || (cp = *linedat)[0] == '\n')
573 /* If line is blank, we've reached end of headers */
574 break;
575 if(cp[linelen - 1] == '\n'){
576 cp[--linelen] = '\0';
577 if(linelen == 0)
578 break;
579 }
580
581 /* Are we in a header? */
582 if(hlp->s_len > 0){
583 if(!su_cs_is_blank(*cp)){
584 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
585 cnt = lcnt;
586 goto jhdrput;
587 }
588 goto jhdrpush;
589 }else{
590 /* Pick up the header field if we have one */
591 while((c = *cp) != ':' && !su_cs_is_space(c) && c != '\0')
592 ++cp;
593 for(;;){
594 if(!su_cs_is_space(c) || c == '\0')
595 break;
596 c = *++cp;
597 }
598 if(c != ':'){
599 /* That won't work with MIME when saving etc., before v15 */
600 if (lineno != 1)
601 /* XXX This disturbs, and may happen multiple times, and we
602 * XXX cannot heal it for multipart except for display <v15 */
603 n_err(_("Malformed message: headers and body not separated "
604 "(with empty line)\n"));
605 if(level != 0)
606 dostat &= ~(1 | 2);
607 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
608 cnt = lcnt;
609 break;
610 }
611
612 cp = *linedat;
613 jhdrpush:
614 if(!(dostat & 4)){
615 hlp = n_string_push_buf(hlp, cp, (u32)linelen);
616 hlp = n_string_push_c(hlp, '\n');
617 }else{
618 boole lblank, xblank;
619
620 for(lblank = FAL0, lcnt = 0; lcnt < linelen; ++cp, ++lcnt){
621 char c8;
622
623 c8 = *cp;
624 if(!(xblank = su_cs_is_blank(c8)) || !lblank){
625 if((lblank = xblank))
626 c8 = ' ';
627 hlp = n_string_push_c(hlp, c8);
628 }
629 }
630 }
631 continue;
632 }
633
634 jhdrput:
635 /* If it is an ignored header, skip it */
636 *(cp = su_mem_find(hlp->s_dat, ':', hlp->s_len)) = '\0';
637 /* C99 */{
638 uz i;
639
640 i = P2UZ(cp - hlp->s_dat);
641 if(hign || (doitp != NULL && n_ignore_is_ign(doitp, hlp->s_dat, i)) ||
642 !su_cs_cmp_case(hlp->s_dat, "status") ||
643 !su_cs_cmp_case(hlp->s_dat, "x-status") ||
644 (action == SEND_MBOX &&
645 (!su_cs_cmp_case(hlp->s_dat, "content-length") ||
646 !su_cs_cmp_case(hlp->s_dat, "lines")) &&
647 !ok_blook(keep_content_length)))
648 goto jhdrtrunc;
649 }
650
651 /* Dump it */
652 if(!hany && (dostat & 4) && level > 0)
653 a_send_out_nl(obuf, NIL, stats);
654 mx_COLOUR(
655 if(mx_COLOUR_IS_ACTIVE())
656 mx_colour_put(mx_COLOUR_ID_VIEW_HEADER, hlp->s_dat);
657 )
658 *cp = ':';
659 _out(hlp->s_dat, hlp->s_len, obuf, convert, action, qf, stats, NIL,NIL);
660 mx_COLOUR(
661 if(mx_COLOUR_IS_ACTIVE())
662 mx_colour_reset();
663 )
664 if(dostat & 4)
665 a_send_out_nl(obuf, qf, stats);
666 hany = TRU1;
667
668 jhdrtrunc:
669 hlp = n_string_trunc(hlp, 0);
670 }
671 hstop = TRU1;
672 if(hlp->s_len > 0)
673 goto jhdrput;
674
675 if(hign /*|| (!hany && (dostat & (1 | 2)))*/){
676 a_send_out_nl(obuf, qf, stats);
677 if(hign)
678 goto jheaders_skip;
679 }
680
681 /* We have reached end of headers, so eventually force out status: field
682 * and note that we are no longer in header fields */
683 if(dostat & 1){
684 statusput(zmp, obuf, qf, stats);
685 hany = TRU1;
686 }
687 if(dostat & 2){
688 xstatusput(zmp, obuf, qf, stats);
689 hany = TRU1;
690 }
691 if((hany /*&& doitp != n_IGNORE_ALL*/) ||
692 (oaction == SEND_DECRYPT && ip->m_parent != NIL &&
693 ip != ip->m_multipart) ||
694 ((oaction == SEND_QUOTE || oaction == SEND_QUOTE_ALL) &&
695 level != 0 && *anyoutput &&
696 (ip->m_mimetype != mx_MIMETYPE_DISCARD &&
697 ip->m_mimetype != mx_MIMETYPE_PKCS7 &&
698 ip->m_mimetype != mx_MIMETYPE_PKCS7 &&
699 ip->m_mimetype != mx_MIMETYPE_ALTERNATIVE &&
700 ip->m_mimetype != mx_MIMETYPE_RELATED &&
701 ip->m_mimetype != mx_MIMETYPE_DIGEST &&
702 ip->m_mimetype != mx_MIMETYPE_MULTI &&
703 ip->m_mimetype != mx_MIMETYPE_SIGNED &&
704 ip->m_mimetype != mx_MIMETYPE_ENCRYPTED))){
705 if(ip->m_mimetype != mx_MIMETYPE_822 || (dostat & 16)){
706 /*XXX (void)*/a_send_out_nl(obuf, NIL, stats);
707 hany = TRU1;
708 }
709 }
710 } /* C99 */
711
712 quoteflt_flush(qf);
713
714 if(ferror(ibuf) || ferror(obuf)){
715 rv = -1;
716 goto jleave;
717 }
718
719 *anyoutput = hany;
720 jheaders_skip:
721 su_mem_set(mthp = &mth_stack, 0, sizeof mth_stack);
722
723 if(oaction == SEND_MBOX){
724 convert = CONV_NONE;
725 goto jsend;
726 }
727
728 switch (ip->m_mimetype) {
729 case mx_MIMETYPE_822:
730 switch (action) {
731 case SEND_TODISP_PARTS:
732 goto jleave;
733 case SEND_TODISP:
734 case SEND_TODISP_ALL:
735 case SEND_QUOTE:
736 case SEND_QUOTE_ALL:
737 if(!(dostat & 16)){ /* XXX */
738 dostat |= 16;
739 a_send_out_nl(obuf, qf, stats);
740 if(ok_blook(rfc822_body_from_)){
741 if(!qf->qf_bypass){
742 uz i;
743
744 i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix, qf->qf_pfix_len,
745 obuf);
746 if(i == qf->qf_pfix_len && stats != NIL)
747 *stats += i;
748 }
749 put_from_(obuf, ip->m_multipart, stats);
750 hany = TRU1;
751 }
752 goto jhdr_redo;
753 }
754 goto jmulti;
755 case SEND_TOSRCH:
756 goto jmulti;
757 case SEND_DECRYPT:
758 goto jmulti;
759 case SEND_TOFILE:
760 case SEND_TOPIPE:
761 put_from_(obuf, ip->m_multipart, stats);
762 /* FALLTHRU */
763 default:
764 break;
765 }
766 break;
767 case mx_MIMETYPE_TEXT_HTML:
768 case mx_MIMETYPE_TEXT:
769 case mx_MIMETYPE_TEXT_PLAIN:
770 switch (action) {
771 case SEND_TODISP:
772 case SEND_TODISP_ALL:
773 case SEND_TODISP_PARTS:
774 case SEND_QUOTE:
775 case SEND_QUOTE_ALL:
776 if((mthp = ip->m_handler) == NIL)
777 mx_mimetype_handler(mthp =
778 ip->m_handler = n_autorec_alloc(sizeof(*mthp)), ip, oaction);
779 switch(mthp->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK){
780 case mx_MIMETYPE_HDL_NIL:
781 if(oaction != SEND_TODISP_PARTS)
782 break;
783 /* FALLTHRU */
784 case mx_MIMETYPE_HDL_MSG:/* TODO these should be part of partinfo! */
785 if(mthp->mth_msg.l > 0)
786 _out(mthp->mth_msg.s, mthp->mth_msg.l, obuf, CONV_NONE,
787 SEND_MBOX, qf, stats, NULL, NULL);
788 /* We would print this as plain text, so better force going home */
789 goto jleave;
790 case mx_MIMETYPE_HDL_CMD:
791 if(oaction == SEND_TODISP_PARTS){
792 if(mthp->mth_flags & mx_MIMETYPE_HDL_COPIOUSOUTPUT)
793 goto jleave;
794 else{
795 /* Because: interactive OR batch mode, so */
796 if(!mx_tty_yesorno(_("Run MIME handler for this part?"),
797 su_state_has(su_STATE_REPRODUCIBLE)))
798 goto jleave;
799 }
800 }
801 break;
802 case mx_MIMETYPE_HDL_TEXT:
803 case mx_MIMETYPE_HDL_PTF:
804 if(oaction == SEND_TODISP_PARTS)
805 goto jleave;
806 break;
807 default:
808 break;
809 }
810 /* FALLTRHU */
811 default:
812 break;
813 }
814 break;
815 case mx_MIMETYPE_DISCARD:
816 if(oaction != SEND_DECRYPT)
817 goto jleave;
818 break;
819 case mx_MIMETYPE_PKCS7:
820 if(oaction != SEND_RFC822 && oaction != SEND_SHOW &&
821 ip->m_multipart != NIL)
822 goto jmulti;
823 /* FALLTHRU */
824 default:
825 switch (action) {
826 case SEND_TODISP:
827 case SEND_TODISP_ALL:
828 case SEND_TODISP_PARTS:
829 case SEND_QUOTE:
830 case SEND_QUOTE_ALL:
831 if((mthp = ip->m_handler) == NIL)
832 mx_mimetype_handler(mthp = ip->m_handler =
833 n_autorec_alloc(sizeof(*mthp)), ip, oaction);
834 switch(mthp->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK){
835 default:
836 case mx_MIMETYPE_HDL_NIL:
837 if (oaction != SEND_TODISP && oaction != SEND_TODISP_ALL &&
838 (level != 0 || cnt))
839 goto jleave;
840 /* FALLTHRU */
841 case mx_MIMETYPE_HDL_MSG:/* TODO these should be part of partinfo! */
842 if(mthp->mth_msg.l > 0)
843 _out(mthp->mth_msg.s, mthp->mth_msg.l, obuf, CONV_NONE,
844 SEND_MBOX, qf, stats, NULL, NULL);
845 /* We would print this as plain text, so better force going home */
846 goto jleave;
847 case mx_MIMETYPE_HDL_CMD:
848 if(oaction == SEND_TODISP_PARTS){
849 if(mthp->mth_flags & mx_MIMETYPE_HDL_COPIOUSOUTPUT)
850 goto jleave;
851 else{
852 /* Because: interactive OR batch mode, so */
853 if(!mx_tty_yesorno(_("Run MIME handler for this part?"),
854 su_state_has(su_STATE_REPRODUCIBLE)))
855 goto jleave;
856 }
857 }
858 break;
859 case mx_MIMETYPE_HDL_TEXT:
860 case mx_MIMETYPE_HDL_PTF:
861 if(oaction == SEND_TODISP_PARTS)
862 goto jleave;
863 break;
864 }
865 break;
866 default:
867 break;
868 }
869 break;
870 case mx_MIMETYPE_ALTERNATIVE:
871 if ((oaction == SEND_TODISP || oaction == SEND_QUOTE) &&
872 !ok_blook(print_alternatives)) {
873 /* XXX This (a) should not remain (b) should be own fun
874 * TODO (despite the fact that v15 will do this completely differently
875 * TODO by having an action-specific "manager" that will traverse the
876 * TODO parsed MIME tree and decide for each part whether it'll be
877 * TODO displayed or not *before* we walk the tree for doing action */
878 struct mpstack {
879 struct mpstack *outer;
880 struct mimepart *mp;
881 } outermost, * volatile curr, * volatile mpsp;
882 enum {
883 _NONE,
884 _DORICH = 1<<0, /* We are looking for rich parts */
885 _HADPART = 1<<1, /* Did print a part already */
886 _NEEDNL = 1<<3 /* Need a visual separator */
887 } flags;
888 struct n_sigman smalter;
889
890 (curr = &outermost)->outer = NULL;
891 curr->mp = ip;
892 flags = ok_blook(mime_alternative_favour_rich) ? _DORICH : _NONE;
893 if(!_send_al7ive_have_better(ip->m_multipart, action,
894 ((flags & _DORICH) != 0)))
895 flags ^= _DORICH;
896
897 n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
898 case 0:
899 break;
900 default:
901 rv = -1;
902 goto jalter_leave;
903 }
904
905 for(np = ip->m_multipart;;){
906 jalter_redo:
907 level = -ABS(level);
908 for(; np != NIL; np = np->m_nextpart){
909 level = -ABS(level);
910 flags |= _NEEDNL;
911
912 switch(np->m_mimetype){
913 case mx_MIMETYPE_ALTERNATIVE:
914 case mx_MIMETYPE_RELATED:
915 case mx_MIMETYPE_DIGEST:
916 case mx_MIMETYPE_SIGNED:
917 case mx_MIMETYPE_ENCRYPTED:
918 case mx_MIMETYPE_MULTI:
919 np->m_flag &= ~MDISPLAY;
920 mpsp = n_autorec_alloc(sizeof *mpsp);
921 mpsp->outer = curr;
922 mpsp->mp = np->m_multipart;
923 curr->mp = np;
924 curr = mpsp;
925 np = mpsp->mp;
926 flags &= ~_NEEDNL;
927 goto jalter_redo;
928 default:
929 if(!(np->m_flag & MDISPLAY)){
930 if(np->m_mimetype != mx_MIMETYPE_DISCARD &&
931 (action == SEND_TODISP ||
932 action == SEND_TODISP_ALL ||
933 action == SEND_TODISP_PARTS))
934 _print_part_info(obuf, np, doitp, level, qf, stats);
935 break;
936 }
937
938 /* This thing we are going to do */
939 quoteflt_flush(qf);
940 flags |= _HADPART;
941 flags &= ~_NEEDNL;
942 rv = ABS(level) + 1;
943 if(level < 0){
944 level = -level;
945 rv = -rv;
946 }
947 rv = sendpart(zmp, np, obuf, doitp, qf, oaction,
948 linedat, linesize, stats, rv, anyoutput);
949 quoteflt_reset(qf, origobuf);
950 if (rv < 0)
951 curr = &outermost; /* Cause overall loop termination */
952 break;
953 }
954 }
955
956 mpsp = curr->outer;
957 if (mpsp == NULL)
958 break;
959 curr = mpsp;
960 np = curr->mp->m_nextpart;
961 }
962 jalter_leave:
963 n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
964 goto jleave;
965 }
966 /* FALLTHRU */
967 case mx_MIMETYPE_RELATED:
968 case mx_MIMETYPE_DIGEST:
969 case mx_MIMETYPE_SIGNED:
970 case mx_MIMETYPE_ENCRYPTED:
971 case mx_MIMETYPE_MULTI:
972 switch(action){
973 case SEND_TODISP:
974 case SEND_TODISP_ALL:
975 case SEND_TODISP_PARTS:
976 case SEND_QUOTE:
977 case SEND_QUOTE_ALL:
978 case SEND_TOFILE:
979 case SEND_TOPIPE:
980 case SEND_TOSRCH:
981 case SEND_DECRYPT:
982 jmulti:
983 if((oaction == SEND_TODISP || oaction == SEND_TODISP_ALL) &&
984 ip->m_multipart != NIL &&
985 ip->m_multipart->m_mimetype == mx_MIMETYPE_DISCARD &&
986 ip->m_multipart->m_nextpart == NULL) {
987 char const *x = _("[Missing multipart boundary - use `show' "
988 "to display the raw message]\n");
989 _out(x, su_cs_len(x), obuf, CONV_NONE, SEND_MBOX, qf, stats,
990 NULL,NULL);
991 }
992
993 level = -ABS(level);
994 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
995 boole volatile ispipe;
996
997 if(np->m_mimetype == mx_MIMETYPE_DISCARD &&
998 oaction != SEND_DECRYPT)
999 continue;
1000
1001 ispipe = FAL0;
1002 switch(oaction){
1003 case SEND_TOFILE:
1004 if (np->m_partstring &&
1005 np->m_partstring[0] == '1' && np->m_partstring[1] == '\0')
1006 break;
1007 stats = NULL;
1008 /* TODO Always open multipart on /dev/null, it's a hack to be
1009 * TODO able to dive into that structure, and still better
1010 * TODO than asking the user for something stupid.
1011 * TODO oh, wait, we did ask for a filename for this MIME mail,
1012 * TODO and that outer container is useless anyway ;-P */
1013 if(np->m_multipart != NULL &&
1014 np->m_mimetype != mx_MIMETYPE_822){
1015 if((obuf = mx_fs_open(n_path_devnull, "w")) == NIL)
1016 continue;
1017 }else if((obuf = newfile(np, &ispipe)) == NIL)
1018 continue;
1019 if(ispipe){
1020 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1021 if(sigsetjmp(_send_pipejmp, 1)){
1022 rv = -1;
1023 goto jpipe_close;
1024 }
1025 }
1026 break;
1027 default:
1028 break;
1029 }
1030
1031 quoteflt_flush(qf);
1032 {
1033 int nlvl = ABS(level) + 1;
1034 if(level < 0){
1035 level = -level;
1036 nlvl = -nlvl;
1037 }
1038 if(sendpart(zmp, np, obuf, doitp, qf, oaction, linedat,
1039 linesize, stats, nlvl, anyoutput) < 0)
1040 rv = -1;
1041 }
1042 quoteflt_reset(qf, origobuf);
1043
1044 if(oaction == SEND_QUOTE){
1045 if(ip->m_mimetype != mx_MIMETYPE_RELATED)
1046 break;
1047 }
1048 if(oaction == SEND_TOFILE && obuf != origobuf){
1049 if(!ispipe)
1050 mx_fs_close(obuf);
1051 else {
1052 jpipe_close:
1053 mx_fs_pipe_close(obuf, TRU1);
1054 safe_signal(SIGPIPE, oldpipe);
1055 }
1056 }
1057 }
1058 goto jleave;
1059 default:
1060 break;
1061 }
1062 break;
1063 }
1064
1065 /* Copy out message body */
1066 if (doitp == n_IGNORE_ALL && level == 0) /* skip final blank line */
1067 --cnt;
1068 switch (ip->m_mime_enc) {
1069 case MIMEE_BIN:
1070 case MIMEE_7B:
1071 case MIMEE_8B:
1072 convert = CONV_NONE;
1073 break;
1074 case MIMEE_QP:
1075 convert = CONV_FROMQP;
1076 break;
1077 case MIMEE_B64:
1078 switch(ip->m_mimetype){
1079 case mx_MIMETYPE_TEXT:
1080 case mx_MIMETYPE_TEXT_PLAIN:
1081 case mx_MIMETYPE_TEXT_HTML:
1082 convert = CONV_FROMB64_T;
1083 break;
1084 default:
1085 switch (mthp->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK) {
1086 case mx_MIMETYPE_HDL_TEXT:
1087 case mx_MIMETYPE_HDL_PTF:
1088 convert = CONV_FROMB64_T;
1089 break;
1090 default:
1091 convert = CONV_FROMB64;
1092 break;
1093 }
1094 break;
1095 }
1096 break;
1097 default:
1098 convert = CONV_NONE;
1099 }
1100
1101 /* TODO Unless we have filters, ensure iconvd==-1 so that mime.c:fwrite_td()
1102 * TODO cannot mess things up misusing outrest as line buffer */
1103 #ifdef mx_HAVE_ICONV
1104 if (iconvd != (iconv_t)-1) {
1105 n_iconv_close(iconvd);
1106 iconvd = (iconv_t)-1;
1107 }
1108 #endif
1109
1110 if(oaction == SEND_DECRYPT || oaction == SEND_MBOX ||
1111 oaction == SEND_RFC822 || oaction == SEND_SHOW)
1112 convert = CONV_NONE;
1113 #ifdef mx_HAVE_ICONV
1114 else if((oaction == SEND_TODISP || oaction == SEND_TODISP_ALL ||
1115 oaction == SEND_TODISP_PARTS ||
1116 oaction == SEND_QUOTE || oaction == SEND_QUOTE_ALL ||
1117 oaction == SEND_TOSRCH || oaction == SEND_TOFILE) &&
1118 (ip->m_mimetype == mx_MIMETYPE_TEXT_PLAIN ||
1119 ip->m_mimetype == mx_MIMETYPE_TEXT_HTML ||
1120 ip->m_mimetype == mx_MIMETYPE_TEXT ||
1121 (mthp->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK
1122 ) == mx_MIMETYPE_HDL_TEXT ||
1123 (mthp->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK
1124 ) == mx_MIMETYPE_HDL_PTF)) {
1125 char const *tcs;
1126
1127 tcs = ok_vlook(ttycharset);
1128 if (su_cs_cmp_case(tcs, ip->m_charset) &&
1129 su_cs_cmp_case(ok_vlook(charset_7bit), ip->m_charset)) {
1130 iconvd = n_iconv_open(tcs, ip->m_charset);
1131 if (iconvd == (iconv_t)-1 && su_err_no() == su_ERR_INVAL) {
1132 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
1133 /*rv = 1; goto jleave;*/
1134 }
1135 }
1136 }
1137 #endif
1138
1139 switch (mthp->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK) {
1140 case mx_MIMETYPE_HDL_CMD:
1141 if(!(mthp->mth_flags & mx_MIMETYPE_HDL_COPIOUSOUTPUT)){
1142 if(oaction != SEND_TODISP_PARTS)
1143 goto jmthp_default;
1144 /* FIXME Ach, what a hack! We need filters.. v15! */
1145 if(convert != CONV_FROMB64_T)
1146 action = SEND_TOPIPE;
1147 }
1148 /* FALLTHRU */
1149 case mx_MIMETYPE_HDL_PTF:
1150 tmpname = NULL;
1151 qbuf = obuf;
1152
1153 term_infd = mx_CHILD_FD_PASS;
1154 if(mthp->mth_flags & (mx_MIMETYPE_HDL_TMPF | mx_MIMETYPE_HDL_NEEDSTERM)){
1155 struct mx_fs_tmp_ctx *fstcp;
1156 char const *pref;
1157 BITENUM_IS(u32,mx_fs_oflags) of;
1158
1159 of = mx_FS_O_RDWR | mx_FS_O_REGISTER;
1160
1161 if(!(mthp->mth_flags & mx_MIMETYPE_HDL_TMPF)){
1162 term_infd = 0;
1163 mthp->mth_flags |= mx_MIMETYPE_HDL_TMPF_FILL;
1164 of |= mx_FS_O_UNLINK;
1165 pref = "mtanonfill";
1166 }else{
1167 /* (async and unlink are mutual exclusive) */
1168 if(mthp->mth_flags & mx_MIMETYPE_HDL_TMPF_UNLINK)
1169 of |= mx_FS_O_REGISTER_UNLINK;
1170
1171 if(mthp->mth_flags & mx_MIMETYPE_HDL_TMPF_NAMETMPL){
1172 pref = mthp->mth_tmpf_nametmpl;
1173 if(mthp->mth_flags & mx_MIMETYPE_HDL_TMPF_NAMETMPL_SUFFIX)
1174 of |= mx_FS_O_SUFFIX;
1175 }else if(mthp->mth_flags & mx_MIMETYPE_HDL_TMPF_FILL)
1176 pref = "mimetypefill";
1177 else
1178 pref = "mimetype";
1179 }
1180
1181 if((pbuf = mx_fs_tmp_open(pref, of,
1182 (mthp->mth_flags & mx_MIMETYPE_HDL_TMPF ? &fstcp : NIL))
1183 ) == NIL)
1184 goto jesend;
1185
1186 if(mthp->mth_flags & mx_MIMETYPE_HDL_TMPF)
1187 tmpname = fstcp->fstc_filename; /* In autorec storage! */
1188
1189 if(mthp->mth_flags & mx_MIMETYPE_HDL_TMPF_FILL){
1190 action = SEND_TOPIPE;
1191 if(term_infd == 0)
1192 term_infd = fileno(pbuf);
1193 goto jsend;
1194 }
1195 }
1196
1197 jpipe_for_real:
1198 pbuf = a_send_pipefile(oaction, mthp, ip, UNVOLATILE(FILE**,&qbuf),
1199 tmpname, term_infd);
1200 if(pbuf == NIL){
1201 jesend:
1202 pbuf = qbuf = NIL;
1203 rv = -1;
1204 goto jend;
1205 }else if((mthp->mth_flags & mx_MIMETYPE_HDL_NEEDSTERM) &&
1206 pbuf == R(FILE*,-1)){
1207 pbuf = qbuf = NIL;
1208 goto jend;
1209 }
1210 tmpname = NIL;
1211
1212 action = SEND_TOPIPE;
1213 if (pbuf != qbuf) {
1214 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1215 if (sigsetjmp(_send_pipejmp, 1))
1216 goto jend;
1217 }
1218 break;
1219
1220 default:
1221 jmthp_default:
1222 mthp->mth_flags = mx_MIMETYPE_HDL_NIL;
1223 pbuf = qbuf = obuf;
1224 break;
1225 }
1226
1227 jsend:
1228 {
1229 boole volatile eof;
1230 boole save_qf_bypass = qf->qf_bypass;
1231 u64 *save_stats = stats;
1232
1233 if (pbuf != origobuf) {
1234 qf->qf_bypass = TRU1;/* XXX legacy (remove filter instead) */
1235 stats = NULL;
1236 }
1237 eof = FAL0;
1238 outrest.s = inrest.s = NULL;
1239 outrest.l = inrest.l = 0;
1240
1241 if (pbuf == qbuf) {
1242 __sendp_sig = 0;
1243 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1244 if (sigsetjmp(__sendp_actjmp, 1)) {
1245 n_pstate &= ~n_PS_BASE64_STRIP_CR;/* (but outer sigman protected) */
1246 if (outrest.s != NULL)
1247 n_free(outrest.s);
1248 if (inrest.s != NULL)
1249 n_free(inrest.s);
1250 #ifdef mx_HAVE_ICONV
1251 if (iconvd != (iconv_t)-1)
1252 n_iconv_close(iconvd);
1253 #endif
1254 safe_signal(SIGPIPE, __sendp_opipe);
1255 n_raise(__sendp_sig);
1256 }
1257 }
1258
1259 quoteflt_reset(qf, pbuf);
1260 if(dostat & 4){
1261 if(pbuf == origobuf) /* TODO */
1262 n_pstate |= n_PS_BASE64_STRIP_CR;
1263 }
1264 while(!eof && fgetline(linedat, linesize, &cnt, &linelen, ibuf, FAL0)){
1265 joutln:
1266 if (_out(*linedat, linelen, pbuf, convert, action, qf, stats, &outrest,
1267 (action & _TD_EOF ? NULL : &inrest)) < 0 || ferror(pbuf)) {
1268 rv = -1; /* XXX Should bail away?! */
1269 break;
1270 }
1271 }
1272 if(ferror(ibuf))
1273 rv = -1;
1274 if(eof <= FAL0 && rv >= 0 && (outrest.l != 0 || inrest.l != 0)){
1275 linelen = 0;
1276 if(eof || inrest.l == 0)
1277 action |= _TD_EOF;
1278 eof = eof ? TRU1 : TRUM1;
1279 goto joutln;
1280 }
1281 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1282 action &= ~_TD_EOF;
1283
1284 /* TODO HACK: when sending to the display we yet get fooled if a message
1285 * TODO doesn't end in a newline, because of our input/output 1:1.
1286 * TODO This should be handled automatically by a display filter, then */
1287 if(rv >= 0 && !qf->qf_nl_last &&
1288 (action == SEND_TODISP || action == SEND_TODISP_ALL ||
1289 action == SEND_QUOTE || action == SEND_QUOTE_ALL))
1290 rv = quoteflt_push(qf, "\n", 1);
1291
1292 quoteflt_flush(qf);
1293
1294 if(!(qf->qf_bypass = save_qf_bypass))
1295 *anyoutput = TRU1;
1296 stats = save_stats;
1297
1298 if (rv >= 0 && (mthp->mth_flags & mx_MIMETYPE_HDL_TMPF_FILL)) {
1299 mthp->mth_flags &= ~mx_MIMETYPE_HDL_TMPF_FILL;
1300 fflush(pbuf);
1301 really_rewind(pbuf);
1302 /* Don't fs_close() a tmp_open() thing due to FS_O_UNREGISTER_UNLINK++ */
1303 goto jpipe_for_real;
1304 }
1305
1306 if (pbuf == qbuf)
1307 safe_signal(SIGPIPE, __sendp_opipe);
1308
1309 if (outrest.s != NULL)
1310 n_free(outrest.s);
1311 if (inrest.s != NULL)
1312 n_free(inrest.s);
1313
1314 }
1315
1316 jend:
1317 if(pbuf != qbuf){
1318 mx_fs_pipe_close(pbuf, !(mthp->mth_flags & mx_MIMETYPE_HDL_ASYNC));
1319 safe_signal(SIGPIPE, oldpipe);
1320 if (rv >= 0 && qbuf != NULL && qbuf != obuf){
1321 *anyoutput = TRU1;
1322 if(!a_send_pipecpy(qbuf, obuf, origobuf, qf, stats))
1323 rv = -1;
1324 }
1325 }
1326
1327 #ifdef mx_HAVE_ICONV
1328 if (iconvd != (iconv_t)-1)
1329 n_iconv_close(iconvd);
1330 #endif
1331
1332 jleave:
1333 NYD_OU;
1334 return rv;
1335 }
1336
1337 static boole
_send_al7ive_have_better(struct mimepart * mpp,enum sendaction action,boole want_rich)1338 _send_al7ive_have_better(struct mimepart *mpp, enum sendaction action,
1339 boole want_rich){
1340 struct mimepart *plain, *rich;
1341 boole rv;
1342 NYD_IN;
1343
1344 rv = FAL0;
1345 plain = rich = NIL;
1346
1347 for(; mpp != NIL; mpp = mpp->m_nextpart){
1348 switch(mpp->m_mimetype){
1349 case mx_MIMETYPE_TEXT_PLAIN:
1350 plain = mpp;
1351 if(!want_rich)
1352 goto jfound;
1353 continue;
1354 case mx_MIMETYPE_ALTERNATIVE:
1355 case mx_MIMETYPE_RELATED:
1356 case mx_MIMETYPE_DIGEST:
1357 case mx_MIMETYPE_SIGNED:
1358 case mx_MIMETYPE_ENCRYPTED:
1359 case mx_MIMETYPE_MULTI:
1360 /* Be simple and recurse */
1361 if(_send_al7ive_have_better(mpp->m_multipart, action, want_rich))
1362 goto jleave;
1363 continue;
1364 default:
1365 break;
1366 }
1367
1368 if(mpp->m_handler == NIL)
1369 mx_mimetype_handler(mpp->m_handler =
1370 su_AUTO_ALLOC(sizeof(*mpp->m_handler)), mpp, action);
1371
1372 switch(mpp->m_handler->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK){
1373 case mx_MIMETYPE_HDL_TEXT:
1374 if(!want_rich)
1375 goto jfound;
1376 if(plain == NIL)
1377 plain = mpp;
1378 break;
1379 case mx_MIMETYPE_HDL_PTF:
1380 if(want_rich)
1381 goto jfound;
1382 if(rich == NIL ||
1383 (rich->m_handler->mth_flags & mx_MIMETYPE_HDL_TYPE_MASK
1384 ) != mx_MIMETYPE_HDL_PTF)
1385 rich = mpp;
1386 break;
1387 case mx_MIMETYPE_HDL_CMD:
1388 if(mpp->m_handler->mth_flags & mx_MIMETYPE_HDL_COPIOUSOUTPUT){
1389 if(want_rich)
1390 goto jfound;
1391 if(rich == NIL)
1392 rich = mpp;
1393 }
1394 /* FALLTHRU */
1395 default:
1396 break;
1397 }
1398 }
1399
1400 /* Without plain part at all, choose an existing rich no matter what */
1401 if((mpp = plain) != NIL || (mpp = rich) != NIL){
1402 jfound:
1403 mpp->m_flag |= MDISPLAY;
1404 ASSERT(mpp->m_parent != NIL);
1405 mpp->m_parent->m_flag |= MDISPLAY;
1406 rv = TRU1;
1407 }
1408
1409 jleave:
1410 NYD_OU;
1411 return rv;
1412 }
1413
1414 static FILE *
newfile(struct mimepart * ip,boole volatile * ispipe)1415 newfile(struct mimepart *ip, boole volatile *ispipe)
1416 {
1417 struct str in, out;
1418 char *f;
1419 FILE *fp;
1420 NYD_IN;
1421
1422 f = ip->m_filename;
1423 *ispipe = FAL0;
1424
1425 if (f != NULL && f != (char*)-1) {
1426 in.s = f;
1427 in.l = su_cs_len(f);
1428 makeprint(&in, &out);
1429 out.l = delctrl(out.s, out.l);
1430 f = savestrbuf(out.s, out.l);
1431 n_free(out.s);
1432 }
1433
1434 /* In interactive mode, let user perform all kind of expansions as desired,
1435 * and offer |SHELL-SPEC pipe targets, too */
1436 if (n_psonce & n_PSO_INTERACTIVE) {
1437 struct str prompt;
1438 struct n_string shou, *shoup;
1439 char *f2, *f3;
1440
1441 shoup = n_string_creat_auto(&shou);
1442
1443 /* TODO If the current part is the first textpart the target
1444 * TODO is implicit from outer `write' etc! */
1445 /* I18N: Filename input prompt with file type indication */
1446 str_concat_csvl(&prompt, _("Enter filename for part "),
1447 (ip->m_partstring != NULL ? ip->m_partstring : n_qm),
1448 " (", ip->m_ct_type_plain, "): ", NULL);
1449 jgetname:
1450 while(mx_tty_getfilename(shoup,
1451 (n_GO_INPUT_CTX_DEFAULT | n_GO_INPUT_HIST_ADD), prompt.s,
1452 ((f != R(char*,-1) && f != NIL) ? n_shexp_quote_cp(f, FAL0) : NIL
1453 )) < TRU1){
1454 }
1455
1456 f2 = n_string_cp(shoup);
1457 if(*f2 == '\0') {
1458 if(n_poption & n_PO_D_V)
1459 n_err(_("... skipping this\n"));
1460 n_string_gut(shoup);
1461 fp = NIL;
1462 goto jleave;
1463 }
1464
1465 if(*f2 == '|')
1466 /* Pipes are expanded by the shell */
1467 f = f2;
1468 else if((f3 = fexpand(f2, (FEXP_LOCAL_FILE | FEXP_NVAR))) == NIL)
1469 /* (Error message written by fexpand()) */
1470 goto jgetname;
1471 else
1472 f = f3;
1473
1474 n_string_gut(shoup);
1475 }
1476
1477 if (f == NULL || f == (char*)-1 || *f == '\0')
1478 fp = NULL;
1479 else if(n_psonce & n_PSO_INTERACTIVE){
1480 if(*f == '|'){
1481 fp = mx_fs_pipe_open(&f[1], "w", ok_vlook(SHELL), NIL, -1);
1482 if(!(*ispipe = (fp != NIL)))
1483 n_perr(f, 0);
1484 }else if((fp = mx_fs_open(f, "w")) == NIL)
1485 n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f, FAL0));
1486 }else{
1487 /* Be very picky in non-interactive mode: actively disallow pipes,
1488 * prevent directory separators, and any filename member that would
1489 * become expanded by the shell if the name would be echo(1)ed */
1490 if(su_cs_first_of(f, "/" n_SHEXP_MAGIC_PATH_CHARS) != su_UZ_MAX){
1491 char c;
1492
1493 for(out.s = n_autorec_alloc((su_cs_len(f) * 3) +1), out.l = 0;
1494 (c = *f++) != '\0';)
1495 if(su_cs_find_c("/" n_SHEXP_MAGIC_PATH_CHARS, c)){
1496 out.s[out.l++] = '%';
1497 n_c_to_hex_base16(&out.s[out.l], c);
1498 out.l += 2;
1499 }else
1500 out.s[out.l++] = c;
1501 out.s[out.l] = '\0';
1502 f = out.s;
1503 }
1504
1505 /* Avoid overwriting of existing files */
1506 while((fp = mx_fs_open(f, "wx")) == NIL){
1507 int e;
1508
1509 if((e = su_err_no()) != su_ERR_EXIST){
1510 n_err(_("Cannot open %s: %s\n"),
1511 n_shexp_quote_cp(f, FAL0), su_err_doc(e));
1512 break;
1513 }
1514
1515 if(ip->m_partstring != NULL)
1516 f = savecatsep(f, '#', ip->m_partstring);
1517 else
1518 f = savecat(f, "#.");
1519 }
1520 }
1521 jleave:
1522 NYD_OU;
1523 return fp;
1524 }
1525
1526 static boole
a_send_pipecpy(FILE * pipebuf,FILE * outbuf,FILE * origobuf,struct quoteflt * qf,u64 * stats)1527 a_send_pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
1528 struct quoteflt *qf, u64 *stats){
1529 sz all_sz, i;
1530 uz linesize, linelen, cnt;
1531 char *line;
1532 boole rv;
1533 NYD_IN;
1534
1535 rv = TRU1;
1536 mx_fs_linepool_aquire(&line, &linesize);
1537 quoteflt_reset(qf, outbuf);
1538
1539 fflush_rewind(pipebuf);
1540 cnt = S(uz,fsize(pipebuf));
1541 all_sz = 0;
1542 while(fgetline(&line, &linesize, &cnt, &linelen, pipebuf, FAL0) != NIL){
1543 if((i = quoteflt_push(qf, line, linelen)) == -1){
1544 rv = FAL0;
1545 break;
1546 }
1547 all_sz += i;
1548 }
1549 if((i = quoteflt_flush(qf)) != -1){
1550 all_sz += i;
1551 if(all_sz > 0 && outbuf == origobuf && stats != NIL)
1552 *stats += all_sz;
1553 }else
1554 rv = FAL0;
1555
1556 mx_fs_linepool_release(line, linesize);
1557
1558 if(ferror(pipebuf))
1559 rv = FAL0;
1560 if(!mx_fs_close(pipebuf))
1561 rv = FAL0;
1562
1563 NYD_OU;
1564 return rv;
1565 }
1566
1567 static void
statusput(const struct message * mp,FILE * obuf,struct quoteflt * qf,u64 * stats)1568 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1569 u64 *stats)
1570 {
1571 char statout[3], *cp = statout;
1572 NYD_IN;
1573
1574 if (mp->m_flag & MREAD)
1575 *cp++ = 'R';
1576 if (!(mp->m_flag & MNEW))
1577 *cp++ = 'O';
1578 *cp = 0;
1579 if (statout[0]) {
1580 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1581 (qf->qf_bypass ? NULL : qf->qf_pfix), statout);
1582 if (i > 0 && stats != NULL)
1583 *stats += i;
1584 }
1585 NYD_OU;
1586 }
1587
1588 static void
xstatusput(const struct message * mp,FILE * obuf,struct quoteflt * qf,u64 * stats)1589 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1590 u64 *stats)
1591 {
1592 char xstatout[4];
1593 char *xp = xstatout;
1594 NYD_IN;
1595
1596 if (mp->m_flag & MFLAGGED)
1597 *xp++ = 'F';
1598 if (mp->m_flag & MANSWERED)
1599 *xp++ = 'A';
1600 if (mp->m_flag & MDRAFTED)
1601 *xp++ = 'T';
1602 *xp = 0;
1603 if (xstatout[0]) {
1604 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1605 (qf->qf_bypass ? NULL : qf->qf_pfix), xstatout);
1606 if (i > 0 && stats != NULL)
1607 *stats += i;
1608 }
1609 NYD_OU;
1610 }
1611
1612 static void
put_from_(FILE * fp,struct mimepart * ip,u64 * stats)1613 put_from_(FILE *fp, struct mimepart *ip, u64 *stats)
1614 {
1615 char const *froma, *date, *nl;
1616 int i;
1617 NYD_IN;
1618
1619 if (ip != NULL && ip->m_from != NULL) {
1620 froma = ip->m_from;
1621 date = n_time_ctime(ip->m_time, NULL);
1622 nl = "\n";
1623 } else {
1624 froma = ok_vlook(LOGNAME);
1625 date = time_current.tc_ctime;
1626 nl = n_empty;
1627 }
1628
1629 mx_COLOUR(
1630 if(mx_COLOUR_IS_ACTIVE())
1631 mx_colour_put(mx_COLOUR_ID_VIEW_FROM_, NULL);
1632 )
1633 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1634 mx_COLOUR(
1635 if(mx_COLOUR_IS_ACTIVE())
1636 mx_colour_reset();
1637 )
1638 if (i > 0 && stats != NULL)
1639 *stats += i;
1640 NYD_OU;
1641 }
1642
1643 FL int
sendmp(struct message * mp,FILE * obuf,struct n_ignore const * doitp,char const * prefix,enum sendaction action,u64 * stats)1644 sendmp(struct message *mp, FILE *obuf, struct n_ignore const *doitp,
1645 char const *prefix, enum sendaction action, u64 *stats)
1646 {
1647 struct n_sigman linedat_protect;
1648 struct quoteflt qf;
1649 boole anyoutput;
1650 FILE *ibuf;
1651 enum mime_parse_flags mpf;
1652 struct mimepart *ip;
1653 uz linesize, cnt, size, i;
1654 char *linedat;
1655 int rv, c;
1656 NYD_IN;
1657
1658 time_current_update(&time_current, TRU1);
1659 rv = -1;
1660 linedat = NULL;
1661 linesize = 0;
1662 quoteflt_init(&qf, prefix, (prefix == NULL));
1663
1664 n_SIGMAN_ENTER_SWITCH(&linedat_protect, n_SIGMAN_ALL){
1665 case 0:
1666 break;
1667 default:
1668 goto jleave;
1669 }
1670
1671 if (mp == dot && action != SEND_TOSRCH)
1672 n_pstate |= n_PS_DID_PRINT_DOT;
1673 if (stats != NULL)
1674 *stats = 0;
1675
1676 /* First line is the From_ line, so no headers there to worry about */
1677 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1678 goto jleave;
1679
1680 cnt = mp->m_size;
1681 size = 0;
1682 {
1683 boole nozap;
1684 char const *cpre = n_empty, *csuf = n_empty;
1685
1686 #ifdef mx_HAVE_COLOUR
1687 if(mx_COLOUR_IS_ACTIVE()){
1688 struct mx_colour_pen *cpen;
1689 struct str const *s;
1690
1691 cpen = mx_colour_pen_create(mx_COLOUR_ID_VIEW_FROM_,NULL);
1692 if((s = mx_colour_pen_to_str(cpen)) != NIL){
1693 cpre = s->s;
1694 s = mx_colour_reset_to_str();
1695 if(s != NIL)
1696 csuf = s->s;
1697 }
1698 }
1699 #endif
1700
1701 nozap = (doitp != n_IGNORE_ALL && doitp != n_IGNORE_FWD &&
1702 action != SEND_RFC822 &&
1703 !n_ignore_is_ign(doitp, "from_", sizeof("from_") -1));
1704 if (mp->m_flag & (MNOFROM | MBADFROM_)) {
1705 if (nozap)
1706 size = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1707 cpre, (int)qf.qf_pfix_len,
1708 (qf.qf_bypass ? n_empty : qf.qf_pfix), fakefrom(mp),
1709 n_time_ctime(mp->m_time, NULL), csuf);
1710 } else if (nozap) {
1711 if (!qf.qf_bypass) {
1712 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1713 if (i != qf.qf_pfix_len)
1714 goto jleave;
1715 size += i;
1716 }
1717 #ifdef mx_HAVE_COLOUR
1718 if(*cpre != '\0'){
1719 fputs(cpre, obuf);
1720 cpre = (char const*)0x1;
1721 }
1722 #endif
1723
1724 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1725 #ifdef mx_HAVE_COLOUR
1726 if(c == '\n' && *csuf != '\0'){
1727 cpre = (char const*)0x1;
1728 fputs(csuf, obuf);
1729 }
1730 #endif
1731 putc(c, obuf);
1732 ++size;
1733 --cnt;
1734 if (c == '\n')
1735 break;
1736 }
1737
1738 #ifdef mx_HAVE_COLOUR
1739 if(*csuf != '\0' && cpre != (char const*)0x1 && *cpre != '\0')
1740 fputs(csuf, obuf);
1741 #endif
1742 } else {
1743 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1744 --cnt;
1745 if (c == '\n')
1746 break;
1747 }
1748 }
1749 }
1750 if (size > 0 && stats != NULL)
1751 *stats += size;
1752
1753 mpf = MIME_PARSE_NONE;
1754 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1755 mpf |= MIME_PARSE_PARTS | MIME_PARSE_DECRYPT;
1756 if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
1757 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
1758 mpf |= MIME_PARSE_FOR_USER_CONTEXT;
1759 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1760 goto jleave;
1761
1762 anyoutput = FAL0;
1763 rv = sendpart(mp, ip, obuf, doitp, &qf, action, &linedat, &linesize,
1764 stats, 0, &anyoutput);
1765
1766 n_sigman_cleanup_ping(&linedat_protect);
1767 jleave:
1768 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1769 quoteflt_destroy(&qf);
1770 if(linedat != NULL)
1771 n_free(linedat);
1772 NYD_OU;
1773 n_sigman_leave(&linedat_protect, n_SIGMAN_VIPSIGS_NTTYOUT);
1774 return rv;
1775 }
1776
1777 #include "su/code-ou.h"
1778 /* s-it-mode */
1779