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