1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Handling of attachments.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE attachments
38 #ifndef HAVE_AMALGAMATION
42 /* We use calloc() for struct attachment */
43 n_CTAV(AC_DEFAULT
== 0);
45 /* Fill in some attachment fields; don't be interactive if number==0 */
46 static struct attachment
* _fill_in(enum n_lexinput_flags lif
,
47 struct attachment
*ap
, char const *file
,
50 /* Ask the user to edit file names and other data for the given attachment */
51 static struct attachment
* _read_attachment_data(enum n_lexinput_flags lif
,
52 struct attachment
*ap
, ui32_t number
);
54 /* Try to create temporary charset converted version */
56 static int _attach_iconv(struct attachment
*ap
);
59 static struct attachment
*
60 _fill_in(enum n_lexinput_flags lif
, struct attachment
*ap
, char const *file
,
63 /* XXX The "attachment-ask-content-*" variables are left undocumented
64 * since "they are for RFC connoisseurs only" ;) */
68 ap
->a_input_charset
= ap
->a_charset
= NULL
;
71 if ((file
= strrchr(file
, '/')) != NULL
)
76 ap
->a_content_type
= mime_type_classify_filename(file
);
77 if (number
> 0 && ok_blook(attachment_ask_content_type
)) {
78 snprintf(prefix
, sizeof prefix
, "#%-5" PRIu32
" Content-Type: ", number
);
79 ap
->a_content_type
= n_lex_input_cp(lif
, prefix
, ap
->a_content_type
);
82 if (number
> 0 && ok_blook(attachment_ask_content_disposition
)) {
83 snprintf(prefix
, sizeof prefix
, "#%-5" PRIu32
" Content-Disposition: ",
85 if ((ap
->a_content_disposition
= n_lex_input_cp(lif
, prefix
,
86 ap
->a_content_disposition
)) == NULL
)
90 ap
->a_content_disposition
= "attachment";
92 if (number
> 0 && ok_blook(attachment_ask_content_id
)) {
93 snprintf(prefix
, sizeof prefix
, "#%-5" PRIu32
" Content-ID: ", number
);
94 ap
->a_content_id
= n_lex_input_cp(lif
, prefix
, ap
->a_content_id
);
96 ap
->a_content_id
= NULL
;
98 if (number
> 0 && ok_blook(attachment_ask_content_description
)) {
99 snprintf(prefix
, sizeof prefix
, "#%-5" PRIu32
" Content-Description: ",
101 ap
->a_content_description
= n_lex_input_cp(lif
, prefix
,
102 ap
->a_content_description
);
108 static sigjmp_buf __atticonv_jmp
; /* TODO oneday, we won't need it no more */
109 static int volatile __atticonv_sig
; /* TODO oneday, we won't need it no more */
111 __atticonv_onsig(int sig
) /* TODO someday, we won't need it no more */
113 NYD_X
; /* Signal handler */
114 __atticonv_sig
= sig
;
115 siglongjmp(__atticonv_jmp
, 1);
118 static struct attachment
*
119 _read_attachment_data(enum n_lexinput_flags lif
,
120 struct attachment
* volatile ap
, ui32_t number
)
122 sighandler_type
volatile ohdl
;
125 struct n_string shou
, *shoup
;
126 char const *cslc
, *cp
, *defcs
;
129 n_UNINIT(cslc
, NULL
);
130 shoup
= n_string_creat(&shou
);
132 hold_sigs(); /* TODO until we have signal manager (see TODO) */
133 ohdl
= safe_signal(SIGINT
, SIG_IGN
);
135 if (sigsetjmp(__atticonv_jmp
, 1)) {
139 safe_signal(SIGINT
, &__atticonv_onsig
);
142 ap
= csalloc(1, sizeof *ap
);
143 else if (ap
->a_msgno
) {
144 char *ecp
= salloc(24);
145 snprintf(ecp
, 24, "#%" PRIu32
, (ui32_t
)ap
->a_msgno
);
147 ap
->a_content_description
= NULL
;
149 } else if (ap
->a_conv
== AC_TMPFILE
) {
151 DBG( ap
->a_tmpf
= NULL
; )
152 ap
->a_conv
= AC_DEFAULT
;
155 rele_sigs(); /* TODO until we have signal manager (see TODO) */
156 snprintf(prefix
, sizeof prefix
, _("#%-5" PRIu32
" filename: "), number
);
158 if ((cp
= ap
->a_name
) != NULL
)
159 cp
= n_shexp_quote_cp(cp
, FAL0
);
160 if ((cp
= n_lex_input_cp(lif
, prefix
, cp
)) == NULL
) {
166 shin
.s
= n_UNCONST(cp
);
168 if((n_shexp_parse_token(shoup
, &shin
, n_SHEXP_PARSE_TRUNC
|
169 n_SHEXP_PARSE_TRIMSPACE
| n_SHEXP_PARSE_LOG
|
170 n_SHEXP_PARSE_IGNORE_EMPTY
) &
171 (n_SHEXP_STATE_STOP
| n_SHEXP_STATE_OUTPUT
|
172 n_SHEXP_STATE_ERR_MASK
)
173 ) != (n_SHEXP_STATE_STOP
| n_SHEXP_STATE_OUTPUT
))
175 cp
= n_string_cp(shoup
);
177 /* May be a message number (XXX add "AC_MSG", use that not .a_msgno) */
180 int msgno
= (int)strtol(cp
+ 1, &ecp
, 10);
182 if (msgno
> 0 && msgno
<= msgCount
&& *ecp
== '\0') {
185 ap
->a_content_type
= ap
->a_content_disposition
=
186 ap
->a_content_id
= NULL
;
187 ap
->a_content_description
= _("Attached message content");
188 if (options
& OPT_INTERACTIVE
)
189 printf(_("~@: added message #%" PRIu32
"\n"), (ui32_t
)msgno
);
194 if ((cp
= fexpand(cp
, FEXP_LOCAL
| FEXP_NVAR
)) != NULL
&&
202 ap
= _fill_in(lif
, ap
, cp
, number
);
205 * Character set of attachments: enum attach_conv
207 cslc
= charset_get_lc();
209 if (!(options
& OPT_INTERACTIVE
))
211 if ((cp
= ap
->a_content_type
) != NULL
&& ascncasecmp(cp
, "text/", 5) != 0 &&
212 !getapproval(_("File doesn't indicate text content, "
213 "edit character sets"), TRU1
)) {
214 ap
->a_conv
= AC_DEFAULT
;
219 charset_iter_reset(NULL
);
222 snprintf(prefix
, sizeof prefix
, _("#%-5" PRIu32
" input charset: "),
224 if ((defcs
= ap
->a_input_charset
) == NULL
)
226 cp
= ap
->a_input_charset
= n_lex_input_cp(lif
, prefix
, defcs
);
228 if (!(options
& OPT_INTERACTIVE
)) {
230 ap
->a_conv
= (cp
!= NULL
) ? AC_FIX_INCS
: AC_DEFAULT
;
235 snprintf(prefix
, sizeof prefix
,
236 _("#%-5" PRIu32
" output (send) charset: "), number
);
237 if ((defcs
= ap
->a_charset
) == NULL
)
238 defcs
= charset_iter();
239 defcs
= ap
->a_charset
= n_lex_input_cp(lif
, prefix
, defcs
);
241 /* Input, no output -> input=as given, output=no conversion at all */
242 if (cp
!= NULL
&& defcs
== NULL
) {
243 ap
->a_conv
= AC_FIX_INCS
;
247 /* No input, no output -> input=*ttycharset*, output=iterator */
248 if (cp
== NULL
&& defcs
== NULL
) {
249 ap
->a_conv
= AC_DEFAULT
;
250 ap
->a_input_charset
= cslc
;
251 ap
->a_charset
= charset_iter();
252 assert(charset_iter_is_valid());
255 /* No input, output -> input=*ttycharset*, output=as given */
256 else if (cp
== NULL
&& defcs
!= NULL
) {
257 ap
->a_conv
= AC_FIX_OUTCS
;
258 ap
->a_input_charset
= cslc
;
260 /* Input, output -> try conversion from input=as given to output=as given */
262 printf(_("Trying conversion from %s to %s\n"), ap
->a_input_charset
,
264 if (_attach_iconv(ap
))
265 ap
->a_conv
= AC_TMPFILE
;
267 ap
->a_conv
= AC_DEFAULT
;
268 ap
->a_input_charset
= cp
;
269 ap
->a_charset
= defcs
;
270 if (!charset_iter_is_valid()) {
271 printf(_("*sendcharsets* and *charset-8bit* iteration "
272 "exhausted, restarting\n"));
279 if (options
& OPT_INTERACTIVE
)
280 printf(_("Added attachment %s\n"), n_shexp_quote_cp(ap
->a_name
, FAL0
));
284 safe_signal(SIGINT
, ohdl
);/* TODO until we have signal manager (see TODO) */
285 if (__atticonv_sig
!= 0) {
288 sigaddset(&nset
, SIGINT
);
289 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
298 _attach_iconv(struct attachment
*ap
)
300 struct str oul
= {NULL
, 0}, inl
= {NULL
, 0};
301 FILE *fo
= NULL
, *fi
= NULL
;
306 hold_sigs(); /* TODO until we have signal manager (see TODO) */
308 icp
= n_iconv_open(ap
->a_charset
, ap
->a_input_charset
);
309 if (icp
== (iconv_t
)-1) {
313 n_perr(_("iconv_open"), 0);
317 if ((fi
= Fopen(ap
->a_name
, "r")) == NULL
) {
318 n_perr(ap
->a_name
, 0);
321 cnt
= (size_t)fsize(fi
);
323 if ((fo
= Ftmp(NULL
, "aticonv", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
325 n_perr(_("temporary mail file"), 0);
330 if (fgetline(&inl
.s
, &lbsize
, &cnt
, &inl
.l
, fi
, 0) == NULL
) {
333 n_perr(_("I/O read error occurred"), 0);
337 if (n_iconv_str(icp
, n_ICONV_IGN_NOREVERSE
, &oul
, &inl
, NULL
) != 0)
339 if ((inl
.l
= fwrite(oul
.s
, sizeof *oul
.s
, oul
.l
, fo
)) != oul
.l
) {
340 n_perr(_("I/O write error occurred"), 0);
354 if (icp
!= (iconv_t
)-1)
357 rele_sigs(); /* TODO until we have signal manager (see TODO) */
362 n_err(_("Cannot convert from %s to %s\n"),
363 ap
->a_input_charset
, ap
->a_charset
);
370 #endif /* HAVE_ICONV */
372 /* TODO add_attachment(): also work with **aphead, not *aphead ... */
373 FL
struct attachment
*
374 add_attachment(enum n_lexinput_flags lif
, struct attachment
*aphead
,
375 char *file
, struct attachment
**newap
)
377 struct attachment
*nap
= NULL
, *ap
;
380 if ((file
= fexpand(file
, FEXP_LOCAL
| FEXP_NVAR
)) == NULL
)
382 if (access(file
, R_OK
) != 0)
385 nap
= _fill_in(lif
, csalloc(1, sizeof *nap
), file
, 0);
388 if (aphead
!= NULL
) {
389 for (ap
= aphead
; ap
->a_flink
!= NULL
; ap
= ap
->a_flink
)
404 append_attachments(enum n_lexinput_flags lif
, struct attachment
**aphead
,
407 struct n_string shou
, *shoup
;
410 shoup
= n_string_creat_auto(&shou
);
412 for(shin
.s
= names
, shin
.l
= UIZ_MAX
;;){
413 struct attachment
*xaph
, *nap
;
414 enum n_shexp_state shs
;
416 shs
= n_shexp_parse_token(shoup
, &shin
, n_SHEXP_PARSE_TRUNC
|
417 n_SHEXP_PARSE_TRIMSPACE
| n_SHEXP_PARSE_LOG
|
418 n_SHEXP_PARSE_IFS_ADD_COMMA
| n_SHEXP_PARSE_IGNORE_EMPTY
);
419 if(shs
& n_SHEXP_STATE_ERR_MASK
)
422 if(shs
& n_SHEXP_STATE_OUTPUT
){
423 if((xaph
= add_attachment(lif
, *aphead
, n_string_cp(shoup
), &nap
)
426 if(options
& OPT_INTERACTIVE
)
427 printf(_("Added attachment %s\n"),
428 n_shexp_quote_cp(nap
->a_name
, FAL0
));
430 n_perr(n_string_cp(shoup
), 0);
433 if(shs
& n_SHEXP_STATE_STOP
)
441 edit_attachments(enum n_lexinput_flags lif
, struct attachment
**aphead
)
443 struct attachment
*ap
, *fap
, *bap
;
448 static bool_t note_given
; /* v15-compat: DROP */
452 printf(_("# Only supports sh(1)ell-style quoting for file names\n"));
456 /* Modify already present ones? */
457 for (ap
= *aphead
; ap
!= NULL
; ap
= fap
) {
458 if (_read_attachment_data(lif
, ap
, attno
) != NULL
) {
464 if ((bap
= ap
->a_blink
) != NULL
)
470 /*else*//* TODO until we have signal manager (see TODO) */
471 if (__atticonv_sig
!= 0)
478 if ((bap
= *aphead
) != NULL
)
479 while (bap
->a_flink
!= NULL
)
481 while ((fap
= _read_attachment_data(lif
, NULL
, attno
)) != NULL
) {
491 if (__atticonv_sig
!= 0) /* TODO until we have signal manager (see TODO) */