Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.bin/bintrans/uudecode.c
34677 views
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
* uudecode [file ...]
34
*
35
* create the specified file, decoding as you go.
36
* used with uuencode.
37
*/
38
#include <sys/param.h>
39
#include <sys/socket.h>
40
#include <sys/stat.h>
41
42
#include <netinet/in.h>
43
44
#include <ctype.h>
45
#include <err.h>
46
#include <errno.h>
47
#include <fcntl.h>
48
#include <libgen.h>
49
#include <pwd.h>
50
#include <resolv.h>
51
#include <stdbool.h>
52
#include <stdio.h>
53
#include <stdlib.h>
54
#include <string.h>
55
#include <unistd.h>
56
57
extern int main_decode(int, char *[]);
58
extern int main_base64_decode(const char *);
59
60
static const char *infile, *outfile;
61
static FILE *infp, *outfp;
62
static bool base64, cflag, iflag, oflag, pflag, rflag, sflag;
63
64
static void usage(void);
65
static int decode(void);
66
static int decode2(void);
67
static int uu_decode(void);
68
static int base64_decode(void);
69
70
int
71
main_base64_decode(const char *in)
72
{
73
base64 = 1;
74
rflag = 1;
75
if (in != NULL) {
76
infile = in;
77
infp = fopen(infile, "r");
78
if (infp == NULL)
79
err(1, "%s", in);
80
} else {
81
infile = "stdin";
82
infp = stdin;
83
}
84
exit(decode());
85
}
86
87
int
88
main_decode(int argc, char *argv[])
89
{
90
int rval, ch;
91
92
if (strcmp(basename(argv[0]), "b64decode") == 0)
93
base64 = true;
94
95
while ((ch = getopt(argc, argv, "cimo:prs")) != -1) {
96
switch (ch) {
97
case 'c':
98
if (oflag || rflag)
99
usage();
100
cflag = true; /* multiple uudecode'd files */
101
break;
102
case 'i':
103
iflag = true; /* ask before override files */
104
break;
105
case 'm':
106
base64 = true;
107
break;
108
case 'o':
109
if (cflag || pflag || rflag || sflag)
110
usage();
111
oflag = true; /* output to the specified file */
112
sflag = true; /* do not strip pathnames for output */
113
outfile = optarg; /* set the output filename */
114
break;
115
case 'p':
116
if (oflag)
117
usage();
118
pflag = true; /* print output to stdout */
119
break;
120
case 'r':
121
if (cflag || oflag)
122
usage();
123
rflag = true; /* decode raw data */
124
break;
125
case 's':
126
if (oflag)
127
usage();
128
sflag = true; /* do not strip pathnames for output */
129
break;
130
default:
131
usage();
132
}
133
}
134
argc -= optind;
135
argv += optind;
136
137
if (*argv != NULL) {
138
rval = 0;
139
do {
140
infp = fopen(infile = *argv, "r");
141
if (infp == NULL) {
142
warn("%s", *argv);
143
rval = 1;
144
continue;
145
}
146
rval |= decode();
147
fclose(infp);
148
} while (*++argv);
149
} else {
150
infile = "stdin";
151
infp = stdin;
152
rval = decode();
153
}
154
exit(rval);
155
}
156
157
static int
158
decode(void)
159
{
160
int r, v;
161
162
if (rflag) {
163
/* relaxed alternative to decode2() */
164
outfile = "/dev/stdout";
165
outfp = stdout;
166
if (base64)
167
return (base64_decode());
168
else
169
return (uu_decode());
170
}
171
v = decode2();
172
if (v == EOF) {
173
warnx("%s: missing or bad \"begin\" line", infile);
174
return (1);
175
}
176
for (r = v; cflag; r |= v) {
177
v = decode2();
178
if (v == EOF)
179
break;
180
}
181
return (r);
182
}
183
184
static int
185
decode2(void)
186
{
187
int flags, fd, mode;
188
size_t n, m;
189
char *p, *q;
190
void *handle;
191
struct passwd *pw;
192
struct stat st;
193
char buf[MAXPATHLEN + 1];
194
195
base64 = false;
196
/* search for header line */
197
for (;;) {
198
if (fgets(buf, sizeof(buf), infp) == NULL)
199
return (EOF);
200
p = buf;
201
if (strncmp(p, "begin-base64 ", 13) == 0) {
202
base64 = true;
203
p += 13;
204
} else if (strncmp(p, "begin ", 6) == 0)
205
p += 6;
206
else
207
continue;
208
/* p points to mode */
209
q = strchr(p, ' ');
210
if (q == NULL)
211
continue;
212
*q++ = '\0';
213
/* q points to filename */
214
n = strlen(q);
215
while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
216
q[--n] = '\0';
217
/* found valid header? */
218
if (n > 0)
219
break;
220
}
221
222
handle = setmode(p);
223
if (handle == NULL) {
224
warnx("%s: unable to parse file mode", infile);
225
return (1);
226
}
227
mode = getmode(handle, 0) & 0666;
228
free(handle);
229
230
if (sflag) {
231
/* don't strip, so try ~user/file expansion */
232
p = NULL;
233
pw = NULL;
234
if (*q == '~')
235
p = strchr(q, '/');
236
if (p != NULL) {
237
*p = '\0';
238
pw = getpwnam(q + 1);
239
*p = '/';
240
}
241
if (pw != NULL) {
242
n = strlen(pw->pw_dir);
243
if (buf + n > p) {
244
/* make room */
245
m = strlen(p);
246
if (sizeof(buf) < n + m) {
247
warnx("%s: bad output filename",
248
infile);
249
return (1);
250
}
251
p = memmove(buf + n, p, m);
252
}
253
q = memcpy(p - n, pw->pw_dir, n);
254
}
255
} else {
256
/* strip down to leaf name */
257
p = strrchr(q, '/');
258
if (p != NULL)
259
q = p + 1;
260
}
261
if (!oflag)
262
outfile = q;
263
264
/* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */
265
if (pflag || strcmp(outfile, "/dev/stdout") == 0)
266
outfp = stdout;
267
else {
268
flags = O_WRONLY | O_CREAT | O_EXCL;
269
if (lstat(outfile, &st) == 0) {
270
if (iflag) {
271
warnc(EEXIST, "%s: %s", infile, outfile);
272
return (0);
273
}
274
switch (st.st_mode & S_IFMT) {
275
case S_IFREG:
276
case S_IFLNK:
277
/* avoid symlink attacks */
278
if (unlink(outfile) == 0 || errno == ENOENT)
279
break;
280
warn("%s: unlink %s", infile, outfile);
281
return (1);
282
case S_IFDIR:
283
warnc(EISDIR, "%s: %s", infile, outfile);
284
return (1);
285
default:
286
if (oflag) {
287
/* trust command-line names */
288
flags &= ~O_EXCL;
289
break;
290
}
291
warnc(EEXIST, "%s: %s", infile, outfile);
292
return (1);
293
}
294
} else if (errno != ENOENT) {
295
warn("%s: %s", infile, outfile);
296
return (1);
297
}
298
if ((fd = open(outfile, flags, mode)) < 0 ||
299
(outfp = fdopen(fd, "w")) == NULL) {
300
warn("%s: %s", infile, outfile);
301
return (1);
302
}
303
}
304
305
if (base64)
306
return (base64_decode());
307
else
308
return (uu_decode());
309
}
310
311
static int
312
get_line(char *buf, size_t size)
313
{
314
315
if (fgets(buf, size, infp) != NULL)
316
return (2);
317
if (rflag)
318
return (0);
319
warnx("%s: %s: short file", infile, outfile);
320
return (1);
321
}
322
323
static int
324
checkend(const char *ptr, const char *end, const char *msg)
325
{
326
size_t n;
327
328
n = strlen(end);
329
if (strncmp(ptr, end, n) != 0 ||
330
strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) {
331
warnx("%s: %s: %s", infile, outfile, msg);
332
return (1);
333
}
334
return (0);
335
}
336
337
static int
338
checkout(int rval)
339
{
340
if (fflush(outfp) != 0) {
341
warn("%s: %s", infile, outfile);
342
rval = 1;
343
}
344
if (outfp != stdout) {
345
(void)fclose(outfp);
346
outfp = stdout;
347
}
348
outfile = "/dev/stdout";
349
return (rval);
350
}
351
352
static int
353
uu_decode(void)
354
{
355
int i, ch;
356
char *p;
357
char buf[MAXPATHLEN+1];
358
359
/* for each input line */
360
for (;;) {
361
switch (get_line(buf, sizeof(buf))) {
362
case 0:
363
return (checkout(0));
364
case 1:
365
return (checkout(1));
366
}
367
368
#define DEC(c) (((c) - ' ') & 077) /* single character decode */
369
#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
370
371
#define OUT_OF_RANGE do { \
372
warnx("%s: %s: character out of range: [%d-%d]", \
373
infile, outfile, ' ', 077 + ' ' + 1); \
374
return (1); \
375
} while (0)
376
377
/*
378
* `i' is used to avoid writing out all the characters
379
* at the end of the file.
380
*/
381
p = buf;
382
if ((i = DEC(*p)) <= 0)
383
break;
384
for (++p; i > 0; p += 4, i -= 3)
385
if (i >= 3) {
386
if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
387
IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
388
OUT_OF_RANGE;
389
390
ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
391
putc(ch, outfp);
392
ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
393
putc(ch, outfp);
394
ch = DEC(p[2]) << 6 | DEC(p[3]);
395
putc(ch, outfp);
396
} else {
397
if (i >= 1) {
398
if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
399
OUT_OF_RANGE;
400
ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
401
putc(ch, outfp);
402
}
403
if (i >= 2) {
404
if (!(IS_DEC(*(p + 1)) &&
405
IS_DEC(*(p + 2))))
406
OUT_OF_RANGE;
407
408
ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
409
putc(ch, outfp);
410
}
411
if (i >= 3) {
412
if (!(IS_DEC(*(p + 2)) &&
413
IS_DEC(*(p + 3))))
414
OUT_OF_RANGE;
415
ch = DEC(p[2]) << 6 | DEC(p[3]);
416
putc(ch, outfp);
417
}
418
}
419
}
420
switch (get_line(buf, sizeof(buf))) {
421
case 0:
422
return (checkout(0));
423
case 1:
424
return (checkout(1));
425
default:
426
return (checkout(checkend(buf, "end", "no \"end\" line")));
427
}
428
}
429
430
static int
431
base64_decode(void)
432
{
433
int n, count, count4;
434
char inbuf[MAXPATHLEN + 1], *p;
435
unsigned char outbuf[MAXPATHLEN * 4];
436
char leftover[MAXPATHLEN + 1];
437
438
leftover[0] = '\0';
439
for (;;) {
440
strcpy(inbuf, leftover);
441
switch (get_line(inbuf + strlen(inbuf),
442
sizeof(inbuf) - strlen(inbuf))) {
443
case 0:
444
return (checkout(0));
445
case 1:
446
return (checkout(1));
447
}
448
449
count = 0;
450
count4 = -1;
451
p = inbuf;
452
while (*p != '\0') {
453
/*
454
* Base64 encoded strings have the following
455
* characters in them: A-Z, a-z, 0-9 and +, / and =
456
*/
457
if (isalnum(*p) || *p == '+' || *p == '/' || *p == '=')
458
count++;
459
if (count % 4 == 0)
460
count4 = p - inbuf;
461
p++;
462
}
463
464
strcpy(leftover, inbuf + count4 + 1);
465
inbuf[count4 + 1] = 0;
466
467
n = b64_pton(inbuf, outbuf, sizeof(outbuf));
468
469
if (n < 0)
470
break;
471
fwrite(outbuf, 1, n, outfp);
472
}
473
return (checkout(checkend(inbuf, "====", "error decoding base64 input stream")));
474
}
475
476
static void
477
usage(void)
478
{
479
480
(void)fprintf(stderr,
481
"usage: uudecode [-cimprs] [file ...]\n"
482
" uudecode [-i] -o output_file [file]\n"
483
" b64decode [-cimprs] [file ...]\n"
484
" b64decode [-i] -o output_file [file]\n");
485
exit(1);
486
}
487
488