mime_detach.c revision 1.4 1 /* $NetBSD: mime_detach.c,v 1.4 2009/04/10 13:08:25 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Anon Ymous.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32
33 #ifdef MIME_SUPPORT
34
35 #include <sys/cdefs.h>
36 #ifndef __lint__
37 __RCSID("$NetBSD: mime_detach.c,v 1.4 2009/04/10 13:08:25 christos Exp $");
38 #endif /* not __lint__ */
39
40 #include <assert.h>
41 #include <err.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #include "def.h"
47 #include "extern.h"
48 #ifdef USE_EDITLINE
49 #include "complete.h"
50 #endif
51 #ifdef MIME_SUPPORT
52 #include "mime.h"
53 #include "mime_child.h"
54 #include "mime_codecs.h"
55 #include "mime_detach.h"
56 #endif
57 #include "sig.h"
58
59
60 static struct {
61 int overwrite;
62 int batch;
63 int ask;
64 } detach_ctl;
65
66 PUBLIC int
67 mime_detach_control(void)
68 {
69 char *cp;
70
71 detach_ctl.batch = value(ENAME_MIME_DETACH_BATCH) != NULL;
72 detach_ctl.ask = detach_ctl.batch ? 0 : 1;
73 detach_ctl.overwrite = 0;
74
75 cp = value(ENAME_MIME_DETACH_OVERWRITE);
76 if (cp == NULL || strcasecmp(cp, "no") == 0)
77 detach_ctl.overwrite = 0;
78
79 else if (*cp== '\0' || strcasecmp(cp, "yes") == 0)
80 detach_ctl.overwrite = 1;
81
82 else if (strcasecmp(cp, "ask") == 0) {
83 detach_ctl.overwrite = 0;
84 detach_ctl.ask = 1;
85 }
86 else {
87 (void)printf("invalid %s setting: %s",
88 ENAME_MIME_DETACH_OVERWRITE, cp);
89 return -1;
90 }
91 return 0;
92 }
93
94 static char *
95 detach_get_fname(char *prompt, char *pathname)
96 {
97 if (!detach_ctl.batch) {
98 char *fname;
99
100 fname = my_gets(&elm.filec, prompt, pathname);
101 if (fname == NULL) /* ignore this attachment */
102 return NULL;
103 (void)strip_WSP(fname);
104 fname = skip_WSP(fname);
105 if (*fname == '\0') /* ignore this attachment */
106 return NULL;
107 pathname = savestr(fname); /* save this or it gets trashed */
108 }
109 else if (detach_ctl.ask)
110 (void)printf("%s%s\n", prompt, pathname);
111
112 return pathname;
113 }
114
115 static enum {
116 DETACH_OPEN_OK,
117 DETACH_NEXT,
118 DETACH_RENAME,
119 DETACH_FAILED
120 }
121 detach_open_core(char *fname, const char *partstr)
122 {
123 int flags;
124 int fd;
125
126 flags = (detach_ctl.overwrite ? 0 : O_EXCL) | O_CREAT | O_TRUNC | O_WRONLY;
127
128 if ((fd = open(fname, flags, 0600)) != -1 &&
129 Fdopen(fd, "w") != NULL)
130 return DETACH_OPEN_OK;
131
132 if (detach_ctl.ask && fd == -1 && errno == EEXIST) {
133 char *p;
134 start:
135 (void)sasprintf(&p, "%-7s overwrite: Always/Never/once/next/rename (ANonr)[n]? ",
136 partstr, fname);
137 p = my_gets(&elm.string, p, NULL);
138 if (p == NULL)
139 goto start;
140
141 (void)strip_WSP(p);
142 p = skip_WSP(p);
143
144 switch (*p) {
145 case 'A': detach_ctl.overwrite = 1;
146 detach_ctl.batch = 1;
147 detach_ctl.ask = 0;
148 /* FALLTHROUGH */
149 case 'o':
150 if (Fopen(fname, "w") != NULL)
151 return DETACH_OPEN_OK;
152 break;
153
154 case 'N': detach_ctl.overwrite = 0;
155 detach_ctl.batch = 1;
156 detach_ctl.ask = 0;
157 /* FALLTHROUGH */
158 case '\0': /* default */
159 case 'n': /* Next */
160 return DETACH_NEXT;
161
162 default:
163 goto start;
164
165 case 'r': /* Rename */
166 return DETACH_RENAME;
167 }
168 }
169 warn(fname);
170 if (fd != -1)
171 (void)close(fd);
172
173 return DETACH_FAILED;
174 }
175
176 static char *
177 detach_open_target(struct mime_info *mip)
178 {
179 char *pathname;
180 char *prompt;
181
182 /*
183 * Get the suggested target pathname.
184 */
185 if (mip->mi_filename != NULL)
186 (void)sasprintf(&pathname, "%s/%s", mip->mi_detachdir, mip->mi_filename);
187 else {
188 if (mip->mi_detachall == 0)
189 return NULL;
190
191 (void)sasprintf(&pathname, "%s/msg-%s.part-%s.%s",
192 mip->mi_detachdir, mip->mi_msgstr,
193 mip->mi_partstr[0] ? mip->mi_partstr : "0",
194 mip->mi_subtype ? mip->mi_subtype : "unknown");
195 }
196
197 /*
198 * Make up the prompt
199 */
200 (void)sasprintf(&prompt, "%-7s filename: ", mip->mi_partstr);
201
202 /*
203 * The main loop.
204 */
205 do {
206 struct stat sb;
207 char *fname;
208
209 if ((fname = detach_get_fname(prompt, pathname)) == NULL)
210 return NULL;
211 /*
212 * Make sure we don't have the name of something other
213 * than a normal file!
214 */
215 if (stat(fname, &sb) == 0 && !S_ISREG(sb.st_mode)) {
216 (void)printf("not a regular file: %s", fname);
217 if (!detach_ctl.ask)
218 return NULL;
219 continue;
220 }
221 switch (detach_open_core(fname, mip->mi_partstr)) {
222 case DETACH_OPEN_OK:
223 return fname;
224 case DETACH_NEXT:
225 return NULL;
226 case DETACH_RENAME:
227 detach_ctl.batch = 0;
228 break;
229 case DETACH_FAILED:
230 break;
231 }
232 } while (!detach_ctl.batch);
233
234 return NULL;
235 }
236
237 /*
238 * The main entry point for detaching.
239 */
240 PUBLIC FILE *
241 mime_detach_parts(struct mime_info *mip)
242 {
243 mime_codec_t dec;
244 char *pathname;
245
246 if (mip->mi_ignore_body || mip->mp->m_blines == 0)
247 return NULL;
248
249 if ((dec = mime_fio_decoder(mip->mi_encoding)) == NULL &&
250 (dec = mime_fio_decoder(MIME_TRANSFER_7BIT)) == NULL)
251 assert(/*CONSTCOND*/ 0); /* this should never get hit! */
252
253 if ((pathname = detach_open_target(mip)) == NULL)
254 return NULL;
255
256 (void)printf("writing: %s\n", pathname);
257
258 /*
259 * XXX - should we do character set conversion here (done by
260 * run_decoder()), or just run dec()?
261 */
262 #if 0
263 mime_run_function(dec, pipe_end(mip), NULL);
264 #else
265 run_decoder(mip, dec);
266 #endif
267 return pipe_end(mip);
268 }
269
270 /*
271 * Set the message part number to be used when constructing a filename
272 * for detaching unnamed parts in detach_open_target(). When
273 * threading, this is not a single number, hence the string value.
274 */
275 PUBLIC void
276 mime_detach_msgnum(struct mime_info *mip, const char *msgstr)
277 {
278 for (/*EMPTY*/; mip; mip = mip->mi_flink)
279 mip->mi_msgstr = msgstr;
280 }
281
282 #endif /* MIME_SUPPORT */
283