11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1983, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 30114594Sobrien#if 0 311590Srgrimes#ifndef lint 3228564Scharnierstatic const char copyright[] = 331590Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 341590Srgrimes The Regents of the University of California. All rights reserved.\n"; 351590Srgrimes#endif /* not lint */ 361590Srgrimes 371590Srgrimes#ifndef lint 381590Srgrimesstatic char sccsid[] = "@(#)uudecode.c 8.2 (Berkeley) 4/2/94"; 3996438Smike#endif /* not lint */ 4028564Scharnier#endif 4196438Smike#include <sys/cdefs.h> 4296438Smike__FBSDID("$FreeBSD: releng/10.3/usr.bin/uudecode/uudecode.c 216370 2010-12-11 08:32:16Z joel $"); 4396438Smike 441590Srgrimes/* 451590Srgrimes * uudecode [file ...] 461590Srgrimes * 471590Srgrimes * create the specified file, decoding as you go. 481590Srgrimes * used with uuencode. 491590Srgrimes */ 501590Srgrimes#include <sys/param.h> 5191661Sjmallett#include <sys/socket.h> 521590Srgrimes#include <sys/stat.h> 531590Srgrimes 5491661Sjmallett#include <netinet/in.h> 5591661Sjmallett 56214010Sedwin#include <ctype.h> 5728564Scharnier#include <err.h> 58106275Sfanf#include <errno.h> 59106275Sfanf#include <fcntl.h> 60111588Sfanf#include <libgen.h> 611590Srgrimes#include <pwd.h> 6291661Sjmallett#include <resolv.h> 631590Srgrimes#include <stdio.h> 6428564Scharnier#include <stdlib.h> 651590Srgrimes#include <string.h> 6628564Scharnier#include <unistd.h> 671590Srgrimes 68106280Sfanfstatic const char *infile, *outfile; 69106280Sfanfstatic FILE *infp, *outfp; 70111588Sfanfstatic int base64, cflag, iflag, oflag, pflag, rflag, sflag; 711590Srgrimes 72106280Sfanfstatic void usage(void); 73106280Sfanfstatic int decode(void); 74106280Sfanfstatic int decode2(void); 75106280Sfanfstatic int uu_decode(void); 76106280Sfanfstatic int base64_decode(void); 7719078Swosch 781590Srgrimesint 7996386Salfredmain(int argc, char *argv[]) 801590Srgrimes{ 8119078Swosch int rval, ch; 821590Srgrimes 83111588Sfanf if (strcmp(basename(argv[0]), "b64decode") == 0) 84111588Sfanf base64 = 1; 85111588Sfanf 86111588Sfanf while ((ch = getopt(argc, argv, "cimo:prs")) != -1) { 87214002Sedwin switch (ch) { 8819078Swosch case 'c': 89111588Sfanf if (oflag || rflag) 9089882Smike usage(); 9119078Swosch cflag = 1; /* multiple uudecode'd files */ 9219078Swosch break; 9332780Swosch case 'i': 9432780Swosch iflag = 1; /* ask before override files */ 9532780Swosch break; 96111588Sfanf case 'm': 97111588Sfanf base64 = 1; 98111588Sfanf break; 9989882Smike case 'o': 100111588Sfanf if (cflag || pflag || rflag || sflag) 10189882Smike usage(); 10289882Smike oflag = 1; /* output to the specified file */ 10389882Smike sflag = 1; /* do not strip pathnames for output */ 10489882Smike outfile = optarg; /* set the output filename */ 10589882Smike break; 10619078Swosch case 'p': 10789882Smike if (oflag) 10889882Smike usage(); 10919078Swosch pflag = 1; /* print output to stdout */ 11019078Swosch break; 111111588Sfanf case 'r': 112111588Sfanf if (cflag || oflag) 113111588Sfanf usage(); 114111588Sfanf rflag = 1; /* decode raw data */ 115111588Sfanf break; 11632780Swosch case 's': 11789882Smike if (oflag) 11889882Smike usage(); 11932780Swosch sflag = 1; /* do not strip pathnames for output */ 12032780Swosch break; 12119078Swosch default: 12228564Scharnier usage(); 12319078Swosch } 12419078Swosch } 125214002Sedwin argc -= optind; 126214002Sedwin argv += optind; 12719078Swosch 128214002Sedwin if (*argv != NULL) { 1291590Srgrimes rval = 0; 1301590Srgrimes do { 131106280Sfanf infp = fopen(infile = *argv, "r"); 132106274Sfanf if (infp == NULL) { 13328564Scharnier warn("%s", *argv); 1341590Srgrimes rval = 1; 1351590Srgrimes continue; 1361590Srgrimes } 1371590Srgrimes rval |= decode(); 138106274Sfanf fclose(infp); 1391590Srgrimes } while (*++argv); 1401590Srgrimes } else { 141106280Sfanf infile = "stdin"; 142106274Sfanf infp = stdin; 1431590Srgrimes rval = decode(); 1441590Srgrimes } 1451590Srgrimes exit(rval); 1461590Srgrimes} 1471590Srgrimes 148106280Sfanfstatic int 14996438Smikedecode(void) 1501590Srgrimes{ 151103200Sfanf int r, v; 15219078Swosch 153111588Sfanf if (rflag) { 154111588Sfanf /* relaxed alternative to decode2() */ 155111588Sfanf outfile = "/dev/stdout"; 156111588Sfanf outfp = stdout; 157111588Sfanf if (base64) 158111588Sfanf return (base64_decode()); 159111588Sfanf else 160111588Sfanf return (uu_decode()); 161111588Sfanf } 162103200Sfanf v = decode2(); 163103200Sfanf if (v == EOF) { 164106280Sfanf warnx("%s: missing or bad \"begin\" line", infile); 165103200Sfanf return (1); 166103200Sfanf } 167103200Sfanf for (r = v; cflag; r |= v) { 168103200Sfanf v = decode2(); 169103200Sfanf if (v == EOF) 17019078Swosch break; 171103200Sfanf } 172103200Sfanf return (r); 17319078Swosch} 17419078Swosch 175106280Sfanfstatic int 176103200Sfanfdecode2(void) 17719078Swosch{ 178111588Sfanf int flags, fd, mode; 179106280Sfanf size_t n, m; 180106280Sfanf char *p, *q; 181106280Sfanf void *handle; 1821590Srgrimes struct passwd *pw; 183103200Sfanf struct stat st; 184214002Sedwin char buf[MAXPATHLEN + 1]; 1851590Srgrimes 186103195Sfanf base64 = 0; 1871590Srgrimes /* search for header line */ 188103200Sfanf for (;;) { 189106274Sfanf if (fgets(buf, sizeof(buf), infp) == NULL) 190103200Sfanf return (EOF); 191103200Sfanf p = buf; 192103200Sfanf if (strncmp(p, "begin-base64 ", 13) == 0) { 193103200Sfanf base64 = 1; 194103200Sfanf p += 13; 195103200Sfanf } else if (strncmp(p, "begin ", 6) == 0) 196103200Sfanf p += 6; 197103200Sfanf else 198103200Sfanf continue; 199103200Sfanf /* p points to mode */ 200103200Sfanf q = strchr(p, ' '); 201103200Sfanf if (q == NULL) 202103200Sfanf continue; 203103200Sfanf *q++ = '\0'; 204103200Sfanf /* q points to filename */ 205103200Sfanf n = strlen(q); 206103200Sfanf while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r')) 207103200Sfanf q[--n] = '\0'; 208103200Sfanf /* found valid header? */ 209103200Sfanf if (n > 0) 210103200Sfanf break; 211103200Sfanf } 21219078Swosch 213106280Sfanf handle = setmode(p); 214106280Sfanf if (handle == NULL) { 215106280Sfanf warnx("%s: unable to parse file mode", infile); 216103200Sfanf return (1); 21791661Sjmallett } 218106280Sfanf mode = getmode(handle, 0) & 0666; 219106280Sfanf free(handle); 2201590Srgrimes 221106280Sfanf if (sflag) { 222103200Sfanf /* don't strip, so try ~user/file expansion */ 223103200Sfanf p = NULL; 224103200Sfanf pw = NULL; 225103200Sfanf if (*q == '~') 226103200Sfanf p = strchr(q, '/'); 227103200Sfanf if (p != NULL) { 228103200Sfanf *p = '\0'; 229103200Sfanf pw = getpwnam(q + 1); 230103200Sfanf *p = '/'; 23132780Swosch } 232103200Sfanf if (pw != NULL) { 233106280Sfanf n = strlen(pw->pw_dir); 234106280Sfanf if (buf + n > p) { 235106280Sfanf /* make room */ 236106280Sfanf m = strlen(p); 237106280Sfanf if (sizeof(buf) < n + m) { 238106280Sfanf warnx("%s: bad output filename", 239106280Sfanf infile); 240106280Sfanf return (1); 241106280Sfanf } 242106280Sfanf p = memmove(buf + n, p, m); 243106280Sfanf } 244106280Sfanf q = memcpy(p - n, pw->pw_dir, n); 2451590Srgrimes } 246112377Sfanf } else { 247103200Sfanf /* strip down to leaf name */ 248103200Sfanf p = strrchr(q, '/'); 249103200Sfanf if (p != NULL) 250106280Sfanf q = p + 1; 2511590Srgrimes } 252106280Sfanf if (!oflag) 253106280Sfanf outfile = q; 2541590Srgrimes 255111586Sfanf /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */ 256111586Sfanf if (pflag || strcmp(outfile, "/dev/stdout") == 0) 257106275Sfanf outfp = stdout; 258106275Sfanf else { 259214002Sedwin flags = O_WRONLY | O_CREAT | O_EXCL; 260106280Sfanf if (lstat(outfile, &st) == 0) { 261106275Sfanf if (iflag) { 262106280Sfanf warnc(EEXIST, "%s: %s", infile, outfile); 263106275Sfanf return (0); 264106275Sfanf } 265106275Sfanf switch (st.st_mode & S_IFMT) { 266106275Sfanf case S_IFREG: 267106275Sfanf case S_IFLNK: 268106275Sfanf /* avoid symlink attacks */ 269106280Sfanf if (unlink(outfile) == 0 || errno == ENOENT) 270106275Sfanf break; 271106280Sfanf warn("%s: unlink %s", infile, outfile); 272106275Sfanf return (1); 273106275Sfanf case S_IFDIR: 274106280Sfanf warnc(EISDIR, "%s: %s", infile, outfile); 275106275Sfanf return (1); 276106275Sfanf default: 277106275Sfanf if (oflag) { 278106275Sfanf /* trust command-line names */ 279106275Sfanf flags &= ~O_EXCL; 280106275Sfanf break; 281106275Sfanf } 282106280Sfanf warnc(EEXIST, "%s: %s", infile, outfile); 283106275Sfanf return (1); 284106275Sfanf } 285106275Sfanf } else if (errno != ENOENT) { 286106280Sfanf warn("%s: %s", infile, outfile); 287106275Sfanf return (1); 288103200Sfanf } 289106280Sfanf if ((fd = open(outfile, flags, mode)) < 0 || 290106280Sfanf (outfp = fdopen(fd, "w")) == NULL) { 291106280Sfanf warn("%s: %s", infile, outfile); 292103197Sfanf return (1); 29332780Swosch } 2941590Srgrimes } 2951590Srgrimes 296103203Sfanf if (base64) 297106280Sfanf return (base64_decode()); 298106280Sfanf else 299106280Sfanf return (uu_decode()); 300106280Sfanf} 301103203Sfanf 302106280Sfanfstatic int 303111596Sfanfgetline(char *buf, size_t size) 304111596Sfanf{ 305214002Sedwin 306111596Sfanf if (fgets(buf, size, infp) != NULL) 307111596Sfanf return (2); 308111884Sfanf if (rflag) 309111596Sfanf return (0); 310111596Sfanf warnx("%s: %s: short file", infile, outfile); 311111596Sfanf return (1); 312111596Sfanf} 313111596Sfanf 314111596Sfanfstatic int 315111596Sfanfcheckend(const char *ptr, const char *end, const char *msg) 316111596Sfanf{ 317111596Sfanf size_t n; 318111596Sfanf 319111596Sfanf n = strlen(end); 320111596Sfanf if (strncmp(ptr, end, n) != 0 || 321111596Sfanf strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) { 322111596Sfanf warnx("%s: %s: %s", infile, outfile, msg); 323111596Sfanf return (1); 324111596Sfanf } 325111596Sfanf if (fclose(outfp) != 0) { 326111596Sfanf warn("%s: %s", infile, outfile); 327111596Sfanf return (1); 328111596Sfanf } 329111596Sfanf return (0); 330111596Sfanf} 331111596Sfanf 332111596Sfanfstatic int 333106280Sfanfuu_decode(void) 334106280Sfanf{ 335106280Sfanf int i, ch; 336106280Sfanf char *p; 337106280Sfanf char buf[MAXPATHLEN+1]; 338106280Sfanf 3391590Srgrimes /* for each input line */ 3401590Srgrimes for (;;) { 341111596Sfanf switch (getline(buf, sizeof(buf))) { 342214002Sedwin case 0: 343214002Sedwin return (0); 344214002Sedwin case 1: 345214002Sedwin return (1); 3461590Srgrimes } 347103203Sfanf 348214002Sedwin#define DEC(c) (((c) - ' ') & 077) /* single character decode */ 349214002Sedwin#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) 35022887Swosch 351103204Sfanf#define OUT_OF_RANGE do { \ 352103204Sfanf warnx("%s: %s: character out of range: [%d-%d]", \ 353106280Sfanf infile, outfile, 1 + ' ', 077 + ' ' + 1); \ 354214002Sedwin return (1); \ 355103204Sfanf} while (0) 35622887Swosch 3571590Srgrimes /* 358103210Smike * `i' is used to avoid writing out all the characters 3591590Srgrimes * at the end of the file. 3601590Srgrimes */ 361111596Sfanf p = buf; 362103210Smike if ((i = DEC(*p)) <= 0) 3631590Srgrimes break; 364103210Smike for (++p; i > 0; p += 4, i -= 3) 365103210Smike if (i >= 3) { 366102890Sfanf if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && 367214002Sedwin IS_DEC(*(p + 2)) && IS_DEC(*(p + 3)))) 368214002Sedwin OUT_OF_RANGE; 36922887Swosch 3701590Srgrimes ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 371106274Sfanf putc(ch, outfp); 3721590Srgrimes ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 373106274Sfanf putc(ch, outfp); 3741590Srgrimes ch = DEC(p[2]) << 6 | DEC(p[3]); 375106274Sfanf putc(ch, outfp); 376214002Sedwin } else { 377103210Smike if (i >= 1) { 37822887Swosch if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) 379103206Smike OUT_OF_RANGE; 3801590Srgrimes ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 381106274Sfanf putc(ch, outfp); 3821590Srgrimes } 383103210Smike if (i >= 2) { 384102890Sfanf if (!(IS_DEC(*(p + 1)) && 385214002Sedwin IS_DEC(*(p + 2)))) 386214002Sedwin OUT_OF_RANGE; 38722887Swosch 3881590Srgrimes ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 389106274Sfanf putc(ch, outfp); 3901590Srgrimes } 391103210Smike if (i >= 3) { 392102890Sfanf if (!(IS_DEC(*(p + 2)) && 393214002Sedwin IS_DEC(*(p + 3)))) 394214002Sedwin OUT_OF_RANGE; 3951590Srgrimes ch = DEC(p[2]) << 6 | DEC(p[3]); 396106274Sfanf putc(ch, outfp); 3971590Srgrimes } 3981590Srgrimes } 3991590Srgrimes } 400111596Sfanf switch (getline(buf, sizeof(buf))) { 401214002Sedwin case 0: 402214002Sedwin return (0); 403214002Sedwin case 1: 404214002Sedwin return (1); 405214002Sedwin default: 406214002Sedwin return (checkend(buf, "end", "no \"end\" line")); 4071590Srgrimes } 4081590Srgrimes} 4091590Srgrimes 410106280Sfanfstatic int 411106280Sfanfbase64_decode(void) 41291661Sjmallett{ 413214010Sedwin int n, count, count4; 414214010Sedwin char inbuf[MAXPATHLEN + 1], *p; 415106280Sfanf unsigned char outbuf[MAXPATHLEN * 4]; 416214010Sedwin char leftover[MAXPATHLEN + 1]; 41791661Sjmallett 418214010Sedwin leftover[0] = '\0'; 419103203Sfanf for (;;) { 420214010Sedwin strcpy(inbuf, leftover); 421214010Sedwin switch (getline(inbuf + strlen(inbuf), 422214010Sedwin sizeof(inbuf) - strlen(inbuf))) { 423214010Sedwin case 0: 424214010Sedwin return (0); 425214010Sedwin case 1: 426214010Sedwin return (1); 427103203Sfanf } 428214010Sedwin 429214010Sedwin count = 0; 430214010Sedwin count4 = -1; 431214010Sedwin p = inbuf; 432214010Sedwin while (*p != '\0') { 433214010Sedwin /* 434214010Sedwin * Base64 encoded strings have the following 435214010Sedwin * characters in them: A-Z, a-z, 0-9 and +, / and = 436214010Sedwin */ 437214010Sedwin if (isalnum(*p) || *p == '+' || *p == '/' || *p == '=') 438214010Sedwin count++; 439214010Sedwin if (count % 4 == 0) 440214010Sedwin count4 = p - inbuf; 441214010Sedwin p++; 442214010Sedwin } 443214010Sedwin 444214010Sedwin strcpy(leftover, inbuf + count4 + 1); 445214010Sedwin inbuf[count4 + 1] = 0; 446214010Sedwin 447106280Sfanf n = b64_pton(inbuf, outbuf, sizeof(outbuf)); 448214002Sedwin 449111596Sfanf if (n < 0) 450111596Sfanf break; 451106280Sfanf fwrite(outbuf, 1, n, outfp); 452103203Sfanf } 453214002Sedwin return (checkend(inbuf, "====", "error decoding base64 input stream")); 45491661Sjmallett} 45591661Sjmallett 45628564Scharnierstatic void 45796438Smikeusage(void) 4581590Srgrimes{ 459214002Sedwin 46096943Sjmallett (void)fprintf(stderr, 461214002Sedwin "usage: uudecode [-cimprs] [file ...]\n" 462214002Sedwin " uudecode [-i] -o output_file [file]\n" 463214002Sedwin " b64decode [-cimprs] [file ...]\n" 464214002Sedwin " b64decode [-i] -o output_file [file]\n"); 4651590Srgrimes exit(1); 4661590Srgrimes} 467