ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uunconc.c
Revision: 1.33
Committed: Sat Sep 24 17:40:45 2022 UTC (19 months, 3 weeks ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.32: +4 -4 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 /*
2 * This file is part of uudeview, the simple and friendly multi-part multi-
3 * file uudecoder program (c) 1994-2001 by Frank Pilhofer. The author may
4 * be contacted at fp@fpx.de
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17 /*
18 * These are the functions that are responsible for decoding. The
19 * original idea is from a freeware utility called "uunconc", and
20 * few lines of this code may still bear a remote resemblance to
21 * its code. If you are the author or know him, contact me.
22 * This program could only decode one multi-part, uuencoded file
23 * where the parts were in order. Base64, XX and BinHex decoding,
24 * support for multi-files and part-ordering covered by myself.
25 **/
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #ifdef SYSTEM_WINDLL
32 #include <windows.h>
33 #endif
34 #ifdef SYSTEM_OS2
35 #include <os2.h>
36 #endif
37
38 #include <stdio.h>
39 #include <ctype.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <errno.h>
43
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47
48 #include <crc32.h>
49 #include <uudeview.h>
50 #include <uuint.h>
51 #include <fptools.h>
52 #include <uustring.h>
53
54 /* for braindead systems */
55 #ifndef SEEK_SET
56 #ifdef L_BEGIN
57 #define SEEK_SET L_BEGIN
58 #else
59 #define SEEK_SET 0
60 #endif
61 #endif
62
63 /*
64 * decoder states
65 */
66
67 #define BEGIN (1)
68 #define DATA (2)
69 #define END (3)
70 #define DONE (4)
71
72 /*
73 * mallocable areas
74 */
75
76 char *uunconc_UUxlat;
77 char *uunconc_UUxlen;
78 char *uunconc_B64xlat;
79 char *uunconc_XXxlat;
80 char *uunconc_BHxlat;
81 char *uunconc_save;
82
83 /*
84 * decoding translation tables and line length table
85 */
86
87 static int * UUxlen; /* initialized in UUInitConc() */
88 static int * UUxlat; /* from the malloc'ed areas above */
89 static int * B64xlat;
90 static int * XXxlat;
91 static int * BHxlat;
92
93 /*
94 * buffer for decoding
95 */
96
97 static char *save[3];
98
99 /*
100 * mallocable areas
101 */
102
103 char *uuncdl_fulline;
104 char *uuncdp_oline;
105
106 /*
107 * Return information for QuickDecode
108 */
109
110 static int uulboundary;
111
112 /*
113 * To prevent warnings when using a char as index into an array
114 */
115
116 #define ACAST(s) ((int)(uchar)(s))
117
118 /*
119 * Initialize decoding tables
120 */
121
122 void
123 UUInitConc (void)
124 {
125 int i, j;
126
127 /*
128 * Update pointers
129 */
130 UUxlen = (int *) uunconc_UUxlen;
131 UUxlat = (int *) uunconc_UUxlat;
132 B64xlat = (int *) uunconc_B64xlat;
133 XXxlat = (int *) uunconc_XXxlat;
134 BHxlat = (int *) uunconc_BHxlat;
135
136 save[0] = uunconc_save;
137 save[1] = uunconc_save + 1200;
138 save[2] = uunconc_save + 2400;
139
140 /* prepare decoding translation table */
141 for(i = 0; i < 256; i++)
142 UUxlat[i] = B64xlat[i] = XXxlat[i] = BHxlat[i] = -1;
143
144 /*
145 * At some time I received a file which used lowercase characters for
146 * uuencoding. This shouldn't be, but let's accept it. Must take special
147 * care that this doesn't break xxdecoding. This is giving me quite a
148 * headache. If this one file hadn't been a Pocahontas picture, I might
149 * have ignored it for good.
150 */
151
152 for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
153 UUxlat[i] /* = UUxlat[i+64] */ = j;
154 for (i = '`', j = 0; i < '`' + 32; i++, j++)
155 UUxlat[i] = j;
156
157 /* add special cases */
158 UUxlat['`'] = UUxlat[' '];
159 UUxlat['~'] = UUxlat['^'];
160
161 /* prepare line length table */
162 UUxlen[0] = 1;
163 for(i = 1, j = 5; i <= 61; i += 3, j += 4)
164 UUxlen[i] = UUxlen[i+1] = UUxlen[i+2] = j;
165
166 /* prepare other tables */
167 for (i=0; i<64; i++) {
168 B64xlat[ACAST(B64EncodeTable[i])] = i;
169 XXxlat [ACAST(XXEncodeTable [i])] = i;
170 BHxlat [ACAST(BHEncodeTable [i])] = i;
171 }
172 }
173
174 /*
175 * Workaround for Netscape
176 */
177
178 /*
179 * Determines whether Netscape may have broken up a data line (by
180 * inserting a newline). This only seems to happen after <a in a
181 * href statement
182 */
183
184 static int
185 UUBrokenByNetscape (char *string)
186 {
187 char *ptr;
188 int len;
189
190 if (string==NULL || (len=strlen(string))<3)
191 return 0;
192
193 if ((ptr = FP_stristr (string, "<a href=")) != NULL) {
194 if (FP_stristr (string, "</a>") > ptr)
195 return 2;
196 }
197
198 ptr = string + len;
199
200 if (len<3) return 0;
201 if (*--ptr == ' ') ptr--;
202 ptr--;
203
204 if (FP_strnicmp (ptr, "<a", 2) == 0)
205 return 1;
206
207 return 0;
208 }
209
210 /*
211 * Try to repair a Netscape-corrupted line of data.
212 * This must only be called on corrupted lines, since non-Netscape
213 * data may even _get_ corrupted by this procedure.
214 *
215 * Some checks are included multiply to speed up the procedure. For
216 * example: (*p1!='<' || strnicmp(p1,"</a>",4)). If the first expression
217 * becomes true, the costly function isn't called :-)
218 *
219 * Since '<', '>', '&' might even be replaced by their html equivalents
220 * in href strings, I'm now using two passes, the first one for &amp; + co,
221 * the second one for hrefs.
222 */
223
224 static int
225 UUNetscapeCollapse (char *string)
226 {
227 char *p1=string, *p2=string;
228 int res = 0;
229
230 if (string==NULL)
231 return 0;
232
233 /*
234 * First pass
235 */
236 while (*p1) {
237 if (*p1 == '&') {
238 if (FP_strnicmp (p1, "&amp;", 5) == 0) { p1+=5; *p2++='&'; res=1; }
239 else if (FP_strnicmp (p1, "&lt;", 4) == 0) { p1+=4; *p2++='<'; res=1; }
240 else if (FP_strnicmp (p1, "&gt;", 4) == 0) { p1+=4; *p2++='>'; res=1; }
241 else *p2++ = *p1++;
242 res = 1;
243 }
244 else *p2++ = *p1++;
245 }
246 *p2 = '\0';
247 /*
248 * Second pass
249 */
250 p1 = p2 = string;
251
252 while (*p1) {
253 if (*p1 == '<') {
254 if ((FP_strnicmp (p1, "<ahref=", 7) == 0 ||
255 FP_strnicmp (p1, "<a href=",8) == 0) &&
256 (FP_strstr (p1, "</a>") != 0 || FP_strstr (p1, "</A>") != 0)) {
257 while (*p1 && *p1!='>') p1++;
258 if (*p1=='\0' || *(p1+1)!='<') return 0;
259 p1++;
260 while (*p1 && (*p1!='<' || FP_strnicmp(p1,"</a>",4)!=0)) {
261 *p2++ = *p1++;
262 }
263 if (FP_strnicmp(p1,"</a>",4) != 0)
264 return 0;
265 p1+=4;
266 res=1;
267 }
268 else
269 *p2++ = *p1++;
270 }
271 else
272 *p2++ = *p1++;
273 }
274 *p2 = '\0';
275
276 return res;
277 }
278
279 /*
280 * The second parameter is 0 if we are still searching for encoded data,
281 * otherwise it indicates the encoding we're using right now. If we're
282 * still in the searching stage, we must be a little more strict in
283 * deciding for or against encoding; there's too much plain text looking
284 * like encoded data :-(
285 */
286
287 int
288 UUValidData (char *ptr, int encoding, int *bhflag)
289 {
290 int i=0, j, len=0, suspicious=0, flag=0;
291 signed char *s = ptr;
292
293 if ((s == NULL) || (*s == '\0')) {
294 return 0; /* bad string */
295 }
296
297 if (encoding == YENC_ENCODED)
298 return YENC_ENCODED;
299
300 i = strlen (s);
301
302 switch (encoding) {
303 case UU_ENCODED:
304 goto _t_UU;
305 case XX_ENCODED:
306 goto _t_XX;
307 case B64ENCODED:
308 goto _t_B64;
309 case BH_ENCODED:
310 goto _t_Binhex;
311 }
312
313 _t_Binhex: /* Binhex Test */
314 len = i; s = ptr;
315
316 /*
317 * bhflag notes the state we're in. Within the data, it's 1. If we're
318 * still looking for the initial :, it's 0
319 */
320 if (*bhflag == 0 && *s != ':') {
321 if (encoding==BH_ENCODED) return 0;
322 goto _t_B64;
323 }
324 else if (*bhflag == 0 /* *s == ':' */) {
325 s++; len--;
326 }
327
328 while (len && BHxlat[ACAST(*s)] != -1) {
329 len--; s++;
330 }
331
332 /* allow space characters at the end of the line if we are sure */
333 /* that this is Binhex encoded data or the line was long enough */
334
335 flag = (*s == ':') ? 0 : 1;
336
337 if (*s == ':' && len>0) {
338 s++; len--;
339 }
340 if (((i>=60 && len<=10) || encoding) && *s==' ') {
341 while (len && *s==' ') {
342 s++; len--;
343 }
344 }
345
346 /*
347 * BinHex data shall have exactly 64 characters (except the last
348 * line). We ignore everything with less than 40 characters to
349 * be flexible
350 */
351
352 if (len != 0 || (flag && i < 40)) {
353 if (encoding==BH_ENCODED) return 0;
354 goto _t_B64;
355 }
356
357 *bhflag = flag;
358
359 return BH_ENCODED;
360
361 _t_B64: /* Base64 Test */
362 len = i; s = ptr;
363
364 /*
365 * Face it: there _are_ Base64 lines that are not a multiple of four
366 * in length :-(
367 *
368 * if (len%4)
369 * goto _t_UU;
370 */
371
372 while (len--) {
373 if (*s < 0 || (B64xlat[ACAST(*s)] == -1 && *s != '=')) {
374 /* allow space characters at the end of the line if we are sure */
375 /* that this is Base64 encoded data or the line was long enough */
376 if (((i>=60 && len<=10) || encoding) && *s++==' ') {
377 while (*s==' ' && len) s++;
378 if (len==0) return B64ENCODED;
379 }
380 if (encoding==B64ENCODED) return 0;
381 goto _t_UU;
382 }
383 else if (*s == '=') { /* special case at end */
384 /* if we know this is B64encoded, allow spaces at end of line */
385 s++;
386 if (*s=='=' && len>=1) {
387 len--; s++;
388 }
389 if (encoding && len && *s==' ') {
390 while (len && *s==' ') {
391 s++; len--;
392 }
393 }
394 if (len != 0) {
395 if (encoding==B64ENCODED) return 0;
396 goto _t_UU;
397 }
398 return B64ENCODED;
399 }
400 s++;
401 }
402 return B64ENCODED;
403
404 _t_UU:
405 len = i; s = ptr;
406
407 if (UUxlat[ACAST(*s)] == -1) { /* uutest */
408 if (encoding==UU_ENCODED) return 0;
409 goto _t_XX;
410 }
411
412 j = UUxlen[UUxlat[ACAST(*s)]];
413
414 if (len-1 == j) /* remove trailing character */
415 len--;
416 if (len != j) {
417 switch (UUxlat[ACAST(*s)]%3) {
418 case 1:
419 if (j-2 == len) j-=2;
420 break;
421 case 2:
422 if (j-1 == len) j-=1;
423 break;
424 }
425 }
426
427 /*
428 * some encoders are broken with respect to encoding the last line of
429 * a file and produce extraoneous characters beyond the expected EOL
430 * So were not too picky here about the last line, as long as it's longer
431 * than necessary and shorter than the maximum
432 * this tolerance broke the xxdecoding, because xxencoded data was
433 * detected as being uuencoded :( so don't accept 'h' as first character
434 * also, if the first character is lowercase, don't accept the line to
435 * have space characters. the only encoder I've heard of which uses
436 * lowercase characters at least accepts the special case of encoding
437 * 0 as `. The strchr() shouldn't be too expensive here as it's only
438 * evaluated if the first character is lowercase, which really shouldn't
439 * be in uuencoded text.
440 */
441 if (len != j &&
442 ((ptr[0] == '-' && ptr[1] == '-' && strstr(ptr,"part")!=NULL) ||
443 !(*ptr != 'M' && *ptr != 'h' &&
444 len > j && len <= UUxlen[UUxlat['M']]))) {
445 if (encoding==UU_ENCODED) return 0;
446 goto _t_XX; /* bad length */
447 }
448
449 if (len != j || islower (*ptr)) {
450 /*
451 * if we are not in a 'uuencoded' state, don't allow the line to have
452 * space characters at all. if we know we _are_ decoding uuencoded
453 * data, the rest of the line, beyond the length of encoded data, may
454 * have spaces.
455 */
456 if (encoding != UU_ENCODED)
457 if (strchr (ptr, ' ') != NULL)
458 goto _t_XX;
459
460 /* suspicious = 1; we're careful here REMOVED 0.4.15 __FP__ */
461 len = j;
462 }
463
464 while (len--) {
465 if (*s < 0 || UUxlat[ACAST(*s++)] < 0) {
466 if (encoding==UU_ENCODED) return 0;
467 goto _t_XX; /* bad code character */
468 }
469 if (*s == ' ' && suspicious) {
470 if (encoding==UU_ENCODED) return 0;
471 goto _t_XX; /* this line looks _too_ suspicious */
472 }
473 }
474 return UU_ENCODED; /* data is valid */
475
476 _t_XX: /* XX Test */
477 len = i; s = ptr;
478
479 if (XXxlat[ACAST(*s)] == -1)
480 return 0;
481
482 j = UUxlen[XXxlat[ACAST(*s)]]; /* Same line length table as UUencoding */
483
484 if (len-1 == j) /* remove trailing character */
485 len--;
486 if (len != j)
487 switch (UUxlat[ACAST(*s)]%3) {
488 case 1:
489 if (j-2 == len) j-=2;
490 break;
491 case 2:
492 if (j-1 == len) j-=1;
493 break;
494 }
495 /*
496 * some encoders are broken with respect to encoding the last line of
497 * a file and produce extraoneous characters beyond the expected EOL
498 * So were not too picky here about the last line, as long as it's longer
499 * than necessary and shorter than the maximum
500 */
501 if (len != j && !(*ptr != 'h' && len > j && len <= UUxlen[UUxlat['h']]))
502 return 0; /* bad length */
503
504 while(len--) {
505 if(*s < 0 || XXxlat[ACAST(*s++)] < 0) {
506 return 0; /* bad code character */
507 }
508 }
509 return XX_ENCODED; /* data is valid */
510 }
511
512 /*
513 * This function may be called upon a line that does not look like
514 * valid encoding on first sight, but might be erroneously encoded
515 * data from Netscape, Lynx or MS Exchange. We might need to read
516 * a new line from the stream, which is why we need the FILE.
517 * Returns the type of encoded data if successful or 0 otherwise.
518 */
519
520 int
521 UURepairData (FILE *datei, char *line, int encoding, int *bhflag)
522 {
523 int nflag, vflag=0, safety=42;
524 char *ptr;
525
526 nflag = UUBrokenByNetscape (line);
527
528 while (vflag == 0 && nflag && safety--) {
529 if (nflag == 1) { /* need next line to repair */
530 if (strlen (line) > 250)
531 break;
532 ptr = line + strlen (line);
533 if (FP_fgets (ptr, 299-(ptr-line), datei) == NULL)
534 break;
535 }
536 else { /* don't need next line to repair */
537 }
538 if (UUNetscapeCollapse (line)) {
539 if ((vflag = UUValidData (line, encoding, bhflag)) == 0)
540 nflag = UUBrokenByNetscape (line);
541 }
542 else
543 nflag = 0;
544 }
545 /*
546 * Sometimes, a line is garbled even without it being split into
547 * the next line. Then we try this in our despair
548 */
549 if (vflag == 0) {
550 if (UUNetscapeCollapse (line))
551 vflag = UUValidData (line, encoding, bhflag);
552 }
553
554 /*
555 * If this line looks uuencoded, but the line is one character short
556 * of a valid line, it was probably broken by MS Exchange. According
557 * to my test cases, there is at most one space character missing;
558 * there are never two spaces together.
559 * If adding a space character helps making this line uuencoded, do
560 * it!
561 */
562
563 if (vflag == 0) {
564 ptr = line + strlen(line);
565 *ptr++ = ' ';
566 *ptr-- = '\0';
567 if ((vflag = UUValidData (line, encoding, bhflag)) != UU_ENCODED) {
568 *ptr = '\0';
569 vflag = 0;
570 }
571 }
572 return vflag;
573 }
574
575 /*
576 * Decode a single encoded line using method
577 */
578
579 size_t
580 UUDecodeLine (char *s, char *d, int method)
581 {
582 int i, j, c, cc, count=0, z1, z2, z3, z4;
583 static int leftover=0;
584 int *table;
585
586 /*
587 * for re-initialization
588 */
589
590 if (s == NULL || d == NULL) {
591 leftover = 0;
592 return 0;
593 }
594
595 /*
596 * To shut up gcc -Wall
597 */
598 z1 = z2 = z3 = z4 = 0;
599
600 if (method == YENC_ENCODED) {
601 while (*s) {
602 if (ecb_expect_false (*s == '=')) {
603 if (*++s != '\0') {
604 d[count++] = (char) ((int) *s - 64 - 42);
605 s++;
606 }
607 }
608 #if 0 /* FP_fgets never leaves CR or LF in the buffer, so skip this */
609 else if (ecb_expect_false (*s == '\n' || *s == '\r')) {
610 s++; /* ignore */
611 }
612 #endif
613 else {
614 d[count++] = (char) ((int) *s++ - 42);
615 }
616 }
617 }
618 else if (method == UU_ENCODED || method == XX_ENCODED) {
619 if (method == UU_ENCODED)
620 table = UUxlat;
621 else
622 table = XXxlat;
623
624 i = table [ACAST(*s++)];
625 j = UUxlen[i] - 1;
626
627 while(j > 0) {
628 c = table[ACAST(*s++)] << 2;
629 cc = table[ACAST(*s++)];
630 c |= (cc >> 4);
631
632 if(i-- > 0)
633 d[count++] = c;
634
635 cc <<= 4;
636 c = table[ACAST(*s++)];
637 cc |= (c >> 2);
638
639 if(i-- > 0)
640 d[count++] = cc;
641
642 c <<= 6;
643 c |= table[ACAST(*s++)];
644
645 if(i-- > 0)
646 d[count++] = c;
647
648 j -= 4;
649 }
650 }
651 else if (method == B64ENCODED) {
652 if (leftover) {
653 strcpy (uuncdl_fulline + leftover, s);
654
655 leftover = 0;
656 s = uuncdl_fulline;
657 }
658
659 while ((z1 = B64xlat[ACAST(*s)]) != -1) {
660 if ((z2 = B64xlat[ACAST(*(s+1))]) == -1) break;
661 if ((z3 = B64xlat[ACAST(*(s+2))]) == -1) break;
662 if ((z4 = B64xlat[ACAST(*(s+3))]) == -1) break;
663
664 d[count++] = (z1 << 2) | (z2 >> 4);
665 d[count++] = (z2 << 4) | (z3 >> 2);
666 d[count++] = (z3 << 6) | (z4);
667
668 s += 4;
669 }
670 if (z1 != -1 && z2 != -1 && *(s+2) == '=') {
671 d[count++] = (z1 << 2) | (z2 >> 4);
672 s+=2;
673 }
674 else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == '=') {
675 d[count++] = (z1 << 2) | (z2 >> 4);
676 d[count++] = (z2 << 4) | (z3 >> 2);
677 s+=3;
678 }
679 while (B64xlat[ACAST(*s)] != -1)
680 uuncdl_fulline[leftover++] = *s++;
681 }
682 else if (method == BH_ENCODED) {
683 if (leftover) {
684 strcpy (uuncdl_fulline + leftover, s);
685
686 leftover = 0;
687 s = uuncdl_fulline;
688 }
689 else if (*s == ':')
690 s++;
691
692 while ((z1 = BHxlat[ACAST(*s)]) != -1) {
693 if ((z2 = BHxlat[ACAST(*(s+1))]) == -1) break;
694 if ((z3 = BHxlat[ACAST(*(s+2))]) == -1) break;
695 if ((z4 = BHxlat[ACAST(*(s+3))]) == -1) break;
696
697 d[count++] = (z1 << 2) | (z2 >> 4);
698 d[count++] = (z2 << 4) | (z3 >> 2);
699 d[count++] = (z3 << 6) | (z4);
700
701 s += 4;
702 }
703 if (z1 != -1 && z2 != -1 && *(s+2) == ':') {
704 d[count++] = (z1 << 2) | (z2 >> 4);
705 s+=2;
706 }
707 else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == ':') {
708 d[count++] = (z1 << 2) | (z2 >> 4);
709 d[count++] = (z2 << 4) | (z3 >> 2);
710 s+=3;
711 }
712 while (BHxlat[ACAST(*s)] != -1)
713 uuncdl_fulline[leftover++] = *s++;
714 }
715
716 return count;
717 }
718
719 /*
720 * ``Decode'' Quoted-Printable text
721 */
722
723 static int
724 UUDecodeQP (FILE *datain, FILE *dataout, int *state,
725 long maxpos, int method, int flags,
726 char *boundary)
727 {
728 char *line=uugen_inbuffer, *p1, *p2;
729 int val;
730
731 uulboundary = -1;
732
733 while (!FP_feof (datain) &&
734 (ftell(datain)<maxpos || flags&FL_TOEND ||
735 (!(flags&FL_PROPER) && uu_fast_scanning))) {
736 if (FP_fgets (line, 1023, datain) == NULL)
737 break;
738 if (ferror (datain)) {
739 UUMessage (UUMSG_ERROR, uustring (S_SOURCE_READ_ERR), strerror (uu_errno = errno));
740 return UURET_IOERR;
741 }
742 line[255] = '\0';
743
744 if (boundary && line[0]=='-' && line[1]=='-' &&
745 strncmp (line+2, boundary, strlen (boundary)) == 0) {
746 if (line[strlen(boundary)+2]=='-')
747 uulboundary = 1;
748 else
749 uulboundary = 0;
750 return UURET_OK;
751 }
752
753 if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
754 UUMessage (UUMSG_NOTE, uustring (S_DECODE_CANCEL));
755 return UURET_CANCEL;
756 }
757
758 p1 = p2 = line;
759
760 while (*p2) {
761 while (*p2 && *p2 != '=')
762 p2++;
763 if (*p2 == '\0')
764 break;
765 *p2 = '\0';
766 fprintf (dataout, "%s", p1);
767 p1 = ++p2;
768
769 if (isxdigit (*p2) && isxdigit (*(p2+1))) {
770 val = ((isdigit(*p2)) ? (*p2-'0') : (tolower(*p2)-'a'+10)) << 4;
771 val |= ((isdigit(*(p2+1)))?(*(p2+1)-'0') : (tolower(*(p2+1))-'a'+10));
772
773 fputc (val, dataout);
774 p2 += 2;
775 p1 = p2;
776 }
777 else if (!*p2) {
778 /* soft line break */
779 goto skip_lbr;
780 break;
781 }
782 else {
783 /* huh? */
784 fputc ('=', dataout);
785 }
786 }
787 /*
788 * p2 points to a nullbyte right after the CR/LF/CRLF
789 */
790 val = 0;
791 while (p2>p1 && isspace (*(p2-1))) {
792 if (*(p2-1) == '\012' || *(p2-1) == '\015')
793 val = 1;
794 p2--;
795 }
796 *p2 = '\0';
797
798 /*
799 * If the part ends directly after this line, the data does not end
800 * with a linebreak. Or, as the docs put it, "the CRLF preceding the
801 * encapsulation line is conceptually attached to the boundary.
802 * So if the part ends here, don't print a line break"
803 */
804 /* something is broken here now, but it was broken before */
805 if (!FP_feof (datain) &&
806 (ftell(datain)<maxpos || flags&FL_TOEND ||
807 (!(flags&FL_PROPER) && uu_fast_scanning)))
808 fprintf (dataout, "%s\n", p1);
809 else
810 fprintf (dataout, "%s", p1);
811
812 skip_lbr: ;
813 }
814 return UURET_OK;
815 }
816
817 /*
818 * ``Decode'' plain text. Our job is to properly handle the EOL sequence
819 */
820
821 static int
822 UUDecodePT (FILE *datain, FILE *dataout, int *state,
823 long maxpos, int method, int flags,
824 char *boundary)
825 {
826 char *line=uugen_inbuffer, *ptr;
827
828 uulboundary = -1;
829
830 while (!FP_feof (datain) &&
831 (ftell(datain)<maxpos || flags&FL_TOEND ||
832 (!(flags&FL_PROPER) && uu_fast_scanning))) {
833 if (FP_fgets (line, 1023, datain) == NULL)
834 break;
835 if (ferror (datain)) {
836 UUMessage (UUMSG_ERROR, uustring (S_SOURCE_READ_ERR), strerror (uu_errno = errno));
837 return UURET_IOERR;
838 }
839 line[255] = '\0';
840
841 if (boundary && line[0]=='-' && line[1]=='-' &&
842 strncmp (line+2, boundary, strlen (boundary)) == 0) {
843 if (line[strlen(boundary)+2]=='-')
844 uulboundary = 1;
845 else
846 uulboundary = 0;
847 return UURET_OK;
848 }
849
850 if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
851 UUMessage (UUMSG_NOTE, uustring (S_DECODE_CANCEL));
852 return UURET_CANCEL;
853 }
854
855 ptr = line + strlen (line);
856
857 /*
858 * If the part ends directly after this line, the data does not end
859 * with a linebreak. Or, as the docs put it, "the CRLF preceding the
860 * encapsulation line is conceptually attached to the boundary.
861 * So if the part ends here, don't print a line break"
862 */
863 if ((ftell(datain)<maxpos || flags&FL_TOEND || flags&FL_PARTIAL ||
864 !boundary || (!(flags&FL_PROPER) && uu_fast_scanning))) {
865 *ptr = '\0';
866 fprintf (dataout, "%s\n", line);
867 }
868 else {
869 *ptr = '\0';
870 fprintf (dataout, "%s", line);
871 }
872 }
873 return UURET_OK;
874 }
875
876 /*
877 * Decode a single field using method. For the moment, this supports
878 * Base64 and Quoted Printable only, to support RFC 1522 header decoding.
879 * Quit when seeing the RFC 1522 ?= end marker.
880 */
881
882 int
883 UUDecodeField (char *s, char *d, int method)
884 {
885 int z1, z2, z3, z4;
886 int count=0;
887
888 if (method == B64ENCODED) {
889 while ((z1 = B64xlat[ACAST(*s)]) != -1) {
890 if ((z2 = B64xlat[ACAST(*(s+1))]) == -1) break;
891 if ((z3 = B64xlat[ACAST(*(s+2))]) == -1) break;
892 if ((z4 = B64xlat[ACAST(*(s+3))]) == -1) break;
893
894 d[count++] = (z1 << 2) | (z2 >> 4);
895 d[count++] = (z2 << 4) | (z3 >> 2);
896 d[count++] = (z3 << 6) | (z4);
897
898 s+=4;
899 }
900 if (z1 != -1 && z2 != -1 && *(s+2) == '=') {
901 d[count++] = (z1 << 2) | (z2 >> 4);
902 s+=2;
903 }
904 else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == '=') {
905 d[count++] = (z1 << 2) | (z2 >> 4);
906 d[count++] = (z2 << 4) | (z3 >> 2);
907 s+=3;
908 }
909 }
910 else if (method == QP_ENCODED) {
911 while (*s && (*s != '?' || *(s+1) != '=')) {
912 while (*s && *s != '=' && (*s != '?' || *(s+1) != '=')) {
913 d[count++] = *s++;
914 }
915 if (*s == '=') {
916 if (isxdigit (*(s+1)) && isxdigit (*(s+2))) {
917 d[count] = (isdigit (*(s+1)) ? (*(s+1)-'0') : (tolower (*(s+1))-'a'+10)) << 4;
918 d[count] |= (isdigit (*(s+2)) ? (*(s+2)-'0') : (tolower (*(s+2))-'a'+10));
919 count++;
920 s+=3;
921 }
922 else if (!s[1]) {
923 d[count++] = '\012';
924 }
925 else {
926 d[count++] = *s++;
927 }
928 }
929 }
930 }
931 else {
932 return -1;
933 }
934
935 d[count] = '\0';
936 return count;
937 }
938
939 int
940 UUDecodePart (FILE *datain, FILE *dataout, int *state,
941 long maxpos, int method, int flags,
942 char *boundary)
943 {
944 char *line, *oline=uuncdp_oline;
945 int warning=0, vlc=0, lc[2], hadct=0;
946 int tc=0, tf=0, vflag, haddata=0, haddh=0;
947 long yefilesize=0, yepartends=0, yenotlastpart=0;
948 crc32_t yepartcrc=CRC32_INIT;
949 static crc32_t yefilecrc;
950 static int bhflag=0;
951 size_t count=0;
952 size_t yepartsize=0;
953 char *ptr;
954
955 if (datain == NULL || dataout == NULL) {
956 yefilecrc = CRC32_INIT;
957 bhflag = 0;
958 return UURET_OK;
959 }
960
961 /*
962 * Use specialized functions for QP_ENCODED and PT_ENCODED plaintext
963 */
964
965 if (method == QP_ENCODED)
966 return UUDecodeQP (datain, dataout, state, maxpos,
967 method, flags, boundary);
968 else if (method == PT_ENCODED)
969 return UUDecodePT (datain, dataout, state, maxpos,
970 method, flags, boundary);
971
972 lc[0] = lc[1] = 0;
973 vflag = 0;
974
975 uulboundary = -1;
976
977 if (method == YENC_ENCODED) {
978 *state = BEGIN;
979 }
980
981 while (!FP_feof (datain) && *state != DONE &&
982 (ftell(datain)<maxpos || flags&FL_TOEND || maxpos==-1 ||
983 (!(flags&FL_PROPER) && uu_fast_scanning))) {
984 if (FP_fgets ((line = uugen_fnbuffer), 1200 - 5, datain) == NULL)
985 break;
986
987 /* optionally skip .. */
988 if (*line == '.' && uu_dotdot)
989 line++;
990
991 if (ferror (datain)) {
992 UUMessage (UUMSG_ERROR, uustring (S_SOURCE_READ_ERR), strerror (uu_errno = errno));
993 return UURET_IOERR;
994 }
995
996 if (!*line) { /* Empty line? */
997 if (*state == DATA &&
998 (method == UU_ENCODED || method == XX_ENCODED))
999 *state = END;
1000
1001 /*
1002 * if we had a whole block of valid lines before, we reset our
1003 * 'valid data' flag, tf. Without this 'if', we'd break decoding
1004 * files with interleaved blank lines. The value of 5 is chosen
1005 * quite arbitrarly.
1006 */
1007
1008 if (vlc > 5)
1009 tf = tc = 0;
1010 vlc = 0;
1011 continue;
1012 }
1013
1014 /*
1015 * Busy Polls
1016 */
1017
1018 if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
1019 UUMessage (UUMSG_NOTE, uustring (S_DECODE_CANCEL));
1020 return UURET_CANCEL;
1021 }
1022
1023 /*
1024 * try to make sense of data
1025 */
1026
1027 line[1200 - 1] = '\0'; /* For Safety of string functions */
1028 count = 0;
1029
1030 if (boundary && line[0]=='-' && line[1]=='-' &&
1031 strncmp (line+2, boundary, strlen (boundary)) == 0) {
1032 if (line[strlen(boundary)+2]=='-')
1033 uulboundary = 1;
1034 else
1035 uulboundary = 0;
1036 return UURET_OK;
1037 }
1038
1039 /*
1040 * Use this pseudo-handling only if !FL_PROPER
1041 */
1042
1043 if ((flags&FL_PROPER) == 0) {
1044 if (strncmp (line, "BEGIN", 5) == 0 &&
1045 FP_strstr (line, "CUT HERE") && !tf) { /* I hate these lines */
1046 tc = tf = vlc = 0;
1047 continue;
1048 }
1049 /* MIME body boundary */
1050 if (line[0] == '-' && line[1] == '-' && method == B64ENCODED) {
1051 if ((haddata || tc) && (haddh || hadct)) {
1052 *state = DONE;
1053 vlc = 0;
1054 lc[0] = lc[1] = 0;
1055 continue;
1056 }
1057 hadct = 0;
1058 haddh = 1;
1059 continue;
1060 }
1061 if (FP_strnicmp (line, "Content-Type", 12) == 0)
1062 hadct = 1;
1063 }
1064
1065 if (*state == BEGIN) {
1066 if ((method == UU_ENCODED || method == XX_ENCODED) &&
1067 (strncmp (line, "begin ", 6) == 0 ||
1068 FP_strnicmp (line, "<pre>begin ", 11) == 0)) { /* for LYNX */
1069 *state = DATA;
1070 continue;
1071 }
1072 else if (method == BH_ENCODED && line[0] == ':') {
1073 if (UUValidData (line, BH_ENCODED, &bhflag) == BH_ENCODED) {
1074 bhflag = 0;
1075 *state = DATA;
1076 }
1077 else
1078 continue;
1079 }
1080 else if (method == YENC_ENCODED &&
1081 strncmp (line, "=ybegin ", 8) == 0 &&
1082 FP_strstr (line, " name=") != NULL) {
1083 *state = DATA;
1084
1085 if ((ptr = FP_strstr (line, " size=")) != NULL) {
1086 ptr += 6;
1087 yefilesize = atoi (ptr);
1088 }
1089 else {
1090 yefilesize = -1;
1091 }
1092
1093 if ((ptr =FP_strstr (line, " part="))) {
1094 int partno = atoi (ptr + 6);
1095
1096 if ((ptr = FP_strstr (line, " total=")))
1097 yenotlastpart = atoi (ptr + 7) != partno;
1098
1099 if (FP_fgets (line, 1200 - 5, datain) == NULL) {
1100 break;
1101 }
1102
1103 if ((ptr = FP_strstr (line, " end=")) == NULL) {
1104 break;
1105 }
1106
1107 yepartends = atoi (ptr + 5);
1108 }
1109 tf = 1;
1110 continue;
1111 }
1112 else {
1113 continue;
1114 }
1115
1116 tc = tf = vlc = 0;
1117 lc[0] = lc[1] = 0;
1118 }
1119 else if ((*state == END || *state == DATA) &&
1120 (method == UU_ENCODED || method == XX_ENCODED)) {
1121 if (strncmp (line, "end", 3) == 0) {
1122 *state = DONE;
1123 break;
1124 }
1125 }
1126
1127 if (*state == DATA && method == YENC_ENCODED &&
1128 strncmp (line, "=yend ", 6) == 0) {
1129 int lastpart = !yenotlastpart && (yepartends == 0 || yepartends >= yefilesize);
1130 yefilecrc = uu_crc32_combine(yefilecrc, yepartcrc, yepartsize);
1131 if ((ptr = FP_strstr (line, " pcrc32=")) != NULL) {
1132 crc32_t pcrc32 = strtoul (ptr + 8, NULL, 16);
1133 if (pcrc32 != yepartcrc) {
1134 UUMessage (UUMSG_WARNING, uustring (S_PCRC_MISMATCH), progress.curfile, progress.partno);
1135 }
1136 } else if ((ptr = FP_strstr (line, " pcrc=")) != NULL) {
1137 crc32_t pcrc32 = strtoul (ptr + 6, NULL, 16);
1138 if (pcrc32 != yepartcrc) {
1139 UUMessage (UUMSG_WARNING, uustring (S_PCRC_MISMATCH), progress.curfile, progress.partno);
1140 }
1141 }
1142 if (lastpart && (ptr = FP_strstr (line, " crc32=")) != NULL)
1143 {
1144 crc32_t fcrc32 = strtoul (ptr + 7, NULL, 16);
1145 if (fcrc32 != yefilecrc) {
1146 UUMessage (UUMSG_WARNING, uustring (S_CRC_MISMATCH), progress.curfile);
1147 }
1148 }
1149 if ((ptr = FP_strstr (line, " size=")) != NULL)
1150 {
1151 size_t size = atol(ptr + 6);
1152 if (size != yepartsize && yefilesize != -1) {
1153 if (size != yefilesize)
1154 UUMessage (UUMSG_WARNING,
1155 uustring (S_PSIZE_MISMATCH), progress.curfile,
1156 progress.partno, yepartsize, size);
1157 else
1158 UUMessage (UUMSG_WARNING,
1159 uustring (S_SIZE_MISMATCH), progress.curfile,
1160 yepartsize, size);
1161 }
1162 }
1163 if (lastpart) {
1164 *state = DONE;
1165 }
1166 break;
1167 }
1168
1169 if (*state == DATA || *state == END) {
1170 if (method==B64ENCODED && line[0]=='-' && line[1]=='-' && tc) {
1171 break;
1172 }
1173
1174 if ((vflag = UUValidData (line, (tf)?method:0, &bhflag)) == 0)
1175 vflag = UURepairData (datain, line, (tf)?method:0, &bhflag);
1176
1177 /*
1178 * correct XX/UUencoded lines that were declared Base64
1179 */
1180
1181 if ((method == XX_ENCODED || method == UU_ENCODED) &&
1182 vflag == B64ENCODED) {
1183 if (UUValidData (line, method, &bhflag) == method)
1184 vflag = method;
1185 }
1186
1187 if (vflag == method) {
1188 if (tf) {
1189 count = UUDecodeLine (line, oline, method);
1190 if (method == YENC_ENCODED) {
1191 yepartcrc = uu_crc32(yepartcrc, oline, count);
1192 yepartsize += count;
1193 }
1194 vlc++; lc[1]++;
1195 }
1196 else if (tc == 3) {
1197 count = UUDecodeLine (save[0], oline, method);
1198 count += UUDecodeLine (save[1], oline + count, method);
1199 count += UUDecodeLine (save[2], oline + count, method);
1200 count += UUDecodeLine (line, oline + count, method);
1201 tf = 1;
1202 tc = 0;
1203
1204 /*
1205 * complain if we had one or two invalid lines amidst of
1206 * correctly encoded data. This usually means that the
1207 * file is in error
1208 */
1209
1210 if (lc[1] > 10 && (lc[0] >= 1 && lc[0] <= 2) && !warning) {
1211 UUMessage (UUMSG_WARNING, uustring (S_DATA_SUSPICIOUS));
1212 warning=1;
1213 }
1214 lc[0] = 0;
1215 lc[1] = 3;
1216 }
1217 else {
1218 FP_strncpy (save[tc++], line, 1200);
1219 }
1220
1221 if (method == UU_ENCODED)
1222 *state = (line[0] == 'M') ? DATA : END;
1223 else if (method == XX_ENCODED)
1224 *state = (line[0] == 'h') ? DATA : END;
1225 else if (method == B64ENCODED)
1226 *state = (strchr (line, '=') == NULL) ? DATA : DONE;
1227 else if (method == BH_ENCODED)
1228 *state = (!line[0] || strchr(line+1,':')==NULL)?DATA:DONE;
1229 }
1230 else {
1231 vlc = tf = tc = 0;
1232 haddh = 0;
1233 lc[0]++;
1234 }
1235 }
1236 else if (*state != DONE) {
1237 return UURET_NOEND;
1238 }
1239
1240 if (count) {
1241 if (method == BH_ENCODED) {
1242 if (UUbhwrite (oline, 1, count, dataout) != count) {
1243 UUMessage (UUMSG_ERROR, uustring (S_WR_ERR_TEMP), strerror (uu_errno = errno));
1244 return UURET_IOERR;
1245 }
1246 }
1247 else if (fwrite (oline, 1, count, dataout) != count) {
1248 UUMessage (UUMSG_ERROR, uustring (S_WR_ERR_TEMP), strerror (uu_errno = errno));
1249 return UURET_IOERR;
1250 }
1251 haddata++;
1252 count = 0;
1253 }
1254 }
1255
1256 if (*state == DONE ||
1257 (*state == DATA && method == B64ENCODED &&
1258 vflag == B64ENCODED && (flags&FL_PROPER || haddh))) {
1259 for (tf=0; tf<tc; tf++)
1260 count += UUDecodeLine (save[tf], oline + count, method);
1261 if (count) {
1262 if (method == BH_ENCODED) {
1263 if (UUbhwrite (oline, 1, count, dataout) != count) {
1264 UUMessage (UUMSG_ERROR, uustring (S_WR_ERR_TEMP), strerror (uu_errno = errno));
1265 return UURET_IOERR;
1266 }
1267 }
1268 else if (fwrite (oline, 1, count, dataout) != count) {
1269 UUMessage (UUMSG_ERROR, uustring (S_WR_ERR_TEMP), strerror (uu_errno = errno));
1270 return UURET_IOERR;
1271 }
1272 }
1273 }
1274 return UURET_OK;
1275 }
1276
1277 /*
1278 * this function decodes the file into a temporary file
1279 */
1280
1281 int
1282 UUDecode (uulist *data)
1283 {
1284 int state=BEGIN, part=-1, res=0, hb;
1285 unsigned long rsize, dsize, numbytes;
1286 FILE *datain, *dataout;
1287 void *datain_buf, *dataout_buf;
1288 unsigned char r[8];
1289 char *mode, *ntmp;
1290 uufile *iter;
1291 size_t bytes;
1292 #ifdef HAVE_MKSTEMP
1293 int tmpfd;
1294 const char *tmpprefix = "uuXXXXXX";
1295 char *tmpdir = NULL;
1296 #endif /* HAVE_MKSTEMP */
1297
1298 if (data == NULL || data->thisfile == NULL)
1299 return UURET_ILLVAL;
1300
1301 if (data->state & UUFILE_TMPFILE)
1302 return UURET_OK;
1303
1304 if (data->state & UUFILE_NODATA)
1305 return UURET_NODATA;
1306
1307 if (data->state & UUFILE_NOBEGIN && !uu_desperate)
1308 return UURET_NODATA;
1309
1310 if (data->uudet == PT_ENCODED)
1311 mode = "wt"; /* open text files in text mode */
1312 else
1313 mode = "wb"; /* otherwise in binary */
1314
1315 #ifdef HAVE_MKSTEMP
1316 if ((getuid()==geteuid()) && (getgid()==getegid())) {
1317 tmpdir=getenv("TMPDIR");
1318 }
1319
1320 if (!tmpdir) {
1321 tmpdir = "/tmp";
1322 }
1323 data->binfile = malloc(strlen(tmpdir)+strlen(tmpprefix)+2);
1324
1325 if (!data->binfile) {
1326 #else
1327 if ((data->binfile = tmpnam (NULL)) == NULL) {
1328 #endif /* HAVE_MKSTEMP */
1329 UUMessage (UUMSG_ERROR, uustring (S_NO_TEMP_NAME));
1330 return UURET_NOMEM;
1331 }
1332
1333 #ifdef HAVE_MKSTEMP
1334 strcpy(data->binfile, tmpdir);
1335 strcat(data->binfile, "/");
1336 strcat(data->binfile, tmpprefix);
1337
1338 if ((tmpfd = mkstemp(data->binfile)) == -1 ||
1339 (dataout = fdopen(tmpfd, mode)) == NULL) {
1340 #else
1341 if ((dataout = fopen (data->binfile, mode)) == NULL) {
1342 #endif /* HAVE_MKSTEMP */
1343 /*
1344 * we couldn't create a temporary file. Usually this means that TMP
1345 * and TEMP aren't set
1346 */
1347 UUMessage (UUMSG_ERROR, uustring (S_WR_ERR_TARGET), data->binfile, strerror (uu_errno = errno));
1348 #ifdef HAVE_MKSTEMP
1349 if (tmpfd != -1) {
1350 unlink(data->binfile);
1351 close(tmpfd);
1352 }
1353 #endif /* HAVE_MKSTEMP */
1354 FP_free (data->binfile);
1355 data->binfile = NULL;
1356 uu_errno = errno;
1357 return UURET_IOERR;
1358 }
1359 UUSETBUF (dataout, dataout_buf, uu_wbuf);
1360 FP_flockfile (dataout);
1361
1362 /*
1363 * we don't have begin lines in Base64 or plain text files.
1364 */
1365 if (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
1366 data->uudet == PT_ENCODED)
1367 state = DATA;
1368
1369 /*
1370 * If we know that the file does not have a begin, we simulate
1371 * it in desperate mode
1372 */
1373
1374 if ((data->state & UUFILE_NOBEGIN) && uu_desperate)
1375 state = DATA;
1376
1377 (void) UUDecodeLine (NULL, NULL, 0); /* init */
1378 (void) UUbhwrite (NULL, 0, 0, NULL); /* dito */
1379 (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* yep */
1380
1381 /*
1382 * initialize progress information
1383 */
1384 progress.action = 0;
1385 if (data->filename != NULL) {
1386 FP_strncpy (progress.curfile,
1387 (strlen(data->filename)>255)?
1388 (data->filename+strlen(data->filename)-255):data->filename,
1389 256);
1390 }
1391 else {
1392 FP_strncpy (progress.curfile,
1393 (strlen(data->binfile)>255)?
1394 (data->binfile+strlen(data->binfile)-255):data->binfile,
1395 256);
1396 }
1397 progress.partno = 0;
1398 progress.numparts = 0;
1399 progress.fsize = -1;
1400 progress.percent = 0;
1401 progress.action = UUACT_DECODING;
1402
1403 iter = data->thisfile;
1404 while (iter) {
1405 progress.numparts = (iter->partno)?iter->partno:1;
1406 iter = iter->NEXT;
1407 }
1408
1409 /*
1410 * let's rock!
1411 */
1412
1413 iter = data->thisfile;
1414 while (iter) {
1415 if (part != -1 && iter->partno != part+1 && !uu_desperate)
1416 break;
1417 else
1418 part = iter->partno;
1419
1420 if (iter->data->sfname == NULL) {
1421 iter = iter->NEXT;
1422 continue;
1423 }
1424
1425 /*
1426 * call our FileCallback to retrieve the file
1427 */
1428
1429 if (uu_FileCallback) {
1430 if ((res = (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
1431 uugen_fnbuffer, 1)) != UURET_OK)
1432 break;
1433 if ((datain = fopen (uugen_fnbuffer, "rb")) == NULL) {
1434 (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
1435 uugen_fnbuffer, 0);
1436 UUMessage (UUMSG_ERROR,
1437 uustring (S_NOT_OPEN_FILE),
1438 uugen_fnbuffer, strerror (uu_errno = errno));
1439 res = UURET_IOERR;
1440 break;
1441 }
1442 }
1443 else {
1444 if ((datain = fopen (iter->data->sfname, "rb")) == NULL) {
1445 UUMessage (UUMSG_ERROR,
1446 uustring (S_NOT_OPEN_FILE),
1447 iter->data->sfname, strerror (uu_errno = errno));
1448 res = UURET_IOERR;
1449 break;
1450 }
1451 FP_strncpy (uugen_fnbuffer, iter->data->sfname, 1024);
1452 }
1453 UUSETBUF (datain, datain_buf, uu_rbuf);
1454 FP_flockfile (datain);
1455
1456 progress.partno = part;
1457 progress.fsize = (iter->data->length)?iter->data->length:-1;
1458 progress.percent = 0;
1459 progress.foffset = iter->data->startpos;
1460
1461 fseek (datain, iter->data->startpos, SEEK_SET);
1462 res = UUDecodePart (datain, dataout, &state,
1463 iter->data->startpos+iter->data->length,
1464 data->uudet, iter->data->flags, NULL);
1465 fclose (datain);
1466 UUCLRBUF (uu_rbuf, datain_buf);
1467
1468 if (uu_FileCallback)
1469 (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname, uugen_fnbuffer, 0);
1470
1471 if (state == DONE || res != UURET_OK)
1472 break;
1473
1474 iter = iter->NEXT;
1475 }
1476
1477 if (state == DATA &&
1478 (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
1479 data->uudet == PT_ENCODED))
1480 state = DONE; /* assume we're done */
1481
1482 if (fclose (dataout)) {
1483 UUMessage (UUMSG_ERROR, uustring (S_WR_ERR_TEMP), strerror (uu_errno = errno));
1484 res = UURET_IOERR;
1485 }
1486 UUCLRBUF (uu_wbuf, dataout_buf);
1487
1488 if (res != UURET_OK || (state != DONE && !uu_desperate)) {
1489 unlink (data->binfile);
1490 FP_free (data->binfile);
1491 data->binfile = NULL;
1492 data->state &= ~UUFILE_TMPFILE;
1493 data->state |= UUFILE_ERROR;
1494
1495 if (res == UURET_OK && state != DONE)
1496 res = UURET_NOEND;
1497 }
1498 else if (res != UURET_OK) {
1499 data->state &= ~UUFILE_DECODED;
1500 data->state |= UUFILE_ERROR | UUFILE_TMPFILE;
1501 }
1502 else {
1503 data->state &= ~UUFILE_ERROR;
1504 data->state |= UUFILE_TMPFILE;
1505 }
1506
1507 /*
1508 * If this was a BinHex file, we must extract its data or resource fork
1509 */
1510
1511 if (data->uudet == BH_ENCODED && data->binfile) {
1512 #ifdef HAVE_MKSTEMP
1513 ntmp = malloc(strlen(tmpdir)+strlen(tmpprefix)+2);
1514 #else
1515 ntmp = tempnam (NULL);
1516 #endif /* HAVE_MKSTEMP */
1517 if (ntmp == NULL) {
1518 UUMessage (UUMSG_ERROR, uustring (S_NO_TEMP_NAME));
1519 progress.action = 0;
1520 return UURET_NOMEM;
1521 }
1522 if ((datain = fopen (data->binfile, "rb")) == NULL) {
1523 UUMessage (UUMSG_ERROR, uustring (S_NOT_OPEN_FILE),
1524 data->binfile, strerror (uu_errno = errno));
1525 progress.action = 0;
1526 free (ntmp);
1527 return UURET_IOERR;
1528 }
1529 UUSETBUF (datain, datain_buf, uu_rbuf);
1530 FP_flockfile (datain);
1531
1532 #ifdef HAVE_MKSTEMP
1533 strcpy(ntmp, tmpdir);
1534 strcat(ntmp, "/");
1535 strcat(ntmp, tmpprefix);
1536 if ((tmpfd = mkstemp(ntmp)) == -1 ||
1537 (dataout = fdopen(tmpfd, "wb")) == NULL) {
1538 #else
1539 if ((dataout = fopen (ntmp, "wb")) == NULL) {
1540 #endif /* HAVE_MKSTEMP */
1541 UUMessage (UUMSG_ERROR,
1542 uustring (S_NOT_OPEN_TARGET),
1543 ntmp, strerror (uu_errno = errno));
1544 progress.action = 0;
1545 fclose (datain);
1546 UUCLRBUF (uu_rbuf, datain_buf);
1547 #ifdef HAVE_MKSTEMP
1548 if (tmpfd != -1) {
1549 unlink(ntmp);
1550 close(tmpfd);
1551 }
1552 #endif /* HAVE_MKSTEMP */
1553 free (ntmp);
1554 return UURET_IOERR;
1555 }
1556 UUSETBUF (dataout, dataout_buf, uu_wbuf);
1557 FP_flockfile (dataout);
1558
1559 /*
1560 * read fork lengths. remember they're in Motorola format
1561 */
1562 r[0] = FP_getc (datain);
1563 hb = (int) r[0] + 22;
1564 fseek (datain, (int) r[0] + 12, SEEK_SET);
1565 fread (r, 1, 8, datain);
1566
1567 dsize = (((long) 1 << 24) * (long) r[0]) +
1568 (((long) 1 << 16) * (long) r[1]) +
1569 (((long) 1 << 8) * (long) r[2]) +
1570 ( (long) r[3]);
1571 rsize = (((long) 1 << 24) * (long) r[4]) +
1572 (((long) 1 << 16) * (long) r[5]) +
1573 (((long) 1 << 8) * (long) r[6]) +
1574 ( (long) r[7]);
1575
1576 UUMessage (UUMSG_MESSAGE, uustring (S_BINHEX_SIZES), dsize, rsize);
1577
1578 if (dsize == 0) {
1579 fseek (datain, dsize + hb + 2, SEEK_SET);
1580 numbytes = rsize;
1581 }
1582 else if (rsize == 0) {
1583 fseek (datain, hb, SEEK_SET);
1584 numbytes = dsize;
1585 }
1586 else {
1587 /* we should let the user have the choice here */
1588 UUMessage (UUMSG_NOTE, uustring (S_BINHEX_BOTH));
1589 fseek (datain, hb, SEEK_SET);
1590 numbytes = dsize;
1591 }
1592
1593 progress.action = 0;
1594 progress.partno = 0;
1595 progress.numparts = 1;
1596 progress.fsize = numbytes ? numbytes : -1;
1597 progress.foffset = hb;
1598 progress.percent = 0;
1599 progress.action = UUACT_COPYING;
1600
1601 /*
1602 * copy the chosen fork
1603 */
1604
1605 while (!FP_feof (datain) && numbytes) {
1606 if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
1607 UUMessage (UUMSG_NOTE, uustring (S_DECODE_CANCEL));
1608 fclose (datain);
1609 UUCLRBUF (uu_rbuf, datain_buf);
1610 fclose (dataout);
1611 UUCLRBUF (uu_wbuf, dataout_buf);
1612 unlink (ntmp);
1613 free (ntmp);
1614 return UURET_CANCEL;
1615 }
1616
1617 bytes = fread (uugen_inbuffer, 1,
1618 (size_t) ((numbytes>1024)?1024:numbytes), datain);
1619
1620 if (ferror (datain) || (bytes == 0 && !FP_feof (datain))) {
1621 progress.action = 0;
1622 UUMessage (UUMSG_ERROR,
1623 uustring (S_SOURCE_READ_ERR),
1624 data->binfile, strerror (uu_errno = errno));
1625 fclose (datain);
1626 UUCLRBUF (uu_rbuf, datain_buf);
1627 fclose (dataout);
1628 UUCLRBUF (uu_wbuf, dataout_buf);
1629 unlink (ntmp);
1630 free (ntmp);
1631 return UURET_IOERR;
1632 }
1633 if (fwrite (uugen_inbuffer, 1, bytes, dataout) != bytes) {
1634 progress.action = 0;
1635 UUMessage (UUMSG_ERROR,
1636 uustring (S_WR_ERR_TARGET),
1637 ntmp, strerror (uu_errno = errno));
1638 fclose (datain);
1639 UUCLRBUF (uu_rbuf, datain_buf);
1640 fclose (dataout);
1641 UUCLRBUF (uu_wbuf, dataout_buf);
1642 unlink (ntmp);
1643 free (ntmp);
1644 return UURET_IOERR;
1645 }
1646 numbytes -= bytes;
1647 }
1648
1649 if (numbytes) {
1650 UUMessage (UUMSG_WARNING,
1651 uustring (S_SHORT_BINHEX),
1652 (data->filename)?data->filename:
1653 (data->subfname)?data->subfname:"???",
1654 numbytes);
1655 }
1656
1657 /*
1658 * replace temp file
1659 */
1660
1661 fclose (datain);
1662 UUCLRBUF (uu_rbuf, datain_buf);
1663 if (fclose (dataout)) {
1664 UUCLRBUF (uu_wbuf, dataout_buf);
1665 UUMessage (UUMSG_ERROR,
1666 uustring (S_WR_ERR_TARGET),
1667 ntmp, strerror (uu_errno = errno));
1668 unlink (ntmp);
1669 free (ntmp);
1670 return UURET_IOERR;
1671 }
1672 UUCLRBUF (uu_wbuf, dataout_buf);
1673
1674 if (unlink (data->binfile)) {
1675 UUMessage (UUMSG_WARNING,
1676 uustring (S_TMP_NOT_REMOVED),
1677 data->binfile, strerror (uu_errno = errno));
1678 }
1679
1680 free (data->binfile);
1681 data->binfile = ntmp;
1682 }
1683
1684 progress.action = 0;
1685 return res;
1686 }
1687
1688 /*
1689 * QuickDecode for proper MIME attachments. We expect the pointer to
1690 * be on the first header line.
1691 */
1692
1693 int
1694 UUQuickDecode (FILE *datain, FILE *dataout, char *boundary, long maxpos)
1695 {
1696 int state=BEGIN, encoding=-1;
1697 headers myenv;
1698
1699 /*
1700 * Read header and find out about encoding.
1701 */
1702
1703 memset (&myenv, 0, sizeof (headers));
1704 UUScanHeader (datain, &myenv);
1705
1706 if (FP_stristr (myenv.ctenc, "uu") != NULL)
1707 encoding = UU_ENCODED;
1708 else if (FP_stristr (myenv.ctenc, "xx") != NULL)
1709 encoding = XX_ENCODED;
1710 else if (FP_stricmp (myenv.ctenc, "base64") == 0)
1711 encoding = B64ENCODED;
1712 else if (FP_stricmp (myenv.ctenc, "quoted-printable") == 0)
1713 encoding = QP_ENCODED;
1714 else
1715 encoding = PT_ENCODED;
1716
1717 UUkillheaders (&myenv);
1718
1719 /*
1720 * okay, so decode this one
1721 */
1722
1723 (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* init */
1724 return UUDecodePart (datain, dataout, &state, maxpos,
1725 encoding, FL_PROPER|FL_TOEND,
1726 boundary);
1727 }