uudecode.c revision 114594
1/*- 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#if 0 35#ifndef lint 36static const char copyright[] = 37"@(#) Copyright (c) 1983, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39#endif /* not lint */ 40 41#ifndef lint 42static char sccsid[] = "@(#)uudecode.c 8.2 (Berkeley) 4/2/94"; 43#endif /* not lint */ 44#endif 45#include <sys/cdefs.h> 46__FBSDID("$FreeBSD: head/usr.bin/uudecode/uudecode.c 114594 2003-05-03 19:44:46Z obrien $"); 47 48/* 49 * uudecode [file ...] 50 * 51 * create the specified file, decoding as you go. 52 * used with uuencode. 53 */ 54#include <sys/param.h> 55#include <sys/socket.h> 56#include <sys/stat.h> 57 58#include <netinet/in.h> 59 60#include <err.h> 61#include <errno.h> 62#include <fcntl.h> 63#include <libgen.h> 64#include <pwd.h> 65#include <resolv.h> 66#include <stdio.h> 67#include <stdlib.h> 68#include <string.h> 69#include <unistd.h> 70 71static const char *infile, *outfile; 72static FILE *infp, *outfp; 73static int base64, cflag, iflag, oflag, pflag, rflag, sflag; 74 75static void usage(void); 76static int decode(void); 77static int decode2(void); 78static int uu_decode(void); 79static int base64_decode(void); 80 81int 82main(int argc, char *argv[]) 83{ 84 int rval, ch; 85 86 if (strcmp(basename(argv[0]), "b64decode") == 0) 87 base64 = 1; 88 89 while ((ch = getopt(argc, argv, "cimo:prs")) != -1) { 90 switch(ch) { 91 case 'c': 92 if (oflag || rflag) 93 usage(); 94 cflag = 1; /* multiple uudecode'd files */ 95 break; 96 case 'i': 97 iflag = 1; /* ask before override files */ 98 break; 99 case 'm': 100 base64 = 1; 101 break; 102 case 'o': 103 if (cflag || pflag || rflag || sflag) 104 usage(); 105 oflag = 1; /* output to the specified file */ 106 sflag = 1; /* do not strip pathnames for output */ 107 outfile = optarg; /* set the output filename */ 108 break; 109 case 'p': 110 if (oflag) 111 usage(); 112 pflag = 1; /* print output to stdout */ 113 break; 114 case 'r': 115 if (cflag || oflag) 116 usage(); 117 rflag = 1; /* decode raw data */ 118 break; 119 case 's': 120 if (oflag) 121 usage(); 122 sflag = 1; /* do not strip pathnames for output */ 123 break; 124 default: 125 usage(); 126 } 127 } 128 argc -= optind; 129 argv += optind; 130 131 if (*argv) { 132 rval = 0; 133 do { 134 infp = fopen(infile = *argv, "r"); 135 if (infp == NULL) { 136 warn("%s", *argv); 137 rval = 1; 138 continue; 139 } 140 rval |= decode(); 141 fclose(infp); 142 } while (*++argv); 143 } else { 144 infile = "stdin"; 145 infp = stdin; 146 rval = decode(); 147 } 148 exit(rval); 149} 150 151static int 152decode(void) 153{ 154 int r, v; 155 156 if (rflag) { 157 /* relaxed alternative to decode2() */ 158 outfile = "/dev/stdout"; 159 outfp = stdout; 160 if (base64) 161 return (base64_decode()); 162 else 163 return (uu_decode()); 164 } 165 v = decode2(); 166 if (v == EOF) { 167 warnx("%s: missing or bad \"begin\" line", infile); 168 return (1); 169 } 170 for (r = v; cflag; r |= v) { 171 v = decode2(); 172 if (v == EOF) 173 break; 174 } 175 return (r); 176} 177 178static int 179decode2(void) 180{ 181 int flags, fd, mode; 182 size_t n, m; 183 char *p, *q; 184 void *handle; 185 struct passwd *pw; 186 struct stat st; 187 char buf[MAXPATHLEN+1]; 188 189 base64 = 0; 190 /* search for header line */ 191 for (;;) { 192 if (fgets(buf, sizeof(buf), infp) == NULL) 193 return (EOF); 194 p = buf; 195 if (strncmp(p, "begin-base64 ", 13) == 0) { 196 base64 = 1; 197 p += 13; 198 } else if (strncmp(p, "begin ", 6) == 0) 199 p += 6; 200 else 201 continue; 202 /* p points to mode */ 203 q = strchr(p, ' '); 204 if (q == NULL) 205 continue; 206 *q++ = '\0'; 207 /* q points to filename */ 208 n = strlen(q); 209 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r')) 210 q[--n] = '\0'; 211 /* found valid header? */ 212 if (n > 0) 213 break; 214 } 215 216 handle = setmode(p); 217 if (handle == NULL) { 218 warnx("%s: unable to parse file mode", infile); 219 return (1); 220 } 221 mode = getmode(handle, 0) & 0666; 222 free(handle); 223 224 if (sflag) { 225 /* don't strip, so try ~user/file expansion */ 226 p = NULL; 227 pw = NULL; 228 if (*q == '~') 229 p = strchr(q, '/'); 230 if (p != NULL) { 231 *p = '\0'; 232 pw = getpwnam(q + 1); 233 *p = '/'; 234 } 235 if (pw != NULL) { 236 n = strlen(pw->pw_dir); 237 if (buf + n > p) { 238 /* make room */ 239 m = strlen(p); 240 if (sizeof(buf) < n + m) { 241 warnx("%s: bad output filename", 242 infile); 243 return (1); 244 } 245 p = memmove(buf + n, p, m); 246 } 247 q = memcpy(p - n, pw->pw_dir, n); 248 } 249 } else { 250 /* strip down to leaf name */ 251 p = strrchr(q, '/'); 252 if (p != NULL) 253 q = p + 1; 254 } 255 if (!oflag) 256 outfile = q; 257 258 /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */ 259 if (pflag || strcmp(outfile, "/dev/stdout") == 0) 260 outfp = stdout; 261 else { 262 flags = O_WRONLY|O_CREAT|O_EXCL; 263 if (lstat(outfile, &st) == 0) { 264 if (iflag) { 265 warnc(EEXIST, "%s: %s", infile, outfile); 266 return (0); 267 } 268 switch (st.st_mode & S_IFMT) { 269 case S_IFREG: 270 case S_IFLNK: 271 /* avoid symlink attacks */ 272 if (unlink(outfile) == 0 || errno == ENOENT) 273 break; 274 warn("%s: unlink %s", infile, outfile); 275 return (1); 276 case S_IFDIR: 277 warnc(EISDIR, "%s: %s", infile, outfile); 278 return (1); 279 default: 280 if (oflag) { 281 /* trust command-line names */ 282 flags &= ~O_EXCL; 283 break; 284 } 285 warnc(EEXIST, "%s: %s", infile, outfile); 286 return (1); 287 } 288 } else if (errno != ENOENT) { 289 warn("%s: %s", infile, outfile); 290 return (1); 291 } 292 if ((fd = open(outfile, flags, mode)) < 0 || 293 (outfp = fdopen(fd, "w")) == NULL) { 294 warn("%s: %s", infile, outfile); 295 return (1); 296 } 297 } 298 299 if (base64) 300 return (base64_decode()); 301 else 302 return (uu_decode()); 303} 304 305static int 306getline(char *buf, size_t size) 307{ 308 if (fgets(buf, size, infp) != NULL) 309 return (2); 310 if (rflag) 311 return (0); 312 warnx("%s: %s: short file", infile, outfile); 313 return (1); 314} 315 316static int 317checkend(const char *ptr, const char *end, const char *msg) 318{ 319 size_t n; 320 321 n = strlen(end); 322 if (strncmp(ptr, end, n) != 0 || 323 strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) { 324 warnx("%s: %s: %s", infile, outfile, msg); 325 return (1); 326 } 327 if (fclose(outfp) != 0) { 328 warn("%s: %s", infile, outfile); 329 return (1); 330 } 331 return (0); 332} 333 334static int 335uu_decode(void) 336{ 337 int i, ch; 338 char *p; 339 char buf[MAXPATHLEN+1]; 340 341 /* for each input line */ 342 for (;;) { 343 switch (getline(buf, sizeof(buf))) { 344 case 0: return (0); 345 case 1: return (1); 346 } 347 348#define DEC(c) (((c) - ' ') & 077) /* single character decode */ 349#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) 350 351#define OUT_OF_RANGE do { \ 352 warnx("%s: %s: character out of range: [%d-%d]", \ 353 infile, outfile, 1 + ' ', 077 + ' ' + 1); \ 354 return (1); \ 355} while (0) 356 357 /* 358 * `i' is used to avoid writing out all the characters 359 * at the end of the file. 360 */ 361 p = buf; 362 if ((i = DEC(*p)) <= 0) 363 break; 364 for (++p; i > 0; p += 4, i -= 3) 365 if (i >= 3) { 366 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && 367 IS_DEC(*(p + 2)) && IS_DEC(*(p + 3)))) 368 OUT_OF_RANGE; 369 370 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 371 putc(ch, outfp); 372 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 373 putc(ch, outfp); 374 ch = DEC(p[2]) << 6 | DEC(p[3]); 375 putc(ch, outfp); 376 } 377 else { 378 if (i >= 1) { 379 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) 380 OUT_OF_RANGE; 381 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 382 putc(ch, outfp); 383 } 384 if (i >= 2) { 385 if (!(IS_DEC(*(p + 1)) && 386 IS_DEC(*(p + 2)))) 387 OUT_OF_RANGE; 388 389 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 390 putc(ch, outfp); 391 } 392 if (i >= 3) { 393 if (!(IS_DEC(*(p + 2)) && 394 IS_DEC(*(p + 3)))) 395 OUT_OF_RANGE; 396 ch = DEC(p[2]) << 6 | DEC(p[3]); 397 putc(ch, outfp); 398 } 399 } 400 } 401 switch (getline(buf, sizeof(buf))) { 402 case 0: return (0); 403 case 1: return (1); 404 default: return (checkend(buf, "end", "no \"end\" line")); 405 } 406} 407 408static int 409base64_decode(void) 410{ 411 int n; 412 char inbuf[MAXPATHLEN+1]; 413 unsigned char outbuf[MAXPATHLEN * 4]; 414 415 for (;;) { 416 switch (getline(inbuf, sizeof(inbuf))) { 417 case 0: return (0); 418 case 1: return (1); 419 } 420 n = b64_pton(inbuf, outbuf, sizeof(outbuf)); 421 if (n < 0) 422 break; 423 fwrite(outbuf, 1, n, outfp); 424 } 425 return (checkend(inbuf, "====", 426 "error decoding base64 input stream")); 427} 428 429static void 430usage(void) 431{ 432 (void)fprintf(stderr, 433"usage: uudecode [-cimprs] [file ...]\n" 434" uudecode [-i] -o output_file [file]\n" 435" b64decode [-cimprs] [file ...]\n" 436" b64decode [-i] -o output_file [file]\n"); 437 exit(1); 438} 439