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