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 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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
40 #ifndef HAVE_AMALGAMATION
44 /* We use calloc() for struct attachment */
47 /* Fill in some attachment fields; don't be interactive if number==0n */
48 static struct attachment
* _fill_in(struct attachment
*ap
,
49 char const *file
, ui32_t number
);
51 /* Ask the user to edit file names and other data for the given attachment */
52 static struct attachment
* _read_attachment_data(struct attachment
*ap
,
55 /* Try to create temporary charset converted version */
57 static int _attach_iconv(struct attachment
*ap
);
60 static struct attachment
*
61 _fill_in(struct attachment
*ap
, char const *file
, ui32_t number
)
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_classify_content_type_by_fileext(file
);
77 if (number
> 0 && ok_blook(attachment_ask_content_type
)) {
78 snprintf(prefix
, sizeof prefix
, "#%u\tContent-Type: ", number
);
79 ap
->a_content_type
= readstr_input(prefix
, ap
->a_content_type
);
82 if (number
> 0 && ok_blook(attachment_ask_content_disposition
)) {
83 snprintf(prefix
, sizeof prefix
, "#%u\tContent-Disposition: ", number
);
84 if ((ap
->a_content_disposition
= readstr_input(prefix
,
85 ap
->a_content_disposition
)) == NULL
)
89 ap
->a_content_disposition
= "attachment";
91 if (number
> 0 && ok_blook(attachment_ask_content_id
)) {
92 snprintf(prefix
, sizeof prefix
, "#%u\tContent-ID: ", number
);
93 ap
->a_content_id
= readstr_input(prefix
, ap
->a_content_id
);
95 ap
->a_content_id
= NULL
;
97 if (number
> 0 && ok_blook(attachment_ask_content_description
)) {
98 snprintf(prefix
, sizeof prefix
, "#%u\tContent-Description: ", number
);
99 ap
->a_content_description
= readstr_input(prefix
,
100 ap
->a_content_description
);
106 static sigjmp_buf __atticonv_jmp
; /* TODO someday, we won't need it no more */
107 static int __atticonv_sig
; /* TODO someday, we won't need it no more */
109 __atticonv_onsig(int sig
) /* TODO someday, we won't need it no more */
111 NYD_X
; /* Signal handler */
112 __atticonv_sig
= sig
;
113 siglongjmp(__atticonv_jmp
, 1);
116 static struct attachment
*
117 _read_attachment_data(struct attachment
* volatile ap
, ui32_t number
)
119 sighandler_type
volatile ohdl
;
121 char const *cslc
= NULL
/*cc uninit?*/, *cp
, *defcs
;
124 hold_sigs(); /* TODO until we have signal manager (see TODO) */
125 ohdl
= safe_signal(SIGINT
, SIG_IGN
);
127 if (sigsetjmp(__atticonv_jmp
, 1)) {
131 safe_signal(SIGINT
, &__atticonv_onsig
);
134 ap
= csalloc(1, sizeof *ap
);
135 else if (ap
->a_msgno
) {
136 char *ecp
= salloc(24);
137 snprintf(ecp
, 24, "#%" PRIu32
, (ui32_t
)ap
->a_msgno
);
139 ap
->a_content_description
= NULL
;
141 } else if (ap
->a_conv
== AC_TMPFILE
) {
143 DBG( ap
->a_tmpf
= NULL
; )
144 ap
->a_conv
= AC_DEFAULT
;
147 rele_sigs(); /* TODO until we have signal manager (see TODO) */
148 snprintf(prefix
, sizeof prefix
, _("#%" PRIu32
"\tfilename: "), number
);
150 if ((ap
->a_name
= readstr_input(prefix
, ap
->a_name
)) == NULL
) {
155 /* May be a message number (XXX add "AC_MSG", use that not .a_msgno) */
156 if (ap
->a_name
[0] == '#') {
158 int msgno
= (int)strtol(ap
->a_name
+ 1, &ecp
, 10);
159 if (msgno
> 0 && msgno
<= msgCount
&& *ecp
== '\0') {
161 ap
->a_content_type
= ap
->a_content_disposition
=
162 ap
->a_content_id
= NULL
;
163 ap
->a_content_description
= _("Attached message content");
164 if (options
& OPT_INTERACTIVE
)
165 printf(_("~@: added message #%" PRIu32
"\n"), (ui32_t
)msgno
);
170 if ((cp
= file_expand(ap
->a_name
)) != NULL
&& !access(cp
, R_OK
)) {
177 ap
= _fill_in(ap
, cp
, number
);
180 * Character set of attachments: enum attach_conv
182 cslc
= charset_get_lc();
184 if (!(options
& OPT_INTERACTIVE
))
186 if ((cp
= ap
->a_content_type
) != NULL
&& ascncasecmp(cp
, "text/", 5) != 0 &&
187 !getapproval(_("Filename doesn't indicate text content - "
188 "edit charsets nonetheless? "), TRU1
)) {
189 ap
->a_conv
= AC_DEFAULT
;
194 charset_iter_reset(NULL
);
197 snprintf(prefix
, sizeof prefix
, _("#%" PRIu32
"\tinput charset: "),
199 if ((defcs
= ap
->a_input_charset
) == NULL
)
201 cp
= ap
->a_input_charset
= readstr_input(prefix
, defcs
);
203 if (!(options
& OPT_INTERACTIVE
)) {
205 ap
->a_conv
= (cp
!= NULL
) ? AC_FIX_INCS
: AC_DEFAULT
;
210 snprintf(prefix
, sizeof prefix
,
211 _("#%" PRIu32
"\toutput (send) charset: "), number
);
212 if ((defcs
= ap
->a_charset
) == NULL
)
213 defcs
= charset_iter();
214 defcs
= ap
->a_charset
= readstr_input(prefix
, defcs
);
216 /* Input, no output -> input=as given, output=no conversion at all */
217 if (cp
!= NULL
&& defcs
== NULL
) {
218 ap
->a_conv
= AC_FIX_INCS
;
222 /* No input, no output -> input=*ttycharset*, output=iterator */
223 if (cp
== NULL
&& defcs
== NULL
) {
224 ap
->a_conv
= AC_DEFAULT
;
225 ap
->a_input_charset
= cslc
;
226 ap
->a_charset
= charset_iter();
227 assert(charset_iter_is_valid());
230 /* No input, output -> input=*ttycharset*, output=as given */
231 else if (cp
== NULL
&& defcs
!= NULL
) {
232 ap
->a_conv
= AC_FIX_OUTCS
;
233 ap
->a_input_charset
= cslc
;
235 /* Input, output -> try conversion from input=as given to output=as given */
237 printf(_("Trying conversion from %s to %s\n"), ap
->a_input_charset
,
239 if (_attach_iconv(ap
))
240 ap
->a_conv
= AC_TMPFILE
;
242 ap
->a_conv
= AC_DEFAULT
;
243 ap
->a_input_charset
= cp
;
244 ap
->a_charset
= defcs
;
245 if (!charset_iter_is_valid()) {
246 printf(_("*sendcharsets* and *charset-8bit* iteration "
247 "exhausted, restarting\n"));
254 if (options
& OPT_INTERACTIVE
)
255 printf(_("~@: added attachment \"%s\"\n"), ap
->a_name
);
257 safe_signal(SIGINT
, ohdl
);/* TODO until we have signal manager (see TODO) */
258 if (__atticonv_sig
!= 0) {
261 sigaddset(&nset
, SIGINT
);
262 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
271 _attach_iconv(struct attachment
*ap
)
273 struct str oul
= {NULL
, 0}, inl
= {NULL
, 0};
274 FILE *fo
= NULL
, *fi
= NULL
;
279 hold_sigs(); /* TODO until we have signal manager (see TODO) */
281 icp
= n_iconv_open(ap
->a_charset
, ap
->a_input_charset
);
282 if (icp
== (iconv_t
)-1) {
286 perror("iconv_open");
290 if ((fi
= Fopen(ap
->a_name
, "r")) == NULL
) {
296 if ((fo
= Ftmp(NULL
, "atic", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
298 perror(_("temporary mail file"));
303 if (fgetline(&inl
.s
, &lbsize
, &cnt
, &inl
.l
, fi
, 0) == NULL
) {
306 perror(_("I/O read error occurred"));
310 if (n_iconv_str(icp
, &oul
, &inl
, NULL
, FAL0
) != 0)
312 if ((inl
.l
= fwrite(oul
.s
, sizeof *oul
.s
, oul
.l
, fo
)) != oul
.l
) {
313 perror(_("I/O write error occurred"));
327 if (icp
!= (iconv_t
)-1)
330 rele_sigs(); /* TODO until we have signal manager (see TODO) */
335 fprintf(stderr
, _("Cannot convert from %s to %s\n"),
336 ap
->a_input_charset
, ap
->a_charset
);
343 #endif /* HAVE_ICONV */
345 /* TODO add_attachment(): also work with **aphead, not *aphead ... */
346 FL
struct attachment
*
347 add_attachment(struct attachment
*aphead
, char *file
, struct attachment
**newap
)
349 struct attachment
*nap
= NULL
, *ap
;
352 if ((file
= file_expand(file
)) == NULL
)
354 if (access(file
, R_OK
) != 0)
357 nap
= _fill_in(csalloc(1, sizeof *nap
), file
, 0);
360 if (aphead
!= NULL
) {
361 for (ap
= aphead
; ap
->a_flink
!= NULL
; ap
= ap
->a_flink
)
376 append_attachments(struct attachment
**aphead
, char *names
)
379 struct attachment
*xaph
, *nap
;
382 while ((cp
= n_strsep(&names
, ',', 1)) != NULL
) {
383 if ((xaph
= add_attachment(*aphead
, cp
, &nap
)) != NULL
) {
385 if (options
& OPT_INTERACTIVE
)
386 printf(_("~@: added attachment \"%s\"\n"), nap
->a_name
);
394 edit_attachments(struct attachment
**aphead
)
396 struct attachment
*ap
, *fap
, *bap
;
400 /* Modify already present ones? */
401 for (ap
= *aphead
; ap
!= NULL
; ap
= fap
) {
402 if (_read_attachment_data(ap
, attno
) != NULL
) {
408 if ((bap
= ap
->a_blink
) != NULL
)
414 /*else*//* TODO until we have signal manager (see TODO) */
415 if (__atticonv_sig
!= 0)
422 if ((bap
= *aphead
) != NULL
)
423 while (bap
->a_flink
!= NULL
)
425 while ((fap
= _read_attachment_data(NULL
, attno
)) != NULL
) {
435 if (__atticonv_sig
!= 0) /* TODO until we have signal manager (see TODO) */