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$");
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