ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uunconc.c
Revision: 1.29
Committed: Sun Dec 13 02:55:12 2020 UTC (5 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-1_8
Changes since 1.28: +19 -17 lines
Log Message:
*** empty log message ***

File Contents

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