]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/uudecode/uudecode.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #if 0
31 #ifndef lint
32 static const char copyright[] =
33 "@(#) Copyright (c) 1983, 1993\n\
34         The Regents of the University of California.  All rights reserved.\n";
35 #endif /* not lint */
36
37 #ifndef lint
38 static char sccsid[] = "@(#)uudecode.c  8.2 (Berkeley) 4/2/94";
39 #endif /* not lint */
40 #endif
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43
44 /*
45  * uudecode [file ...]
46  *
47  * create the specified file, decoding as you go.
48  * used with uuencode.
49  */
50 #include <sys/param.h>
51 #include <sys/socket.h>
52 #include <sys/stat.h>
53
54 #include <netinet/in.h>
55
56 #include <ctype.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <libgen.h>
61 #include <pwd.h>
62 #include <resolv.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67
68 static const char *infile, *outfile;
69 static FILE *infp, *outfp;
70 static int base64, cflag, iflag, oflag, pflag, rflag, sflag;
71
72 static void     usage(void);
73 static int      decode(void);
74 static int      decode2(void);
75 static int      uu_decode(void);
76 static int      base64_decode(void);
77
78 int
79 main(int argc, char *argv[])
80 {
81         int rval, ch;
82
83         if (strcmp(basename(argv[0]), "b64decode") == 0)
84                 base64 = 1;
85
86         while ((ch = getopt(argc, argv, "cimo:prs")) != -1) {
87                 switch (ch) {
88                 case 'c':
89                         if (oflag || rflag)
90                                 usage();
91                         cflag = 1; /* multiple uudecode'd files */
92                         break;
93                 case 'i':
94                         iflag = 1; /* ask before override files */
95                         break;
96                 case 'm':
97                         base64 = 1;
98                         break;
99                 case 'o':
100                         if (cflag || pflag || rflag || sflag)
101                                 usage();
102                         oflag = 1; /* output to the specified file */
103                         sflag = 1; /* do not strip pathnames for output */
104                         outfile = optarg; /* set the output filename */
105                         break;
106                 case 'p':
107                         if (oflag)
108                                 usage();
109                         pflag = 1; /* print output to stdout */
110                         break;
111                 case 'r':
112                         if (cflag || oflag)
113                                 usage();
114                         rflag = 1; /* decode raw data */
115                         break;
116                 case 's':
117                         if (oflag)
118                                 usage();
119                         sflag = 1; /* do not strip pathnames for output */
120                         break;
121                 default:
122                         usage();
123                 }
124         }
125         argc -= optind;
126         argv += optind;
127
128         if (*argv != NULL) {
129                 rval = 0;
130                 do {
131                         infp = fopen(infile = *argv, "r");
132                         if (infp == NULL) {
133                                 warn("%s", *argv);
134                                 rval = 1;
135                                 continue;
136                         }
137                         rval |= decode();
138                         fclose(infp);
139                 } while (*++argv);
140         } else {
141                 infile = "stdin";
142                 infp = stdin;
143                 rval = decode();
144         }
145         exit(rval);
146 }
147
148 static int
149 decode(void)
150 {
151         int r, v;
152
153         if (rflag) {
154                 /* relaxed alternative to decode2() */
155                 outfile = "/dev/stdout";
156                 outfp = stdout;
157                 if (base64)
158                         return (base64_decode());
159                 else
160                         return (uu_decode());
161         }
162         v = decode2();
163         if (v == EOF) {
164                 warnx("%s: missing or bad \"begin\" line", infile);
165                 return (1);
166         }
167         for (r = v; cflag; r |= v) {
168                 v = decode2();
169                 if (v == EOF)
170                         break;
171         }
172         return (r);
173 }
174
175 static int
176 decode2(void)
177 {
178         int flags, fd, mode;
179         size_t n, m;
180         char *p, *q;
181         void *handle;
182         struct passwd *pw;
183         struct stat st;
184         char buf[MAXPATHLEN + 1];
185
186         base64 = 0;
187         /* search for header line */
188         for (;;) {
189                 if (fgets(buf, sizeof(buf), infp) == NULL)
190                         return (EOF);
191                 p = buf;
192                 if (strncmp(p, "begin-base64 ", 13) == 0) {
193                         base64 = 1;
194                         p += 13;
195                 } else if (strncmp(p, "begin ", 6) == 0)
196                         p += 6;
197                 else
198                         continue;
199                 /* p points to mode */
200                 q = strchr(p, ' ');
201                 if (q == NULL)
202                         continue;
203                 *q++ = '\0';
204                 /* q points to filename */
205                 n = strlen(q);
206                 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
207                         q[--n] = '\0';
208                 /* found valid header? */
209                 if (n > 0)
210                         break;
211         }
212
213         handle = setmode(p);
214         if (handle == NULL) {
215                 warnx("%s: unable to parse file mode", infile);
216                 return (1);
217         }
218         mode = getmode(handle, 0) & 0666;
219         free(handle);
220
221         if (sflag) {
222                 /* don't strip, so try ~user/file expansion */
223                 p = NULL;
224                 pw = NULL;
225                 if (*q == '~')
226                         p = strchr(q, '/');
227                 if (p != NULL) {
228                         *p = '\0';
229                         pw = getpwnam(q + 1);
230                         *p = '/';
231                 }
232                 if (pw != NULL) {
233                         n = strlen(pw->pw_dir);
234                         if (buf + n > p) {
235                                 /* make room */
236                                 m = strlen(p);
237                                 if (sizeof(buf) < n + m) {
238                                         warnx("%s: bad output filename",
239                                             infile);
240                                         return (1);
241                                 }
242                                 p = memmove(buf + n, p, m);
243                         }
244                         q = memcpy(p - n, pw->pw_dir, n);
245                 }
246         } else {
247                 /* strip down to leaf name */
248                 p = strrchr(q, '/');
249                 if (p != NULL)
250                         q = p + 1;
251         }
252         if (!oflag)
253                 outfile = q;
254
255         /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */
256         if (pflag || strcmp(outfile, "/dev/stdout") == 0)
257                 outfp = stdout;
258         else {
259                 flags = O_WRONLY | O_CREAT | O_EXCL;
260                 if (lstat(outfile, &st) == 0) {
261                         if (iflag) {
262                                 warnc(EEXIST, "%s: %s", infile, outfile);
263                                 return (0);
264                         }
265                         switch (st.st_mode & S_IFMT) {
266                         case S_IFREG:
267                         case S_IFLNK:
268                                 /* avoid symlink attacks */
269                                 if (unlink(outfile) == 0 || errno == ENOENT)
270                                         break;
271                                 warn("%s: unlink %s", infile, outfile);
272                                 return (1);
273                         case S_IFDIR:
274                                 warnc(EISDIR, "%s: %s", infile, outfile);
275                                 return (1);
276                         default:
277                                 if (oflag) {
278                                         /* trust command-line names */
279                                         flags &= ~O_EXCL;
280                                         break;
281                                 }
282                                 warnc(EEXIST, "%s: %s", infile, outfile);
283                                 return (1);
284                         }
285                 } else if (errno != ENOENT) {
286                         warn("%s: %s", infile, outfile);
287                         return (1);
288                 }
289                 if ((fd = open(outfile, flags, mode)) < 0 ||
290                     (outfp = fdopen(fd, "w")) == NULL) {
291                         warn("%s: %s", infile, outfile);
292                         return (1);
293                 }
294         }
295
296         if (base64)
297                 return (base64_decode());
298         else
299                 return (uu_decode());
300 }
301
302 static int
303 getline(char *buf, size_t size)
304 {
305
306         if (fgets(buf, size, infp) != NULL)
307                 return (2);
308         if (rflag)
309                 return (0);
310         warnx("%s: %s: short file", infile, outfile);
311         return (1);
312 }
313
314 static int
315 checkend(const char *ptr, const char *end, const char *msg)
316 {
317         size_t n;
318
319         n = strlen(end);
320         if (strncmp(ptr, end, n) != 0 ||
321             strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) {
322                 warnx("%s: %s: %s", infile, outfile, msg);
323                 return (1);
324         }
325         if (fclose(outfp) != 0) {
326                 warn("%s: %s", infile, outfile);
327                 return (1);
328         }
329         return (0);
330 }
331
332 static int
333 uu_decode(void)
334 {
335         int i, ch;
336         char *p;
337         char buf[MAXPATHLEN+1];
338
339         /* for each input line */
340         for (;;) {
341                 switch (getline(buf, sizeof(buf))) {
342                 case 0:
343                         return (0);
344                 case 1:
345                         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                         } else {
377                                 if (i >= 1) {
378                                         if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
379                                                 OUT_OF_RANGE;
380                                         ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
381                                         putc(ch, outfp);
382                                 }
383                                 if (i >= 2) {
384                                         if (!(IS_DEC(*(p + 1)) &&
385                                             IS_DEC(*(p + 2))))
386                                                 OUT_OF_RANGE;
387
388                                         ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
389                                         putc(ch, outfp);
390                                 }
391                                 if (i >= 3) {
392                                         if (!(IS_DEC(*(p + 2)) &&
393                                             IS_DEC(*(p + 3))))
394                                                 OUT_OF_RANGE;
395                                         ch = DEC(p[2]) << 6 | DEC(p[3]);
396                                         putc(ch, outfp);
397                                 }
398                         }
399         }
400         switch (getline(buf, sizeof(buf))) {
401         case 0:
402                 return (0);
403         case 1:
404                 return (1);
405         default:
406                 return (checkend(buf, "end", "no \"end\" line"));
407         }
408 }
409
410 static int
411 base64_decode(void)
412 {
413         int n, count, count4;
414         char inbuf[MAXPATHLEN + 1], *p;
415         unsigned char outbuf[MAXPATHLEN * 4];
416         char leftover[MAXPATHLEN + 1];
417
418         leftover[0] = '\0';
419         for (;;) {
420                 strcpy(inbuf, leftover);
421                 switch (getline(inbuf + strlen(inbuf),
422                     sizeof(inbuf) - strlen(inbuf))) {
423                 case 0:
424                         return (0);
425                 case 1:
426                         return (1);
427                 }
428
429                 count = 0;
430                 count4 = -1;
431                 p = inbuf;
432                 while (*p != '\0') {
433                         /*
434                          * Base64 encoded strings have the following
435                          * characters in them: A-Z, a-z, 0-9 and +, / and =
436                          */
437                         if (isalnum(*p) || *p == '+' || *p == '/' || *p == '=')
438                                 count++;
439                         if (count % 4 == 0)
440                                 count4 = p - inbuf;
441                         p++;
442                 }
443
444                 strcpy(leftover, inbuf + count4 + 1);
445                 inbuf[count4 + 1] = 0;
446
447                 n = b64_pton(inbuf, outbuf, sizeof(outbuf));
448
449                 if (n < 0)
450                         break;
451                 fwrite(outbuf, 1, n, outfp);
452         }
453         return (checkend(inbuf, "====", "error decoding base64 input stream"));
454 }
455
456 static void
457 usage(void)
458 {
459
460         (void)fprintf(stderr,
461             "usage: uudecode [-cimprs] [file ...]\n"
462             "       uudecode [-i] -o output_file [file]\n"
463             "       b64decode [-cimprs] [file ...]\n"
464             "       b64decode [-i] -o output_file [file]\n");
465         exit(1);
466 }