ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uunconc.c
Revision: 1.8
Committed: Sat Apr 6 01:53:30 2002 UTC (22 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.7: +21 -6 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.2 #include <uudeview.h>
53 root 1.1 #include <uuint.h>
54     #include <fptools.h>
55     #include <uustring.h>
56    
57 root 1.8 char * uunconc_id = "$Id: uunconc.c,v 1.7 2002/04/05 21:56:57 root Exp $";
58 root 1.1
59     /* for braindead systems */
60     #ifndef SEEK_SET
61     #ifdef L_BEGIN
62     #define SEEK_SET L_BEGIN
63     #else
64     #define SEEK_SET 0
65     #endif
66     #endif
67    
68     /*
69     * decoder states
70     */
71    
72     #define BEGIN (1)
73     #define DATA (2)
74     #define END (3)
75     #define DONE (4)
76    
77     /*
78     * mallocable areas
79     */
80    
81     char *uunconc_UUxlat;
82     char *uunconc_UUxlen;
83     char *uunconc_B64xlat;
84     char *uunconc_XXxlat;
85     char *uunconc_BHxlat;
86     char *uunconc_save;
87    
88     /*
89     * decoding translation tables and line length table
90     */
91    
92     static int * UUxlen; /* initialized in UUInitConc() */
93     static int * UUxlat; /* from the malloc'ed areas above */
94     static int * B64xlat;
95     static int * XXxlat;
96     static int * BHxlat;
97    
98     /*
99     * buffer for decoding
100     */
101    
102     static char *save[3];
103    
104     /*
105     * mallocable areas
106     */
107    
108     char *uuncdl_fulline;
109     char *uuncdp_oline;
110    
111     /*
112     * Return information for QuickDecode
113     */
114    
115     static int uulboundary;
116    
117     /*
118     * To prevent warnings when using a char as index into an array
119     */
120    
121     #define ACAST(s) ((int)(uchar)(s))
122    
123     /*
124     * Initialize decoding tables
125     */
126    
127     void
128     UUInitConc (void)
129     {
130     int i, j;
131    
132     /*
133     * Update pointers
134     */
135     UUxlen = (int *) uunconc_UUxlen;
136     UUxlat = (int *) uunconc_UUxlat;
137     B64xlat = (int *) uunconc_B64xlat;
138     XXxlat = (int *) uunconc_XXxlat;
139     BHxlat = (int *) uunconc_BHxlat;
140    
141     save[0] = uunconc_save;
142     save[1] = uunconc_save + 256;
143     save[2] = uunconc_save + 512;
144    
145     /* prepare decoding translation table */
146     for(i = 0; i < 256; i++)
147     UUxlat[i] = B64xlat[i] = XXxlat[i] = BHxlat[i] = -1;
148    
149     /*
150     * At some time I received a file which used lowercase characters for
151     * uuencoding. This shouldn't be, but let's accept it. Must take special
152     * care that this doesn't break xxdecoding. This is giving me quite a
153     * headache. If this one file hadn't been a Pocahontas picture, I might
154     * have ignored it for good.
155     */
156    
157     for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
158     UUxlat[i] /* = UUxlat[i+64] */ = j;
159     for (i = '`', j = 0; i < '`' + 32; i++, j++)
160     UUxlat[i] = j;
161    
162     /* add special cases */
163     UUxlat['`'] = UUxlat[' '];
164     UUxlat['~'] = UUxlat['^'];
165    
166     /* prepare line length table */
167     UUxlen[0] = 1;
168 root 1.2 for(i = 1, j = 5; i <= 61; i += 3, j += 4)
169 root 1.1 UUxlen[i] = UUxlen[i+1] = UUxlen[i+2] = j;
170    
171     /* prepare other tables */
172     for (i=0; i<64; i++) {
173     B64xlat[ACAST(B64EncodeTable[i])] = i;
174     XXxlat [ACAST(XXEncodeTable [i])] = i;
175     BHxlat [ACAST(BHEncodeTable [i])] = i;
176     }
177     }
178    
179     /*
180     * Workaround for Netscape
181     */
182    
183     /*
184     * Determines whether Netscape may have broken up a data line (by
185     * inserting a newline). This only seems to happen after <a in a
186     * href statement
187     */
188    
189     int
190     UUBrokenByNetscape (char *string)
191     {
192     char *ptr;
193     int len;
194    
195     if (string==NULL || (len=strlen(string))<3)
196     return 0;
197    
198 root 1.5 if ((ptr = _FP_stristr (string, "<a href=")) != NULL) {
199     if (_FP_stristr (string, "</a>") > ptr)
200 root 1.1 return 2;
201     }
202    
203     ptr = string + len;
204    
205     while (len && (*(ptr-1)=='\015' || *(ptr-1)=='\012')) {
206     ptr--; len--;
207     }
208     if (len<3) return 0;
209     if (*--ptr == ' ') ptr--;
210     ptr--;
211    
212 root 1.5 if (_FP_strnicmp (ptr, "<a", 2) == 0)
213 root 1.1 return 1;
214    
215     return 0;
216     }
217    
218     /*
219     * Try to repair a Netscape-corrupted line of data.
220     * This must only be called on corrupted lines, since non-Netscape
221     * data may even _get_ corrupted by this procedure.
222     *
223     * Some checks are included multiply to speed up the procedure. For
224     * example: (*p1!='<' || strnicmp(p1,"</a>",4)). If the first expression
225     * becomes true, the costly function isn't called :-)
226     *
227     * Since '<', '>', '&' might even be replaced by their html equivalents
228     * in href strings, I'm now using two passes, the first one for &amp; + co,
229     * the second one for hrefs.
230     */
231    
232     int
233     UUNetscapeCollapse (char *string)
234     {
235     char *p1=string, *p2=string;
236     int res = 0;
237    
238     if (string==NULL)
239     return 0;
240    
241     /*
242     * First pass
243     */
244     while (*p1) {
245     if (*p1 == '&') {
246 root 1.5 if (_FP_strnicmp (p1, "&amp;", 5) == 0) { p1+=5; *p2++='&'; res=1; }
247     else if (_FP_strnicmp (p1, "&lt;", 4) == 0) { p1+=4; *p2++='<'; res=1; }
248     else if (_FP_strnicmp (p1, "&gt;", 4) == 0) { p1+=4; *p2++='>'; res=1; }
249 root 1.1 else *p2++ = *p1++;
250     res = 1;
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.5 if ((_FP_strnicmp (p1, "<ahref=", 7) == 0 ||
263     _FP_strnicmp (p1, "<a href=",8) == 0) &&
264 root 1.6 (_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.5 while (*p1 && (*p1!='<' || _FP_strnicmp(p1,"</a>",4)!=0)) {
269 root 1.1 *p2++ = *p1++;
270     }
271 root 1.5 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.4 if ((s == NULL) || (*s == '\0')) {
302     return 0; /* bad string */
303 root 1.1 }
304 root 1.4
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.4 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.5 /* 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     ptr = line + strlen (line);
545     while (ptr>line && (*(ptr-1)=='\015' || *(ptr-1)=='\012'))
546     ptr--;
547 root 1.5 if (_FP_fgets (ptr, 255-(ptr-line), datei) == NULL)
548 root 1.1 break;
549     }
550     else { /* don't need next line to repair */
551     }
552     if (UUNetscapeCollapse (line)) {
553     if ((vflag = UUValidData (line, encoding, bhflag)) == 0)
554     nflag = UUBrokenByNetscape (line);
555     }
556     else
557     nflag = 0;
558     }
559     /*
560     * Sometimes, a line is garbled even without it being split into
561     * the next line. Then we try this in our despair
562     */
563     if (vflag == 0) {
564     if (UUNetscapeCollapse (line))
565     vflag = UUValidData (line, encoding, bhflag);
566     }
567    
568     /*
569     * If this line looks uuencoded, but the line is one character short
570     * of a valid line, it was probably broken by MS Exchange. According
571     * to my test cases, there is at most one space character missing;
572     * there are never two spaces together.
573     * If adding a space character helps making this line uuencoded, do
574     * it!
575     */
576    
577     if (vflag == 0) {
578     ptr = line + strlen(line);
579     while (ptr>line && (*(ptr-1)=='\012' || *(ptr-1)=='\015')) {
580     ptr--;
581     }
582     *ptr++ = ' ';
583     *ptr-- = '\0';
584     if ((vflag = UUValidData (line, encoding, bhflag)) != UU_ENCODED) {
585     *ptr = '\0';
586     vflag = 0;
587     }
588     }
589     return vflag;
590     }
591    
592     /*
593     * Decode a single encoded line using method
594     */
595    
596     size_t
597     UUDecodeLine (char *s, char *d, int method)
598     {
599     int i, j, c, cc, count=0, z1, z2, z3, z4;
600     static int leftover=0;
601     int *table;
602    
603     /*
604     * for re-initialization
605     */
606    
607     if (s == NULL || d == NULL) {
608     leftover = 0;
609     return 0;
610     }
611    
612 root 1.8 /* optionally skip .. */
613     if (*s == '.' && uu_dotdot)
614     s++;
615    
616 root 1.1 /*
617     * To shut up gcc -Wall
618     */
619     z1 = z2 = z3 = z4 = 0;
620    
621     if (method == UU_ENCODED || method == XX_ENCODED) {
622     if (method == UU_ENCODED)
623     table = UUxlat;
624     else
625     table = XXxlat;
626    
627 root 1.4 i = table [ACAST(*s++)];
628 root 1.1 j = UUxlen[i] - 1;
629    
630     while(j > 0) {
631 root 1.4 c = table[ACAST(*s++)] << 2;
632     cc = table[ACAST(*s++)];
633 root 1.1 c |= (cc >> 4);
634    
635     if(i-- > 0)
636     d[count++] = c;
637    
638     cc <<= 4;
639 root 1.4 c = table[ACAST(*s++)];
640 root 1.1 cc |= (c >> 2);
641    
642     if(i-- > 0)
643     d[count++] = cc;
644    
645     c <<= 6;
646 root 1.4 c |= table[ACAST(*s++)];
647 root 1.1
648     if(i-- > 0)
649     d[count++] = c;
650    
651     j -= 4;
652     }
653     }
654     else if (method == B64ENCODED) {
655     if (leftover) {
656 root 1.8 int len = strlen (s);
657    
658     if (len > 1200 - 1 - leftover)
659     len = 1200 - 1 - leftover;
660    
661     memcpy (uuncdl_fulline + leftover, s, len);
662     uuncdl_fulline[leftover + len] = 0;
663 root 1.7
664 root 1.1 leftover = 0;
665     s = uuncdl_fulline;
666     }
667    
668     while ((z1 = B64xlat[ACAST(*s)]) != -1) {
669     if ((z2 = B64xlat[ACAST(*(s+1))]) == -1) break;
670     if ((z3 = B64xlat[ACAST(*(s+2))]) == -1) break;
671     if ((z4 = B64xlat[ACAST(*(s+3))]) == -1) break;
672    
673     d[count++] = (z1 << 2) | (z2 >> 4);
674     d[count++] = (z2 << 4) | (z3 >> 2);
675     d[count++] = (z3 << 6) | (z4);
676    
677     s += 4;
678     }
679     if (z1 != -1 && z2 != -1 && *(s+2) == '=') {
680     d[count++] = (z1 << 2) | (z2 >> 4);
681     s+=2;
682     }
683     else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == '=') {
684     d[count++] = (z1 << 2) | (z2 >> 4);
685     d[count++] = (z2 << 4) | (z3 >> 2);
686     s+=3;
687     }
688     while (B64xlat[ACAST(*s)] != -1)
689     uuncdl_fulline[leftover++] = *s++;
690     }
691     else if (method == BH_ENCODED) {
692     if (leftover) {
693 root 1.8 int len = strlen (s);
694    
695     if (len > 1200 - 1 - leftover)
696     len = 1200 - 1 - leftover;
697    
698     memcpy (uuncdl_fulline + leftover, s, len);
699     uuncdl_fulline[leftover + len] = 0;
700    
701 root 1.1 leftover = 0;
702     s = uuncdl_fulline;
703     }
704     else if (*s == ':')
705     s++;
706    
707     while ((z1 = BHxlat[ACAST(*s)]) != -1) {
708     if ((z2 = BHxlat[ACAST(*(s+1))]) == -1) break;
709     if ((z3 = BHxlat[ACAST(*(s+2))]) == -1) break;
710     if ((z4 = BHxlat[ACAST(*(s+3))]) == -1) break;
711    
712     d[count++] = (z1 << 2) | (z2 >> 4);
713     d[count++] = (z2 << 4) | (z3 >> 2);
714     d[count++] = (z3 << 6) | (z4);
715    
716     s += 4;
717     }
718     if (z1 != -1 && z2 != -1 && *(s+2) == ':') {
719     d[count++] = (z1 << 2) | (z2 >> 4);
720     s+=2;
721     }
722     else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == ':') {
723     d[count++] = (z1 << 2) | (z2 >> 4);
724     d[count++] = (z2 << 4) | (z3 >> 2);
725     s+=3;
726     }
727     while (BHxlat[ACAST(*s)] != -1)
728     uuncdl_fulline[leftover++] = *s++;
729     }
730 root 1.4 else if (method == YENC_ENCODED) {
731     while (*s) {
732     if (*s == '=') {
733     if (*++s != '\0') {
734     d[count++] = (char) ((int) *s - 64 - 42);
735     s++;
736     }
737     }
738 root 1.8 else if (*s == '\n' || *s == '\r') {
739 root 1.4 s++; /* ignore */
740     }
741     else {
742     d[count++] = (char) ((int) *s++ - 42);
743     }
744     }
745     }
746 root 1.1
747     return count;
748     }
749    
750     /*
751     * ``Decode'' Quoted-Printable text
752     */
753    
754     int
755     UUDecodeQP (FILE *datain, FILE *dataout, int *state,
756     long maxpos, int method, int flags,
757     char *boundary)
758     {
759     char *line=uugen_inbuffer, *p1, *p2;
760     int val;
761    
762     uulboundary = -1;
763    
764     while (!feof (datain) &&
765     (ftell(datain)<maxpos || flags&FL_TOEND ||
766     (!(flags&FL_PROPER) && uu_fast_scanning))) {
767 root 1.5 if (_FP_fgets (line, 255, datain) == NULL)
768 root 1.1 break;
769     if (ferror (datain)) {
770     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
771     uustring (S_SOURCE_READ_ERR),
772     strerror (uu_errno = errno));
773     return UURET_IOERR;
774     }
775     line[255] = '\0';
776    
777     if (boundary && line[0]=='-' && line[1]=='-' &&
778     strncmp (line+2, boundary, strlen (boundary)) == 0) {
779     if (line[strlen(boundary)+2]=='-')
780     uulboundary = 1;
781     else
782     uulboundary = 0;
783     return UURET_OK;
784     }
785    
786     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
787     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
788     uustring (S_DECODE_CANCEL));
789     return UURET_CANCEL;
790     }
791    
792     p1 = p2 = line;
793    
794     while (*p2) {
795     while (*p2 && *p2 != '=')
796     p2++;
797     if (*p2 == '\0')
798     break;
799     *p2 = '\0';
800     fprintf (dataout, "%s", p1);
801     p1 = ++p2;
802    
803     if (isxdigit (*p2) && isxdigit (*(p2+1))) {
804     val = ((isdigit(*p2)) ? (*p2-'0') : (tolower(*p2)-'a'+10)) << 4;
805     val |= ((isdigit(*(p2+1)))?(*(p2+1)-'0') : (tolower(*(p2+1))-'a'+10));
806    
807     fputc (val, dataout);
808     p2 += 2;
809     p1 = p2;
810     }
811     else if (*p2 == '\012' || *(p2+1) == '\015') {
812     /* soft line break */
813     *p2 = '\0';
814     break;
815     }
816     else {
817     /* huh? */
818     fputc ('=', dataout);
819     }
820     }
821     /*
822     * p2 points to a nullbyte right after the CR/LF/CRLF
823     */
824     val = 0;
825     while (p2>p1 && isspace (*(p2-1))) {
826     if (*(p2-1) == '\012' || *(p2-1) == '\015')
827     val = 1;
828     p2--;
829     }
830     *p2 = '\0';
831    
832     /*
833     * If the part ends directly after this line, the data does not end
834     * with a linebreak. Or, as the docs put it, "the CRLF preceding the
835     * encapsulation line is conceptually attached to the boundary.
836     * So if the part ends here, don't print a line break"
837     */
838     if (val && (!feof (datain) &&
839     (ftell(datain)<maxpos || flags&FL_TOEND ||
840     (!(flags&FL_PROPER) && uu_fast_scanning))))
841     fprintf (dataout, "%s\n", p1);
842     else
843     fprintf (dataout, "%s", p1);
844     }
845     return UURET_OK;
846     }
847    
848     /*
849     * ``Decode'' plain text. Our job is to properly handle the EOL sequence
850     */
851    
852     int
853     UUDecodePT (FILE *datain, FILE *dataout, int *state,
854     long maxpos, int method, int flags,
855     char *boundary)
856     {
857     char *line=uugen_inbuffer, *ptr;
858    
859     uulboundary = -1;
860    
861     while (!feof (datain) &&
862     (ftell(datain)<maxpos || flags&FL_TOEND ||
863     (!(flags&FL_PROPER) && uu_fast_scanning))) {
864 root 1.5 if (_FP_fgets (line, 255, datain) == NULL)
865 root 1.1 break;
866     if (ferror (datain)) {
867     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
868     uustring (S_SOURCE_READ_ERR),
869     strerror (uu_errno = errno));
870     return UURET_IOERR;
871     }
872     line[255] = '\0';
873    
874     if (boundary && line[0]=='-' && line[1]=='-' &&
875     strncmp (line+2, boundary, strlen (boundary)) == 0) {
876     if (line[strlen(boundary)+2]=='-')
877     uulboundary = 1;
878     else
879     uulboundary = 0;
880     return UURET_OK;
881     }
882    
883     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
884     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
885     uustring (S_DECODE_CANCEL));
886     return UURET_CANCEL;
887     }
888    
889     ptr = line + strlen (line);
890    
891     while (ptr>line && (*(ptr-1) == '\012' || *(ptr-1) == '\015'))
892     ptr--;
893    
894    
895     /*
896     * If the part ends directly after this line, the data does not end
897     * with a linebreak. Or, as the docs put it, "the CRLF preceding the
898     * encapsulation line is conceptually attached to the boundary.
899     * So if the part ends here, don't print a line break"
900     */
901     if ((*ptr == '\012' || *ptr == '\015') &&
902 root 1.2 (ftell(datain)<maxpos || flags&FL_TOEND || flags&FL_PARTIAL ||
903     !boundary || (!(flags&FL_PROPER) && uu_fast_scanning))) {
904 root 1.1 *ptr = '\0';
905     fprintf (dataout, "%s\n", line);
906     }
907     else {
908     *ptr = '\0';
909     fprintf (dataout, "%s", line);
910     }
911     }
912     return UURET_OK;
913     }
914    
915     int
916     UUDecodePart (FILE *datain, FILE *dataout, int *state,
917     long maxpos, int method, int flags,
918     char *boundary)
919     {
920     char *line=uugen_fnbuffer, *oline=uuncdp_oline;
921     int warning=0, vlc=0, lc[2], hadct=0;
922     int tc=0, tf=0, vflag, haddata=0, haddh=0;
923 root 1.4 long yefilesize=0, yepartends=0;
924 root 1.1 static int bhflag=0;
925     size_t count=0;
926 root 1.4 char *ptr;
927 root 1.1
928     if (datain == NULL || dataout == NULL) {
929     bhflag = 0;
930     return UURET_OK;
931     }
932    
933     /*
934     * Use specialized functions for QP_ENCODED and PT_ENCODED plaintext
935     */
936    
937     if (method == QP_ENCODED)
938     return UUDecodeQP (datain, dataout, state, maxpos,
939     method, flags, boundary);
940     else if (method == PT_ENCODED)
941     return UUDecodePT (datain, dataout, state, maxpos,
942     method, flags, boundary);
943    
944     lc[0] = lc[1] = 0;
945     vflag = 0;
946    
947     uulboundary = -1;
948    
949 root 1.4 if (method == YENC_ENCODED) {
950     *state = BEGIN;
951     }
952    
953 root 1.1 while (!feof (datain) && *state != DONE &&
954     (ftell(datain)<maxpos || flags&FL_TOEND || maxpos==-1 ||
955     (!(flags&FL_PROPER) && uu_fast_scanning))) {
956 root 1.5 if (_FP_fgets (line, 299, datain) == NULL)
957 root 1.1 break;
958 root 1.4
959 root 1.1 if (ferror (datain)) {
960     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
961     uustring (S_SOURCE_READ_ERR),
962     strerror (uu_errno = errno));
963     return UURET_IOERR;
964     }
965 root 1.4
966 root 1.1 if (line[0]=='\015' || line[0]=='\012') { /* Empty line? */
967     if (*state == DATA &&
968     (method == UU_ENCODED || method == XX_ENCODED))
969     *state = END;
970 root 1.4
971 root 1.1 /*
972     * if we had a whole block of valid lines before, we reset our
973     * 'valid data' flag, tf. Without this 'if', we'd break decoding
974     * files with interleaved blank lines. The value of 5 is chosen
975     * quite arbitrarly.
976     */
977 root 1.4
978 root 1.1 if (vlc > 5)
979     tf = tc = 0;
980     vlc = 0;
981     continue;
982     }
983    
984     /*
985     * Busy Polls
986     */
987    
988     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
989     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
990     uustring (S_DECODE_CANCEL));
991     return UURET_CANCEL;
992     }
993    
994     /*
995     * try to make sense of data
996     */
997    
998 root 1.4 line[299] = '\0'; /* For Safety of string functions */
999 root 1.1 count = 0;
1000    
1001     if (boundary && line[0]=='-' && line[1]=='-' &&
1002     strncmp (line+2, boundary, strlen (boundary)) == 0) {
1003     if (line[strlen(boundary)+2]=='-')
1004     uulboundary = 1;
1005     else
1006     uulboundary = 0;
1007     return UURET_OK;
1008     }
1009    
1010     /*
1011     * Use this pseudo-handling only if !FL_PROPER
1012     */
1013    
1014     if ((flags&FL_PROPER) == 0) {
1015     if (strncmp (line, "BEGIN", 5) == 0 &&
1016 root 1.5 _FP_strstr (line, "CUT HERE") && !tf) { /* I hate these lines */
1017 root 1.1 tc = tf = vlc = 0;
1018     continue;
1019     }
1020     /* MIME body boundary */
1021     if (line[0] == '-' && line[1] == '-' && method == B64ENCODED) {
1022     if ((haddata || tc) && (haddh || hadct)) {
1023     *state = DONE;
1024     vlc = 0;
1025     lc[0] = lc[1] = 0;
1026     continue;
1027     }
1028     hadct = 0;
1029     haddh = 1;
1030     continue;
1031     }
1032 root 1.5 if (_FP_strnicmp (line, "Content-Type", 12) == 0)
1033 root 1.1 hadct = 1;
1034     }
1035    
1036     if (*state == BEGIN) {
1037 root 1.4 if ((method == UU_ENCODED || method == XX_ENCODED) &&
1038     (strncmp (line, "begin ", 6) == 0 ||
1039 root 1.5 _FP_strnicmp (line, "<pre>begin ", 11) == 0)) { /* for LYNX */
1040 root 1.1 *state = DATA;
1041     continue;
1042     }
1043     else if (method == BH_ENCODED && line[0] == ':') {
1044     if (UUValidData (line, BH_ENCODED, &bhflag) == BH_ENCODED) {
1045     bhflag = 0;
1046     *state = DATA;
1047     }
1048     else
1049     continue;
1050     }
1051 root 1.4 else if (method == YENC_ENCODED &&
1052     strncmp (line, "=ybegin ", 8) == 0 &&
1053 root 1.5 _FP_strstr (line, " size=") != NULL &&
1054     _FP_strstr (line, " name=") != NULL) {
1055 root 1.4 *state = DATA;
1056    
1057 root 1.5 ptr = _FP_strstr (line, " size=") + 6;
1058 root 1.4 yefilesize = atoi (ptr);
1059    
1060 root 1.5 if (_FP_strstr (line, " part=") != NULL) {
1061     if (_FP_fgets (line, 299, datain) == NULL) {
1062 root 1.4 break;
1063     }
1064    
1065 root 1.5 if ((ptr = _FP_strstr (line, " end=")) == NULL) {
1066 root 1.4 break;
1067     }
1068    
1069     yepartends = atoi (ptr + 5);
1070     }
1071     tf = 1;
1072     continue;
1073     }
1074     else {
1075 root 1.1 continue;
1076 root 1.4 }
1077 root 1.1
1078     tc = tf = vlc = 0;
1079     lc[0] = lc[1] = 0;
1080     }
1081     else if ((*state == END) &&
1082     (method == UU_ENCODED || method == XX_ENCODED)) {
1083     if (strncmp (line, "end", 3) == 0) {
1084     *state = DONE;
1085     break;
1086     }
1087     }
1088 root 1.4
1089     if (*state == DATA && method == YENC_ENCODED &&
1090     strncmp (line, "=yend ", 6) == 0) {
1091     if (yepartends == 0 || yepartends >= yefilesize) {
1092     *state = DONE;
1093     }
1094     break;
1095     }
1096    
1097 root 1.1 if (*state == DATA || *state == END) {
1098     if (method==B64ENCODED && line[0]=='-' && line[1]=='-' && tc) {
1099     break;
1100     }
1101    
1102     if ((vflag = UUValidData (line, (tf)?method:0, &bhflag)) == 0)
1103     vflag = UURepairData (datain, line, (tf)?method:0, &bhflag);
1104    
1105     /*
1106     * correct XX/UUencoded lines that were declared Base64
1107     */
1108    
1109     if ((method == XX_ENCODED || method == UU_ENCODED) &&
1110     vflag == B64ENCODED) {
1111     if (UUValidData (line, method, &bhflag) == method)
1112     vflag = method;
1113     }
1114    
1115     if (vflag == method) {
1116     if (tf) {
1117     count = UUDecodeLine (line, oline, method);
1118     vlc++; lc[1]++;
1119     }
1120     else if (tc == 3) {
1121     count = UUDecodeLine (save[0], oline, method);
1122     count += UUDecodeLine (save[1], oline + count, method);
1123     count += UUDecodeLine (save[2], oline + count, method);
1124     count += UUDecodeLine (line, oline + count, method);
1125     tf = 1;
1126     tc = 0;
1127    
1128     /*
1129     * complain if we had one or two invalid lines amidst of
1130     * correctly encoded data. This usually means that the
1131     * file is in error
1132     */
1133    
1134     if (lc[1] > 10 && (lc[0] >= 1 && lc[0] <= 2) && !warning) {
1135     UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
1136     uustring (S_DATA_SUSPICIOUS));
1137     warning=1;
1138     }
1139     lc[0] = 0;
1140     lc[1] = 3;
1141     }
1142     else {
1143 root 1.5 _FP_strncpy (save[tc++], line, 256);
1144 root 1.1 }
1145 root 1.4
1146 root 1.1 if (method == UU_ENCODED)
1147     *state = (line[0] == 'M') ? DATA : END;
1148     else if (method == XX_ENCODED)
1149     *state = (line[0] == 'h') ? DATA : END;
1150     else if (method == B64ENCODED)
1151     *state = (strchr (line, '=') == NULL) ? DATA : DONE;
1152     else if (method == BH_ENCODED)
1153     *state = (!line[0] || strchr(line+1,':')==NULL)?DATA:DONE;
1154     }
1155     else {
1156     vlc = tf = tc = 0;
1157     haddh = 0;
1158     lc[0]++;
1159     }
1160     }
1161     else if (*state != DONE) {
1162     return UURET_NOEND;
1163     }
1164 root 1.4
1165 root 1.1 if (count) {
1166     if (method == BH_ENCODED) {
1167     if (UUbhwrite (oline, 1, count, dataout) != count) {
1168     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1169     uustring (S_WR_ERR_TEMP),
1170     strerror (uu_errno = errno));
1171     return UURET_IOERR;
1172     }
1173     }
1174     else if (fwrite (oline, 1, count, dataout) != count) {
1175     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1176     uustring (S_WR_ERR_TEMP),
1177     strerror (uu_errno = errno));
1178     return UURET_IOERR;
1179     }
1180     haddata++;
1181     count = 0;
1182     }
1183     }
1184    
1185     if (*state == DONE ||
1186     (*state == DATA && method == B64ENCODED &&
1187     vflag == B64ENCODED && (flags&FL_PROPER || haddh))) {
1188     for (tf=0; tf<tc; tf++)
1189     count += UUDecodeLine (save[tf], oline + count, method);
1190     if (count) {
1191     if (method == BH_ENCODED) {
1192     if (UUbhwrite (oline, 1, count, dataout) != count) {
1193     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1194     uustring (S_WR_ERR_TEMP),
1195     strerror (uu_errno = errno));
1196     return UURET_IOERR;
1197     }
1198     }
1199     else if (fwrite (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     }
1207     return UURET_OK;
1208     }
1209    
1210     /*
1211     * this function decodes the file into a temporary file
1212     */
1213    
1214     int
1215     UUDecode (uulist *data)
1216     {
1217     int state=BEGIN, part=-1, res=0, hb;
1218     long rsize, dsize, numbytes;
1219     FILE *datain, *dataout;
1220     unsigned char r[8];
1221     char *mode, *ntmp;
1222     uufile *iter;
1223     size_t bytes;
1224 root 1.3 #ifdef HAVE_MKSTEMP
1225     int tmpfd;
1226     const char *tmpprefix = "uuXXXXXX";
1227     char *tmpdir = NULL;
1228     #endif /* HAVE_MKSTEMP */
1229 root 1.1
1230     if (data == NULL || data->thisfile == NULL)
1231     return UURET_ILLVAL;
1232    
1233     if (data->state & UUFILE_TMPFILE)
1234     return UURET_OK;
1235    
1236     if (data->state & UUFILE_NODATA)
1237     return UURET_NODATA;
1238    
1239     if (data->state & UUFILE_NOBEGIN && !uu_desperate)
1240     return UURET_NODATA;
1241    
1242 root 1.2 if (data->uudet == PT_ENCODED)
1243 root 1.1 mode = "wt"; /* open text files in text mode */
1244     else
1245     mode = "wb"; /* otherwise in binary */
1246    
1247 root 1.3 #ifdef HAVE_MKSTEMP
1248     if ((getuid()==geteuid()) && (getgid()==getegid())) {
1249     tmpdir=getenv("TMPDIR");
1250     }
1251    
1252     if (!tmpdir) {
1253     tmpdir = "/tmp";
1254     }
1255     data->binfile = malloc(strlen(tmpdir)+strlen(tmpprefix)+2);
1256    
1257     if (!data->binfile) {
1258     #else
1259 root 1.1 if ((data->binfile = tempnam (NULL, "uu")) == NULL) {
1260 root 1.3 #endif /* HAVE_MKSTEMP */
1261 root 1.1 UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1262     uustring (S_NO_TEMP_NAME));
1263     return UURET_NOMEM;
1264     }
1265    
1266 root 1.3 #ifdef HAVE_MKSTEMP
1267     strcpy(data->binfile, tmpdir);
1268     strcat(data->binfile, "/");
1269     strcat(data->binfile, tmpprefix);
1270    
1271     if ((tmpfd = mkstemp(data->binfile)) == -1 ||
1272     (dataout = fdopen(tmpfd, mode)) == NULL) {
1273     #else
1274 root 1.1 if ((dataout = fopen (data->binfile, mode)) == NULL) {
1275 root 1.3 #endif /* HAVE_MKSTEMP */
1276 root 1.1 /*
1277     * we couldn't create a temporary file. Usually this means that TMP
1278     * and TEMP aren't set
1279     */
1280     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1281     uustring (S_WR_ERR_TARGET),
1282     data->binfile, strerror (uu_errno = errno));
1283 root 1.3 #ifdef HAVE_MKSTEMP
1284     if (tmpfd != -1) {
1285     unlink(data->binfile);
1286     close(tmpfd);
1287     }
1288     #endif /* HAVE_MKSTEMP */
1289 root 1.5 _FP_free (data->binfile);
1290 root 1.1 data->binfile = NULL;
1291     uu_errno = errno;
1292     return UURET_IOERR;
1293     }
1294 root 1.3
1295 root 1.1 /*
1296     * we don't have begin lines in Base64 or plain text files.
1297     */
1298     if (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
1299     data->uudet == PT_ENCODED)
1300     state = DATA;
1301    
1302     /*
1303     * If we know that the file does not have a begin, we simulate
1304     * it in desperate mode
1305     */
1306    
1307     if ((data->state & UUFILE_NOBEGIN) && uu_desperate)
1308     state = DATA;
1309    
1310     (void) UUDecodeLine (NULL, NULL, 0); /* init */
1311     (void) UUbhwrite (NULL, 0, 0, NULL); /* dito */
1312     (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* yep */
1313    
1314     /*
1315     * initialize progress information
1316     */
1317     progress.action = 0;
1318     if (data->filename != NULL) {
1319 root 1.5 _FP_strncpy (progress.curfile,
1320 root 1.1 (strlen(data->filename)>255)?
1321     (data->filename+strlen(data->filename)-255):data->filename,
1322     256);
1323     }
1324     else {
1325 root 1.5 _FP_strncpy (progress.curfile,
1326 root 1.1 (strlen(data->binfile)>255)?
1327     (data->binfile+strlen(data->binfile)-255):data->binfile,
1328     256);
1329     }
1330     progress.partno = 0;
1331     progress.numparts = 0;
1332     progress.fsize = -1;
1333     progress.percent = 0;
1334     progress.action = UUACT_DECODING;
1335    
1336     iter = data->thisfile;
1337     while (iter) {
1338     progress.numparts = (iter->partno)?iter->partno:1;
1339     iter = iter->NEXT;
1340     }
1341    
1342     /*
1343     * let's rock!
1344     */
1345    
1346     iter = data->thisfile;
1347     while (iter) {
1348     if (part != -1 && iter->partno != part+1)
1349     break;
1350     else
1351     part = iter->partno;
1352    
1353     if (iter->data->sfname == NULL) {
1354     iter = iter->NEXT;
1355     continue;
1356     }
1357    
1358     /*
1359     * call our FileCallback to retrieve the file
1360     */
1361    
1362     if (uu_FileCallback) {
1363     if ((res = (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
1364     uugen_fnbuffer, 1)) != UURET_OK)
1365     break;
1366     if ((datain = fopen (uugen_fnbuffer, "rb")) == NULL) {
1367     (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
1368     uugen_fnbuffer, 0);
1369     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1370     uustring (S_NOT_OPEN_FILE),
1371     uugen_fnbuffer, strerror (uu_errno = errno));
1372     res = UURET_IOERR;
1373     break;
1374     }
1375     }
1376     else {
1377     if ((datain = fopen (iter->data->sfname, "rb")) == NULL) {
1378     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1379     uustring (S_NOT_OPEN_FILE),
1380     iter->data->sfname, strerror (uu_errno = errno));
1381     res = UURET_IOERR;
1382     break;
1383     }
1384 root 1.5 _FP_strncpy (uugen_fnbuffer, iter->data->sfname, 1024);
1385 root 1.1 }
1386    
1387     progress.partno = part;
1388     progress.fsize = (iter->data->length)?iter->data->length:-1;
1389     progress.percent = 0;
1390     progress.foffset = iter->data->startpos;
1391    
1392     fseek (datain, iter->data->startpos, SEEK_SET);
1393     res = UUDecodePart (datain, dataout, &state,
1394     iter->data->startpos+iter->data->length,
1395     data->uudet, iter->data->flags, NULL);
1396     fclose (datain);
1397    
1398     if (uu_FileCallback)
1399     (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname, uugen_fnbuffer, 0);
1400    
1401     if (state == DONE || res != UURET_OK)
1402     break;
1403    
1404     iter = iter->NEXT;
1405     }
1406    
1407     if (state == DATA &&
1408     (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
1409     data->uudet == PT_ENCODED))
1410     state = DONE; /* assume we're done */
1411    
1412     fclose (dataout);
1413    
1414     if (res != UURET_OK || (state != DONE && !uu_desperate)) {
1415     unlink (data->binfile);
1416 root 1.5 _FP_free (data->binfile);
1417 root 1.1 data->binfile = NULL;
1418     data->state &= ~UUFILE_TMPFILE;
1419     data->state |= UUFILE_ERROR;
1420    
1421     if (res == UURET_OK && state != DONE)
1422     res = UURET_NOEND;
1423     }
1424     else if (res != UURET_OK) {
1425     data->state &= ~UUFILE_DECODED;
1426     data->state |= UUFILE_ERROR | UUFILE_TMPFILE;
1427     }
1428     else {
1429     data->state &= ~UUFILE_ERROR;
1430     data->state |= UUFILE_TMPFILE;
1431     }
1432    
1433     /*
1434     * If this was a BinHex file, we must extract its data or resource fork
1435     */
1436    
1437     if (data->uudet == BH_ENCODED && data->binfile) {
1438 root 1.3 #ifdef HAVE_MKSTEMP
1439     ntmp = malloc(strlen(tmpdir)+strlen(tmpprefix)+2);
1440    
1441     if (ntmp == NULL) {
1442     #else
1443 root 1.1 if ((ntmp = tempnam (NULL, "uu")) == NULL) {
1444 root 1.3 #endif /* HAVE_MKSTEMP */
1445 root 1.1 UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1446     uustring (S_NO_TEMP_NAME));
1447     progress.action = 0;
1448     return UURET_NOMEM;
1449     }
1450     if ((datain = fopen (data->binfile, "rb")) == NULL) {
1451     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1452     uustring (S_NOT_OPEN_FILE),
1453     data->binfile, strerror (uu_errno = errno));
1454     progress.action = 0;
1455     free (ntmp);
1456     return UURET_IOERR;
1457     }
1458 root 1.3 #ifdef HAVE_MKSTEMP
1459     strcpy(ntmp, tmpdir);
1460     strcat(ntmp, "/");
1461     strcat(ntmp, tmpprefix);
1462     if ((tmpfd = mkstemp(ntmp)) == -1 ||
1463     (dataout = fdopen(tmpfd, "wb")) == NULL) {
1464     #else
1465 root 1.1 if ((dataout = fopen (ntmp, "wb")) == NULL) {
1466 root 1.3 #endif /* HAVE_MKSTEMP */
1467 root 1.1 UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1468     uustring (S_NOT_OPEN_TARGET),
1469     ntmp, strerror (uu_errno = errno));
1470     progress.action = 0;
1471     fclose (datain);
1472 root 1.3 #ifdef HAVE_MKSTEMP
1473     if (tmpfd != -1) {
1474     unlink(ntmp);
1475     close(tmpfd);
1476     }
1477     #endif /* HAVE_MKSTEMP */
1478 root 1.1 free (ntmp);
1479     return UURET_IOERR;
1480     }
1481 root 1.3
1482 root 1.1 /*
1483     * read fork lengths. remember they're in Motorola format
1484     */
1485     r[0] = fgetc (datain);
1486     hb = (int) r[0] + 22;
1487     fseek (datain, (int) r[0] + 12, SEEK_SET);
1488     fread (r, 1, 8, datain);
1489    
1490     dsize = (((long) 1 << 24) * (long) r[0]) +
1491     (((long) 1 << 16) * (long) r[1]) +
1492     (((long) 1 << 8) * (long) r[2]) +
1493     ( (long) r[3]);
1494     rsize = (((long) 1 << 24) * (long) r[4]) +
1495     (((long) 1 << 16) * (long) r[5]) +
1496     (((long) 1 << 8) * (long) r[6]) +
1497     ( (long) r[7]);
1498    
1499     UUMessage (uunconc_id, __LINE__, UUMSG_MESSAGE,
1500     uustring (S_BINHEX_SIZES),
1501     dsize, rsize);
1502    
1503     if (dsize == 0) {
1504     fseek (datain, dsize + hb + 2, SEEK_SET);
1505     numbytes = rsize;
1506     }
1507     else if (rsize == 0) {
1508     fseek (datain, hb, SEEK_SET);
1509     numbytes = dsize;
1510     }
1511     else {
1512     /* we should let the user have the choice here */
1513     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
1514     uustring (S_BINHEX_BOTH));
1515     fseek (datain, hb, SEEK_SET);
1516     numbytes = dsize;
1517     }
1518    
1519     progress.action = 0;
1520     progress.partno = 0;
1521     progress.numparts = 1;
1522     progress.fsize = (numbytes)?numbytes:-1;
1523     progress.foffset = hb;
1524     progress.percent = 0;
1525     progress.action = UUACT_COPYING;
1526    
1527     /*
1528     * copy the chosen fork
1529     */
1530    
1531     while (!feof (datain) && numbytes) {
1532     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
1533     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
1534     uustring (S_DECODE_CANCEL));
1535     fclose (datain);
1536     fclose (dataout);
1537     unlink (ntmp);
1538     free (ntmp);
1539     return UURET_CANCEL;
1540     }
1541    
1542     bytes = fread (uugen_inbuffer, 1,
1543     (size_t) ((numbytes>1024)?1024:numbytes), datain);
1544    
1545     if (ferror (datain) || (bytes == 0 && !feof (datain))) {
1546     progress.action = 0;
1547     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1548     uustring (S_SOURCE_READ_ERR),
1549     data->binfile, strerror (uu_errno = errno));
1550     fclose (datain);
1551     fclose (dataout);
1552     unlink (ntmp);
1553     free (ntmp);
1554     return UURET_IOERR;
1555     }
1556     if (fwrite (uugen_inbuffer, 1, bytes, dataout) != bytes) {
1557     progress.action = 0;
1558     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1559     uustring (S_WR_ERR_TARGET),
1560     ntmp, strerror (uu_errno = errno));
1561     fclose (datain);
1562     fclose (dataout);
1563     unlink (ntmp);
1564     free (ntmp);
1565     return UURET_IOERR;
1566     }
1567     numbytes -= bytes;
1568     }
1569    
1570     if (numbytes) {
1571     UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
1572     uustring (S_SHORT_BINHEX),
1573     (data->filename)?data->filename:
1574     (data->subfname)?data->subfname:"???",
1575     numbytes);
1576     }
1577    
1578     /*
1579     * replace temp file
1580     */
1581    
1582     fclose (datain);
1583     fclose (dataout);
1584    
1585     if (unlink (data->binfile)) {
1586     UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
1587     uustring (S_TMP_NOT_REMOVED),
1588     data->binfile, strerror (uu_errno = errno));
1589     }
1590    
1591     free (data->binfile);
1592     data->binfile = ntmp;
1593     }
1594    
1595     progress.action = 0;
1596     return res;
1597     }
1598    
1599     /*
1600     * QuickDecode for proper MIME attachments. We expect the pointer to
1601     * be on the first header line.
1602     */
1603    
1604     int
1605     UUQuickDecode (FILE *datain, FILE *dataout, char *boundary, long maxpos)
1606     {
1607     int state=BEGIN, encoding=-1;
1608     headers myenv;
1609    
1610     /*
1611     * Read header and find out about encoding.
1612     */
1613    
1614     memset (&myenv, 0, sizeof (headers));
1615     UUScanHeader (datain, &myenv);
1616    
1617 root 1.5 if (_FP_stristr (myenv.ctenc, "uu") != NULL)
1618 root 1.1 encoding = UU_ENCODED;
1619 root 1.5 else if (_FP_stristr (myenv.ctenc, "xx") != NULL)
1620 root 1.1 encoding = XX_ENCODED;
1621 root 1.5 else if (_FP_stricmp (myenv.ctenc, "base64") == 0)
1622 root 1.1 encoding = B64ENCODED;
1623 root 1.5 else if (_FP_stricmp (myenv.ctenc, "quoted-printable") == 0)
1624 root 1.1 encoding = QP_ENCODED;
1625     else
1626     encoding = PT_ENCODED;
1627    
1628     UUkillheaders (&myenv);
1629    
1630     /*
1631     * okay, so decode this one
1632     */
1633    
1634     (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* init */
1635     return UUDecodePart (datain, dataout, &state, maxpos,
1636     encoding, FL_PROPER|FL_TOEND,
1637     boundary);
1638     }