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