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 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 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++)) != '\0') { 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 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 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 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 }