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