1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
24
25 /* Copyright 2004 Sun Microsystems, Inc. All rights reserved. */
26 /* Use is subject to license terms. */
27
28 #pragma ident "%Z%%M% %I% %E% SMI"
29
30 /*
31 * uudecode [-o outfile | -p] [input]
32 *
33 * create the specified file, decoding as you go.
34 * used with uuencode.
35 */
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <pwd.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <locale.h>
45 #include <nl_types.h>
46 #include <langinfo.h>
47 #include <iconv.h>
48 #include <limits.h>
49 #include <errno.h>
50 #include <ctype.h>
51 #include <signal.h>
52 #include <stdarg.h>
53
54 #define BUFSIZE 90 /* must be a multiple of 3 */
55
56 #define TABLE_SIZE 0x40
57
58 #define isvalid(octet) (octet <= 0x40)
59
60 /*
61 * base64 decoding table
62 */
63 /* BEGIN CSTYLED */
64 static char base64tab[] = {
65 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
66 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
67 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
68 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
69 '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
70 '\377', '\377', '\377', 62, '\377', '\377', '\377', 63,
71 52, 53, 54, 55, 56, 57, 58, 59,
72 60, 61, '\377', '\377', '\377', '\377', '\377', '\377',
73 '\377', 0, 1, 2, 3, 4, 5, 6,
74 7, 8, 9, 10, 11, 12, 13, 14,
75 15, 16, 17, 18, 19, 20, 21, 22,
76 23, 24, 25, '\377', '\377', '\377', '\377', '\377',
77 '\377', 26, 27, 28, 29, 30, 31, 32,
78 33, 34, 35, 36, 37, 38, 39, 40,
79 41, 42, 43, 44, 45, 46, 47, 48,
80 49, 50, 51, '\377', '\377', '\377', '\377', '\377'
81 };
82 /* END CSTYLED */
83
84 static char decode_table[UCHAR_MAX + 1];
85
86 /* DEC is the basic 1 character decoding function (historical algorithm) */
87 #define DEC(c) decode_table[c]
88
89 /* true if the character is in the base64 encoding table */
90 #define validbase64(c) (('A' <= (c) && (c) <= 'Z') || \
91 ('a' <= (c) && (c) <= 'z') || \
92 ('0' <= (c) && (c) <= '9') || \
93 (c) == '+' || (c) == '/')
94
95 static void decode(FILE *, FILE *, int);
96 static int outdec(unsigned char *, unsigned char *, int);
97 static int outdec64(unsigned char *, unsigned char *, int);
98
99 /* from usr/src/cmd/chmod/common.c */
100
101 void errmsg(int severity, int code, char *format, ...);
102
103 extern mode_t newmode(char *ms, mode_t new_mode, mode_t umsk,
104 char *file, char *path);
105
106 static char *prog;
107 static char outfile[PATH_MAX];
108 static int mode_err = 0; /* mode conversion error flag */
109
110 int
main(int argc,char ** argv)111 main(int argc, char **argv)
112 {
113 FILE *in, *out;
114 int pipeout = 0;
115 int oflag = 0;
116 int i;
117 mode_t mode, p_umask;
118 char dest[PATH_MAX];
119 char modebits[1024];
120 char buf[LINE_MAX];
121 int c, errflag = 0;
122 struct stat sbuf;
123 int base64flag = 0;
124
125 prog = argv[0];
126 (void) signal(SIGPIPE, SIG_DFL);
127 bzero(dest, sizeof (dest));
128 outfile[0] = '\0';
129
130 /* Set locale environment variables local definitions */
131 (void) setlocale(LC_ALL, "");
132 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
133 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
134 #endif
135 (void) textdomain(TEXT_DOMAIN);
136 p_umask = umask((mode_t)0);
137
138 while ((c = getopt(argc, argv, "o:p")) != EOF) {
139 switch (c) {
140 case 'o':
141 oflag++;
142 (void) strncpy(outfile, optarg, sizeof (outfile));
143 break;
144 case 'p':
145 pipeout++;
146 break;
147 default:
148 case '?':
149 errflag++;
150 break;
151 }
152 }
153 argc -= optind;
154 argv = &argv[optind];
155
156 /* optional input arg */
157 if (argc > 0) {
158 if ((in = fopen(*argv, "r")) == NULL) {
159 perror(*argv);
160 exit(1);
161 }
162 argv++; argc--;
163 } else {
164 in = stdin;
165 errno = 0;
166 if (fstat(fileno(in), &sbuf) < 0) {
167 perror("stdin");
168 exit(1);
169 }
170 }
171
172 if ((argc > 0) || errflag || (oflag && pipeout)) {
173 (void) fprintf(stderr,
174 gettext("Usage: %s [-o outfile | -p] [infile]\n"), prog);
175 exit(2);
176 }
177
178 /* search for header line */
179 for (;;) {
180 if (fgets(buf, sizeof (buf), in) == NULL) {
181 /* suppress message if we printed a mode error */
182 if (mode_err == 0)
183 (void) fprintf(stderr,
184 gettext("No begin line\n"));
185 exit(3);
186 }
187 /*
188 * the check for begin-base64 obviously needs to come
189 * first, since both algorithms' begin strings start
190 * with 'begin'. Also verify that there is a valid
191 * octal or symbolic file mode.
192 */
193 if (strncmp(buf, "begin-base64", 12) == 0) {
194 base64flag = 1;
195 mode_err = 0;
196 if ((sscanf(buf + 13, "%1023s %1023s",
197 modebits, dest) == 2) &&
198 ((sscanf(modebits, "%lo", &mode) == 1) ||
199 ((mode = newmode(modebits, 0, p_umask,
200 "", "")) != 0) && mode_err == 0))
201 break;
202 } else if (strncmp(buf, "begin", 5) == 0) {
203 base64flag = 0;
204 mode_err = 0;
205 if ((sscanf(buf + 6, "%1023s %1023s",
206 modebits, dest) == 2) &&
207 ((sscanf(modebits, "%lo", &mode) == 1) ||
208 ((mode = newmode(modebits, 0, p_umask,
209 "", "")) != 0) && mode_err == 0))
210 break;
211 }
212 }
213
214 /*
215 * Now that we know the type of encoding used, we can
216 * initialize the decode table.
217 */
218 if (base64flag == 0) {
219 (void) memset(decode_table, 0xFF, sizeof (decode_table));
220 for (i = 0; i <= TABLE_SIZE; i++)
221 decode_table[(unsigned char)i + 0x20] =
222 (unsigned char)i & 0x3F;
223 } else
224 (void) memcpy(decode_table, base64tab, sizeof (base64tab));
225
226 /*
227 * Filename specified on the command line with -o
228 * overrides filename in the encoded file.
229 */
230 if (outfile[0] != '\0')
231 (void) strncpy(dest, outfile, sizeof (dest));
232
233 if (pipeout ||
234 (dest[0] == '-' && dest[1] == '\0' && outfile[0] == '\0')) {
235 out = stdout;
236 bzero(outfile, sizeof (outfile));
237 bzero(dest, sizeof (dest));
238 } else {
239 /* handle ~user/file format */
240 if (dest[0] == '~') {
241 char *sl;
242 struct passwd *user;
243 char dnbuf[100];
244
245 sl = strchr(dest, '/');
246 if (sl == NULL) {
247 (void) fprintf(stderr,
248 gettext("Illegal ~user\n"));
249 exit(3);
250 }
251 *sl++ = 0;
252 user = getpwnam(dest+1);
253 if (user == NULL) {
254 (void) fprintf(stderr,
255 gettext("No such user as %s\n"), dest);
256 exit(4);
257 }
258 (void) strncpy(dnbuf, user->pw_dir, sizeof (dnbuf));
259 (void) strlcat(dnbuf, "/", sizeof (dnbuf));
260 (void) strlcat(dnbuf, sl, sizeof (dnbuf));
261 (void) strncpy(dest, dnbuf, sizeof (dest));
262 }
263 }
264 /* if not using stdout, create file */
265 if (dest[0] != '\0') {
266 if ((out = fopen(dest, "w")) == NULL) {
267 perror(dest);
268 exit(4);
269 }
270 (void) chmod(dest, mode & 0777);
271 }
272
273 decode(in, out, base64flag);
274
275 if (fclose(out) == EOF) {
276 perror(prog);
277 exit(6);
278 }
279
280 return (0);
281 }
282
283 /*
284 * copy from in to out, decoding as you go along.
285 */
286
287 static void
decode(FILE * in,FILE * out,int base64)288 decode(FILE *in, FILE *out, int base64)
289 {
290 char inbuf[120], *ibp, *iptr;
291 unsigned char outbuf[BUFSIZE], *obp, *optr;
292 int n, octets, warned, endseen, numbase64chars;
293 unsigned char chr[4], curchr, ch;
294 longlong_t line;
295
296 if (! base64) { /* use historical algorithm */
297 warned = 0;
298 for (line = 1; ; line++) {
299 /* for each input line */
300 if (fgets(inbuf, sizeof (inbuf), in) == NULL) {
301 (void) fprintf(stderr,
302 gettext("No end line\n"));
303 exit(5);
304 }
305
306 /* Is line == 'end\n'? */
307 if (strcmp(inbuf, "end\n") == 0) {
308 break;
309 }
310
311 n = DEC(inbuf[0]);
312
313 if (n < 0)
314 continue;
315
316 /*
317 * Decode data lines.
318 *
319 * Note that uuencode/uudecode uses only the portable
320 * character set for encoded data and the portable
321 * character set characters must be represented in
322 * a single byte. We use this knowledge to reuse
323 * buffer space while decoding.
324 */
325 octets = n;
326 obp = (unsigned char *) &inbuf[0];
327 ibp = &inbuf[1];
328 while (octets > 0) {
329 if ((ch = outdec((unsigned char *)obp,
330 (unsigned char *)ibp, octets))
331 != 0x20) {
332 /* invalid characters where detected */
333 if (!warned) {
334 warned = 1;
335 (void) fprintf(stderr,
336 gettext("Invalid character"
337 " (0x%x) on line"
338 " %lld\n"), ch, line);
339 }
340 break;
341 }
342 ibp += 4;
343 obp += 3;
344 octets -= 3;
345 }
346 /*
347 * Only write out uncorrupted lines
348 */
349 if (octets <= 0) {
350 (void) fwrite(inbuf, n, 1, out);
351 }
352 }
353 } else { /* use base64 algorithm */
354 endseen = numbase64chars = 0;
355 optr = outbuf;
356 while ((fgets(inbuf, sizeof (inbuf), in)) != NULL) {
357 /* process an input line */
358 iptr = inbuf;
359 while ((curchr = *(iptr++)) != NULL) {
360 /* decode chars */
361 if (curchr == '=') /* if end */
362 endseen++;
363
364 if (validbase64(curchr))
365 chr[numbase64chars++] = curchr;
366 /*
367 * if we've gathered 4 base64 octets
368 * we need to decode and output them
369 */
370 if (numbase64chars == 4) {
371 /*LINTED*/
372 if (optr - outbuf > BUFSIZE - 3) {
373 (void) fwrite(outbuf,
374 /*LINTED*/
375 (size_t)(optr - outbuf),
376 1, out);
377 if (ferror(out)) {
378 perror(prog);
379 exit(6);
380 }
381 optr = outbuf;
382 }
383 octets = outdec64(optr, chr, 4);
384 optr += octets;
385 numbase64chars = 0;
386 }
387 }
388 /*
389 * handle any remaining base64 octets at end
390 */
391 if (endseen && numbase64chars > 0) {
392 octets = outdec64(optr, chr, numbase64chars);
393 optr += octets;
394 numbase64chars = 0;
395 }
396 }
397 /*
398 * if we have generated any additional output
399 * in the buffer, write it out
400 */
401 if (optr != outbuf) {
402 /*LINTED*/
403 (void) fwrite(outbuf, (size_t)(optr - outbuf),
404 1, out);
405 if (ferror(out)) {
406 perror(prog);
407 exit(6);
408 }
409 }
410
411 if (endseen == 0) {
412 (void) fprintf(stderr, gettext("No end line\n"));
413 exit(5);
414 }
415 }
416 }
417
418 /*
419 * historical algorithm
420 *
421 * output a group of 3 bytes (4 input characters).
422 * the input chars are pointed to by p, they are to
423 * be output to file f. n is used to tell us not to
424 * output all of them at the end of the file.
425 */
426
427 static int
outdec(unsigned char * out,unsigned char * in,int n)428 outdec(unsigned char *out, unsigned char *in, int n)
429 {
430 unsigned char b0 = DEC(*(in++));
431 unsigned char b1 = DEC(*(in++));
432 unsigned char b2 = DEC(*(in++));
433 unsigned char b3 = DEC(*in);
434
435 if (!isvalid(b0)) {
436 return (*(in-3));
437 }
438 if (!isvalid(b1)) {
439 return (*(in-2));
440 }
441
442 *(out++) = (b0 << 2) | (b1 >> 4);
443
444 if (n >= 2) {
445 if (!isvalid(b2)) {
446 return (*(in - 1));
447 }
448
449 *(out++) = (b1 << 4) | (b2 >> 2);
450
451 if (n >= 3) {
452 if (!isvalid(b3)) {
453 return (*in);
454 }
455 *out = (b2 << 6) | b3;
456 }
457 }
458 return (0x20); /* a know good value */
459 }
460
461 /*
462 * base64 algorithm
463 *
464 * Takes a pointer to the current position in the output buffer,
465 * a pointer to the (up to) 4 byte base64 input buffer and a
466 * count of the number of valid input bytes.
467 *
468 * Return the number of bytes placed in the output buffer
469 */
470 static int
outdec64(unsigned char * out,unsigned char * chr,int num)471 outdec64(unsigned char *out, unsigned char *chr, int num)
472 {
473
474 unsigned char char1, char2, char3, char4;
475 unsigned char *outptr = out;
476 int rc = 0;
477
478 switch (num) {
479 case 0:
480 case 1: /* these are impossible */
481 default:
482 break;
483 case 2: /* 2 base64 bytes == 1 decoded byte */
484 char1 = base64tab[chr[0]] & 0xFF;
485 char2 = base64tab[chr[1]] & 0xFF;
486 *(outptr++) = ((char1 << 2) & 0xFC) |
487 ((char2 >> 4) & 0x03);
488 rc = 1;
489 break;
490 case 3: /* 3 base64 bytes == 2 decoded bytes */
491 char1 = base64tab[chr[0]] & 0xFF;
492 char2 = base64tab[chr[1]] & 0xFF;
493 char3 = base64tab[chr[2]] & 0xFF;
494 *(outptr++) = ((char1 << 2) & 0xFC) |
495 ((char2 >> 4) & 0x03);
496 *(outptr++) = ((char2 << 4) & 0xF0) |
497 ((char3 >> 2) & 0x0F);
498 rc = 2;
499 break;
500 case 4: /* 4 base64 bytes == 3 decoded bytes */
501 char1 = base64tab[chr[0]] & 0xFF;
502 char2 = base64tab[chr[1]] & 0xFF;
503 char3 = base64tab[chr[2]] & 0xFF;
504 char4 = base64tab[chr[3]] & 0xFF;
505 *(outptr++) = ((char1 << 2) & 0xFC) |
506 ((char2 >> 4) & 0x03);
507 *(outptr++) = ((char2 << 4) & 0xF0) |
508 ((char3 >> 2) & 0x0F);
509 *(outptr++) = ((char3 << 6) & 0xC0) |
510 (char4 & 0x3F);
511 rc = 3;
512 break;
513 }
514 return (rc);
515 }
516
517 /*
518 * error message routine called by newmode.
519 *
520 * The severity and code are ignored here. If this routine gets
521 * called, we set a global flag (which can be tested after return
522 * from here) which tells us whether or not a valid mode has been
523 * parsed or if we printed an error message.
524 */
525
526 /*ARGSUSED*/
527 void
errmsg(int severity,int code,char * format,...)528 errmsg(int severity, int code, char *format, ...)
529 {
530 va_list ap;
531
532 va_start(ap, format);
533
534 (void) fprintf(stderr, "uudecode: ");
535 (void) fprintf(stderr, format, ap);
536
537 va_end(ap);
538
539 mode_err = 1;
540 }
541