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
53 extern int main_encode(int, char *[]);
54 extern int main_base64_encode(const char *, const char *);
55
56 static void encode(void);
57 static void base64_encode(void);
58 static int arg_to_col(const char *);
59 static void usage(void) __dead2;
60
61 static FILE *output;
62 static int mode;
63 static bool raw;
64 static char **av;
65 static int columns = 76;
66
67 int
main_base64_encode(const char * in,const char * w)68 main_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
82 int
main_encode(int argc,char * argv[])83 main_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 */
158 static void
base64_encode(void)159 base64_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 */
200 static void
encode(void)201 encode(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
246 static int
arg_to_col(const char * w)247 arg_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
268 static void
usage(void)269 usage(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