uudecode.c revision 103204
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 103204 2002-09-10 21:14:14Z 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); 76int 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 if (base64) 245 return (base64_decode(buffn)); 246 247 /* for each input line */ 248 for (;;) { 249 if (fgets(p = buf, sizeof(buf), stdin) == NULL) { 250 warnx("%s: short file", filename); 251 return (1); 252 } 253 254#define DEC(c) (((c) - ' ') & 077) /* single character decode */ 255#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) 256 257#define OUT_OF_RANGE do { \ 258 warnx("%s: %s: character out of range: [%d-%d]", \ 259 filename, buffn, 1 + ' ', 077 + ' ' + 1); \ 260 return (1); \ 261} while (0) 262 263 /* 264 * `n' is used to avoid writing out all the characters 265 * at the end of the file. 266 */ 267 if ((n = DEC(*p)) <= 0) 268 break; 269 for (++p; n > 0; p += 4, n -= 3) 270 if (n >= 3) { 271 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && 272 IS_DEC(*(p + 2)) && IS_DEC(*(p + 3)))) 273 OUT_OF_RANGE 274 275 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 276 putchar(ch); 277 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 278 putchar(ch); 279 ch = DEC(p[2]) << 6 | DEC(p[3]); 280 putchar(ch); 281 } 282 else { 283 if (n >= 1) { 284 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) 285 OUT_OF_RANGE 286 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 287 putchar(ch); 288 } 289 if (n >= 2) { 290 if (!(IS_DEC(*(p + 1)) && 291 IS_DEC(*(p + 2)))) 292 OUT_OF_RANGE 293 294 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 295 putchar(ch); 296 } 297 if (n >= 3) { 298 if (!(IS_DEC(*(p + 2)) && 299 IS_DEC(*(p + 3)))) 300 OUT_OF_RANGE 301 ch = DEC(p[2]) << 6 | DEC(p[3]); 302 putchar(ch); 303 } 304 } 305 } 306 if (fgets(buf, sizeof(buf), stdin) == NULL || 307 (strcmp(buf, "end") && strcmp(buf, "end\n") && 308 strcmp(buf, "end\r\n"))) { 309 warnx("%s: no \"end\" line", filename); 310 return (1); 311 } 312 return (0); 313} 314 315int 316base64_decode(const char *outname) 317{ 318 int n; 319 char buf[MAXPATHLEN+1]; 320 unsigned char out[MAXPATHLEN * 4]; 321 322 for (;;) { 323 if (fgets(buf, sizeof(buf), stdin) == NULL) { 324 warnx("%s: short file", filename); 325 return (1); 326 } 327 if (strcmp(buf, "====") == 0 || 328 strcmp(buf, "====\n") == 0 || 329 strcmp(buf, "====\r\n") == 0) 330 return (0); 331 n = strlen(buf); 332 while (n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r')) 333 buf[--n] = '\0'; 334 n = b64_pton(buf, out, sizeof(out)); 335 if (n < 0) { 336 warnx("%s: %s: error decoding base64 input stream", filename, outname); 337 return (1); 338 } 339 fwrite(out, 1, n, stdout); 340 } 341} 342 343static void 344usage(void) 345{ 346 (void)fprintf(stderr, 347"usage: uudecode [-cips] [file ...]\n" 348" uudecode [-i] -o output_file [file]\n" 349" b64decode [-cips] [file ...]\n" 350" b64decode [-i] -o output_file [file]\n"); 351 exit(1); 352} 353