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