ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uunconc.c
Revision: 1.3.2.5
Committed: Sun Apr 18 19:55:46 2004 UTC (20 years, 1 month ago) by root
Content type: text/plain
Branch: UUDEVIEW
CVS Tags: UUDEVIEW-0-5-20
Changes since 1.3.2.4: +4 -4 lines
Log Message:
*** empty log message ***

File Contents

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