ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uunconc.c
Revision: 1.31
Committed: Sat Sep 24 06:05:03 2022 UTC (20 months, 1 week ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.30: +1 -3 lines
Log Message:
*** empty log message ***

File Contents

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