uudecode.c revision 103197
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 * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3528564Scharnierstatic const char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1983, 1993\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381590Srgrimes#endif /* not lint */
391590Srgrimes
4096438Smike#if 0
411590Srgrimes#ifndef lint
421590Srgrimesstatic char sccsid[] = "@(#)uudecode.c	8.2 (Berkeley) 4/2/94";
4396438Smike#endif /* not lint */
4428564Scharnier#endif
451590Srgrimes
4696438Smike#include <sys/cdefs.h>
4796438Smike__FBSDID("$FreeBSD: head/usr.bin/uudecode/uudecode.c 103197 2002-09-10 18:52:03Z fanf $");
4896438Smike
491590Srgrimes/*
501590Srgrimes * uudecode [file ...]
511590Srgrimes *
521590Srgrimes * create the specified file, decoding as you go.
531590Srgrimes * used with uuencode.
541590Srgrimes */
551590Srgrimes#include <sys/param.h>
5691661Sjmallett#include <sys/socket.h>
571590Srgrimes#include <sys/stat.h>
581590Srgrimes
5991661Sjmallett#include <netinet/in.h>
6091661Sjmallett
6128564Scharnier#include <err.h>
621590Srgrimes#include <pwd.h>
6391661Sjmallett#include <resolv.h>
641590Srgrimes#include <stdio.h>
6528564Scharnier#include <stdlib.h>
661590Srgrimes#include <string.h>
6728564Scharnier#include <unistd.h>
681590Srgrimes
6987304Sdwmaloneconst char *filename;
7089882Smikechar *outfile;
7189882Smikeint cflag, iflag, oflag, pflag, sflag;
721590Srgrimes
7392922Simpstatic void usage(void);
7492922Simpint	decode(void);
7592922Simpint	decode2(int);
7692922Simpvoid	base64_decode(const char *);
7719078Swosch
781590Srgrimesint
7996386Salfredmain(int argc, char *argv[])
801590Srgrimes{
8119078Swosch	int rval, ch;
821590Srgrimes
8389882Smike	while ((ch = getopt(argc, argv, "cio:ps")) != -1) {
8419078Swosch		switch(ch) {
8519078Swosch		case 'c':
8689882Smike			if (oflag)
8789882Smike				usage();
8819078Swosch			cflag = 1; /* multiple uudecode'd files */
8919078Swosch			break;
9032780Swosch		case 'i':
9132780Swosch			iflag = 1; /* ask before override files */
9232780Swosch			break;
9389882Smike		case 'o':
9489882Smike			if (cflag || pflag || sflag)
9589882Smike				usage();
9689882Smike			oflag = 1; /* output to the specified file */
9789882Smike			sflag = 1; /* do not strip pathnames for output */
9889882Smike			outfile = optarg; /* set the output filename */
9989882Smike			break;
10019078Swosch		case 'p':
10189882Smike			if (oflag)
10289882Smike				usage();
10319078Swosch			pflag = 1; /* print output to stdout */
10419078Swosch			break;
10532780Swosch		case 's':
10689882Smike			if (oflag)
10789882Smike				usage();
10832780Swosch			sflag = 1; /* do not strip pathnames for output */
10932780Swosch			break;
11019078Swosch		default:
11128564Scharnier			usage();
11219078Swosch		}
11319078Swosch	}
11419078Swosch        argc -= optind;
11519078Swosch        argv += optind;
11619078Swosch
11719078Swosch	if (*argv) {
1181590Srgrimes		rval = 0;
1191590Srgrimes		do {
1201590Srgrimes			if (!freopen(filename = *argv, "r", stdin)) {
12128564Scharnier				warn("%s", *argv);
1221590Srgrimes				rval = 1;
1231590Srgrimes				continue;
1241590Srgrimes			}
1251590Srgrimes			rval |= decode();
1261590Srgrimes		} while (*++argv);
1271590Srgrimes	} else {
1281590Srgrimes		filename = "stdin";
1291590Srgrimes		rval = decode();
1301590Srgrimes	}
1311590Srgrimes	exit(rval);
1321590Srgrimes}
1331590Srgrimes
13419078Swoschint
13596438Smikedecode(void)
1361590Srgrimes{
13719078Swosch	int flag;
13819078Swosch
13919078Swosch	/* decode only one file per input stream */
140102890Sfanf	if (!cflag)
141103197Sfanf		return (decode2(0));
14219078Swosch
14319078Swosch	/* multiple uudecode'd files */
14419078Swosch	for (flag = 0; ; flag++)
14519078Swosch		if (decode2(flag))
146103197Sfanf			return (1);
14719078Swosch		else if (feof(stdin))
14819078Swosch			break;
14919078Swosch
150103197Sfanf	return (0);
15119078Swosch}
15219078Swosch
15319078Swoschint
15496438Smikedecode2(int flag)
15519078Swosch{
1561590Srgrimes	struct passwd *pw;
1571590Srgrimes	register int n;
15858828Ssheldonh	register char ch, *p;
159103195Sfanf	int base64, n1;
16092623Sjmallett	char buf[MAXPATHLEN+1];
16192623Sjmallett	char buffn[MAXPATHLEN+1]; /* file name buffer */
16292623Sjmallett	char *mode, *s;
16392623Sjmallett	void *mode_handle;
164103194Sfanf	struct stat st;
1651590Srgrimes
166103195Sfanf	base64 = 0;
1671590Srgrimes	/* search for header line */
1681590Srgrimes	do {
1691590Srgrimes		if (!fgets(buf, sizeof(buf), stdin)) {
17019078Swosch			if (flag) /* no error */
171103197Sfanf				return (0);
17219078Swosch
17328564Scharnier			warnx("%s: no \"begin\" line", filename);
174103197Sfanf			return (1);
1751590Srgrimes		}
17691661Sjmallett	} while (strncmp(buf, "begin", 5) != 0);
17722887Swosch
17891661Sjmallett	if (strncmp(buf, "begin-base64", 12) == 0)
17991661Sjmallett		base64 = 1;
18091661Sjmallett
18192623Sjmallett	/* Parse the header: begin{,-base64} mode outfile. */
18292623Sjmallett	s = strtok(buf, " ");
18392623Sjmallett	if (s == NULL)
18492623Sjmallett		errx(1, "no mode or filename in input file");
18592623Sjmallett	s = strtok(NULL, " ");
18692623Sjmallett	if (s == NULL)
18792623Sjmallett		errx(1, "no mode in input file");
18892623Sjmallett	else {
18992623Sjmallett		mode = strdup(s);
19092623Sjmallett		if (mode == NULL)
19192623Sjmallett			err(1, "strdup()");
19291661Sjmallett	}
19392623Sjmallett	if (!oflag) {
19492637Sjmallett		outfile = strtok(NULL, "\r\n");
19592623Sjmallett		if (outfile == NULL)
19692623Sjmallett			errx(1, "no filename in input file");
19792623Sjmallett	}
1981590Srgrimes
19992623Sjmallett	if (strlcpy(buf, outfile, sizeof(buf)) >= sizeof(buf))
20092623Sjmallett		errx(1, "%s: filename too long", outfile);
20132780Swosch	if (!sflag && !pflag) {
202102890Sfanf		strlcpy(buffn, buf, sizeof(buffn));
20332780Swosch		if (strrchr(buffn, '/') != NULL)
20432780Swosch			strncpy(buf, strrchr(buffn, '/') + 1, sizeof(buf));
20532780Swosch		if (buf[0] == '\0') {
20632780Swosch			warnx("%s: illegal filename", buffn);
207103197Sfanf			return (1);
20832780Swosch		}
20932780Swosch
21092623Sjmallett		/* handle ~user/file format */
21192623Sjmallett		if (buf[0] == '~') {
21292623Sjmallett			if (!(p = index(buf, '/'))) {
21392623Sjmallett				warnx("%s: illegal ~user", filename);
214103197Sfanf				return (1);
21592623Sjmallett			}
21692623Sjmallett			*p++ = '\0';
21792623Sjmallett			if (!(pw = getpwnam(buf + 1))) {
21892623Sjmallett				warnx("%s: no user %s", filename, buf);
219103197Sfanf				return (1);
22092623Sjmallett			}
22192623Sjmallett			n = strlen(pw->pw_dir);
22292623Sjmallett			n1 = strlen(p);
22392623Sjmallett			if (n + n1 + 2 > MAXPATHLEN) {
22492623Sjmallett				warnx("%s: path too long", filename);
225103197Sfanf				return (1);
22692623Sjmallett			}
22792623Sjmallett			bcopy(p, buf + n + 1, n1 + 1);
22892623Sjmallett			bcopy(pw->pw_dir, buf, n);
22992623Sjmallett			buf[n] = '/';
2301590Srgrimes		}
2311590Srgrimes	}
2321590Srgrimes
2331590Srgrimes	/* create output file, set mode */
23419078Swosch	if (pflag)
23519078Swosch		; /* print to stdout */
23619078Swosch
23732780Swosch	else {
23892623Sjmallett		mode_handle = setmode(mode);
23992623Sjmallett		if (mode_handle == NULL)
24092623Sjmallett			err(1, "setmode()");
24158666Ssheldonh		if (iflag && !access(buf, F_OK)) {
242103195Sfanf			warnx("not overwritten: %s", buf);
243103197Sfanf			return (0);
244103194Sfanf		} else if (freopen(buf, "w", stdout) == NULL ||
245103194Sfanf		    stat(buf, &st) < 0 || (S_ISREG(st.st_mode) &&
246103194Sfanf		    fchmod(fileno(stdout), getmode(mode_handle, 0) & 0666))) {
24732780Swosch			warn("%s: %s", buf, filename);
248103197Sfanf			return (1);
24932780Swosch		}
25092623Sjmallett		free(mode_handle);
25192623Sjmallett		free(mode);
2521590Srgrimes	}
25322887Swosch	strcpy(buffn, buf); /* store file name from header line */
2541590Srgrimes
2551590Srgrimes	/* for each input line */
25691661Sjmallettnext:
2571590Srgrimes	for (;;) {
2581590Srgrimes		if (!fgets(p = buf, sizeof(buf), stdin)) {
25928564Scharnier			warnx("%s: short file", filename);
260103197Sfanf			return (1);
2611590Srgrimes		}
26291661Sjmallett		if (base64) {
26391661Sjmallett			if (strncmp(buf, "====", 4) == 0)
26491661Sjmallett				return (0);
26591661Sjmallett			base64_decode(buf);
26691661Sjmallett			goto next;
26791661Sjmallett		}
2681590Srgrimes#define	DEC(c)	(((c) - ' ') & 077)		/* single character decode */
26924263Swosch#define IS_DEC(c) ( (((c) - ' ') >= 0) &&  (((c) - ' ') <= 077 + 1) )
27022887Swosch/* #define IS_DEC(c) (1) */
27122887Swosch
27222887Swosch#define OUT_OF_RANGE \
27322887Swosch{	\
27428564Scharnier    warnx( \
27528564Scharnier"\n\tinput file: %s\n\tencoded file: %s\n\tcharacter out of range: [%d-%d]", \
27628564Scharnier 	filename, buffn, 1 + ' ', 077 + ' ' + 1); \
277103197Sfanf        return (1); \
27822887Swosch}
27922887Swosch
2801590Srgrimes		/*
2811590Srgrimes		 * `n' is used to avoid writing out all the characters
2821590Srgrimes		 * at the end of the file.
2831590Srgrimes		 */
2841590Srgrimes		if ((n = DEC(*p)) <= 0)
2851590Srgrimes			break;
2861590Srgrimes		for (++p; n > 0; p += 4, n -= 3)
2871590Srgrimes			if (n >= 3) {
288102890Sfanf				if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
28922887Swosch				     IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
29022887Swosch                                	OUT_OF_RANGE
29122887Swosch
2921590Srgrimes				ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
293103195Sfanf				putchar(ch);
2941590Srgrimes				ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
295103195Sfanf				putchar(ch);
2961590Srgrimes				ch = DEC(p[2]) << 6 | DEC(p[3]);
297103195Sfanf				putchar(ch);
2981590Srgrimes			}
2991590Srgrimes			else {
3001590Srgrimes				if (n >= 1) {
30122887Swosch					if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
30222887Swosch	                                	OUT_OF_RANGE
3031590Srgrimes					ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
304103195Sfanf					putchar(ch);
3051590Srgrimes				}
3061590Srgrimes				if (n >= 2) {
307102890Sfanf					if (!(IS_DEC(*(p + 1)) &&
30822887Swosch						IS_DEC(*(p + 2))))
30922887Swosch		                                OUT_OF_RANGE
31022887Swosch
3111590Srgrimes					ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
312103195Sfanf					putchar(ch);
3131590Srgrimes				}
3141590Srgrimes				if (n >= 3) {
315102890Sfanf					if (!(IS_DEC(*(p + 2)) &&
31622887Swosch						IS_DEC(*(p + 3))))
31722887Swosch		                                OUT_OF_RANGE
3181590Srgrimes					ch = DEC(p[2]) << 6 | DEC(p[3]);
319103195Sfanf					putchar(ch);
3201590Srgrimes				}
3211590Srgrimes			}
3221590Srgrimes	}
323102890Sfanf	if (fgets(buf, sizeof(buf), stdin) == NULL ||
32422894Swosch	    (strcmp(buf, "end") && strcmp(buf, "end\n") &&
32522894Swosch	     strcmp(buf, "end\r\n"))) {
32628564Scharnier		warnx("%s: no \"end\" line", filename);
327103197Sfanf		return (1);
3281590Srgrimes	}
329103197Sfanf	return (0);
3301590Srgrimes}
3311590Srgrimes
33291661Sjmallettvoid
33396438Smikebase64_decode(const char *stream)
33491661Sjmallett{
33591661Sjmallett	unsigned char out[MAXPATHLEN * 4];
33691661Sjmallett	int rv;
33791661Sjmallett
33895101Sjmallett	if (index(stream, '\r') != NULL)
33995101Sjmallett		*index(stream, '\r') = '\0';
34095101Sjmallett	if (index(stream, '\n') != NULL)
34195101Sjmallett		*index(stream, '\n') = '\0';
34291661Sjmallett	rv = b64_pton(stream, out, (sizeof(out) / sizeof(out[0])));
34391661Sjmallett	if (rv == -1)
34491661Sjmallett		errx(1, "b64_pton: error decoding base64 input stream");
34595103Sjmallett	fwrite(out, 1, rv, stdout);
34691661Sjmallett}
34791661Sjmallett
34828564Scharnierstatic void
34996438Smikeusage(void)
3501590Srgrimes{
35196943Sjmallett	(void)fprintf(stderr,
35296943Sjmallett"usage: uudecode [-cips] [file ...]\n"
35396943Sjmallett"       uudecode [-i] -o output_file [file]\n"
35496943Sjmallett"       b64decode [-cips] [file ...]\n"
35596943Sjmallett"       b64decode [-i] -o output_file [file]\n");
3561590Srgrimes	exit(1);
3571590Srgrimes}
358