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