]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.bin/uudecode/uudecode.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.bin / uudecode / uudecode.c
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 #if 0
35 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1983, 1993\n\
38         The Regents of the University of California.  All rights reserved.\n";
39 #endif /* not lint */
40
41 #ifndef lint
42 static char sccsid[] = "@(#)uudecode.c  8.2 (Berkeley) 4/2/94";
43 #endif /* not lint */
44 #endif
45 #include <sys/cdefs.h>
46 __FBSDID("$FreeBSD$");
47
48 /*
49  * uudecode [file ...]
50  *
51  * create the specified file, decoding as you go.
52  * used with uuencode.
53  */
54 #include <sys/param.h>
55 #include <sys/socket.h>
56 #include <sys/stat.h>
57
58 #include <netinet/in.h>
59
60 #include <err.h>
61 #include <errno.h>
62 #include <fcntl.h>
63 #include <libgen.h>
64 #include <pwd.h>
65 #include <resolv.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70
71 static const char *infile, *outfile;
72 static FILE *infp, *outfp;
73 static int base64, cflag, iflag, oflag, pflag, rflag, sflag;
74
75 static void     usage(void);
76 static int      decode(void);
77 static int      decode2(void);
78 static int      uu_decode(void);
79 static int      base64_decode(void);
80
81 int
82 main(int argc, char *argv[])
83 {
84         int rval, ch;
85
86         if (strcmp(basename(argv[0]), "b64decode") == 0)
87                 base64 = 1;
88
89         while ((ch = getopt(argc, argv, "cimo:prs")) != -1) {
90                 switch(ch) {
91                 case 'c':
92                         if (oflag || rflag)
93                                 usage();
94                         cflag = 1; /* multiple uudecode'd files */
95                         break;
96                 case 'i':
97                         iflag = 1; /* ask before override files */
98                         break;
99                 case 'm':
100                         base64 = 1;
101                         break;
102                 case 'o':
103                         if (cflag || pflag || rflag || sflag)
104                                 usage();
105                         oflag = 1; /* output to the specified file */
106                         sflag = 1; /* do not strip pathnames for output */
107                         outfile = optarg; /* set the output filename */
108                         break;
109                 case 'p':
110                         if (oflag)
111                                 usage();
112                         pflag = 1; /* print output to stdout */
113                         break;
114                 case 'r':
115                         if (cflag || oflag)
116                                 usage();
117                         rflag = 1; /* decode raw data */
118                         break;
119                 case 's':
120                         if (oflag)
121                                 usage();
122                         sflag = 1; /* do not strip pathnames for output */
123                         break;
124                 default:
125                         usage();
126                 }
127         }
128         argc -= optind;
129         argv += optind;
130
131         if (*argv) {
132                 rval = 0;
133                 do {
134                         infp = fopen(infile = *argv, "r");
135                         if (infp == NULL) {
136                                 warn("%s", *argv);
137                                 rval = 1;
138                                 continue;
139                         }
140                         rval |= decode();
141                         fclose(infp);
142                 } while (*++argv);
143         } else {
144                 infile = "stdin";
145                 infp = stdin;
146                 rval = decode();
147         }
148         exit(rval);
149 }
150
151 static int
152 decode(void)
153 {
154         int r, v;
155
156         if (rflag) {
157                 /* relaxed alternative to decode2() */
158                 outfile = "/dev/stdout";
159                 outfp = stdout;
160                 if (base64)
161                         return (base64_decode());
162                 else
163                         return (uu_decode());
164         }
165         v = decode2();
166         if (v == EOF) {
167                 warnx("%s: missing or bad \"begin\" line", infile);
168                 return (1);
169         }
170         for (r = v; cflag; r |= v) {
171                 v = decode2();
172                 if (v == EOF)
173                         break;
174         }
175         return (r);
176 }
177
178 static int
179 decode2(void)
180 {
181         int flags, fd, mode;
182         size_t n, m;
183         char *p, *q;
184         void *handle;
185         struct passwd *pw;
186         struct stat st;
187         char buf[MAXPATHLEN+1];
188
189         base64 = 0;
190         /* search for header line */
191         for (;;) {
192                 if (fgets(buf, sizeof(buf), infp) == NULL)
193                         return (EOF);
194                 p = buf;
195                 if (strncmp(p, "begin-base64 ", 13) == 0) {
196                         base64 = 1;
197                         p += 13;
198                 } else if (strncmp(p, "begin ", 6) == 0)
199                         p += 6;
200                 else
201                         continue;
202                 /* p points to mode */
203                 q = strchr(p, ' ');
204                 if (q == NULL)
205                         continue;
206                 *q++ = '\0';
207                 /* q points to filename */
208                 n = strlen(q);
209                 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
210                         q[--n] = '\0';
211                 /* found valid header? */
212                 if (n > 0)
213                         break;
214         }
215
216         handle = setmode(p);
217         if (handle == NULL) {
218                 warnx("%s: unable to parse file mode", infile);
219                 return (1);
220         }
221         mode = getmode(handle, 0) & 0666;
222         free(handle);
223
224         if (sflag) {
225                 /* don't strip, so try ~user/file expansion */
226                 p = NULL;
227                 pw = NULL;
228                 if (*q == '~')
229                         p = strchr(q, '/');
230                 if (p != NULL) {
231                         *p = '\0';
232                         pw = getpwnam(q + 1);
233                         *p = '/';
234                 }
235                 if (pw != NULL) {
236                         n = strlen(pw->pw_dir);
237                         if (buf + n > p) {
238                                 /* make room */
239                                 m = strlen(p);
240                                 if (sizeof(buf) < n + m) {
241                                         warnx("%s: bad output filename",
242                                             infile);
243                                         return (1);
244                                 }
245                                 p = memmove(buf + n, p, m);
246                         }
247                         q = memcpy(p - n, pw->pw_dir, n);
248                 }
249         } else {
250                 /* strip down to leaf name */
251                 p = strrchr(q, '/');
252                 if (p != NULL)
253                         q = p + 1;
254         }
255         if (!oflag)
256                 outfile = q;
257
258         /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */
259         if (pflag || strcmp(outfile, "/dev/stdout") == 0)
260                 outfp = stdout;
261         else {
262                 flags = O_WRONLY|O_CREAT|O_EXCL;
263                 if (lstat(outfile, &st) == 0) {
264                         if (iflag) {
265                                 warnc(EEXIST, "%s: %s", infile, outfile);
266                                 return (0);
267                         }
268                         switch (st.st_mode & S_IFMT) {
269                         case S_IFREG:
270                         case S_IFLNK:
271                                 /* avoid symlink attacks */
272                                 if (unlink(outfile) == 0 || errno == ENOENT)
273                                         break;
274                                 warn("%s: unlink %s", infile, outfile);
275                                 return (1);
276                         case S_IFDIR:
277                                 warnc(EISDIR, "%s: %s", infile, outfile);
278                                 return (1);
279                         default:
280                                 if (oflag) {
281                                         /* trust command-line names */
282                                         flags &= ~O_EXCL;
283                                         break;
284                                 }
285                                 warnc(EEXIST, "%s: %s", infile, outfile);
286                                 return (1);
287                         }
288                 } else if (errno != ENOENT) {
289                         warn("%s: %s", infile, outfile);
290                         return (1);
291                 }
292                 if ((fd = open(outfile, flags, mode)) < 0 ||
293                     (outfp = fdopen(fd, "w")) == NULL) {
294                         warn("%s: %s", infile, outfile);
295                         return (1);
296                 }
297         }
298
299         if (base64)
300                 return (base64_decode());
301         else
302                 return (uu_decode());
303 }
304
305 static int
306 getline(char *buf, size_t size)
307 {
308         if (fgets(buf, size, infp) != NULL)
309                 return (2);
310         if (rflag)
311                 return (0);
312         warnx("%s: %s: short file", infile, outfile);
313         return (1);
314 }
315
316 static int
317 checkend(const char *ptr, const char *end, const char *msg)
318 {
319         size_t n;
320
321         n = strlen(end);
322         if (strncmp(ptr, end, n) != 0 ||
323             strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) {
324                 warnx("%s: %s: %s", infile, outfile, msg);
325                 return (1);
326         }
327         if (fclose(outfp) != 0) {
328                 warn("%s: %s", infile, outfile);
329                 return (1);
330         }
331         return (0);
332 }
333
334 static int
335 uu_decode(void)
336 {
337         int i, ch;
338         char *p;
339         char buf[MAXPATHLEN+1];
340
341         /* for each input line */
342         for (;;) {
343                 switch (getline(buf, sizeof(buf))) {
344                 case 0: return (0);
345                 case 1: return (1);
346                 }
347
348 #define DEC(c)  (((c) - ' ') & 077)             /* single character decode */
349 #define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
350
351 #define OUT_OF_RANGE do {                                               \
352         warnx("%s: %s: character out of range: [%d-%d]",                \
353             infile, outfile, 1 + ' ', 077 + ' ' + 1);                   \
354         return (1);                                                     \
355 } while (0)
356
357                 /*
358                  * `i' is used to avoid writing out all the characters
359                  * at the end of the file.
360                  */
361                 p = buf;
362                 if ((i = DEC(*p)) <= 0)
363                         break;
364                 for (++p; i > 0; p += 4, i -= 3)
365                         if (i >= 3) {
366                                 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
367                                      IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
368                                         OUT_OF_RANGE;
369
370                                 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
371                                 putc(ch, outfp);
372                                 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
373                                 putc(ch, outfp);
374                                 ch = DEC(p[2]) << 6 | DEC(p[3]);
375                                 putc(ch, outfp);
376                         }
377                         else {
378                                 if (i >= 1) {
379                                         if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
380                                                 OUT_OF_RANGE;
381                                         ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
382                                         putc(ch, outfp);
383                                 }
384                                 if (i >= 2) {
385                                         if (!(IS_DEC(*(p + 1)) &&
386                                                 IS_DEC(*(p + 2))))
387                                                 OUT_OF_RANGE;
388
389                                         ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
390                                         putc(ch, outfp);
391                                 }
392                                 if (i >= 3) {
393                                         if (!(IS_DEC(*(p + 2)) &&
394                                                 IS_DEC(*(p + 3))))
395                                                 OUT_OF_RANGE;
396                                         ch = DEC(p[2]) << 6 | DEC(p[3]);
397                                         putc(ch, outfp);
398                                 }
399                         }
400         }
401         switch (getline(buf, sizeof(buf))) {
402         case 0:  return (0);
403         case 1:  return (1);
404         default: return (checkend(buf, "end", "no \"end\" line"));
405         }
406 }
407
408 static int
409 base64_decode(void)
410 {
411         int n;
412         char inbuf[MAXPATHLEN+1];
413         unsigned char outbuf[MAXPATHLEN * 4];
414
415         for (;;) {
416                 switch (getline(inbuf, sizeof(inbuf))) {
417                 case 0: return (0);
418                 case 1: return (1);
419                 }
420                 n = b64_pton(inbuf, outbuf, sizeof(outbuf));
421                 if (n < 0)
422                         break;
423                 fwrite(outbuf, 1, n, outfp);
424         }
425         return (checkend(inbuf, "====",
426                     "error decoding base64 input stream"));
427 }
428
429 static void
430 usage(void)
431 {
432         (void)fprintf(stderr,
433 "usage: uudecode [-cimprs] [file ...]\n"
434 "       uudecode [-i] -o output_file [file]\n"
435 "       b64decode [-cimprs] [file ...]\n"
436 "       b64decode [-i] -o output_file [file]\n");
437         exit(1);
438 }