Enable WANT_QUOTE_FOLD, *_ASSERT -> *_DEBUG, more cleanup
[s-mailx.git] / attachments.c
blobc4931f16c870a4b7a9765b856cb0a1abb235b73f
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 - 2013 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.
40 #include "nail.h"
42 /* We use calloc() for struct attachment */
43 CTA(AC_DEFAULT == 0);
45 /* Fill in some attachment fields; don't be interactive if number==0n */
46 static struct attachment * _fill_in(struct attachment *ap,
47 char const *file, ui_it number);
49 /* Ask the user to edit file names and other data for the given attachment */
50 static struct attachment * _read_attachment_data(struct attachment *ap,
51 ui_it number);
53 /* Try to create temporary charset converted version */
54 #ifdef HAVE_ICONV
55 static int _attach_iconv(struct attachment *ap);
56 #endif
58 static struct attachment *
59 _fill_in(struct attachment *ap, char const *file, ui_it number)
62 * XXX The "attachment-ask-content-*" variables are left undocumented
63 * since they are for RFC connoisseurs only.
65 char prefix[80 * 2];
67 ap->a_name = file;
68 if ((file = strrchr(file, '/')) != NULL)
69 ++file;
70 else
71 file = ap->a_name;
73 ap->a_content_type = mime_classify_content_type_by_fileext(file);
74 if (number > 0 && value("attachment-ask-content-type")) {
75 snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number);
76 ap->a_content_type = readstr_input(prefix, ap->a_content_type);
79 if (number > 0 && value("attachment-ask-content-disposition")) {
80 snprintf(prefix, sizeof prefix,
81 "#%u\tContent-Disposition: ", number);
82 ap->a_content_disposition = readstr_input(prefix,
83 ap->a_content_disposition);
85 if (ap->a_content_disposition == NULL)
86 ap->a_content_disposition = "attachment";
88 if (number > 0 && value("attachment-ask-content-id")) {
89 snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number);
90 ap->a_content_id = readstr_input(prefix, ap->a_content_id);
93 if (number > 0 && value("attachment-ask-content-description")) {
94 snprintf(prefix, sizeof prefix,
95 "#%u\tContent-Description: ", number);
96 ap->a_content_description = readstr_input(prefix,
97 ap->a_content_description);
99 return ap;
102 static struct attachment *
103 _read_attachment_data(struct attachment *ap, ui_it number)
105 char prefix[80 * 2];
106 char const *cslc = NULL/*cc uninit?*/, *cp, *defcs;
108 if (ap == NULL)
109 ap = csalloc(1, sizeof *ap);
110 else if (ap->a_msgno) {
111 char *ecp = salloc(16);
112 snprintf(ecp, 16, "#%u", (ui_it)ap->a_msgno);
113 ap->a_msgno = 0;
114 ap->a_name = ecp;
115 } else if (ap->a_conv == AC_TMPFILE) {
116 Fclose(ap->a_tmpf);
117 ap->a_conv = AC_DEFAULT;
120 snprintf(prefix, sizeof prefix, tr(50, "#%u\tfilename: "), number);
121 for (;;) {
122 if ((ap->a_name = readstr_input(prefix, ap->a_name)) == NULL) {
123 ap = NULL;
124 goto jleave;
126 /* May be a message number */
127 if (ap->a_name[0] == '#') {
128 char *ecp;
129 int msgno = (int)strtol(ap->a_name + 1, &ecp, 10);
130 if (msgno > 0 && msgno <= msgCount && *ecp == '\0') {
131 ap->a_msgno = msgno;
132 ap->a_content_description =
133 tr(513, "Attached message content");
134 if (options & OPT_INTERACTIVE)
135 printf(tr(2, "~@: added message #%u\n"),
136 (ui_it)msgno);
137 goto jleave;
140 if ((cp = file_expand(ap->a_name)) != NULL &&
141 access(cp, R_OK) == 0) {
142 ap->a_name = cp;
143 break;
145 perror(cp);
148 ap = _fill_in(ap, cp, number);
151 * Character set of attachments: enum attach_conv
153 cslc = charset_get_lc();
154 #ifdef HAVE_ICONV
155 if (! (options & OPT_INTERACTIVE))
156 goto jcs;
157 if ((cp = ap->a_content_type) != NULL &&
158 ascncasecmp(cp, "text/", 5) != 0 &&
159 ! yorn(tr(162, "Filename doesn't indicate text "
160 "content - want to edit charsets? "))) {
161 ap->a_conv = AC_DEFAULT;
162 goto jleave;
165 charset_iter_reset(NULL);
166 jcs:
167 #endif
168 snprintf(prefix, sizeof prefix, tr(160, "#%u\tinput charset: "),
169 number);
170 if ((defcs = ap->a_input_charset) == NULL)
171 defcs = cslc;
172 cp = ap->a_input_charset = readstr_input(prefix, defcs);
173 #ifdef HAVE_ICONV
174 if (! (options & OPT_INTERACTIVE)) {
175 #endif
176 ap->a_conv = (cp != NULL) ? AC_FIX_INCS : AC_DEFAULT;
177 #ifdef HAVE_ICONV
178 goto jleave;
181 snprintf(prefix, sizeof prefix, tr(161, "#%u\toutput (send) charset: "),
182 number);
183 if ((defcs = ap->a_charset) == NULL)
184 defcs = charset_iter_next();
185 defcs = ap->a_charset = readstr_input(prefix, defcs);
187 if (cp != NULL && defcs == NULL) {
188 ap->a_conv = AC_FIX_INCS;
189 goto jdone;
191 if (cp == NULL && defcs == NULL) {
192 ap->a_conv = AC_DEFAULT;
193 ap->a_input_charset = cslc;
194 ap->a_charset = charset_iter_current();
195 } else if (cp == NULL && defcs != NULL) {
196 ap->a_conv = AC_FIX_OUTCS;
197 ap->a_input_charset = cslc;
198 } else
199 ap->a_conv = AC_TMPFILE;
201 printf(tr(197, "Trying conversion from %s to %s\n"),
202 ap->a_input_charset, ap->a_charset);
203 if (_attach_iconv(ap))
204 ap->a_conv = AC_TMPFILE;
205 else {
206 ap->a_input_charset = cp;
207 ap->a_charset = defcs;
208 goto jcs;
210 jdone:
211 #endif
212 if (options & OPT_INTERACTIVE)
213 printf(tr(19, "~@: added attachment \"%s\"\n"), ap->a_name);
214 jleave:
215 return ap;
218 #ifdef HAVE_ICONV
219 static int
220 _attach_iconv(struct attachment *ap)
222 struct str oul = {NULL, 0}, inl = {NULL, 0};
223 FILE *fo = NULL, *fi = NULL;
224 size_t cnt, lbsize;
225 iconv_t icp;
227 if ((icp = n_iconv_open(ap->a_charset, ap->a_input_charset))
228 == (iconv_t)-1) {
229 if (errno == EINVAL)
230 goto jeconv;
231 else
232 perror("iconv_open");
233 goto jerr;
236 if ((fi = Fopen(ap->a_name, "r")) == NULL) {
237 perror(ap->a_name);
238 goto jerr;
240 cnt = fsize(fi);
242 inl.s = NULL;
243 if ((fo = Ftemp(&inl.s, "aiconv", "w+", 0600, 1)) == NULL) {
244 perror(tr(51, "temporary mail file"));
245 inl.s = NULL;
246 goto jerr;
248 unlink(inl.s);
249 Ftfree(&inl.s);
251 for (inl.s = NULL, lbsize = 0;;) {
252 if (fgetline(&inl.s, &lbsize, &cnt, &inl.l, fi, 0) == NULL) {
253 if (! cnt)
254 break;
255 perror(tr(195, "I/O read error occurred"));
256 goto jerr;
259 if (n_iconv_str(icp, &oul, &inl, NULL, FAL0) != 0)
260 goto jeconv;
261 if ((inl.l=fwrite(oul.s, sizeof *oul.s, oul.l, fo)) != oul.l) {
262 perror(tr(196, "I/O write error occurred"));
263 goto jerr;
266 fflush_rewind(fo);
268 ap->a_tmpf = fo;
269 jleave:
270 if (inl.s != NULL)
271 free(inl.s);
272 if (oul.s != NULL)
273 free(oul.s);
274 if (fi != NULL)
275 Fclose(fi);
276 if (icp != (iconv_t)-1)
277 n_iconv_close(icp);
278 return (fo != NULL);
279 jeconv:
280 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
281 ap->a_input_charset, ap->a_charset);
282 jerr:
283 if (fo != NULL)
284 Fclose(fo);
285 fo = NULL;
286 goto jleave;
288 #endif /* HAVE_ICONV */
290 struct attachment *
291 add_attachment(struct attachment *aphead, char *file, struct attachment **newap)
293 struct attachment *nap = NULL, *ap;
295 if ((file = file_expand(file)) == NULL)
296 goto jleave;
297 if (access(file, R_OK) != 0)
298 goto jleave;
300 nap = _fill_in(csalloc(1, sizeof *nap), file, 0);
301 if (newap != NULL)
302 *newap = nap;
303 if (aphead != NULL) {
304 for (ap = aphead; ap->a_flink != NULL; ap = ap->a_flink)
306 ap->a_flink = nap;
307 nap->a_blink = ap;
308 } else {
309 nap->a_blink = NULL;
310 aphead = nap;
312 nap = aphead;
313 jleave:
314 return nap;
317 struct attachment *
318 append_attachments(struct attachment *aphead, char *names)
320 char *cp;
321 struct attachment *xaph, *nap;
323 while ((cp = strcomma(&names, 1)) != NULL) {
324 if ((xaph = add_attachment(aphead, cp, &nap)) != NULL) {
325 aphead = xaph;
326 if (options & OPT_INTERACTIVE)
327 printf(tr(19, "~@: added attachment \"%s\"\n"),
328 nap->a_name);
329 } else
330 perror(cp);
332 return aphead;
335 struct attachment *
336 edit_attachments(struct attachment *aphead)
338 struct attachment *ap, *nap;
339 ui_it attno = 1;
341 /* Modify already present ones? */
342 for (ap = aphead; ap != NULL; ap = ap->a_flink) {
343 if (_read_attachment_data(ap, attno) != NULL) {
344 ++attno;
345 continue;
347 nap = ap->a_flink;
348 if (ap->a_blink != NULL)
349 ap->a_blink->a_flink = nap;
350 else
351 aphead = nap;
352 if (nap != NULL)
353 nap->a_blink = ap->a_blink;
354 else
355 goto jleave;
358 /* Add some more? */
359 while ((nap = _read_attachment_data(NULL, attno)) != NULL) {
360 if ((ap = aphead) != NULL) {
361 while (ap->a_flink != NULL)
362 ap = ap->a_flink;
363 ap->a_flink = nap;
365 nap->a_blink = ap;
366 nap->a_flink = NULL;
367 if (aphead == NULL)
368 aphead = nap;
369 ++attno;
371 jleave:
372 return aphead;