uudecode.c revision 103201
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 103201 2002-09-10 20:53:46Z 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; 71int cflag, iflag, oflag, pflag, sflag; 72 73static void usage(void); 74int decode(void); 75int decode2(void); 76void base64_decode(const char *); 77 78int 79main(int argc, char *argv[]) 80{ 81 int rval, ch; 82 83 while ((ch = getopt(argc, argv, "cio:ps")) != -1) { 84 switch(ch) { 85 case 'c': 86 if (oflag) 87 usage(); 88 cflag = 1; /* multiple uudecode'd files */ 89 break; 90 case 'i': 91 iflag = 1; /* ask before override files */ 92 break; 93 case 'o': 94 if (cflag || pflag || sflag) 95 usage(); 96 oflag = 1; /* output to the specified file */ 97 sflag = 1; /* do not strip pathnames for output */ 98 outfile = optarg; /* set the output filename */ 99 break; 100 case 'p': 101 if (oflag) 102 usage(); 103 pflag = 1; /* print output to stdout */ 104 break; 105 case 's': 106 if (oflag) 107 usage(); 108 sflag = 1; /* do not strip pathnames for output */ 109 break; 110 default: 111 usage(); 112 } 113 } 114 argc -= optind; 115 argv += optind; 116 117 if (*argv) { 118 rval = 0; 119 do { 120 if (freopen(filename = *argv, "r", stdin) == NULL) { 121 warn("%s", *argv); 122 rval = 1; 123 continue; 124 } 125 rval |= decode(); 126 } while (*++argv); 127 } else { 128 filename = "stdin"; 129 rval = decode(); 130 } 131 exit(rval); 132} 133 134int 135decode(void) 136{ 137 int r, v; 138 139 v = decode2(); 140 if (v == EOF) { 141 warnx("%s: missing or bad \"begin\" line", filename); 142 return (1); 143 } 144 for (r = v; cflag; r |= v) { 145 v = decode2(); 146 if (v == EOF) 147 break; 148 } 149 return (r); 150} 151 152int 153decode2(void) 154{ 155 int base64; 156 int n; 157 char ch, *p, *q; 158 void *mode; 159 struct passwd *pw; 160 struct stat st; 161 char buf[MAXPATHLEN+1]; 162 char buffn[MAXPATHLEN+1]; /* file name buffer */ 163 164 base64 = 0; 165 /* search for header line */ 166 for (;;) { 167 if (fgets(buf, sizeof(buf), stdin) == NULL) 168 return (EOF); 169 p = buf; 170 if (strncmp(p, "begin-base64 ", 13) == 0) { 171 base64 = 1; 172 p += 13; 173 } else if (strncmp(p, "begin ", 6) == 0) 174 p += 6; 175 else 176 continue; 177 /* p points to mode */ 178 q = strchr(p, ' '); 179 if (q == NULL) 180 continue; 181 *q++ = '\0'; 182 /* q points to filename */ 183 n = strlen(q); 184 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r')) 185 q[--n] = '\0'; 186 /* found valid header? */ 187 if (n > 0) 188 break; 189 } 190 191 mode = setmode(p); 192 if (mode == NULL) { 193 warnx("%s: unable to parse file mode", filename); 194 return (1); 195 } 196 197 if (oflag) { 198 /* use command-line filename */ 199 n = strlcpy(buffn, outfile, sizeof(buffn)); 200 } else if (sflag) { 201 /* don't strip, so try ~user/file expansion */ 202 p = NULL; 203 pw = NULL; 204 if (*q == '~') 205 p = strchr(q, '/'); 206 if (p != NULL) { 207 *p = '\0'; 208 pw = getpwnam(q + 1); 209 *p = '/'; 210 } 211 if (pw != NULL) { 212 strlcpy(buffn, pw->pw_dir, sizeof(buffn)); 213 n = strlcat(buffn, p, sizeof(buffn)); 214 } else { 215 n = strlcpy(buffn, q, sizeof(buffn)); 216 } 217 } else { 218 /* strip down to leaf name */ 219 p = strrchr(q, '/'); 220 if (p != NULL) 221 n = strlcpy(buffn, p+1, sizeof(buffn)); 222 else 223 n = strlcpy(buffn, q, sizeof(buffn)); 224 } 225 if (n >= sizeof(buffn) || *buffn == '\0') { 226 warnx("%s: bad output filename", filename); 227 return (1); 228 } 229 230 if (!pflag) { 231 if (iflag && !access(buffn, F_OK)) { 232 warnx("not overwritten: %s", buffn); 233 return (0); 234 } 235 if (freopen(buffn, "w", stdout) == NULL || 236 stat(buffn, &st) < 0 || (S_ISREG(st.st_mode) && 237 fchmod(fileno(stdout), getmode(mode, 0) & 0666) < 0)) { 238 warn("%s: %s", filename, buffn); 239 return (1); 240 } 241 } 242 free(mode); 243 244 /* for each input line */ 245 for (;;) { 246 if (fgets(p = buf, sizeof(buf), stdin) == NULL) { 247 warnx("%s: short file", filename); 248 return (1); 249 } 250 if (base64) { 251 if (strncmp(buf, "====", 4) == 0) 252 return (0); 253 base64_decode(buf); 254 continue; 255 } 256#define DEC(c) (((c) - ' ') & 077) /* single character decode */ 257#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) 258/* #define IS_DEC(c) (1) */ 259 260#define OUT_OF_RANGE \ 261{ \ 262 warnx( \ 263"\n\tinput file: %s\n\tencoded file: %s\n\tcharacter out of range: [%d-%d]", \ 264 filename, buffn, 1 + ' ', 077 + ' ' + 1); \ 265 return (1); \ 266} 267 268 /* 269 * `n' is used to avoid writing out all the characters 270 * at the end of the file. 271 */ 272 if ((n = DEC(*p)) <= 0) 273 break; 274 for (++p; n > 0; p += 4, n -= 3) 275 if (n >= 3) { 276 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && 277 IS_DEC(*(p + 2)) && IS_DEC(*(p + 3)))) 278 OUT_OF_RANGE 279 280 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 281 putchar(ch); 282 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 283 putchar(ch); 284 ch = DEC(p[2]) << 6 | DEC(p[3]); 285 putchar(ch); 286 } 287 else { 288 if (n >= 1) { 289 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) 290 OUT_OF_RANGE 291 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 292 putchar(ch); 293 } 294 if (n >= 2) { 295 if (!(IS_DEC(*(p + 1)) && 296 IS_DEC(*(p + 2)))) 297 OUT_OF_RANGE 298 299 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 300 putchar(ch); 301 } 302 if (n >= 3) { 303 if (!(IS_DEC(*(p + 2)) && 304 IS_DEC(*(p + 3)))) 305 OUT_OF_RANGE 306 ch = DEC(p[2]) << 6 | DEC(p[3]); 307 putchar(ch); 308 } 309 } 310 } 311 if (fgets(buf, sizeof(buf), stdin) == NULL || 312 (strcmp(buf, "end") && strcmp(buf, "end\n") && 313 strcmp(buf, "end\r\n"))) { 314 warnx("%s: no \"end\" line", filename); 315 return (1); 316 } 317 return (0); 318} 319 320void 321base64_decode(const char *stream) 322{ 323 unsigned char out[MAXPATHLEN * 4]; 324 int rv; 325 326 if (index(stream, '\r') != NULL) 327 *index(stream, '\r') = '\0'; 328 if (index(stream, '\n') != NULL) 329 *index(stream, '\n') = '\0'; 330 rv = b64_pton(stream, out, (sizeof(out) / sizeof(out[0]))); 331 if (rv == -1) 332 errx(1, "b64_pton: error decoding base64 input stream"); 333 fwrite(out, 1, rv, stdout); 334} 335 336static void 337usage(void) 338{ 339 (void)fprintf(stderr, 340"usage: uudecode [-cips] [file ...]\n" 341" uudecode [-i] -o output_file [file]\n" 342" b64decode [-cips] [file ...]\n" 343" b64decode [-i] -o output_file [file]\n"); 344 exit(1); 345} 346