xref: /netbsd-src/usr.bin/mail/mime_detach.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: mime_detach.c,v 1.3 2008/04/28 20:24:14 martin 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.3 2008/04/28 20:24:14 martin 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 
58 
59 static struct {
60 	int overwrite;
61 	int batch;
62 	int ask;
63 } detach_ctl;
64 
65 PUBLIC int
66 mime_detach_control(void)
67 {
68 	char *cp;
69 
70 	detach_ctl.batch     = value(ENAME_MIME_DETACH_BATCH) != NULL;
71 	detach_ctl.ask       = detach_ctl.batch ? 0 : 1;
72 	detach_ctl.overwrite = 0;
73 
74 	cp = value(ENAME_MIME_DETACH_OVERWRITE);
75 	if (cp == NULL || strcasecmp(cp, "no") == 0)
76 		detach_ctl.overwrite = 0;
77 
78 	else if (*cp== '\0' || strcasecmp(cp, "yes") == 0)
79 		detach_ctl.overwrite = 1;
80 
81 	else if (strcasecmp(cp, "ask") == 0) {
82 		detach_ctl.overwrite = 0;
83 		detach_ctl.ask       = 1;
84 	}
85 	else {
86 		(void)printf("invalid %s setting: %s",
87 		    ENAME_MIME_DETACH_OVERWRITE, cp);
88 		return -1;
89 	}
90 	return 0;
91 }
92 
93 static char *
94 detach_get_fname(char *prompt, char *pathname)
95 {
96 	if (!detach_ctl.batch) {
97 		char *fname;
98 		fname = my_gets(&elm.filec, prompt, pathname);
99 		fname = skip_WSP(fname); /* XXX - do this? */
100 		if (*fname == '\0')	/* ignore this attachment */
101 			return NULL;
102 		pathname = savestr(fname);	/* save this or it gets trashed */
103 	}
104 	else if (detach_ctl.ask)
105 		(void)printf("%s%s\n", prompt, pathname);
106 
107 	return pathname;
108 }
109 
110 static enum {
111 	DETACH_OPEN_OK,
112 	DETACH_NEXT,
113 	DETACH_RENAME,
114 	DETACH_FAILED
115 }
116 detach_open_core(char *fname, const char *partstr)
117 {
118 	int flags;
119 	int fd;
120 
121 	flags = (detach_ctl.overwrite ? 0 : O_EXCL) | O_CREAT | O_TRUNC | O_WRONLY;
122 
123 	if ((fd = open(fname, flags, 0600)) != -1 &&
124 	    Fdopen(fd, "w") != NULL)
125 		return DETACH_OPEN_OK;
126 
127 	if (detach_ctl.ask && fd == -1 && errno == EEXIST) {
128 		char *p;
129  start:
130 		(void)sasprintf(&p, "%-7s overwrite: Always/Never/once/next/rename (ANonr)[n]? ",
131 		    partstr, fname);
132 		p = my_gets(&elm.string, p, NULL);
133 		p = skip_WSP(p);
134 		switch (*p) {
135 		case 'A':	detach_ctl.overwrite = 1;
136 				detach_ctl.batch = 1;
137 				detach_ctl.ask = 0;
138 				/* FALLTHROUGH */
139 		case 'o':
140 			if (Fopen(fname, "w") != NULL)
141 				return DETACH_OPEN_OK;
142 			break;
143 
144 		case 'N':	detach_ctl.overwrite = 0;
145 				detach_ctl.batch = 1;
146 				detach_ctl.ask = 0;
147 				/* FALLTHROUGH */
148 		case '\0':	/* default */
149 		case 'n':	/* Next */
150 			return DETACH_NEXT;
151 
152 		default:
153 			goto start;
154 
155 		case 'r':	/* Rename */
156 			return DETACH_RENAME;
157 		}
158 	}
159 	warn(fname);
160 	if (fd != -1)
161 		(void)close(fd);
162 
163 	return DETACH_FAILED;
164 }
165 
166 static char *
167 detach_open_target(struct mime_info *mip)
168 {
169 	char *pathname;
170 	char *prompt;
171 
172 	/*
173 	 * Get the suggested target pathname.
174 	 */
175 	if (mip->mi_filename != NULL)
176 		(void)sasprintf(&pathname, "%s/%s", mip->mi_detachdir, mip->mi_filename);
177 	else {
178 		if (mip->mi_detachall == 0)
179 			return NULL;
180 
181 		(void)sasprintf(&pathname, "%s/msg-%s.part-%s.%s",
182 		    mip->mi_detachdir, mip->mi_msgstr,
183 		    mip->mi_partstr[0] ? mip->mi_partstr : "0",
184 		    mip->mi_subtype ? mip->mi_subtype : "unknown");
185 	}
186 
187 	/*
188 	 * Make up the prompt
189 	 */
190 	(void)sasprintf(&prompt, "%-7s filename: ", mip->mi_partstr);
191 
192 	/*
193 	 * The main loop.
194 	 */
195 	do {
196 		struct stat sb;
197 		char *fname;
198 
199 		if ((fname = detach_get_fname(prompt, pathname)) == NULL)
200 			return NULL;
201 		/*
202 		 * Make sure we don't have the name of something other
203 		 * than a normal file!
204 		 */
205 		if (stat(fname, &sb) == 0 && !S_ISREG(sb.st_mode)) {
206 			(void)printf("not a regular file: %s", fname);
207 			if (!detach_ctl.ask)
208 				return NULL;
209 			continue;
210 		}
211 		switch (detach_open_core(fname, mip->mi_partstr)) {
212 		case DETACH_OPEN_OK:
213 			return fname;
214 		case DETACH_NEXT:
215 			return NULL;
216 		case DETACH_RENAME:
217 			detach_ctl.batch = 0;
218 			break;
219 		case DETACH_FAILED:
220 			break;
221 		}
222 	} while (!detach_ctl.batch);
223 
224 	return NULL;
225 }
226 
227 /*
228  * The main entry point for detaching.
229  */
230 PUBLIC FILE *
231 mime_detach_parts(struct mime_info *mip)
232 {
233 	mime_codec_t dec;
234 	char *pathname;
235 
236 	if (mip->mi_ignore_body || mip->mp->m_blines == 0)
237 		return NULL;
238 
239 	if ((dec = mime_fio_decoder(mip->mi_encoding)) == NULL &&
240 	    (dec = mime_fio_decoder(MIME_TRANSFER_7BIT)) == NULL)
241 		assert(/*CONSTCOND*/ 0); /* this should never get hit! */
242 
243 	if ((pathname = detach_open_target(mip)) == NULL)
244 		return NULL;
245 
246 	(void)printf("writing: %s\n", pathname);
247 
248 	/*
249 	 * XXX - should we do character set conversion here (done by
250 	 * run_decoder()), or just run dec()?
251 	 */
252 #if 0
253 	mime_run_function(dec, pipe_end(mip), NULL);
254 #else
255 	run_decoder(mip, dec);
256 #endif
257 	return pipe_end(mip);
258 }
259 
260 /*
261  * Set the message part number to be used when constructing a filename
262  * for detaching unnamed parts in detach_open_target().  When
263  * threading, this is not a single number, hence the string value.
264  */
265 PUBLIC void
266 mime_detach_msgnum(struct mime_info *mip, const char *msgstr)
267 {
268 	for (/*EMPTY*/; mip; mip = mip->mi_flink)
269 		mip->mi_msgstr = msgstr;
270 }
271 
272 #endif /* MIME_SUPPORT */
273