Drop `defines', add `unaccount', rename `undefine'..
[s-mailx.git] / attachments.c
blob1b005da304b72b3554ba77e6ead50a43f2861ab5
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>.
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 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 /* We use calloc() for struct attachment */
45 CTA(AC_DEFAULT == 0);
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,
53 ui32_t number);
55 /* Try to create temporary charset converted version */
56 #ifdef HAVE_ICONV
57 static int _attach_iconv(struct attachment *ap);
58 #endif
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" ;) */
65 char prefix[80 * 2];
66 NYD_ENTER;
68 ap->a_name = file;
69 if ((file = strrchr(file, '/')) != NULL)
70 ++file;
71 else
72 file = ap->a_name;
74 ap->a_content_type = mime_classify_content_type_by_fileext(file);
75 if (number > 0 && ok_blook(attachment_ask_content_type)) {
76 snprintf(prefix, sizeof prefix, "#%u\tContent-Type: ", number);
77 ap->a_content_type = readstr_input(prefix, ap->a_content_type);
80 if (number > 0 && ok_blook(attachment_ask_content_disposition)) {
81 snprintf(prefix, sizeof prefix, "#%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 && ok_blook(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 && ok_blook(attachment_ask_content_description)) {
94 snprintf(prefix, sizeof prefix, "#%u\tContent-Description: ", number);
95 ap->a_content_description = readstr_input(prefix,
96 ap->a_content_description);
98 NYD_LEAVE;
99 return ap;
102 static struct attachment *
103 _read_attachment_data(struct attachment *ap, ui32_t number)
105 char prefix[80 * 2];
106 char const *cslc = NULL/*cc uninit?*/, *cp, *defcs;
107 NYD_ENTER;
109 if (ap == NULL)
110 ap = csalloc(1, sizeof *ap);
111 else if (ap->a_msgno) {
112 char *ecp = salloc(16);
113 snprintf(ecp, 16, "#%u", (ui_it)ap->a_msgno);
114 ap->a_msgno = 0;
115 ap->a_name = ecp;
116 } else if (ap->a_conv == AC_TMPFILE) {
117 Fclose(ap->a_tmpf);
118 ap->a_conv = AC_DEFAULT;
121 snprintf(prefix, sizeof prefix, tr(50, "#%u\tfilename: "), number);
122 for (;;) {
123 if ((ap->a_name = readstr_input(prefix, ap->a_name)) == NULL) {
124 ap = NULL;
125 goto jleave;
127 /* May be a message number */
128 if (ap->a_name[0] == '#') {
129 char *ecp;
130 int msgno = (int)strtol(ap->a_name + 1, &ecp, 10);
131 if (msgno > 0 && msgno <= msgCount && *ecp == '\0') {
132 ap->a_msgno = msgno;
133 ap->a_content_description = tr(513, "Attached message content");
134 if (options & OPT_INTERACTIVE)
135 printf(tr(2, "~@: added message #%u\n"), msgno);
136 goto jleave;
139 if ((cp = file_expand(ap->a_name)) != NULL && access(cp, R_OK) == 0) {
140 ap->a_name = cp;
141 break;
143 perror(cp);
146 ap = _fill_in(ap, cp, number);
149 * Character set of attachments: enum attach_conv
151 cslc = charset_get_lc();
152 #ifdef HAVE_ICONV
153 if (!(options & OPT_INTERACTIVE))
154 goto jcs;
155 if ((cp = ap->a_content_type) != NULL && ascncasecmp(cp, "text/", 5) != 0 &&
156 !yorn(tr(162,
157 "Filename doesn't indicate text content - want to edit charsets? "))
159 ap->a_conv = AC_DEFAULT;
160 goto jleave;
163 charset_iter_reset(NULL);
164 jcs:
165 #endif
166 snprintf(prefix, sizeof prefix, tr(160, "#%u\tinput charset: "), number);
167 if ((defcs = ap->a_input_charset) == NULL)
168 defcs = cslc;
169 cp = ap->a_input_charset = readstr_input(prefix, defcs);
170 #ifdef HAVE_ICONV
171 if (!(options & OPT_INTERACTIVE)) {
172 #endif
173 ap->a_conv = (cp != NULL) ? AC_FIX_INCS : AC_DEFAULT;
174 #ifdef HAVE_ICONV
175 goto jleave;
178 snprintf(prefix, sizeof prefix, tr(161, "#%u\toutput (send) charset: "),
179 number);
180 if ((defcs = ap->a_charset) == NULL)
181 defcs = charset_iter_next();
182 defcs = ap->a_charset = readstr_input(prefix, defcs);
184 if (cp != NULL && defcs == NULL) {
185 ap->a_conv = AC_FIX_INCS;
186 goto jdone;
188 if (cp == NULL && defcs == NULL) {
189 ap->a_conv = AC_DEFAULT;
190 ap->a_input_charset = cslc;
191 ap->a_charset = charset_iter_current();
192 } else if (cp == NULL && defcs != NULL) {
193 ap->a_conv = AC_FIX_OUTCS;
194 ap->a_input_charset = cslc;
195 } else
196 ap->a_conv = AC_TMPFILE;
198 printf(tr(197, "Trying conversion from %s to %s\n"), ap->a_input_charset,
199 ap->a_charset);
200 if (_attach_iconv(ap))
201 ap->a_conv = AC_TMPFILE;
202 else {
203 ap->a_input_charset = cp;
204 ap->a_charset = defcs;
205 goto jcs;
207 jdone:
208 #endif
209 if (options & OPT_INTERACTIVE)
210 printf(tr(19, "~@: added attachment \"%s\"\n"), ap->a_name);
211 jleave:
212 NYD_LEAVE;
213 return ap;
216 #ifdef HAVE_ICONV
217 static int
218 _attach_iconv(struct attachment *ap)
220 struct str oul = {NULL, 0}, inl = {NULL, 0};
221 FILE *fo = NULL, *fi = NULL;
222 size_t cnt, lbsize;
223 iconv_t icp;
224 NYD_ENTER;
226 icp = n_iconv_open(ap->a_charset, ap->a_input_charset);
227 if (icp == (iconv_t)-1) {
228 if (errno == EINVAL)
229 goto jeconv;
230 else
231 perror("iconv_open");
232 goto jerr;
235 if ((fi = Fopen(ap->a_name, "r")) == NULL) {
236 perror(ap->a_name);
237 goto jerr;
239 cnt = fsize(fi);
241 if ((fo = Ftmp(NULL, "atic", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
242 NULL) {
243 perror(tr(51, "temporary mail file"));
244 goto jerr;
247 for (lbsize = 0;;) {
248 if (fgetline(&inl.s, &lbsize, &cnt, &inl.l, fi, 0) == NULL) {
249 if (!cnt)
250 break;
251 perror(tr(195, "I/O read error occurred"));
252 goto jerr;
255 if (n_iconv_str(icp, &oul, &inl, NULL, FAL0) != 0)
256 goto jeconv;
257 if ((inl.l = fwrite(oul.s, sizeof *oul.s, oul.l, fo)) != oul.l) {
258 perror(tr(196, "I/O write error occurred"));
259 goto jerr;
262 fflush_rewind(fo);
264 ap->a_tmpf = fo;
265 jleave:
266 if (inl.s != NULL)
267 free(inl.s);
268 if (oul.s != NULL)
269 free(oul.s);
270 if (fi != NULL)
271 Fclose(fi);
272 if (icp != (iconv_t)-1)
273 n_iconv_close(icp);
274 NYD_LEAVE;
275 return (fo != NULL);
277 jeconv:
278 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
279 ap->a_input_charset, ap->a_charset);
280 jerr:
281 if (fo != NULL)
282 Fclose(fo);
283 fo = NULL;
284 goto jleave;
286 #endif /* HAVE_ICONV */
288 FL struct attachment *
289 add_attachment(struct attachment *aphead, char *file, struct attachment **newap)
291 struct attachment *nap = NULL, *ap;
292 NYD_ENTER;
294 if ((file = file_expand(file)) == NULL)
295 goto jleave;
296 if (access(file, R_OK) != 0)
297 goto jleave;
299 nap = _fill_in(csalloc(1, sizeof *nap), file, 0);
300 if (newap != NULL)
301 *newap = nap;
302 if (aphead != NULL) {
303 for (ap = aphead; ap->a_flink != NULL; ap = ap->a_flink)
305 ap->a_flink = nap;
306 nap->a_blink = ap;
307 } else {
308 nap->a_blink = NULL;
309 aphead = nap;
311 nap = aphead;
312 jleave:
313 NYD_LEAVE;
314 return nap;
317 FL struct attachment *
318 append_attachments(struct attachment *aphead, char *names)
320 char *cp;
321 struct attachment *xaph, *nap;
322 NYD_ENTER;
324 while ((cp = n_strsep(&names, ',', 1)) != NULL) {
325 if ((xaph = add_attachment(aphead, cp, &nap)) != NULL) {
326 aphead = xaph;
327 if (options & OPT_INTERACTIVE)
328 printf(tr(19, "~@: added attachment \"%s\"\n"), nap->a_name);
329 } else
330 perror(cp);
332 NYD_LEAVE;
333 return aphead;
336 FL struct attachment *
337 edit_attachments(struct attachment *aphead)
339 struct attachment *ap, *nap;
340 ui_it attno = 1;
341 NYD_ENTER;
343 /* Modify already present ones? */
344 for (ap = aphead; ap != NULL; ap = ap->a_flink) {
345 if (_read_attachment_data(ap, attno) != NULL) {
346 ++attno;
347 continue;
349 nap = ap->a_flink;
350 if (ap->a_blink != NULL)
351 ap->a_blink->a_flink = nap;
352 else
353 aphead = nap;
354 if (nap != NULL)
355 nap->a_blink = ap->a_blink;
356 else
357 goto jleave;
360 /* Add some more? */
361 while ((nap = _read_attachment_data(NULL, attno)) != NULL) {
362 if ((ap = aphead) != NULL) {
363 while (ap->a_flink != NULL)
364 ap = ap->a_flink;
365 ap->a_flink = nap;
367 nap->a_blink = ap;
368 nap->a_flink = NULL;
369 if (aphead == NULL)
370 aphead = nap;
371 ++attno;
373 jleave:
374 NYD_LEAVE;
375 return aphead;
378 /* vim:set fenc=utf-8:s-it-mode */