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