Fix false resource release <-> double free (Bob Tennent)..
[s-mailx.git] / attachments.c
blob3e09c316a240f738aed2d590ca5448c2829834cb
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 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
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
13 * are met:
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
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE attachments
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 /* We use calloc() for struct attachment */
47 CTA(AC_DEFAULT == 0);
49 /* Fill in some attachment fields; don't be interactive if number==0n */
50 static struct attachment * _fill_in(struct attachment *ap,
51 char const *file, ui32_t number);
53 /* Ask the user to edit file names and other data for the given attachment */
54 static struct attachment * _read_attachment_data(struct attachment *ap,
55 ui32_t number);
57 /* Try to create temporary charset converted version */
58 #ifdef HAVE_ICONV
59 static int _attach_iconv(struct attachment *ap);
60 #endif
62 static struct attachment *
63 _fill_in(struct attachment *ap, char const *file, ui32_t number)
65 /* XXX The "attachment-ask-content-*" variables are left undocumented
66 * since "they are for RFC connoisseurs only" ;) */
67 char prefix[80 * 2];
68 NYD_ENTER;
70 ap->a_input_charset = ap->a_charset = NULL;
72 ap->a_name = file;
73 if ((file = strrchr(file, '/')) != NULL)
74 ++file;
75 else
76 file = ap->a_name;
78 ap->a_content_type = mime_type_by_filename(file);
79 if (number > 0 && ok_blook(attachment_ask_content_type)) {
80 snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number);
81 ap->a_content_type = readstr_input(prefix, ap->a_content_type);
84 if (number > 0 && ok_blook(attachment_ask_content_disposition)) {
85 snprintf(prefix, sizeof prefix, "#%u\tContent-Disposition: ", number);
86 if ((ap->a_content_disposition = readstr_input(prefix,
87 ap->a_content_disposition)) == NULL)
88 goto jcdis;
89 } else
90 jcdis:
91 ap->a_content_disposition = "attachment";
93 if (number > 0 && ok_blook(attachment_ask_content_id)) {
94 snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number);
95 ap->a_content_id = readstr_input(prefix, ap->a_content_id);
96 } else
97 ap->a_content_id = NULL;
99 if (number > 0 && ok_blook(attachment_ask_content_description)) {
100 snprintf(prefix, sizeof prefix, "#%u\tContent-Description: ", number);
101 ap->a_content_description = readstr_input(prefix,
102 ap->a_content_description);
104 NYD_LEAVE;
105 return ap;
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 */
110 static void
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(struct attachment * volatile ap, ui32_t number)
121 sighandler_type volatile ohdl;
122 char prefix[80 * 2];
123 char const *cslc = NULL/*cc uninit?*/, *cp, *defcs;
124 NYD_ENTER;
126 hold_sigs(); /* TODO until we have signal manager (see TODO) */
127 ohdl = safe_signal(SIGINT, SIG_IGN);
128 __atticonv_sig = 0;
129 if (sigsetjmp(__atticonv_jmp, 1)) {
130 ap = NULL;
131 goto jleave;
133 safe_signal(SIGINT, &__atticonv_onsig);
135 if (ap == NULL)
136 ap = csalloc(1, sizeof *ap);
137 else if (ap->a_msgno) {
138 char *ecp = salloc(24);
139 snprintf(ecp, 24, "#%" PRIu32, (ui32_t)ap->a_msgno);
140 ap->a_msgno = 0;
141 ap->a_content_description = NULL;
142 ap->a_name = ecp;
143 } else if (ap->a_conv == AC_TMPFILE) {
144 Fclose(ap->a_tmpf);
145 DBG( ap->a_tmpf = NULL; )
146 ap->a_conv = AC_DEFAULT;
149 rele_sigs(); /* TODO until we have signal manager (see TODO) */
150 snprintf(prefix, sizeof prefix, _("#%" PRIu32 "\tfilename: "), number);
151 for (;;) {
152 if ((cp = ap->a_name) != NULL)
153 cp = fexpand_nshell_quote(cp);
154 if ((cp = readstr_input(prefix, cp)) == NULL) {
155 ap->a_name = NULL;
156 ap = NULL;
157 goto jleave;
160 /* May be a message number (XXX add "AC_MSG", use that not .a_msgno) */
161 if (cp[0] == '#') {
162 char *ecp;
163 int msgno = (int)strtol(cp + 1, &ecp, 10);
165 if (msgno > 0 && msgno <= msgCount && *ecp == '\0') {
166 ap->a_name = cp;
167 ap->a_msgno = msgno;
168 ap->a_content_type = ap->a_content_disposition =
169 ap->a_content_id = NULL;
170 ap->a_content_description = _("Attached message content");
171 if (options & OPT_INTERACTIVE)
172 printf(_("~@: added message #%" PRIu32 "\n"), (ui32_t)msgno);
173 goto jleave;
177 if ((cp = fexpand(cp, FEXP_LOCAL | FEXP_NSHELL)) != NULL &&
178 !access(cp, R_OK)) {
179 ap->a_name = cp;
180 break;
182 perror(cp);
185 ap = _fill_in(ap, cp, number);
188 * Character set of attachments: enum attach_conv
190 cslc = charset_get_lc();
191 #ifdef HAVE_ICONV
192 if (!(options & OPT_INTERACTIVE))
193 goto jcs;
194 if ((cp = ap->a_content_type) != NULL && ascncasecmp(cp, "text/", 5) != 0 &&
195 !getapproval(_("Filename doesn't indicate text content - "
196 "edit charsets nonetheless? "), TRU1)) {
197 ap->a_conv = AC_DEFAULT;
198 goto jleave;
201 jcs_restart:
202 charset_iter_reset(NULL);
203 jcs:
204 #endif
205 snprintf(prefix, sizeof prefix, _("#%" PRIu32 "\tinput charset: "),
206 number);
207 if ((defcs = ap->a_input_charset) == NULL)
208 defcs = cslc;
209 cp = ap->a_input_charset = readstr_input(prefix, defcs);
210 #ifdef HAVE_ICONV
211 if (!(options & OPT_INTERACTIVE)) {
212 #endif
213 ap->a_conv = (cp != NULL) ? AC_FIX_INCS : AC_DEFAULT;
214 #ifdef HAVE_ICONV
215 goto jleave;
218 snprintf(prefix, sizeof prefix,
219 _("#%" PRIu32 "\toutput (send) charset: "), number);
220 if ((defcs = ap->a_charset) == NULL)
221 defcs = charset_iter();
222 defcs = ap->a_charset = readstr_input(prefix, defcs);
224 /* Input, no output -> input=as given, output=no conversion at all */
225 if (cp != NULL && defcs == NULL) {
226 ap->a_conv = AC_FIX_INCS;
227 goto jdone;
230 /* No input, no output -> input=*ttycharset*, output=iterator */
231 if (cp == NULL && defcs == NULL) {
232 ap->a_conv = AC_DEFAULT;
233 ap->a_input_charset = cslc;
234 ap->a_charset = charset_iter();
235 assert(charset_iter_is_valid());
236 charset_iter_next();
238 /* No input, output -> input=*ttycharset*, output=as given */
239 else if (cp == NULL && defcs != NULL) {
240 ap->a_conv = AC_FIX_OUTCS;
241 ap->a_input_charset = cslc;
243 /* Input, output -> try conversion from input=as given to output=as given */
245 printf(_("Trying conversion from %s to %s\n"), ap->a_input_charset,
246 ap->a_charset);
247 if (_attach_iconv(ap))
248 ap->a_conv = AC_TMPFILE;
249 else {
250 ap->a_conv = AC_DEFAULT;
251 ap->a_input_charset = cp;
252 ap->a_charset = defcs;
253 if (!charset_iter_is_valid()) {
254 printf(_("*sendcharsets* and *charset-8bit* iteration "
255 "exhausted, restarting\n"));
256 goto jcs_restart;
258 goto jcs;
260 jdone:
261 #endif
262 if (options & OPT_INTERACTIVE)
263 printf(_("~@: added attachment \"%s\"\n"), ap->a_name);
264 jleave:
265 safe_signal(SIGINT, ohdl);/* TODO until we have signal manager (see TODO) */
266 if (__atticonv_sig != 0) {
267 sigset_t nset;
268 sigemptyset(&nset);
269 sigaddset(&nset, SIGINT);
270 sigprocmask(SIG_UNBLOCK, &nset, NULL);
271 /* Caller kills */
273 NYD_LEAVE;
274 return ap;
277 #ifdef HAVE_ICONV
278 static int
279 _attach_iconv(struct attachment *ap)
281 struct str oul = {NULL, 0}, inl = {NULL, 0};
282 FILE *fo = NULL, *fi = NULL;
283 size_t cnt, lbsize;
284 iconv_t icp;
285 NYD_ENTER;
287 hold_sigs(); /* TODO until we have signal manager (see TODO) */
289 icp = n_iconv_open(ap->a_charset, ap->a_input_charset);
290 if (icp == (iconv_t)-1) {
291 if (errno == EINVAL)
292 goto jeconv;
293 else
294 perror("iconv_open");
295 goto jerr;
298 if ((fi = Fopen(ap->a_name, "r")) == NULL) {
299 perror(ap->a_name);
300 goto jerr;
302 cnt = fsize(fi);
304 if ((fo = Ftmp(NULL, "atic", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
305 NULL) {
306 perror(_("temporary mail file"));
307 goto jerr;
310 for (lbsize = 0;;) {
311 if (fgetline(&inl.s, &lbsize, &cnt, &inl.l, fi, 0) == NULL) {
312 if (!cnt)
313 break;
314 perror(_("I/O read error occurred"));
315 goto jerr;
318 if (n_iconv_str(icp, &oul, &inl, NULL, FAL0) != 0)
319 goto jeconv;
320 if ((inl.l = fwrite(oul.s, sizeof *oul.s, oul.l, fo)) != oul.l) {
321 perror(_("I/O write error occurred"));
322 goto jerr;
325 fflush_rewind(fo);
327 ap->a_tmpf = fo;
328 jleave:
329 if (inl.s != NULL)
330 free(inl.s);
331 if (oul.s != NULL)
332 free(oul.s);
333 if (fi != NULL)
334 Fclose(fi);
335 if (icp != (iconv_t)-1)
336 n_iconv_close(icp);
338 rele_sigs(); /* TODO until we have signal manager (see TODO) */
339 NYD_LEAVE;
340 return (fo != NULL);
342 jeconv:
343 fprintf(stderr, _("Cannot convert from %s to %s\n"),
344 ap->a_input_charset, ap->a_charset);
345 jerr:
346 if (fo != NULL)
347 Fclose(fo);
348 fo = NULL;
349 goto jleave;
351 #endif /* HAVE_ICONV */
353 /* TODO add_attachment(): also work with **aphead, not *aphead ... */
354 FL struct attachment *
355 add_attachment(struct attachment *aphead, char *file, struct attachment **newap)
357 struct attachment *nap = NULL, *ap;
358 NYD_ENTER;
360 if ((file = fexpand(file, FEXP_LOCAL | FEXP_NSHELL)) == NULL)
361 goto jleave;
362 if (access(file, R_OK) != 0)
363 goto jleave;
365 nap = _fill_in(csalloc(1, sizeof *nap), file, 0);
366 if (newap != NULL)
367 *newap = nap;
368 if (aphead != NULL) {
369 for (ap = aphead; ap->a_flink != NULL; ap = ap->a_flink)
371 ap->a_flink = nap;
372 nap->a_blink = ap;
373 } else {
374 nap->a_blink = NULL;
375 aphead = nap;
377 nap = aphead;
378 jleave:
379 NYD_LEAVE;
380 return nap;
383 FL void
384 append_attachments(struct attachment **aphead, char *names)
386 char *cp;
387 struct attachment *xaph, *nap;
388 NYD_ENTER;
390 while ((cp = n_strsep(&names, ',', 1)) != NULL) {
391 xaph = add_attachment(*aphead, fexpand_nshell_quote(cp), &nap);
392 if (xaph != NULL) {
393 *aphead = xaph;
394 if (options & OPT_INTERACTIVE)
395 printf(_("~@: added attachment \"%s\"\n"), nap->a_name);
396 } else
397 perror(cp);
399 NYD_LEAVE;
402 FL void
403 edit_attachments(struct attachment **aphead)
405 struct attachment *ap, *fap, *bap;
406 ui32_t attno = 1;
407 NYD_ENTER;
409 printf(_("# Be aware that \"\\\" must be escaped: \"\\\\\", \"\\$HOME\"\n"));
411 /* Modify already present ones? */
412 for (ap = *aphead; ap != NULL; ap = fap) {
413 if (_read_attachment_data(ap, attno) != NULL) {
414 fap = ap->a_flink;
415 ++attno;
416 continue;
418 fap = ap->a_flink;
419 if ((bap = ap->a_blink) != NULL)
420 bap->a_flink = fap;
421 else
422 *aphead = fap;
423 if (fap != NULL)
424 fap->a_blink = bap;
425 /*else*//* TODO until we have signal manager (see TODO) */
426 if (__atticonv_sig != 0)
427 n_raise(SIGINT);
428 if (fap == NULL)
429 goto jleave;
432 /* Add some more? */
433 if ((bap = *aphead) != NULL)
434 while (bap->a_flink != NULL)
435 bap = bap->a_flink;
436 while ((fap = _read_attachment_data(NULL, attno)) != NULL) {
437 if (bap != NULL)
438 bap->a_flink = fap;
439 else
440 *aphead = fap;
441 fap->a_blink = bap;
442 fap->a_flink = NULL;
443 bap = fap;
444 ++attno;
446 if (__atticonv_sig != 0) /* TODO until we have signal manager (see TODO) */
447 n_raise(SIGINT);
448 jleave:
449 NYD_LEAVE;
452 /* s-it-mode */