uudecode.c revision 106274
1/*- 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static const char copyright[] = 36"@(#) Copyright (c) 1983, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#if 0 41#ifndef lint 42static char sccsid[] = "@(#)uudecode.c 8.2 (Berkeley) 4/2/94"; 43#endif /* not lint */ 44#endif 45 46#include <sys/cdefs.h> 47__FBSDID("$FreeBSD: head/usr.bin/uudecode/uudecode.c 106274 2002-11-01 00:29:00Z fanf $"); 48 49/* 50 * uudecode [file ...] 51 * 52 * create the specified file, decoding as you go. 53 * used with uuencode. 54 */ 55#include <sys/param.h> 56#include <sys/socket.h> 57#include <sys/stat.h> 58 59#include <netinet/in.h> 60 61#include <err.h> 62#include <pwd.h> 63#include <resolv.h> 64#include <stdio.h> 65#include <stdlib.h> 66#include <string.h> 67#include <unistd.h> 68 69const char *filename; 70char *outfile; 71FILE *infp, *outfp; 72int cflag, iflag, oflag, pflag, sflag; 73 74static void usage(void); 75int decode(void); 76int decode2(void); 77int base64_decode(const char *); 78 79int 80main(int argc, char *argv[]) 81{ 82 int rval, ch; 83 84 while ((ch = getopt(argc, argv, "cio:ps")) != -1) { 85 switch(ch) { 86 case 'c': 87 if (oflag) 88 usage(); 89 cflag = 1; /* multiple uudecode'd files */ 90 break; 91 case 'i': 92 iflag = 1; /* ask before override files */ 93 break; 94 case 'o': 95 if (cflag || pflag || sflag) 96 usage(); 97 oflag = 1; /* output to the specified file */ 98 sflag = 1; /* do not strip pathnames for output */ 99 outfile = optarg; /* set the output filename */ 100 break; 101 case 'p': 102 if (oflag) 103 usage(); 104 pflag = 1; /* print output to stdout */ 105 break; 106 case 's': 107 if (oflag) 108 usage(); 109 sflag = 1; /* do not strip pathnames for output */ 110 break; 111 default: 112 usage(); 113 } 114 } 115 argc -= optind; 116 argv += optind; 117 118 if (*argv) { 119 rval = 0; 120 do { 121 infp = fopen(filename = *argv, "r"); 122 if (infp == NULL) { 123 warn("%s", *argv); 124 rval = 1; 125 continue; 126 } 127 rval |= decode(); 128 fclose(infp); 129 } while (*++argv); 130 } else { 131 filename = "stdin"; 132 infp = stdin; 133 rval = decode(); 134 } 135 exit(rval); 136} 137 138int 139decode(void) 140{ 141 int r, v; 142 143 v = decode2(); 144 if (v == EOF) { 145 warnx("%s: missing or bad \"begin\" line", filename); 146 return (1); 147 } 148 for (r = v; cflag; r |= v) { 149 v = decode2(); 150 if (v == EOF) 151 break; 152 } 153 return (r); 154} 155 156int 157decode2(void) 158{ 159 int base64, i; 160 size_t n; 161 char ch, *p, *q; 162 void *mode; 163 struct passwd *pw; 164 struct stat st; 165 char buf[MAXPATHLEN+1]; 166 char buffn[MAXPATHLEN+1]; /* file name buffer */ 167 168 base64 = 0; 169 /* search for header line */ 170 for (;;) { 171 if (fgets(buf, sizeof(buf), infp) == NULL) 172 return (EOF); 173 p = buf; 174 if (strncmp(p, "begin-base64 ", 13) == 0) { 175 base64 = 1; 176 p += 13; 177 } else if (strncmp(p, "begin ", 6) == 0) 178 p += 6; 179 else 180 continue; 181 /* p points to mode */ 182 q = strchr(p, ' '); 183 if (q == NULL) 184 continue; 185 *q++ = '\0'; 186 /* q points to filename */ 187 n = strlen(q); 188 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r')) 189 q[--n] = '\0'; 190 /* found valid header? */ 191 if (n > 0) 192 break; 193 } 194 195 mode = setmode(p); 196 if (mode == NULL) { 197 warnx("%s: unable to parse file mode", filename); 198 return (1); 199 } 200 201 if (oflag) { 202 /* use command-line filename */ 203 n = strlcpy(buffn, outfile, sizeof(buffn)); 204 } else if (sflag) { 205 /* don't strip, so try ~user/file expansion */ 206 p = NULL; 207 pw = NULL; 208 if (*q == '~') 209 p = strchr(q, '/'); 210 if (p != NULL) { 211 *p = '\0'; 212 pw = getpwnam(q + 1); 213 *p = '/'; 214 } 215 if (pw != NULL) { 216 strlcpy(buffn, pw->pw_dir, sizeof(buffn)); 217 n = strlcat(buffn, p, sizeof(buffn)); 218 } else { 219 n = strlcpy(buffn, q, sizeof(buffn)); 220 } 221 } else { 222 /* strip down to leaf name */ 223 p = strrchr(q, '/'); 224 if (p != NULL) 225 n = strlcpy(buffn, p+1, sizeof(buffn)); 226 else 227 n = strlcpy(buffn, q, sizeof(buffn)); 228 } 229 if (n >= sizeof(buffn) || *buffn == '\0') { 230 warnx("%s: bad output filename", filename); 231 return (1); 232 } 233 234 if (!pflag) { 235 if (iflag && !access(buffn, F_OK)) { 236 warnx("not overwritten: %s", buffn); 237 return (0); 238 } 239 if ((outfp = fopen(buffn, "w")) == NULL || 240 stat(buffn, &st) < 0 || (S_ISREG(st.st_mode) && 241 fchmod(fileno(outfp), getmode(mode, 0) & 0666) < 0)) { 242 warn("%s: %s", filename, buffn); 243 return (1); 244 } 245 } 246 free(mode); 247 248 if (base64) 249 return (base64_decode(buffn)); 250 251 /* for each input line */ 252 for (;;) { 253 if (fgets(p = buf, sizeof(buf), infp) == NULL) { 254 warnx("%s: short file", filename); 255 return (1); 256 } 257 258#define DEC(c) (((c) - ' ') & 077) /* single character decode */ 259#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) 260 261#define OUT_OF_RANGE do { \ 262 warnx("%s: %s: character out of range: [%d-%d]", \ 263 filename, buffn, 1 + ' ', 077 + ' ' + 1); \ 264 return (1); \ 265} while (0) 266 267 /* 268 * `i' is used to avoid writing out all the characters 269 * at the end of the file. 270 */ 271 if ((i = DEC(*p)) <= 0) 272 break; 273 for (++p; i > 0; p += 4, i -= 3) 274 if (i >= 3) { 275 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && 276 IS_DEC(*(p + 2)) && IS_DEC(*(p + 3)))) 277 OUT_OF_RANGE; 278 279 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 280 putc(ch, outfp); 281 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 282 putc(ch, outfp); 283 ch = DEC(p[2]) << 6 | DEC(p[3]); 284 putc(ch, outfp); 285 } 286 else { 287 if (i >= 1) { 288 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) 289 OUT_OF_RANGE; 290 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 291 putc(ch, outfp); 292 } 293 if (i >= 2) { 294 if (!(IS_DEC(*(p + 1)) && 295 IS_DEC(*(p + 2)))) 296 OUT_OF_RANGE; 297 298 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 299 putc(ch, outfp); 300 } 301 if (i >= 3) { 302 if (!(IS_DEC(*(p + 2)) && 303 IS_DEC(*(p + 3)))) 304 OUT_OF_RANGE; 305 ch = DEC(p[2]) << 6 | DEC(p[3]); 306 putc(ch, outfp); 307 } 308 } 309 } 310 if (fgets(buf, sizeof(buf), infp) == NULL || 311 (strcmp(buf, "end") && strcmp(buf, "end\n") && 312 strcmp(buf, "end\r\n"))) { 313 warnx("%s: no \"end\" line", filename); 314 return (1); 315 } 316 if (fclose(outfp) != 0) { 317 warnx("%s: %s", filename, buffn); 318 return (1); 319 } 320 return (0); 321} 322 323int 324base64_decode(const char *outname) 325{ 326 int n; 327 char buf[MAXPATHLEN+1]; 328 unsigned char out[MAXPATHLEN * 4]; 329 330 for (;;) { 331 if (fgets(buf, sizeof(buf), infp) == NULL) { 332 warnx("%s: short file", filename); 333 return (1); 334 } 335 if (strcmp(buf, "====") == 0 || 336 strcmp(buf, "====\n") == 0 || 337 strcmp(buf, "====\r\n") == 0) 338 return (0); 339 n = strlen(buf); 340 while (n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r')) 341 buf[--n] = '\0'; 342 n = b64_pton(buf, out, sizeof(out)); 343 if (n < 0) { 344 warnx("%s: %s: error decoding base64 input stream", filename, outname); 345 return (1); 346 } 347 fwrite(out, 1, n, outfp); 348 } 349} 350 351static void 352usage(void) 353{ 354 (void)fprintf(stderr, 355"usage: uudecode [-cips] [file ...]\n" 356" uudecode [-i] -o output_file [file]\n" 357" b64decode [-cips] [file ...]\n" 358" b64decode [-i] -o output_file [file]\n"); 359 exit(1); 360} 361