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