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