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