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 301590Srgrimes#if 0 311590Srgrimes#ifndef lint 321590Srgrimesstatic const char copyright[] = 331590Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 341590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3527270Scharnier#endif /* not lint */ 361590Srgrimes 371590Srgrimes#ifndef lint 381590Srgrimesstatic char sccsid[] = "@(#)uudecode.c 8.2 (Berkeley) 4/2/94"; 391590Srgrimes#endif /* not lint */ 401590Srgrimes#endif 4127270Scharnier#include <sys/cdefs.h> 421590Srgrimes__FBSDID("$FreeBSD$"); 4327270Scharnier 4427270Scharnier/* 4550477Speter * uudecode [file ...] 461590Srgrimes * 471590Srgrimes * create the specified file, decoding as you go. 481590Srgrimes * used with uuencode. 491590Srgrimes */ 5027270Scharnier#include <sys/param.h> 511590Srgrimes#include <sys/socket.h> 521590Srgrimes#include <sys/stat.h> 5318725Sjkh 5427270Scharnier#include <netinet/in.h> 5513678Swosch 5627270Scharnier#include <ctype.h> 571590Srgrimes#include <err.h> 5827270Scharnier#include <errno.h> 5927270Scharnier#include <fcntl.h> 6027270Scharnier#include <libgen.h> 6127270Scharnier#include <pwd.h> 621590Srgrimes#include <resolv.h> 631590Srgrimes#include <stdio.h> 641590Srgrimes#include <stdlib.h> 651590Srgrimes#include <string.h> 661590Srgrimes#include <unistd.h> 6749123Sgreen 681590Srgrimesstatic const char *infile, *outfile; 691590Srgrimesstatic FILE *infp, *outfp; 701590Srgrimesstatic int base64, cflag, iflag, oflag, pflag, rflag, sflag; 711590Srgrimes 721590Srgrimesstatic void usage(void); 731590Srgrimesstatic int decode(void); 741590Srgrimesstatic int decode2(void); 751590Srgrimesstatic int uu_decode(void); 7649123Sgreenstatic int base64_decode(void); 7749123Sgreen 7849123Sgreenint 7949123Sgreenmain(int argc, char *argv[]) 8049123Sgreen{ 8149123Sgreen int rval, ch; 821590Srgrimes 831590Srgrimes if (strcmp(basename(argv[0]), "b64decode") == 0) 841590Srgrimes base64 = 1; 851590Srgrimes 861590Srgrimes while ((ch = getopt(argc, argv, "cimo:prs")) != -1) { 871590Srgrimes switch (ch) { 881590Srgrimes case 'c': 891590Srgrimes if (oflag || rflag) 901590Srgrimes usage(); 911590Srgrimes cflag = 1; /* multiple uudecode'd files */ 921590Srgrimes break; 9327270Scharnier case 'i': 941590Srgrimes iflag = 1; /* ask before override files */ 951590Srgrimes break; 961590Srgrimes case 'm': 971590Srgrimes base64 = 1; 9818725Sjkh break; 9966590Sru case 'o': 10018725Sjkh if (cflag || pflag || rflag || sflag) 10118725Sjkh usage(); 10218725Sjkh oflag = 1; /* output to the specified file */ 10327270Scharnier sflag = 1; /* do not strip pathnames for output */ 10427270Scharnier outfile = optarg; /* set the output filename */ 10518725Sjkh break; 10666590Sru case 'p': 10718725Sjkh if (oflag) 10818725Sjkh usage(); 1091590Srgrimes pflag = 1; /* print output to stdout */ 1101590Srgrimes break; 1111590Srgrimes case 'r': 11213678Swosch if (cflag || oflag) 11313678Swosch usage(); 11413678Swosch rflag = 1; /* decode raw data */ 11513678Swosch break; 11613678Swosch case 's': 11727270Scharnier if (oflag) 1181590Srgrimes usage(); 1191590Srgrimes sflag = 1; /* do not strip pathnames for output */ 1201590Srgrimes break; 1211590Srgrimes default: 1221590Srgrimes usage(); 1231590Srgrimes } 1241590Srgrimes } 12549123Sgreen argc -= optind; 12649123Sgreen argv += optind; 12749123Sgreen 12849123Sgreen if (*argv != NULL) { 12949123Sgreen rval = 0; 13049123Sgreen do { 1311590Srgrimes infp = fopen(infile = *argv, "r"); 1321590Srgrimes if (infp == NULL) { 13349123Sgreen warn("%s", *argv); 13449123Sgreen rval = 1; 13549123Sgreen continue; 1361590Srgrimes } 1371590Srgrimes rval |= decode(); 1381590Srgrimes fclose(infp); 13927270Scharnier } while (*++argv); 14027270Scharnier } else { 14127270Scharnier infile = "stdin"; 14249123Sgreen infp = stdin; 14327270Scharnier rval = decode(); 14427270Scharnier } 14527270Scharnier exit(rval); 14627270Scharnier} 1471590Srgrimes 1481590Srgrimesstatic int 1491590Srgrimesdecode(void) 1501590Srgrimes{ 1511590Srgrimes int r, v; 1521590Srgrimes 1531590Srgrimes if (rflag) { 1541590Srgrimes /* relaxed alternative to decode2() */ 1551590Srgrimes outfile = "/dev/stdout"; 1561590Srgrimes outfp = stdout; 1571590Srgrimes if (base64) 1581590Srgrimes return (base64_decode()); 1591590Srgrimes else 1601590Srgrimes return (uu_decode()); 1611590Srgrimes } 1621590Srgrimes v = decode2(); 1631590Srgrimes if (v == EOF) { 1641590Srgrimes warnx("%s: missing or bad \"begin\" line", infile); 1651590Srgrimes return (1); 1661590Srgrimes } 1671590Srgrimes for (r = v; cflag; r |= v) { 1681590Srgrimes v = decode2(); 1691590Srgrimes if (v == EOF) 1701590Srgrimes break; 171 } 172 return (r); 173} 174 175static int 176decode2(void) 177{ 178 int flags, fd, mode; 179 size_t n, m; 180 char *p, *q; 181 void *handle; 182 struct passwd *pw; 183 struct stat st; 184 char buf[MAXPATHLEN + 1]; 185 186 base64 = 0; 187 /* search for header line */ 188 for (;;) { 189 if (fgets(buf, sizeof(buf), infp) == NULL) 190 return (EOF); 191 p = buf; 192 if (strncmp(p, "begin-base64 ", 13) == 0) { 193 base64 = 1; 194 p += 13; 195 } else if (strncmp(p, "begin ", 6) == 0) 196 p += 6; 197 else 198 continue; 199 /* p points to mode */ 200 q = strchr(p, ' '); 201 if (q == NULL) 202 continue; 203 *q++ = '\0'; 204 /* q points to filename */ 205 n = strlen(q); 206 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r')) 207 q[--n] = '\0'; 208 /* found valid header? */ 209 if (n > 0) 210 break; 211 } 212 213 handle = setmode(p); 214 if (handle == NULL) { 215 warnx("%s: unable to parse file mode", infile); 216 return (1); 217 } 218 mode = getmode(handle, 0) & 0666; 219 free(handle); 220 221 if (sflag) { 222 /* don't strip, so try ~user/file expansion */ 223 p = NULL; 224 pw = NULL; 225 if (*q == '~') 226 p = strchr(q, '/'); 227 if (p != NULL) { 228 *p = '\0'; 229 pw = getpwnam(q + 1); 230 *p = '/'; 231 } 232 if (pw != NULL) { 233 n = strlen(pw->pw_dir); 234 if (buf + n > p) { 235 /* make room */ 236 m = strlen(p); 237 if (sizeof(buf) < n + m) { 238 warnx("%s: bad output filename", 239 infile); 240 return (1); 241 } 242 p = memmove(buf + n, p, m); 243 } 244 q = memcpy(p - n, pw->pw_dir, n); 245 } 246 } else { 247 /* strip down to leaf name */ 248 p = strrchr(q, '/'); 249 if (p != NULL) 250 q = p + 1; 251 } 252 if (!oflag) 253 outfile = q; 254 255 /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */ 256 if (pflag || strcmp(outfile, "/dev/stdout") == 0) 257 outfp = stdout; 258 else { 259 flags = O_WRONLY | O_CREAT | O_EXCL; 260 if (lstat(outfile, &st) == 0) { 261 if (iflag) { 262 warnc(EEXIST, "%s: %s", infile, outfile); 263 return (0); 264 } 265 switch (st.st_mode & S_IFMT) { 266 case S_IFREG: 267 case S_IFLNK: 268 /* avoid symlink attacks */ 269 if (unlink(outfile) == 0 || errno == ENOENT) 270 break; 271 warn("%s: unlink %s", infile, outfile); 272 return (1); 273 case S_IFDIR: 274 warnc(EISDIR, "%s: %s", infile, outfile); 275 return (1); 276 default: 277 if (oflag) { 278 /* trust command-line names */ 279 flags &= ~O_EXCL; 280 break; 281 } 282 warnc(EEXIST, "%s: %s", infile, outfile); 283 return (1); 284 } 285 } else if (errno != ENOENT) { 286 warn("%s: %s", infile, outfile); 287 return (1); 288 } 289 if ((fd = open(outfile, flags, mode)) < 0 || 290 (outfp = fdopen(fd, "w")) == NULL) { 291 warn("%s: %s", infile, outfile); 292 return (1); 293 } 294 } 295 296 if (base64) 297 return (base64_decode()); 298 else 299 return (uu_decode()); 300} 301 302static int 303getline(char *buf, size_t size) 304{ 305 306 if (fgets(buf, size, infp) != NULL) 307 return (2); 308 if (rflag) 309 return (0); 310 warnx("%s: %s: short file", infile, outfile); 311 return (1); 312} 313 314static int 315checkend(const char *ptr, const char *end, const char *msg) 316{ 317 size_t n; 318 319 n = strlen(end); 320 if (strncmp(ptr, end, n) != 0 || 321 strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) { 322 warnx("%s: %s: %s", infile, outfile, msg); 323 return (1); 324 } 325 if (fclose(outfp) != 0) { 326 warn("%s: %s", infile, outfile); 327 return (1); 328 } 329 return (0); 330} 331 332static int 333uu_decode(void) 334{ 335 int i, ch; 336 char *p; 337 char buf[MAXPATHLEN+1]; 338 339 /* for each input line */ 340 for (;;) { 341 switch (getline(buf, sizeof(buf))) { 342 case 0: 343 return (0); 344 case 1: 345 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 } else { 377 if (i >= 1) { 378 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) 379 OUT_OF_RANGE; 380 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 381 putc(ch, outfp); 382 } 383 if (i >= 2) { 384 if (!(IS_DEC(*(p + 1)) && 385 IS_DEC(*(p + 2)))) 386 OUT_OF_RANGE; 387 388 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 389 putc(ch, outfp); 390 } 391 if (i >= 3) { 392 if (!(IS_DEC(*(p + 2)) && 393 IS_DEC(*(p + 3)))) 394 OUT_OF_RANGE; 395 ch = DEC(p[2]) << 6 | DEC(p[3]); 396 putc(ch, outfp); 397 } 398 } 399 } 400 switch (getline(buf, sizeof(buf))) { 401 case 0: 402 return (0); 403 case 1: 404 return (1); 405 default: 406 return (checkend(buf, "end", "no \"end\" line")); 407 } 408} 409 410static int 411base64_decode(void) 412{ 413 int n, count, count4; 414 char inbuf[MAXPATHLEN + 1], *p; 415 unsigned char outbuf[MAXPATHLEN * 4]; 416 char leftover[MAXPATHLEN + 1]; 417 418 leftover[0] = '\0'; 419 for (;;) { 420 strcpy(inbuf, leftover); 421 switch (getline(inbuf + strlen(inbuf), 422 sizeof(inbuf) - strlen(inbuf))) { 423 case 0: 424 return (0); 425 case 1: 426 return (1); 427 } 428 429 count = 0; 430 count4 = -1; 431 p = inbuf; 432 while (*p != '\0') { 433 /* 434 * Base64 encoded strings have the following 435 * characters in them: A-Z, a-z, 0-9 and +, / and = 436 */ 437 if (isalnum(*p) || *p == '+' || *p == '/' || *p == '=') 438 count++; 439 if (count % 4 == 0) 440 count4 = p - inbuf; 441 p++; 442 } 443 444 strcpy(leftover, inbuf + count4 + 1); 445 inbuf[count4 + 1] = 0; 446 447 n = b64_pton(inbuf, outbuf, sizeof(outbuf)); 448 449 if (n < 0) 450 break; 451 fwrite(outbuf, 1, n, outfp); 452 } 453 return (checkend(inbuf, "====", "error decoding base64 input stream")); 454} 455 456static void 457usage(void) 458{ 459 460 (void)fprintf(stderr, 461 "usage: uudecode [-cimprs] [file ...]\n" 462 " uudecode [-i] -o output_file [file]\n" 463 " b64decode [-cimprs] [file ...]\n" 464 " b64decode [-i] -o output_file [file]\n"); 465 exit(1); 466} 467