uudecode.c revision 103206
1170754Sdelphij/*-
2170754Sdelphij * Copyright (c) 1983, 1993
3170754Sdelphij *	The Regents of the University of California.  All rights reserved.
4170754Sdelphij *
5170754Sdelphij * Redistribution and use in source and binary forms, with or without
6170754Sdelphij * modification, are permitted provided that the following conditions
7170754Sdelphij * are met:
8170754Sdelphij * 1. Redistributions of source code must retain the above copyright
9170754Sdelphij *    notice, this list of conditions and the following disclaimer.
10170754Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
11170754Sdelphij *    notice, this list of conditions and the following disclaimer in the
12170754Sdelphij *    documentation and/or other materials provided with the distribution.
13170754Sdelphij * 3. All advertising materials mentioning features or use of this software
14170754Sdelphij *    must display the following acknowledgement:
15170754Sdelphij *	This product includes software developed by the University of
16170754Sdelphij *	California, Berkeley and its contributors.
17170754Sdelphij * 4. Neither the name of the University nor the names of its contributors
18170754Sdelphij *    may be used to endorse or promote products derived from this software
19170754Sdelphij *    without specific prior written permission.
20170754Sdelphij *
21170754Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22170754Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23170754Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24170754Sdelphij * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25170754Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26170754Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27170754Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28170754Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29170754Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30170754Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31170754Sdelphij * SUCH DAMAGE.
32170754Sdelphij */
33170754Sdelphij
34170754Sdelphij#ifndef lint
35170754Sdelphijstatic const char copyright[] =
36170754Sdelphij"@(#) Copyright (c) 1983, 1993\n\
37170754Sdelphij	The Regents of the University of California.  All rights reserved.\n";
38170754Sdelphij#endif /* not lint */
39170754Sdelphij
40170754Sdelphij#if 0
41170754Sdelphij#ifndef lint
42170754Sdelphijstatic char sccsid[] = "@(#)uudecode.c	8.2 (Berkeley) 4/2/94";
43170754Sdelphij#endif /* not lint */
44170754Sdelphij#endif
45170754Sdelphij
46170754Sdelphij#include <sys/cdefs.h>
47170754Sdelphij__FBSDID("$FreeBSD: head/usr.bin/uudecode/uudecode.c 103206 2002-09-11 01:00:56Z mike $");
48170754Sdelphij
49170754Sdelphij/*
50170754Sdelphij * uudecode [file ...]
51170754Sdelphij *
52170754Sdelphij * create the specified file, decoding as you go.
53170754Sdelphij * used with uuencode.
54170754Sdelphij */
55170754Sdelphij#include <sys/param.h>
56170754Sdelphij#include <sys/socket.h>
57170754Sdelphij#include <sys/stat.h>
58170754Sdelphij
59170754Sdelphij#include <netinet/in.h>
60170754Sdelphij
61170754Sdelphij#include <err.h>
62170754Sdelphij#include <pwd.h>
63170754Sdelphij#include <resolv.h>
64170754Sdelphij#include <stdio.h>
65170754Sdelphij#include <stdlib.h>
66170754Sdelphij#include <string.h>
67170754Sdelphij#include <unistd.h>
68170754Sdelphij
69170754Sdelphijconst char *filename;
70170754Sdelphijchar *outfile;
71170754Sdelphijint cflag, iflag, oflag, pflag, sflag;
72170754Sdelphij
73170754Sdelphijstatic void usage(void);
74170754Sdelphijint	decode(void);
75170754Sdelphijint	decode2(void);
76170754Sdelphijint	base64_decode(const char *);
77170754Sdelphij
78170754Sdelphijint
79170754Sdelphijmain(int argc, char *argv[])
80170754Sdelphij{
81170754Sdelphij	int rval, ch;
82170754Sdelphij
83170754Sdelphij	while ((ch = getopt(argc, argv, "cio:ps")) != -1) {
84170754Sdelphij		switch(ch) {
85170754Sdelphij		case 'c':
86170754Sdelphij			if (oflag)
87170754Sdelphij				usage();
88170754Sdelphij			cflag = 1; /* multiple uudecode'd files */
89170754Sdelphij			break;
90170754Sdelphij		case 'i':
91170754Sdelphij			iflag = 1; /* ask before override files */
92170754Sdelphij			break;
93170754Sdelphij		case 'o':
94170754Sdelphij			if (cflag || pflag || sflag)
95170754Sdelphij				usage();
96170754Sdelphij			oflag = 1; /* output to the specified file */
97170754Sdelphij			sflag = 1; /* do not strip pathnames for output */
98170754Sdelphij			outfile = optarg; /* set the output filename */
99170754Sdelphij			break;
100170754Sdelphij		case 'p':
101170754Sdelphij			if (oflag)
102170754Sdelphij				usage();
103170754Sdelphij			pflag = 1; /* print output to stdout */
104170754Sdelphij			break;
105170754Sdelphij		case 's':
106170754Sdelphij			if (oflag)
107170754Sdelphij				usage();
108170754Sdelphij			sflag = 1; /* do not strip pathnames for output */
109170754Sdelphij			break;
110170754Sdelphij		default:
111170754Sdelphij			usage();
112170754Sdelphij		}
113170754Sdelphij	}
114170754Sdelphij        argc -= optind;
115170754Sdelphij        argv += optind;
116170754Sdelphij
117170754Sdelphij	if (*argv) {
118170754Sdelphij		rval = 0;
119170754Sdelphij		do {
120170754Sdelphij			if (freopen(filename = *argv, "r", stdin) == NULL) {
121170754Sdelphij				warn("%s", *argv);
122170754Sdelphij				rval = 1;
123170754Sdelphij				continue;
124170754Sdelphij			}
125170754Sdelphij			rval |= decode();
126170754Sdelphij		} while (*++argv);
127170754Sdelphij	} else {
128170754Sdelphij		filename = "stdin";
129170754Sdelphij		rval = decode();
130170754Sdelphij	}
131170754Sdelphij	exit(rval);
132170754Sdelphij}
133170754Sdelphij
134170754Sdelphijint
135170754Sdelphijdecode(void)
136170754Sdelphij{
137170754Sdelphij	int r, v;
138170754Sdelphij
139170754Sdelphij	v = decode2();
140170754Sdelphij	if (v == EOF) {
141170754Sdelphij		warnx("%s: missing or bad \"begin\" line", filename);
142170754Sdelphij		return (1);
143170754Sdelphij	}
144170754Sdelphij	for (r = v; cflag; r |= v) {
145170754Sdelphij		v = decode2();
146170754Sdelphij		if (v == EOF)
147170754Sdelphij			break;
148170754Sdelphij	}
149170754Sdelphij	return (r);
150170754Sdelphij}
151170754Sdelphij
152170754Sdelphijint
153170754Sdelphijdecode2(void)
154170754Sdelphij{
155170754Sdelphij	int base64;
156170754Sdelphij	size_t n;
157170754Sdelphij	char ch, *p, *q;
158170754Sdelphij	void *mode;
159170754Sdelphij	struct passwd *pw;
160170754Sdelphij	struct stat st;
161170754Sdelphij	char buf[MAXPATHLEN+1];
162170754Sdelphij	char buffn[MAXPATHLEN+1]; /* file name buffer */
163170754Sdelphij
164170754Sdelphij	base64 = 0;
165170754Sdelphij	/* search for header line */
166170754Sdelphij	for (;;) {
167170754Sdelphij		if (fgets(buf, sizeof(buf), stdin) == NULL)
168170754Sdelphij			return (EOF);
169170754Sdelphij		p = buf;
170170754Sdelphij		if (strncmp(p, "begin-base64 ", 13) == 0) {
171170754Sdelphij			base64 = 1;
172170754Sdelphij			p += 13;
173170754Sdelphij		} else if (strncmp(p, "begin ", 6) == 0)
174170754Sdelphij			p += 6;
175170754Sdelphij		else
176170754Sdelphij			continue;
177170754Sdelphij		/* p points to mode */
178170754Sdelphij		q = strchr(p, ' ');
179170754Sdelphij		if (q == NULL)
180170754Sdelphij			continue;
181170754Sdelphij		*q++ = '\0';
182170754Sdelphij		/* q points to filename */
183170754Sdelphij		n = strlen(q);
184170754Sdelphij		while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
185170754Sdelphij			q[--n] = '\0';
186170754Sdelphij		/* found valid header? */
187170754Sdelphij		if (n > 0)
188170754Sdelphij			break;
189170754Sdelphij	}
190170754Sdelphij
191170754Sdelphij	mode = setmode(p);
192170754Sdelphij	if (mode == NULL) {
193170754Sdelphij		warnx("%s: unable to parse file mode", filename);
194170754Sdelphij		return (1);
195170754Sdelphij	}
196170754Sdelphij
197170754Sdelphij	if (oflag) {
198170754Sdelphij		/* use command-line filename */
199170754Sdelphij		n = strlcpy(buffn, outfile, sizeof(buffn));
200170754Sdelphij	} else if (sflag) {
201170754Sdelphij		/* don't strip, so try ~user/file expansion */
202170754Sdelphij		p = NULL;
203170754Sdelphij		pw = NULL;
204170754Sdelphij		if (*q == '~')
205170754Sdelphij			p = strchr(q, '/');
206170754Sdelphij		if (p != NULL) {
207170754Sdelphij			*p = '\0';
208170754Sdelphij			pw = getpwnam(q + 1);
209170754Sdelphij			*p = '/';
210170754Sdelphij		}
211170754Sdelphij		if (pw != NULL) {
212170754Sdelphij			strlcpy(buffn, pw->pw_dir, sizeof(buffn));
213170754Sdelphij			n = strlcat(buffn, p, sizeof(buffn));
214170754Sdelphij		} else {
215170754Sdelphij			n = strlcpy(buffn, q, sizeof(buffn));
216170754Sdelphij		}
217170754Sdelphij	} else {
218170754Sdelphij		/* strip down to leaf name */
219170754Sdelphij		p = strrchr(q, '/');
220170754Sdelphij		if (p != NULL)
221170754Sdelphij			n = strlcpy(buffn, p+1, sizeof(buffn));
222170754Sdelphij		else
223170754Sdelphij			n = strlcpy(buffn, q, sizeof(buffn));
224170754Sdelphij	}
225170754Sdelphij	if (n >= sizeof(buffn) || *buffn == '\0') {
226170754Sdelphij		warnx("%s: bad output filename", filename);
227170754Sdelphij		return (1);
228170754Sdelphij	}
229170754Sdelphij
230170754Sdelphij	if (!pflag) {
231170754Sdelphij		if (iflag && !access(buffn, F_OK)) {
232170754Sdelphij			warnx("not overwritten: %s", buffn);
233170754Sdelphij			return (0);
234170754Sdelphij		}
235170754Sdelphij		if (freopen(buffn, "w", stdout) == NULL ||
236170754Sdelphij		    stat(buffn, &st) < 0 || (S_ISREG(st.st_mode) &&
237170754Sdelphij		    fchmod(fileno(stdout), getmode(mode, 0) & 0666) < 0)) {
238170754Sdelphij			warn("%s: %s", filename, buffn);
239170754Sdelphij			return (1);
240170754Sdelphij		}
241170754Sdelphij	}
242170754Sdelphij	free(mode);
243170754Sdelphij
244170754Sdelphij	if (base64)
245170754Sdelphij		return (base64_decode(buffn));
246170754Sdelphij
247170754Sdelphij	/* for each input line */
248170754Sdelphij	for (;;) {
249170754Sdelphij		if (fgets(p = buf, sizeof(buf), stdin) == NULL) {
250170754Sdelphij			warnx("%s: short file", filename);
251170754Sdelphij			return (1);
252170754Sdelphij		}
253170754Sdelphij
254170754Sdelphij#define	DEC(c)	(((c) - ' ') & 077)		/* single character decode */
255170754Sdelphij#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
256
257#define OUT_OF_RANGE do {						\
258	warnx("%s: %s: character out of range: [%d-%d]",		\
259	    filename, buffn, 1 + ' ', 077 + ' ' + 1);			\
260        return (1);							\
261} while (0)
262
263		/*
264		 * `n' is used to avoid writing out all the characters
265		 * at the end of the file.
266		 */
267		if ((n = DEC(*p)) <= 0)
268			break;
269		for (++p; n > 0; p += 4, n -= 3)
270			if (n >= 3) {
271				if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
272				     IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
273                                	OUT_OF_RANGE;
274
275				ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
276				putchar(ch);
277				ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
278				putchar(ch);
279				ch = DEC(p[2]) << 6 | DEC(p[3]);
280				putchar(ch);
281			}
282			else {
283				if (n >= 1) {
284					if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
285	                                	OUT_OF_RANGE;
286					ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
287					putchar(ch);
288				}
289				if (n >= 2) {
290					if (!(IS_DEC(*(p + 1)) &&
291						IS_DEC(*(p + 2))))
292		                                OUT_OF_RANGE;
293
294					ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
295					putchar(ch);
296				}
297				if (n >= 3) {
298					if (!(IS_DEC(*(p + 2)) &&
299						IS_DEC(*(p + 3))))
300		                                OUT_OF_RANGE;
301					ch = DEC(p[2]) << 6 | DEC(p[3]);
302					putchar(ch);
303				}
304			}
305	}
306	if (fgets(buf, sizeof(buf), stdin) == NULL ||
307	    (strcmp(buf, "end") && strcmp(buf, "end\n") &&
308	     strcmp(buf, "end\r\n"))) {
309		warnx("%s: no \"end\" line", filename);
310		return (1);
311	}
312	return (0);
313}
314
315int
316base64_decode(const char *outname)
317{
318	int n;
319	char buf[MAXPATHLEN+1];
320	unsigned char out[MAXPATHLEN * 4];
321
322	for (;;) {
323		if (fgets(buf, sizeof(buf), stdin) == NULL) {
324			warnx("%s: short file", filename);
325			return (1);
326		}
327		if (strcmp(buf, "====") == 0 ||
328		    strcmp(buf, "====\n") == 0 ||
329		    strcmp(buf, "====\r\n") == 0)
330			return (0);
331		n = strlen(buf);
332		while (n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r'))
333			buf[--n] = '\0';
334		n = b64_pton(buf, out, sizeof(out));
335		if (n < 0) {
336			warnx("%s: %s: error decoding base64 input stream", filename, outname);
337			return (1);
338		}
339		fwrite(out, 1, n, stdout);
340	}
341}
342
343static void
344usage(void)
345{
346	(void)fprintf(stderr,
347"usage: uudecode [-cips] [file ...]\n"
348"       uudecode [-i] -o output_file [file]\n"
349"       b64decode [-cips] [file ...]\n"
350"       b64decode [-i] -o output_file [file]\n");
351	exit(1);
352}
353