1 /*
2  * Decode one or more uuencoded article back to binary form.
3  *
4  * UNIX/NN VERSION
5  *	This version cannot be used as a stand-alone uud!
6  * 	This version is made: 16 June 1989.
7  *
8  * From the Berkeley original, modified by MSD, RDR, JPHD & WLS.
9  */
10 
11 #include <unistd.h>
12 #include <string.h>
13 #include "config.h"
14 #include "global.h"
15 #include "save.h"
16 #include "nn_term.h"
17 
18 /* decode.c */
19 
20 static int      strncmp_skip(char *buf, char *str, int n);
21 static int      decode_line(char *buf, register int len, int dont_write);
22 static void     inittbls(void);
23 static int      gettable(FILE * in);
24 static void     new_file(void);
25 
26  /* #define DEC_DEBUG *//* never define this */
27 
28 char           *decode_header_file = "Decode.Headers";
29 int             decode_skip_prefix = 2;
30 int             decode_keep = 0;
31 
32 #define MAXCHAR 256
33 #define LINELEN 256
34 #define NORMLEN 60		/* allows for 80 encoded chars per line */
35 
36 #define SEQMAX 'z'
37 #define SEQMIN 'a'
38 
39 static char     seqc, partn;
40 static int      first, secnd, check;
41 
42 #define MAX_PREFIX 10
43 static int      prefix_lgt, set_prefix;
44 static char     prefix_str[MAX_PREFIX];
45 
46 static FILE    *out;
47 static char    *target;
48 static char     blank;
49 static int      chtbl[MAXCHAR], cdlen[NORMLEN + 3];
50 static char     ofname[FILENAME], arcname[FILENAME];
51 static int      state, arcpart;
52 
53 #define	NO_ADVANCE		0x10
54 
55 #define	DECODE_TEXT			1
56 #define	FIND_BEGIN			2
57 #define	FIND_BEGIN_AFTER_ERROR		3
58 #define FIND_BEGIN_AFTER_INCLUDE	4
59 #define NEW_BEGIN			(5 | NO_ADVANCE)
60 #define	FOUND_END	       		(6 | NO_ADVANCE)
61 #define FOUND_INCLUDE			(7 | NO_ADVANCE)
62 #define	SKIP_LEADING			8
63 #define	SKIP_TRAILING			(9 | NO_ADVANCE)
64 #define DECODE_ERROR			(10 | NO_ADVANCE)
65 #define OTHER_ERROR			(11 | NO_ADVANCE)
66 
67 #define MIN_DECODE_LEADING 8	/* lines to decode ok when doing skip-leading */
68 
69 #ifdef DEC_DEBUG
70 char           *state_tbl[] = {
71     "-", "decode", "find begin", "find a/error", "find a/include",
72     "new begin", "found end", "found include", "skip leading",
73     "skip trail", "error", "other error"
74 };
75 
76 #endif
77 
78 /*
79  * decode one line, write on out file
80  */
81 
82 static int
strncmp_skip(char * buf,char * str,int n)83 strncmp_skip(char *buf, char *str, int n)
84 {
85     register int    i;
86     register char  *line = buf;
87 
88     if (!set_prefix)
89 	return strncmp(line, str, n);
90 
91     if (decode_skip_prefix > MAX_PREFIX)
92 	decode_skip_prefix = MAX_PREFIX;
93 
94     for (i = 0; i <= decode_skip_prefix; i++, line++) {
95 	if (*line == NUL)
96 	    break;
97 	if (*line == '#' || *line == ':')
98 	    break;
99 	if (strncmp(line, str, n))
100 	    continue;
101 	prefix_lgt = i;
102 	if (i)
103 	    strncpy(prefix_str, buf, i);
104 	set_prefix = 0;
105 
106 #ifdef DEC_DEBUG
107 	msg("match %s", str);
108 	user_delay(1);
109 #endif
110 
111 	return 0;
112     }
113 
114     return 1;
115 }
116 
117 static int
decode_line(char * buf,register int len,int dont_write)118 decode_line(char *buf, register int len, int dont_write)
119  /* len		actual input line length */
120  /* dont_write	doing leading check */
121 {
122     char            outl[LINELEN];
123     register char  *bp, *ut;
124     register int   *trtbl = chtbl;
125     register int    n;
126     register int    blen;	/* binary length (from decoded file) */
127     register int    rlen;	/* calculated input line length */
128 
129     /*
130      * Get the binary line length.
131      */
132     if ((blen = trtbl[buf[0]]) < 0) {
133 	if (strncmp(buf, "begin", 5) == 0 || strncmp(buf, "table", 5) == 0)
134 	    return NEW_BEGIN;
135 
136 	if (state == SKIP_LEADING)
137 	    return SKIP_LEADING;
138 
139 	/*
140 	 * end of uuencoded file ?
141 	 */
142 	if (strncmp(buf, "end", 3) == 0)
143 	    return FOUND_END;
144 
145 	/*
146 	 * end of current file ? : get next one.
147 	 */
148 	if (strncmp(buf, "include", 7) == 0)
149 	    return FOUND_INCLUDE;
150 
151 	/*
152 	 * trailing garbage
153 	 */
154 	return SKIP_TRAILING;
155     }
156 
157     /*
158      * Some common endings that survive to this point. Could fail if a line
159      * really started with one of these, but this is sufficiently unlikely,
160      * plus it handles lots of news post formats.
161      */
162     if (state != SKIP_LEADING &&
163 	(strncmp(buf, "END", 3) == 0 ||
164 	 strncmp(buf, "CUT", 3) == 0))
165 	return SKIP_TRAILING;
166 
167     rlen = cdlen[blen];
168     if (len < rlen)
169 	goto d_err;
170     if (len > (rlen + 2))
171 	goto d_err;		/* line too long */
172 
173     /*
174      * Is it the empty line before the end line ?
175      */
176     if (blen == 0)
177 	return state;
178 
179     /*
180      * Pad with blanks.
181      */
182     for (bp = buf + len, n = rlen - len; --n >= 0;)
183 	*bp++ = blank;
184 
185     /*
186      * Verify
187      */
188     for (n = rlen, bp = buf; --n >= 0; bp++)
189 	if (trtbl[*bp] < 0) {
190 
191 #ifdef DEC_DEBUG
192 	    msg("%s - verify failed %d '%.30s'",
193 		state_tbl[state & 0xf], rlen - n, buf);
194 	    user_delay(2);
195 #endif
196 
197 	    goto d_err;
198 	}
199 
200     /*
201      * Check for uuencodes that append a 'z' to each line....
202      */
203     if (check) {
204 	if (secnd) {
205 	    secnd = 0;
206 	    if (buf[rlen] == SEQMAX) {
207 		check = 0;
208 	    }
209 	} else if (first) {
210 	    first = 0;
211 	    secnd = 1;
212 	    if (buf[rlen] != SEQMAX) {
213 		check = 0;
214 	    }
215 	}
216     }
217 
218     /*
219      * There we check.
220      */
221     if (check) {
222 	if (buf[rlen] != seqc) {
223 
224 #ifdef DEC_DEBUG
225 	    msg("check failed %d != %d", buf[rlen], seqc);
226 	    user_delay(1);
227 #endif
228 
229 	    goto d_err;
230 	}
231 	if (--seqc < SEQMIN)
232 	    seqc = SEQMAX;
233     }
234     if (dont_write)
235 	return DECODE_TEXT;
236 
237     /*
238      * output a group of 3 bytes (4 input characters).
239      */
240     ut = outl;
241     n = blen;
242     bp = &buf[1];
243     while (--n >= 0) {
244 	*(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4;
245 	if (n > 0) {
246 	    *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2);
247 	    n--;
248 	}
249 	if (n > 0) {
250 	    *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]];
251 	    n--;
252 	}
253 	bp += 4;
254     }
255     if ((int) fwrite(outl, 1, blen, out) <= 0) {
256 	msg("Error on writing decoded file");
257 	return OTHER_ERROR;
258     }
259     return DECODE_TEXT;
260 
261 d_err:
262     if (state == SKIP_LEADING)
263 	return SKIP_LEADING;
264     return DECODE_ERROR;
265 
266 }
267 
268 
269 
270 /*
271  * Install the table in memory for later use.
272  */
273 static void
inittbls(void)274 inittbls(void)
275 {
276     register int    i, j;
277 
278     /*
279      * Set up the default translation table.
280      */
281     for (i = 0; i < ' '; i++)
282 	chtbl[i] = -1;
283     for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
284 	chtbl[i] = j;
285     for (i = ' ' + 64; i < MAXCHAR; i++)
286 	chtbl[i] = -1;
287     chtbl['`'] = chtbl[' '];	/* common mutation */
288     chtbl['~'] = chtbl['^'];	/* an other common mutation */
289     blank = ' ';
290 
291     /*
292      * set up the line length table, to avoid computing lotsa * and / ...
293      */
294     cdlen[0] = 1;
295     for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4)
296 	cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j));
297 }
298 
299 static int
gettable(FILE * in)300 gettable(FILE * in)
301 {
302     char            buf[LINELEN], *line;
303     register int    c, n = 0;
304     register char  *cpt;
305 
306     for (c = 0; c < MAXCHAR; c++)
307 	chtbl[c] = -1;
308 
309     for (;;) {
310 	if (fgets(buf, sizeof buf, in) == NULL) {
311 	    msg("EOF while in translation table.");
312 	    return -1;
313 	}
314 	line = buf + prefix_lgt;
315 	if ((prefix_lgt > 0 && strncmp(buf, prefix_str, prefix_lgt)) ||
316 	    strncmp(line, "begin", 5) == 0) {
317 	    msg("Incomplete translation table.");
318 	    return -1;
319 	}
320 	cpt = line + strlen(line) - 1;
321 	*cpt = ' ';
322 	while (*(cpt) == ' ') {
323 	    *cpt = 0;
324 	    cpt--;
325 	}
326 	cpt = line;
327 	while (*cpt) {
328 	    c = *cpt;
329 	    if (chtbl[c] != -1) {
330 		msg("Duplicate char in translation table.");
331 		return -1;
332 	    }
333 	    if (n == 0)
334 		blank = c;
335 	    chtbl[c] = n++;
336 	    if (n >= 64)
337 		return 0;
338 	    cpt++;
339 	}
340     }
341 }
342 
343 static void
new_file(void)344 new_file(void)
345 {
346     out = NULL;
347     seqc = SEQMAX;
348     partn = 'a';
349     check = 1;
350     first = 1;
351     secnd = 0;
352     state = FIND_BEGIN;
353     prefix_lgt = 0;
354     set_prefix = decode_skip_prefix;
355     arcpart = 0;
356     inittbls();
357 }
358 
359 void
uud_start(char * dir)360 uud_start(char *dir)
361 {
362     target = dir;
363     new_file();
364 }
365 
366 void
uud_end(void)367 uud_end(void)
368 {
369     if (out != NULL) {
370 	fclose(out);
371 	if (decode_keep) {
372 	    msg("%s INCOMPLETE -- removed", arcname);
373 	    unlink(ofname);
374 	    user_delay(5);
375 	}
376 	out = NULL;
377     }
378 }
379 
380 int
uudecode(register article_header * ah,FILE * in)381 uudecode(register article_header * ah, FILE * in)
382 {
383     mode_t          mode;
384     int             onedone, len, lead_check = 0;
385     char            buf[LINELEN], part[2], *line;
386     long            real_size, start_offset;
387     long            expect_size;
388 
389     onedone = 0;
390 
391     /*
392      * search for header or translation table line.
393      */
394     start_offset = ftell(in);
395 
396     for (;;) {
397 	if ((state & NO_ADVANCE) == 0) {
398 	    if (ftell(in) >= ah->lpos)
399 		break;
400 	    if (fgets(buf, sizeof buf, in) == NULL)
401 		break;
402 	}
403 	if (s_keyboard)
404 	    return -1;
405 
406 	len = strlen(buf);
407 	if (len > 0 && buf[len - 1] == NL)
408 	    buf[--len] = NUL;
409 
410 #ifdef DEC_DEBUG
411 	if (state != ostate) {
412 	    msg("%s->%s - '%.30s'", state_tbl[ostate & 0xf], state_tbl[state & 0xf], buf);
413 	    user_delay(2);
414 	    ostate = state;
415 	}
416 #endif
417 
418 	switch (state) {
419 
420 	    case NEW_BEGIN:
421 		if (out != NULL) {
422 		    uud_end();
423 		    user_delay(5);
424 		}
425 		new_file();
426 
427 		/* FALLTHRU */
428 	    case FIND_BEGIN:
429 	    case FIND_BEGIN_AFTER_ERROR:
430 	    case FIND_BEGIN_AFTER_INCLUDE:
431 		set_prefix = decode_skip_prefix;
432 		if (strncmp_skip(buf, "table", 5) == 0) {
433 		    gettable(in);
434 		    continue;
435 		}
436 		if (strncmp_skip(buf, "begin", 5))
437 		    continue;
438 
439 		line = buf + prefix_lgt;
440 
441 		if (state == FIND_BEGIN_AFTER_INCLUDE) {
442 		    if (sscanf(line, "begin part %1s%s", part, arcname) != 2) {
443 			msg("Invalid 'begin' line after 'include'");
444 			continue;
445 		    }
446 		    partn++;
447 		    if (partn > 'z')
448 			partn = 'a';
449 		    if (part[0] != partn) {
450 			msg("PARTS NOT IN SEQUENCE: %s -- removed", arcname);
451 			user_delay(5);
452 			fclose(out);
453 			unlink(ofname);
454 			new_file();
455 			state = FIND_BEGIN_AFTER_ERROR;
456 			goto err;
457 		    }
458 		} else {
459 		    if (sscanf(line, "begin %o %s", (unsigned int *) &mode, arcname) != 2)
460 			continue;
461 
462 		    if (target != NULL)
463 			sprintf(ofname, "%s%s", target, arcname);
464 		    else
465 			strcpy(ofname, arcname);
466 
467 		    if ((out = open_file(ofname, OPEN_CREATE)) == NULL) {
468 			msg("Cannot create file: %s", ofname);
469 			goto err;
470 		    }
471 		    chmod(ofname, mode & 0666);
472 
473 		    if (decode_header_file)
474 			store_header(ah, in, target, decode_header_file);
475 		}
476 
477 		state = DECODE_TEXT;
478 		continue;
479 
480 	    case SKIP_LEADING:
481 		if (len > prefix_lgt &&
482 		(!prefix_lgt || strncmp(buf, prefix_str, prefix_lgt) == 0)) {
483 		    state = decode_line(buf + prefix_lgt, len - prefix_lgt, 1);
484 		    if (state == DECODE_TEXT) {
485 			if (++lead_check == MIN_DECODE_LEADING) {
486 			    fseek(in, start_offset, 0);
487 			    continue;
488 			}
489 			state = SKIP_LEADING;
490 			continue;
491 		    }
492 		} else {
493 		    set_prefix = decode_skip_prefix;
494 		    if (strncmp_skip(buf, "begin", 5) == 0 ||
495 			strncmp_skip(buf, "table", 5) == 0)
496 			state = NEW_BEGIN;
497 		}
498 
499 		lead_check = 0;
500 		start_offset = ftell(in);
501 		continue;
502 
503 	    case DECODE_TEXT:
504 		if (len <= prefix_lgt ||
505 		 (prefix_lgt > 0 && strncmp(buf, prefix_str, prefix_lgt))) {
506 		    state = SKIP_TRAILING;
507 		    continue;
508 		}
509 		if (onedone == 0) {
510 		    msg("Decoding%s: %s (part %d)",
511 		      prefix_lgt ? " & Unsharing" : "", arcname, ++arcpart);
512 
513 		    onedone = 1;
514 		}
515 		state = decode_line(buf + prefix_lgt, len - prefix_lgt, 0);
516 		continue;
517 
518 	    case FOUND_END:
519 		real_size = ftell(out);
520 		fclose(out);
521 
522 		if (ftell(in) >= ah->lpos || fgets(buf, sizeof buf, in) == NULL) {
523 		    new_file();
524 		    break;
525 		}
526 		if ((!prefix_lgt || strncmp(buf, prefix_str, prefix_lgt) == 0) &&
527 		    sscanf(buf + prefix_lgt, "size%ld", &expect_size) == 1 &&
528 		    real_size != expect_size) {
529 
530 		    msg("%s decoded with wrong size %ld (exp. %ld)",
531 			arcname, real_size, expect_size);
532 		    user_delay(3);
533 		} else {
534 		    msg("%s complete", arcname);
535 		    user_delay(1);
536 		}
537 
538 		new_file();
539 		state = NEW_BEGIN;
540 		continue;
541 
542 	    case FOUND_INCLUDE:
543 		state = FIND_BEGIN_AFTER_INCLUDE;
544 		return 0;
545 
546 	    case SKIP_TRAILING:
547 		state = SKIP_LEADING;
548 		return 0;
549 
550 	    case DECODE_ERROR:
551 		state = SKIP_TRAILING;
552 		continue;
553 
554 	    case OTHER_ERROR:
555 		fclose(out);
556 		new_file();
557 		state = FIND_BEGIN_AFTER_ERROR;
558 		goto err;
559 	}
560 
561 	break;			/* break in switch => break in loop */
562     }
563 
564     if (onedone) {
565 	if (state == DECODE_TEXT)
566 	    state = SKIP_LEADING;
567 	return 0;
568     }
569     if (state == FIND_BEGIN_AFTER_ERROR)
570 	return -1;
571     msg("No 'begin' line");
572 
573 err:
574     user_delay(2);
575     return -1;
576 }
577