1 /* $NetBSD: mime_detach.c,v 1.2 2007/10/23 14:58:44 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.2 2007/10/23 14:58:44 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_WSP(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_WSP(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