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