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

File Contents

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