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 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 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 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 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 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