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