1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1983, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*
33 * uuencode [input] output
34 *
35 * Encode a file so it can be mailed to a remote system.
36 */
37#include <sys/param.h>
38#include <sys/socket.h>
39#include <sys/stat.h>
40
41#include <netinet/in.h>
42
43#include <err.h>
44#include <errno.h>
45#include <libgen.h>
46#include <resolv.h>
47#include <stdio.h>
48#include <stdbool.h>
49#include <stdlib.h>
50#include <string.h>
51#include <unistd.h>
52
53extern int main_encode(int, char *[]);
54extern int main_base64_encode(const char *, const char *);
55
56static void encode(void);
57static void base64_encode(void);
58static int arg_to_col(const char *);
59static void usage(void) __dead2;
60
61static FILE *output;
62static int mode;
63static bool raw;
64static char **av;
65static int columns = 76;
66
67int
68main_base64_encode(const char *in, const char *w)
69{
70	raw = 1;
71	if (in != NULL && freopen(in, "r", stdin) == NULL)
72		err(1, "%s", in);
73	output = stdout;
74	if (w != NULL)
75		columns = arg_to_col(w);
76	base64_encode();
77	if (fflush(output) != 0)
78		errx(1, "write error");
79	exit(0);
80}
81
82int
83main_encode(int argc, char *argv[])
84{
85	struct stat sb;
86	bool base64;
87	int ch;
88	const char *outfile;
89
90	base64 = false;
91	outfile = NULL;
92
93	if (strcmp(basename(argv[0]), "b64encode") == 0)
94		base64 = 1;
95
96	while ((ch = getopt(argc, argv, "mo:rw:")) != -1) {
97		switch (ch) {
98		case 'm':
99			base64 = true;
100			break;
101		case 'o':
102			outfile = optarg;
103			break;
104		case 'r':
105			raw = true;
106			break;
107		case 'w':
108			columns = arg_to_col(optarg);
109			break;
110		case '?':
111		default:
112			usage();
113		}
114	}
115	argv += optind;
116	argc -= optind;
117
118	switch (argc) {
119	case 2:			/* optional first argument is input file */
120		if (!freopen(*argv, "r", stdin) || fstat(fileno(stdin), &sb))
121			err(1, "%s", *argv);
122#define	RWX	(S_IRWXU|S_IRWXG|S_IRWXO)
123		mode = sb.st_mode & RWX;
124		++argv;
125		break;
126	case 1:
127#define	RW	(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
128		mode = RW & ~umask(RW);
129		break;
130	case 0:
131	default:
132		usage();
133	}
134
135	av = argv;
136
137	if (outfile != NULL) {
138		output = fopen(outfile, "w+");
139		if (output == NULL)
140			err(1, "unable to open %s for output", outfile);
141	} else
142		output = stdout;
143	if (base64)
144		base64_encode();
145	else
146		encode();
147	if (fflush(output) != 0)
148		errx(1, "write error");
149	exit(0);
150}
151
152/* ENC is the basic 1 character encoding function to make a char printing */
153#define	ENC(c) ((c) ? ((c) & 077) + ' ': '`')
154
155/*
156 * Copy from in to out, encoding in base64 as you go along.
157 */
158static void
159base64_encode(void)
160{
161	/*
162	 * This buffer's length should be a multiple of 24 bits to avoid "="
163	 * padding. Once it reached ~1 KB, further expansion didn't improve
164	 * performance for me.
165	 */
166	unsigned char buf[1023];
167	char buf2[sizeof(buf) * 2 + 1];
168	size_t n;
169	unsigned carry = 0;
170	int rv, written;
171
172	if (!raw)
173		fprintf(output, "begin-base64 %o %s\n", mode, *av);
174	while ((n = fread(buf, 1, sizeof(buf), stdin))) {
175		rv = b64_ntop(buf, n, buf2, nitems(buf2));
176		if (rv == -1)
177			errx(1, "b64_ntop: error encoding base64");
178		if (columns == 0) {
179			fputs(buf2, output);
180			continue;
181		}
182		for (int i = 0; i < rv; i += written) {
183			written = fprintf(output, "%.*s", columns - carry,
184			    &buf2[i]);
185
186			carry = (carry + written) % columns;
187			if (carry == 0)
188				fputc('\n', output);
189		}
190	}
191	if (columns == 0 || carry != 0)
192		fputc('\n', output);
193	if (!raw)
194		fprintf(output, "====\n");
195}
196
197/*
198 * Copy from in to out, encoding as you go along.
199 */
200static void
201encode(void)
202{
203	int ch, n;
204	char *p;
205	char buf[80];
206
207	if (!raw)
208		(void)fprintf(output, "begin %o %s\n", mode, *av);
209	while ((n = fread(buf, 1, 45, stdin))) {
210		ch = ENC(n);
211		if (fputc(ch, output) == EOF)
212			break;
213		for (p = buf; n > 0; n -= 3, p += 3) {
214			/* Pad with nulls if not a multiple of 3. */
215			if (n < 3) {
216				p[2] = '\0';
217				if (n < 2)
218					p[1] = '\0';
219			}
220			ch = *p >> 2;
221			ch = ENC(ch);
222			if (fputc(ch, output) == EOF)
223				break;
224			ch = ((*p << 4) & 060) | ((p[1] >> 4) & 017);
225			ch = ENC(ch);
226			if (fputc(ch, output) == EOF)
227				break;
228			ch = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03);
229			ch = ENC(ch);
230			if (fputc(ch, output) == EOF)
231				break;
232			ch = p[2] & 077;
233			ch = ENC(ch);
234			if (fputc(ch, output) == EOF)
235				break;
236		}
237		if (fputc('\n', output) == EOF)
238			break;
239	}
240	if (ferror(stdin))
241		errx(1, "read error");
242	if (!raw)
243		(void)fprintf(output, "%c\nend\n", ENC('\0'));
244}
245
246static int
247arg_to_col(const char *w)
248{
249	char *ep;
250	long option;
251
252	errno = 0;
253	option = strtol(w, &ep, 10);
254	if (option > INT_MAX)
255		errno = ERANGE;
256	else if (ep[0] != '\0')
257		errno = EINVAL;
258	if (errno != 0)
259		err(2, NULL);
260
261	if (option < 0) {
262		errno = EINVAL;
263		err(2, "columns argument must be non-negative");
264	}
265	return (option);
266}
267
268static void
269usage(void)
270{
271	(void)fprintf(stderr,
272"usage: uuencode [-m] [-o outfile] [infile] remotefile\n"
273"       b64encode [-o outfile] [infile] remotefile\n");
274	exit(1);
275}
276