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

# Content
1 /*
2 * This file is part of uudeview, the simple and friendly multi-part multi-
3 * file uudecoder program (c) 1994-2001 by Frank Pilhofer. The author may
4 * be contacted at fp@fpx.de
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17 /*
18 * These are the functions that are responsible for decoding. The
19 * original idea is from a freeware utility called "uunconc", and
20 * few lines of this code may still bear a remote resemblance to
21 * its code. If you are the author or know him, contact me.
22 * This program could only decode one multi-part, uuencoded file
23 * where the parts were in order. Base64, XX and BinHex decoding,
24 * support for multi-files and part-ordering covered by myself.
25 **/
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #ifdef SYSTEM_WINDLL
32 #include <windows.h>
33 #endif
34 #ifdef SYSTEM_OS2
35 #include <os2.h>
36 #endif
37
38 #include <stdio.h>
39 #include <ctype.h>
40
41 #ifdef STDC_HEADERS
42 #include <stdlib.h>
43 #include <string.h>
44 #endif
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 #ifdef HAVE_ERRNO_H
49 #include <errno.h>
50 #endif
51
52 #include <crc32.h>
53 #include <uudeview.h>
54 #include <uuint.h>
55 #include <fptools.h>
56 #include <uustring.h>
57
58 char * uunconc_id = "$Id$";
59
60 /* for braindead systems */
61 #ifndef SEEK_SET
62 #ifdef L_BEGIN
63 #define SEEK_SET L_BEGIN
64 #else
65 #define SEEK_SET 0
66 #endif
67 #endif
68
69 /*
70 * decoder states
71 */
72
73 #define BEGIN (1)
74 #define DATA (2)
75 #define END (3)
76 #define DONE (4)
77
78 /*
79 * mallocable areas
80 */
81
82 char *uunconc_UUxlat;
83 char *uunconc_UUxlen;
84 char *uunconc_B64xlat;
85 char *uunconc_XXxlat;
86 char *uunconc_BHxlat;
87 char *uunconc_save;
88
89 /*
90 * decoding translation tables and line length table
91 */
92
93 static int * UUxlen; /* initialized in UUInitConc() */
94 static int * UUxlat; /* from the malloc'ed areas above */
95 static int * B64xlat;
96 static int * XXxlat;
97 static int * BHxlat;
98
99 /*
100 * buffer for decoding
101 */
102
103 static char *save[3];
104
105 /*
106 * mallocable areas
107 */
108
109 char *uuncdl_fulline;
110 char *uuncdp_oline;
111
112 /*
113 * Return information for QuickDecode
114 */
115
116 static int uulboundary;
117
118 /*
119 * To prevent warnings when using a char as index into an array
120 */
121
122 #define ACAST(s) ((int)(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 save[1] = uunconc_save + 1200;
144 save[2] = uunconc_save + 2400;
145
146 /* prepare decoding translation table */
147 for(i = 0; i < 256; i++)
148 UUxlat[i] = B64xlat[i] = XXxlat[i] = BHxlat[i] = -1;
149
150 /*
151 * At some time I received a file which used lowercase characters for
152 * uuencoding. This shouldn't be, but let's accept it. Must take special
153 * care that this doesn't break xxdecoding. This is giving me quite a
154 * headache. If this one file hadn't been a Pocahontas picture, I might
155 * have ignored it for good.
156 */
157
158 for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
159 UUxlat[i] /* = UUxlat[i+64] */ = j;
160 for (i = '`', j = 0; i < '`' + 32; i++, j++)
161 UUxlat[i] = j;
162
163 /* add special cases */
164 UUxlat['`'] = UUxlat[' '];
165 UUxlat['~'] = UUxlat['^'];
166
167 /* prepare line length table */
168 UUxlen[0] = 1;
169 for(i = 1, j = 5; i <= 61; i += 3, j += 4)
170 UUxlen[i] = UUxlen[i+1] = UUxlen[i+2] = j;
171
172 /* prepare other tables */
173 for (i=0; i<64; i++) {
174 B64xlat[ACAST(B64EncodeTable[i])] = i;
175 XXxlat [ACAST(XXEncodeTable [i])] = i;
176 BHxlat [ACAST(BHEncodeTable [i])] = i;
177 }
178 }
179
180 /*
181 * Workaround for Netscape
182 */
183
184 /*
185 * Determines whether Netscape may have broken up a data line (by
186 * inserting a newline). This only seems to happen after <a in a
187 * href statement
188 */
189
190 int
191 UUBrokenByNetscape (char *string)
192 {
193 char *ptr;
194 int len;
195
196 if (string==NULL || (len=strlen(string))<3)
197 return 0;
198
199 if ((ptr = FP_stristr (string, "<a href=")) != NULL) {
200 if (FP_stristr (string, "</a>") > ptr)
201 return 2;
202 }
203
204 ptr = string + len;
205
206 if (len<3) return 0;
207 if (*--ptr == ' ') ptr--;
208 ptr--;
209
210 if (FP_strnicmp (ptr, "<a", 2) == 0)
211 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 *
221 * 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 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 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 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 while (*p1 && *p1!='>') p1++;
264 if (*p1=='\0' || *(p1+1)!='<') return 0;
265 p1++;
266 while (*p1 && (*p1!='<' || FP_strnicmp(p1,"</a>",4)!=0)) {
267 *p2++ = *p1++;
268 }
269 if (FP_strnicmp(p1,"</a>",4) != 0)
270 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 signed char *s = ptr;
298
299 if ((s == NULL) || (*s == '\0')) {
300 return 0; /* bad string */
301 }
302
303 if (encoding == YENC_ENCODED)
304 return YENC_ENCODED;
305
306 i = strlen (s);
307
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 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 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 /* suspicious = 1; we're careful here REMOVED 0.4.15 __FP__ */
467 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 if (strlen (line) > 250)
537 break;
538 ptr = line + strlen (line);
539 if (FP_fgets (ptr, 299-(ptr-line), datei) == NULL)
540 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 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 if (method == UU_ENCODED)
626 table = UUxlat;
627 else
628 table = XXxlat;
629
630 i = table [ACAST(*s++)];
631 j = UUxlen[i] - 1;
632
633 while(j > 0) {
634 c = table[ACAST(*s++)] << 2;
635 cc = table[ACAST(*s++)];
636 c |= (cc >> 4);
637
638 if(i-- > 0)
639 d[count++] = c;
640
641 cc <<= 4;
642 c = table[ACAST(*s++)];
643 cc |= (c >> 2);
644
645 if(i-- > 0)
646 d[count++] = cc;
647
648 c <<= 6;
649 c |= table[ACAST(*s++)];
650
651 if(i-- > 0)
652 d[count++] = c;
653
654 j -= 4;
655 }
656 }
657 else if (method == B64ENCODED) {
658 if (leftover) {
659 strcpy (uuncdl_fulline + leftover, s);
660
661 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 strcpy (uuncdl_fulline + leftover, s);
691
692 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 while (!FP_feof (datain) &&
740 (ftell(datain)<maxpos || flags&FL_TOEND ||
741 (!(flags&FL_PROPER) && uu_fast_scanning))) {
742 if (FP_fgets (line, 1023, datain) == NULL)
743 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 else if (!*p2) {
787 /* soft line break */
788 goto skip_lbr;
789 break;
790 }
791 else {
792 /* huh? */
793 fputc ('=', dataout);
794 }
795 }
796 /*
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
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 /* something is broken here now, but it was broken before */
814 if (!FP_feof (datain) &&
815 (ftell(datain)<maxpos || flags&FL_TOEND ||
816 (!(flags&FL_PROPER) && uu_fast_scanning)))
817 fprintf (dataout, "%s\n", p1);
818 else
819 fprintf (dataout, "%s", p1);
820
821 skip_lbr: ;
822 }
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 while (!FP_feof (datain) &&
840 (ftell(datain)<maxpos || flags&FL_TOEND ||
841 (!(flags&FL_PROPER) && uu_fast_scanning))) {
842 if (FP_fgets (line, 1023, datain) == NULL)
843 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 if ((ftell(datain)<maxpos || flags&FL_TOEND || flags&FL_PARTIAL ||
876 !boundary || (!(flags&FL_PROPER) && uu_fast_scanning))) {
877 *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 /*
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 else if (!s[1]) {
935 d[count++] = '\012';
936 }
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 int
952 UUDecodePart (FILE *datain, FILE *dataout, int *state,
953 long maxpos, int method, int flags,
954 char *boundary)
955 {
956 char *line, *oline=uuncdp_oline;
957 int warning=0, vlc=0, lc[2], hadct=0;
958 int tc=0, tf=0, vflag, haddata=0, haddh=0;
959 long yefilesize=0, yepartends=0, yenotlastpart=0;
960 crc32_t yepartcrc=CRC32_INIT;
961 static crc32_t yefilecrc;
962 static int bhflag=0;
963 size_t count=0;
964 size_t yepartsize=0;
965 char *ptr;
966
967 if (datain == NULL || dataout == NULL) {
968 yefilecrc = CRC32_INIT;
969 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 if (method == YENC_ENCODED) {
990 *state = BEGIN;
991 }
992
993 while (!FP_feof (datain) && *state != DONE &&
994 (ftell(datain)<maxpos || flags&FL_TOEND || maxpos==-1 ||
995 (!(flags&FL_PROPER) && uu_fast_scanning))) {
996 if (FP_fgets ((line = uugen_fnbuffer), 1200 - 5, datain) == NULL)
997 break;
998
999 /* optionally skip .. */
1000 if (*line == '.' && uu_dotdot)
1001 line++;
1002
1003 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
1010 if (!*line) { /* Empty line? */
1011 if (*state == DATA &&
1012 (method == UU_ENCODED || method == XX_ENCODED))
1013 *state = END;
1014
1015 /*
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
1022 if (vlc > 5)
1023 tf = tc = 0;
1024 vlc = 0;
1025 continue;
1026 }
1027
1028 /*
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 line[1200 - 1] = '\0'; /* For Safety of string functions */
1043 count = 0;
1044
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 FP_strstr (line, "CUT HERE") && !tf) { /* I hate these lines */
1061 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 if (FP_strnicmp (line, "Content-Type", 12) == 0)
1077 hadct = 1;
1078 }
1079
1080 if (*state == BEGIN) {
1081 if ((method == UU_ENCODED || method == XX_ENCODED) &&
1082 (strncmp (line, "begin ", 6) == 0 ||
1083 FP_strnicmp (line, "<pre>begin ", 11) == 0)) { /* for LYNX */
1084 *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 else if (method == YENC_ENCODED &&
1096 strncmp (line, "=ybegin ", 8) == 0 &&
1097 FP_strstr (line, " name=") != NULL) {
1098 *state = DATA;
1099
1100 if ((ptr = FP_strstr (line, " size=")) != NULL) {
1101 ptr += 6;
1102 yefilesize = atoi (ptr);
1103 }
1104 else {
1105 yefilesize = -1;
1106 }
1107
1108 if ((ptr =FP_strstr (line, " part="))) {
1109 int partno = atoi (ptr + 6);
1110
1111 if ((ptr = FP_strstr (line, " total=")))
1112 yenotlastpart = atoi (ptr + 7) != partno;
1113
1114 if (FP_fgets (line, 1200 - 5, datain) == NULL) {
1115 break;
1116 }
1117
1118 if ((ptr = FP_strstr (line, " end=")) == NULL) {
1119 break;
1120 }
1121
1122 yepartends = atoi (ptr + 5);
1123 }
1124 tf = 1;
1125 continue;
1126 }
1127 else {
1128 continue;
1129 }
1130
1131 tc = tf = vlc = 0;
1132 lc[0] = lc[1] = 0;
1133 }
1134 else if ((*state == END || *state == DATA) &&
1135 (method == UU_ENCODED || method == XX_ENCODED)) {
1136 if (strncmp (line, "end", 3) == 0) {
1137 *state = DONE;
1138 break;
1139 }
1140 }
1141
1142 if (*state == DATA && method == YENC_ENCODED &&
1143 strncmp (line, "=yend ", 6) == 0) {
1144 int lastpart = !yenotlastpart && (yepartends == 0 || yepartends >= yefilesize);
1145 yefilecrc = uu_crc32_combine(yefilecrc, yepartcrc, yepartsize);
1146 if ((ptr = FP_strstr (line, " pcrc32=")) != NULL) {
1147 crc32_t pcrc32 = strtoul (ptr + 8, NULL, 16);
1148 if (pcrc32 != yepartcrc) {
1149 UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
1150 uustring (S_PCRC_MISMATCH), progress.curfile, progress.partno);
1151 }
1152 } else if ((ptr = FP_strstr (line, " pcrc=")) != NULL) {
1153 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 }
1159 if (lastpart && (ptr = FP_strstr (line, " crc32=")) != NULL)
1160 {
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 if ((ptr = FP_strstr (line, " size=")) != NULL)
1168 {
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 if (lastpart) {
1182 *state = DONE;
1183 }
1184 break;
1185 }
1186
1187 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 if (method == YENC_ENCODED) {
1209 yepartcrc = uu_crc32(yepartcrc, oline, count);
1210 yepartsize += count;
1211 }
1212 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 FP_strncpy (save[tc++], line, 1200);
1238 }
1239
1240 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
1259 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 for (tf=0; tf<tc; tf++)
1283 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 unsigned long rsize, dsize, numbytes;
1313 FILE *datain, *dataout;
1314 void *datain_buf, *dataout_buf;
1315 unsigned char r[8];
1316 char *mode, *ntmp;
1317 uufile *iter;
1318 size_t bytes;
1319 #ifdef HAVE_MKSTEMP
1320 int tmpfd;
1321 const char *tmpprefix = "uuXXXXXX";
1322 char *tmpdir = NULL;
1323 #endif /* HAVE_MKSTEMP */
1324
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 if (data->uudet == PT_ENCODED)
1338 mode = "wt"; /* open text files in text mode */
1339 else
1340 mode = "wb"; /* otherwise in binary */
1341
1342 #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 if ((data->binfile = tmpnam (NULL)) == NULL) {
1355 #endif /* HAVE_MKSTEMP */
1356 UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1357 uustring (S_NO_TEMP_NAME));
1358 return UURET_NOMEM;
1359 }
1360
1361 #ifdef HAVE_MKSTEMP
1362 strcpy(data->binfile, tmpdir);
1363 strcat(data->binfile, "/");
1364 strcat(data->binfile, tmpprefix);
1365
1366 if ((tmpfd = mkstemp(data->binfile)) == -1 ||
1367 (dataout = fdopen(tmpfd, mode)) == NULL) {
1368 #else
1369 if ((dataout = fopen (data->binfile, mode)) == NULL) {
1370 #endif /* HAVE_MKSTEMP */
1371 /*
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 #ifdef HAVE_MKSTEMP
1379 if (tmpfd != -1) {
1380 unlink(data->binfile);
1381 close(tmpfd);
1382 }
1383 #endif /* HAVE_MKSTEMP */
1384 FP_free (data->binfile);
1385 data->binfile = NULL;
1386 uu_errno = errno;
1387 return UURET_IOERR;
1388 }
1389 UUSETBUF (dataout, dataout_buf, uu_wbuf);
1390 FP_flockfile (dataout);
1391
1392 /*
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 FP_strncpy (progress.curfile,
1417 (strlen(data->filename)>255)?
1418 (data->filename+strlen(data->filename)-255):data->filename,
1419 256);
1420 }
1421 else {
1422 FP_strncpy (progress.curfile,
1423 (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
1439 /*
1440 * let's rock!
1441 */
1442
1443 iter = data->thisfile;
1444 while (iter) {
1445 if (part != -1 && iter->partno != part+1 && !uu_desperate)
1446 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 FP_strncpy (uugen_fnbuffer, iter->data->sfname, 1024);
1482 }
1483 UUSETBUF (datain, datain_buf, uu_rbuf);
1484 FP_flockfile (datain);
1485
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 UUCLRBUF (uu_rbuf, datain_buf);
1497
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 if (state == DATA &&
1508 (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
1509 data->uudet == PT_ENCODED))
1510 state = DONE; /* assume we're done */
1511
1512 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 UUCLRBUF (uu_wbuf, dataout_buf);
1519
1520 if (res != UURET_OK || (state != DONE && !uu_desperate)) {
1521 unlink (data->binfile);
1522 FP_free (data->binfile);
1523 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 #ifdef HAVE_MKSTEMP
1545 ntmp = malloc(strlen(tmpdir)+strlen(tmpprefix)+2);
1546 #else
1547 ntmp = tempnam (NULL);
1548 #endif /* HAVE_MKSTEMP */
1549 if (ntmp == NULL) {
1550 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 UUSETBUF (datain, datain_buf, uu_rbuf);
1564 FP_flockfile (datain);
1565
1566 #ifdef HAVE_MKSTEMP
1567 strcpy(ntmp, tmpdir);
1568 strcat(ntmp, "/");
1569 strcat(ntmp, tmpprefix);
1570 if ((tmpfd = mkstemp(ntmp)) == -1 ||
1571 (dataout = fdopen(tmpfd, "wb")) == NULL) {
1572 #else
1573 if ((dataout = fopen (ntmp, "wb")) == NULL) {
1574 #endif /* HAVE_MKSTEMP */
1575 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 UUCLRBUF (uu_rbuf, datain_buf);
1581 #ifdef HAVE_MKSTEMP
1582 if (tmpfd != -1) {
1583 unlink(ntmp);
1584 close(tmpfd);
1585 }
1586 #endif /* HAVE_MKSTEMP */
1587 free (ntmp);
1588 return UURET_IOERR;
1589 }
1590 UUSETBUF (dataout, dataout_buf, uu_wbuf);
1591 FP_flockfile (dataout);
1592
1593 /*
1594 * read fork lengths. remember they're in Motorola format
1595 */
1596 r[0] = FP_getc (datain);
1597 hb = (int) r[0] + 22;
1598 fseek (datain, (int) r[0] + 12, SEEK_SET);
1599 fread (r, 1, 8, datain);
1600
1601 dsize = (((long) 1 << 24) * (long) r[0]) +
1602 (((long) 1 << 16) * (long) r[1]) +
1603 (((long) 1 << 8) * (long) r[2]) +
1604 ( (long) r[3]);
1605 rsize = (((long) 1 << 24) * (long) r[4]) +
1606 (((long) 1 << 16) * (long) r[5]) +
1607 (((long) 1 << 8) * (long) r[6]) +
1608 ( (long) r[7]);
1609
1610 UUMessage (uunconc_id, __LINE__, UUMSG_MESSAGE,
1611 uustring (S_BINHEX_SIZES),
1612 dsize, rsize);
1613
1614 if (dsize == 0) {
1615 fseek (datain, dsize + hb + 2, SEEK_SET);
1616 numbytes = rsize;
1617 }
1618 else if (rsize == 0) {
1619 fseek (datain, hb, SEEK_SET);
1620 numbytes = dsize;
1621 }
1622 else {
1623 /* we should let the user have the choice here */
1624 UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
1625 uustring (S_BINHEX_BOTH));
1626 fseek (datain, hb, SEEK_SET);
1627 numbytes = dsize;
1628 }
1629
1630 progress.action = 0;
1631 progress.partno = 0;
1632 progress.numparts = 1;
1633 progress.fsize = numbytes ? numbytes : -1;
1634 progress.foffset = hb;
1635 progress.percent = 0;
1636 progress.action = UUACT_COPYING;
1637
1638 /*
1639 * copy the chosen fork
1640 */
1641
1642 while (!FP_feof (datain) && numbytes) {
1643 if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
1644 UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
1645 uustring (S_DECODE_CANCEL));
1646 fclose (datain);
1647 UUCLRBUF (uu_rbuf, datain_buf);
1648 fclose (dataout);
1649 UUCLRBUF (uu_wbuf, dataout_buf);
1650 unlink (ntmp);
1651 free (ntmp);
1652 return UURET_CANCEL;
1653 }
1654
1655 bytes = fread (uugen_inbuffer, 1,
1656 (size_t) ((numbytes>1024)?1024:numbytes), datain);
1657
1658 if (ferror (datain) || (bytes == 0 && !FP_feof (datain))) {
1659 progress.action = 0;
1660 UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1661 uustring (S_SOURCE_READ_ERR),
1662 data->binfile, strerror (uu_errno = errno));
1663 fclose (datain);
1664 UUCLRBUF (uu_rbuf, datain_buf);
1665 fclose (dataout);
1666 UUCLRBUF (uu_wbuf, dataout_buf);
1667 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 UUCLRBUF (uu_rbuf, datain_buf);
1678 fclose (dataout);
1679 UUCLRBUF (uu_wbuf, dataout_buf);
1680 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 UUCLRBUF (uu_rbuf, datain_buf);
1701 if (fclose (dataout)) {
1702 UUCLRBUF (uu_wbuf, dataout_buf);
1703 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 UUCLRBUF (uu_wbuf, dataout_buf);
1711
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 if (FP_stristr (myenv.ctenc, "uu") != NULL)
1745 encoding = UU_ENCODED;
1746 else if (FP_stristr (myenv.ctenc, "xx") != NULL)
1747 encoding = XX_ENCODED;
1748 else if (FP_stricmp (myenv.ctenc, "base64") == 0)
1749 encoding = B64ENCODED;
1750 else if (FP_stricmp (myenv.ctenc, "quoted-printable") == 0)
1751 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 }