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