ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uunconc.c
Revision: 1.21
Committed: Fri May 27 13:52:27 2011 UTC (15 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-1_5, rel-1_4, rel-1_6, rel-1_62
Changes since 1.20: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

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