nail.h: struct message.m_spamscore: ui_it -> ui32_t
[s-mailx.git] / attachments.c
blobe11dd1308a0eeb4d3170490a20ad4f4efa48e68f
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,
82 "#%u\tContent-Disposition: ", number);
83 ap->a_content_disposition = readstr_input(prefix,
84 ap->a_content_disposition);
86 if (ap->a_content_disposition == NULL)
87 ap->a_content_disposition = "attachment";
89 if (number > 0 && ok_blook(attachment_ask_content_id)) {
90 snprintf(prefix, sizeof prefix, "#%u\tContent-ID: ", number);
91 ap->a_content_id = readstr_input(prefix, ap->a_content_id);
94 if (number > 0 && ok_blook(attachment_ask_content_description)) {
95 snprintf(prefix, sizeof prefix, "#%u\tContent-Description: ", number);
96 ap->a_content_description = readstr_input(prefix,
97 ap->a_content_description);
99 NYD_LEAVE;
100 return ap;
103 static struct attachment *
104 _read_attachment_data(struct attachment *ap, ui32_t number)
106 char prefix[80 * 2];
107 char const *cslc = NULL/*cc uninit?*/, *cp, *defcs;
108 NYD_ENTER;
110 if (ap == NULL)
111 ap = csalloc(1, sizeof *ap);
112 else if (ap->a_msgno) {
113 char *ecp = salloc(16);
114 snprintf(ecp, 16, "#%u", (ui_it)ap->a_msgno);
115 ap->a_msgno = 0;
116 ap->a_name = ecp;
117 } else if (ap->a_conv == AC_TMPFILE) {
118 Fclose(ap->a_tmpf);
119 ap->a_conv = AC_DEFAULT;
122 snprintf(prefix, sizeof prefix, tr(50, "#%u\tfilename: "), number);
123 for (;;) {
124 if ((ap->a_name = readstr_input(prefix, ap->a_name)) == NULL) {
125 ap = NULL;
126 goto jleave;
128 /* May be a message number */
129 if (ap->a_name[0] == '#') {
130 char *ecp;
131 int msgno = (int)strtol(ap->a_name + 1, &ecp, 10);
132 if (msgno > 0 && msgno <= msgCount && *ecp == '\0') {
133 ap->a_msgno = msgno;
134 ap->a_content_description = tr(513, "Attached message content");
135 if (options & OPT_INTERACTIVE)
136 printf(tr(2, "~@: added message #%u\n"), msgno);
137 goto jleave;
140 if ((cp = file_expand(ap->a_name)) != NULL && access(cp, R_OK) == 0) {
141 ap->a_name = cp;
142 break;
144 perror(cp);
147 ap = _fill_in(ap, cp, number);
150 * Character set of attachments: enum attach_conv
152 cslc = charset_get_lc();
153 #ifdef HAVE_ICONV
154 if (!(options & OPT_INTERACTIVE))
155 goto jcs;
156 if ((cp = ap->a_content_type) != NULL && ascncasecmp(cp, "text/", 5) != 0 &&
157 !yorn(tr(162,
158 "Filename doesn't indicate text content - want to edit charsets? "))
160 ap->a_conv = AC_DEFAULT;
161 goto jleave;
164 charset_iter_reset(NULL);
165 jcs:
166 #endif
167 snprintf(prefix, sizeof prefix, tr(160, "#%u\tinput charset: "), number);
168 if ((defcs = ap->a_input_charset) == NULL)
169 defcs = cslc;
170 cp = ap->a_input_charset = readstr_input(prefix, defcs);
171 #ifdef HAVE_ICONV
172 if (!(options & OPT_INTERACTIVE)) {
173 #endif
174 ap->a_conv = (cp != NULL) ? AC_FIX_INCS : AC_DEFAULT;
175 #ifdef HAVE_ICONV
176 goto jleave;
179 snprintf(prefix, sizeof prefix, tr(161, "#%u\toutput (send) charset: "),
180 number);
181 if ((defcs = ap->a_charset) == NULL)
182 defcs = charset_iter_next();
183 defcs = ap->a_charset = readstr_input(prefix, defcs);
185 if (cp != NULL && defcs == NULL) {
186 ap->a_conv = AC_FIX_INCS;
187 goto jdone;
189 if (cp == NULL && defcs == NULL) {
190 ap->a_conv = AC_DEFAULT;
191 ap->a_input_charset = cslc;
192 ap->a_charset = charset_iter_current();
193 } else if (cp == NULL && defcs != NULL) {
194 ap->a_conv = AC_FIX_OUTCS;
195 ap->a_input_charset = cslc;
196 } else
197 ap->a_conv = AC_TMPFILE;
199 printf(tr(197, "Trying conversion from %s to %s\n"), ap->a_input_charset,
200 ap->a_charset);
201 if (_attach_iconv(ap))
202 ap->a_conv = AC_TMPFILE;
203 else {
204 ap->a_input_charset = cp;
205 ap->a_charset = defcs;
206 goto jcs;
208 jdone:
209 #endif
210 if (options & OPT_INTERACTIVE)
211 printf(tr(19, "~@: added attachment \"%s\"\n"), ap->a_name);
212 jleave:
213 NYD_LEAVE;
214 return ap;
217 #ifdef HAVE_ICONV
218 static int
219 _attach_iconv(struct attachment *ap)
221 struct str oul = {NULL, 0}, inl = {NULL, 0};
222 FILE *fo = NULL, *fi = NULL;
223 size_t cnt, lbsize;
224 iconv_t icp;
225 NYD_ENTER;
227 icp = n_iconv_open(ap->a_charset, ap->a_input_charset);
228 if (icp == (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 if ((fo = Ftmp(NULL, "atic", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
243 NULL) {
244 perror(tr(51, "temporary mail file"));
245 goto jerr;
248 for (lbsize = 0;;) {
249 if (fgetline(&inl.s, &lbsize, &cnt, &inl.l, fi, 0) == NULL) {
250 if (!cnt)
251 break;
252 perror(tr(195, "I/O read error occurred"));
253 goto jerr;
256 if (n_iconv_str(icp, &oul, &inl, NULL, FAL0) != 0)
257 goto jeconv;
258 if ((inl.l = fwrite(oul.s, sizeof *oul.s, oul.l, fo)) != oul.l) {
259 perror(tr(196, "I/O write error occurred"));
260 goto jerr;
263 fflush_rewind(fo);
265 ap->a_tmpf = fo;
266 jleave:
267 if (inl.s != NULL)
268 free(inl.s);
269 if (oul.s != NULL)
270 free(oul.s);
271 if (fi != NULL)
272 Fclose(fi);
273 if (icp != (iconv_t)-1)
274 n_iconv_close(icp);
275 NYD_LEAVE;
276 return (fo != NULL);
278 jeconv:
279 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
280 ap->a_input_charset, ap->a_charset);
281 jerr:
282 if (fo != NULL)
283 Fclose(fo);
284 fo = NULL;
285 goto jleave;
287 #endif /* HAVE_ICONV */
289 FL struct attachment *
290 add_attachment(struct attachment *aphead, char *file,
291 struct attachment **newap)
293 struct attachment *nap = NULL, *ap;
294 NYD_ENTER;
296 if ((file = file_expand(file)) == NULL)
297 goto jleave;
298 if (access(file, R_OK) != 0)
299 goto jleave;
301 nap = _fill_in(csalloc(1, sizeof *nap), file, 0);
302 if (newap != NULL)
303 *newap = nap;
304 if (aphead != NULL) {
305 for (ap = aphead; ap->a_flink != NULL; ap = ap->a_flink)
307 ap->a_flink = nap;
308 nap->a_blink = ap;
309 } else {
310 nap->a_blink = NULL;
311 aphead = nap;
313 nap = aphead;
314 jleave:
315 NYD_LEAVE;
316 return nap;
319 FL struct attachment *
320 append_attachments(struct attachment *aphead, char *names)
322 char *cp;
323 struct attachment *xaph, *nap;
324 NYD_ENTER;
326 while ((cp = strcomma(&names, 1)) != NULL) {
327 if ((xaph = add_attachment(aphead, cp, &nap)) != NULL) {
328 aphead = xaph;
329 if (options & OPT_INTERACTIVE)
330 printf(tr(19, "~@: added attachment \"%s\"\n"), nap->a_name);
331 } else
332 perror(cp);
334 NYD_LEAVE;
335 return aphead;
338 FL struct attachment *
339 edit_attachments(struct attachment *aphead)
341 struct attachment *ap, *nap;
342 ui_it attno = 1;
343 NYD_ENTER;
345 /* Modify already present ones? */
346 for (ap = aphead; ap != NULL; ap = ap->a_flink) {
347 if (_read_attachment_data(ap, attno) != NULL) {
348 ++attno;
349 continue;
351 nap = ap->a_flink;
352 if (ap->a_blink != NULL)
353 ap->a_blink->a_flink = nap;
354 else
355 aphead = nap;
356 if (nap != NULL)
357 nap->a_blink = ap->a_blink;
358 else
359 goto jleave;
362 /* Add some more? */
363 while ((nap = _read_attachment_data(NULL, attno)) != NULL) {
364 if ((ap = aphead) != NULL) {
365 while (ap->a_flink != NULL)
366 ap = ap->a_flink;
367 ap->a_flink = nap;
369 nap->a_blink = ap;
370 nap->a_flink = NULL;
371 if (aphead == NULL)
372 aphead = nap;
373 ++attno;
375 jleave:
376 NYD_LEAVE;
377 return aphead;
380 /* vim:set fenc=utf-8:s-it-mode */