1/*	$NetBSD: mime_detach.c,v 1.9 2017/11/09 20:27:50 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.9 2017/11/09 20:27:50 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
60static struct {
61	int overwrite;
62	int batch;
63	int ask;
64} detach_ctl;
65
66PUBLIC int
67mime_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
94static char *
95detach_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
115static enum {
116	DETACH_OPEN_OK,
117	DETACH_NEXT,
118	DETACH_RENAME,
119	DETACH_FAILED
120}
121detach_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 | O_CLOEXEC, 0600)) != -1 &&
129	    Fdopen(fd, "wef") != 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 %s: 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, "wef") != 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("%s", fname);
170	if (fd != -1)
171		(void)close(fd);
172
173	return DETACH_FAILED;
174}
175
176static char *
177detach_open_target(struct mime_info *mip)
178{
179	char *pathname;
180	char *prompt;
181	const char *partstr;
182	const char *subtype;
183
184	/*
185	 * XXX: If partstr == NULL, we probably shouldn't be detaching
186	 * anything, but let's be liberal and try to do something with
187	 * the block anyway.
188	 */
189	partstr = mip->mi_partstr && mip->mi_partstr[0] ? mip->mi_partstr : "0";
190	subtype = mip->mi_subtype ? mip->mi_subtype : "unknown";
191
192	/*
193	 * Get the suggested target pathname.
194	 */
195	if (mip->mi_filename != NULL)
196		(void)sasprintf(&pathname, "%s/%s", mip->mi_detachdir,
197		    mip->mi_filename);
198	else {
199		if (mip->mi_detachall == 0)
200			return NULL;
201
202		(void)sasprintf(&pathname, "%s/msg-%s.part-%s.%s",
203		    mip->mi_detachdir, mip->mi_msgstr,
204		    partstr, subtype);
205	}
206
207	/*
208	 * Make up the prompt
209	 */
210	(void)sasprintf(&prompt, "%-7s filename: ", partstr);
211
212	/*
213	 * The main loop.
214	 */
215	do {
216		struct stat sb;
217		char *fname;
218
219		if ((fname = detach_get_fname(prompt, pathname)) == NULL)
220			return NULL;
221		/*
222		 * Make sure we don't have the name of something other
223		 * than a normal file!
224		 */
225		if (stat(fname, &sb) == 0 && !S_ISREG(sb.st_mode)) {
226			(void)printf("not a regular file: %s", fname);
227			if (!detach_ctl.ask)
228				return NULL;
229			continue;
230		}
231		switch (detach_open_core(fname, partstr)) {
232		case DETACH_OPEN_OK:
233			return fname;
234		case DETACH_NEXT:
235			return NULL;
236		case DETACH_RENAME:
237			detach_ctl.batch = 0;
238			break;
239		case DETACH_FAILED:
240			break;
241		}
242	} while (!detach_ctl.batch);
243
244	return NULL;
245}
246
247/*
248 * The main entry point for detaching.
249 */
250PUBLIC FILE *
251mime_detach_parts(struct mime_info *mip)
252{
253	mime_codec_t dec;
254	char *pathname;
255
256	if (mip->mi_ignore_body || mip->mp->m_blines == 0)
257		return NULL;
258
259	if ((dec = mime_fio_decoder(mip->mi_encoding)) == NULL &&
260	    (dec = mime_fio_decoder(MIME_TRANSFER_7BIT)) == NULL)
261		assert(/*CONSTCOND*/ 0); /* this should never get hit! */
262
263	if ((pathname = detach_open_target(mip)) == NULL)
264		return NULL;
265
266	(void)printf("writing: %s\n", pathname);
267
268	/*
269	 * XXX - should we do character set conversion here (done by
270	 * run_decoder()), or just run dec()?
271	 */
272#if 0
273	mime_run_function(dec, pipe_end(mip), NULL);
274#else
275	run_decoder(mip, dec);
276#endif
277	return pipe_end(mip);
278}
279
280/*
281 * Set the message part number to be used when constructing a filename
282 * for detaching unnamed parts in detach_open_target().  When
283 * threading, this is not a single number, hence the string value.
284 */
285PUBLIC void
286mime_detach_msgnum(struct mime_info *mip, const char *msgstr)
287{
288	for (/*EMPTY*/; mip; mip = mip->mi_flink)
289		mip->mi_msgstr = msgstr;
290}
291
292#endif /* MIME_SUPPORT */
293