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