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 very central functions of UUDeview. Here, we scan a file
19 * and decide whether it contains encoded data or not. ScanPart() must
20 * be called repeatedly on the same file until feof(file). Each time,
21 * it returns information about the next part found within.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #ifdef SYSTEM_WINDLL
29 #include <windows.h>
30 #endif
31 #ifdef SYSTEM_OS2
32 #include <os2.h>
33 #endif
34
35 #include <stdio.h>
36 #include <ctype.h>
37
38 #ifdef STDC_HEADERS
39 #include <stdlib.h>
40 #include <string.h>
41 #endif
42 #ifdef HAVE_MALLOC_H
43 #include <malloc.h>
44 #endif
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 #ifdef HAVE_MEMORY_H
49 #include <memory.h>
50 #endif
51 #ifdef HAVE_ERRNO_H
52 #include <errno.h>
53 #endif
54
55 #include <uudeview.h>
56 #include <uuint.h>
57 #include <fptools.h>
58 #include <uustring.h>
59
60 char * uuscan_id = "$Id: uuscan.c,v 1.46 2004/03/01 22:52:27 fp Exp $";
61
62 /*
63 * Header fields we recognize as such. See RFC822. We add "From ",
64 * the usual marker for a beginning of a new message, and a couple
65 * of usual MDA, News and MIME headers.
66 * We make a distinction of MIME headers as we need the difference
67 * to scan the bodies from partial multipart messages.
68 */
69
70 static char *knownmsgheaders[] = {
71 "From ", "Return-Path:", "Received:", "Reply-To:",
72 "From:", "Sender:", "Resent-Reply-To:", "Resent-From:",
73 "Resent-Sender:", "Date:", "Resent-Date:", "To:",
74 "Resent-To:", "Cc:", "Bcc:", "Resent-bcc:",
75 "Message-ID:", "Resent-Message-Id:", "In-Reply-To:",
76 "References:", "Keywords:", "Subject:", "Comments:",
77
78 "Delivery-Date:", "Posted-Date:", "Received-Date:",
79 "Precedence:",
80
81 "Path:", "Newsgroups:", "Organization:", "Lines:",
82 "NNTP-Posting-Host:",
83 NULL
84 };
85
86 static char *knownmimeheaders[] = {
87 "Mime-Version:", "Content-Transfer-Encoding:",
88 "Content-Type:", "Content-Disposition:",
89 "Content-Description:", "Content-Length:",
90 NULL
91 };
92
93 /*
94 * for MIME (plaintext) parts without filename
95 */
96 int mimseqno;
97
98 /*
99 * how many lines do we read when checking for headers
100 */
101 #define WAITHEADER 10
102
103 /*
104 * The stack for encapsulated Multipart messages
105 */
106 #define MSMAXDEPTH 3
107
108 int mssdepth = 0;
109 scanstate multistack[MSMAXDEPTH+1];
110
111 /*
112 * The state and the local envelope
113 */
114 headers localenv;
115 scanstate sstate;
116
117 /*
118 * mallocable areas
119 */
120
121 char *uuscan_shlline;
122 char *uuscan_shlline2;
123 char *uuscan_pvvalue;
124 char *uuscan_phtext;
125 char *uuscan_sdline;
126 char *uuscan_sdbhds1;
127 char *uuscan_sdbhds2;
128 char *uuscan_spline;
129
130 /*
131 * Macro: print cancellation message in UUScanPart
132 */
133
134 #define SPCANCEL() {UUMessage(uuscan_id,__LINE__,UUMSG_NOTE,uustring(S_SCAN_CANCEL));*errcode=UURET_CANCEL;goto ScanPartEmergency;}
135
136 /*
137 * Is line empty? A line is empty if it is composed of whitespace.
138 */
139
140 static int
IsLineEmpty(char * data)141 IsLineEmpty (char *data)
142 {
143 if (data == NULL) return 0;
144 while (*data && isspace (*data)) data++;
145 return ((*data)?0:1);
146 }
147
148 /*
149 * Is this a header line? A header line has alphanumeric characters
150 * followed by a colon.
151 */
152
153 static int
IsHeaderLine(char * data)154 IsHeaderLine (char *data)
155 {
156 if (data == NULL) return 0;
157 if (*data == ':') return 0;
158 while (*data && (isalnum (*data) || *data=='-')) data++;
159 return (*data == ':') ? 1 : 0;
160 }
161
162 /*
163 * Scans a potentially folded header line from the input file. If
164 * initial is non-NULL, it is the first line of the header, useful
165 * if the calling function just coincidentally noticed that this is
166 * a header.
167 * RFC0822 does not specify a maximum length for headers, but we
168 * truncate everything beyond an insane value of 1024 characters.
169 */
170
171 static char *
ScanHeaderLine(FILE * datei,char * initial)172 ScanHeaderLine (FILE *datei, char *initial)
173 {
174 char *ptr=uuscan_shlline;
175 char *ptr2, *p1, *p2, *p3;
176 int llength, c;
177 long curpos;
178 int hadcr;
179
180 if (initial) {
181 _FP_strncpy (uuscan_shlline, initial, 1024);
182 }
183 else {
184 /* read first line */
185 if (feof (datei) || ferror (datei))
186 return NULL;
187 if (_FP_fgets (uuscan_shlline, 1023, datei) == NULL)
188 return NULL;
189 uuscan_shlline[1023] = '\0';
190 }
191
192 llength = strlen (uuscan_shlline);
193 hadcr = 0;
194
195 /* strip whitespace at end */
196 ptr = uuscan_shlline + llength;
197 while (llength && isspace(*(ptr-1))) {
198 if (*(ptr-1) == '\012' || *(ptr-1) == '\015')
199 hadcr = 1;
200 ptr--; llength--;
201 }
202 if (llength == 0) {
203 uuscan_shlline[0] = '\0';
204 return uuscan_shlline;
205 }
206
207 while (!feof (datei)) {
208 c = fgetc (datei);
209 if (feof (datei))
210 break;
211
212 /*
213 * If the line didn't have a CR, it was longer than 256 characters
214 * and is continued anyway.
215 */
216
217 if (hadcr==1 && c != ' ' && c != '\t') {
218 /* no LWSP-char, header line does not continue */
219 ungetc (c, datei);
220 break;
221 }
222 while (!feof (datei) && (c == ' ' || c == '\t'))
223 c = fgetc (datei);
224
225 if (!feof (datei))
226 ungetc (c, datei); /* push back for fgets() */
227
228 /* insert a single LWSP */
229 if (hadcr==1 && llength < 1023) {
230 *ptr++ = ' ';
231 llength++;
232 }
233 *ptr = '\0'; /* make lint happier */
234
235 if (feof (datei))
236 break;
237
238 /* read next line */
239 curpos = ftell (datei);
240 if (_FP_fgets (uugen_inbuffer, 255, datei) == NULL)
241 break;
242 uugen_inbuffer[255] = '\0';
243
244 if (IsLineEmpty (uugen_inbuffer)) { /* oops */
245 fseek (datei, curpos, SEEK_SET);
246 break;
247 }
248
249 _FP_strncpy (ptr, uugen_inbuffer, 1024-llength);
250
251 /*
252 * see if line was terminated with CR. Otherwise, it continues ...
253 */
254 c = strlen (uugen_inbuffer);
255 if (c>0 &&
256 (uugen_inbuffer[c-1] == '\012' || uugen_inbuffer[c-1] == '\015'))
257 hadcr = 1;
258 else
259 hadcr = 0;
260
261 /*
262 * strip whitespace
263 */
264
265 c = strlen (ptr);
266 ptr += c;
267 llength += c;
268 while (llength && isspace(*(ptr-1))) {
269 ptr--; llength--;
270 }
271 }
272
273 *ptr = '\0';
274
275 if (llength == 0)
276 return NULL;
277
278 /*
279 * Now that we've read the header line, we can RFC 1522-decode it
280 */
281
282 ptr = uuscan_shlline;
283 ptr2 = uuscan_shlline2;
284
285 while (*ptr) {
286 /*
287 * Look for =? magic
288 */
289
290 if (*ptr == '=' && *(ptr+1) == '?') {
291 /*
292 * Let p1 point to the charset, look for next question mark
293 */
294
295 p1 = p2 = ptr+2;
296
297 while (*p2 && *p2 != '?') {
298 p2++;
299 }
300
301 if (*p2 == '?' &&
302 (*(p2+1) == 'q' || *(p2+1) == 'Q' ||
303 *(p2+1) == 'b' || *(p2+1) == 'B') &&
304 *(p2+2) == '?') {
305 /*
306 * Let p2 point to the encoding, look for ?= magic
307 */
308
309 p2++;
310 p3=p2+2;
311
312 while (*p3 && (*p3 != '?' || *(p3+1) != '=')) {
313 p3++;
314 }
315
316 if (*p3 == '?' && *(p3+1) == '=') {
317 /*
318 * Alright, we've found an RFC 1522 header field
319 */
320 if (*p2 == 'q' || *p2 == 'Q') {
321 c = UUDecodeField (p2+2, ptr2, QP_ENCODED);
322 }
323 else if (*p2 == 'b' || *p2 == 'B') {
324 c = UUDecodeField (p2+2, ptr2, B64ENCODED);
325 }
326 if (c >= 0) {
327 ptr2 += c;
328 ptr = p3+2;
329 continue;
330 }
331 }
332 }
333 }
334
335 *ptr2++ = *ptr++;
336 }
337
338 *ptr2 = 0;
339
340 return uuscan_shlline2;
341 }
342
343 /*
344 * Extract the value from a MIME attribute=value pair. This function
345 * receives a pointer to the attribute.
346 */
347 static char *
ParseValue(char * attribute)348 ParseValue (char *attribute)
349 {
350 char *ptr=uuscan_pvvalue;
351 int length=0;
352
353 if (attribute == NULL)
354 return NULL;
355
356 while ((isalnum(*attribute) || *attribute=='_') && *attribute != '=')
357 attribute++;
358
359 while (isspace(*attribute))
360 attribute++;
361
362 if (*attribute == '=') {
363 attribute++;
364 while (isspace (*attribute))
365 attribute++;
366 }
367 else
368 return NULL;
369
370 if (*attribute == '"') {
371 /* quoted-string */
372 attribute++;
373 while (*attribute && *attribute != '"' && length < 255) {
374 *ptr++ = *attribute++;
375 length++;
376 }
377 *ptr = '\0';
378 }
379 else {
380 /* tspecials from RFC1521 */
381 /*
382 * Note - exclude '[', ']' and ';' on popular request; these are
383 * used in some Content-Type fields by the Klez virus, and people
384 * who feed their virus scanners with the output of UUDeview would
385 * like to catch it!
386 */
387
388 while (*attribute && !isspace (*attribute) &&
389 *attribute != '(' && *attribute != ')' &&
390 *attribute != '<' && *attribute != '>' &&
391 *attribute != '@' && *attribute != ',' &&
392 /* *attribute != ';' && */ *attribute != ':' &&
393 *attribute != '\\' &&*attribute != '"' &&
394 *attribute != '/' && /* *attribute != '[' &&
395 *attribute != ']' && */ *attribute != '?' &&
396 *attribute != '=' && length < 255) {
397 *ptr++ = *attribute++;
398 length++;
399 }
400
401 *ptr = '\0';
402 }
403 return uuscan_pvvalue;
404 }
405
406 /*
407 * Extract the information we need from header fields
408 */
409
410 static headers *
ParseHeader(headers * theheaders,char * line)411 ParseHeader (headers *theheaders, char *line)
412 {
413 char **variable=NULL;
414 char *value, *ptr, *thenew;
415 int delimit, length;
416
417 if (line == NULL)
418 return theheaders;
419
420 if (_FP_strnicmp (line, "From:", 5) == 0) {
421 if (theheaders->from) return theheaders;
422 variable = &theheaders->from;
423 value = line+5;
424 delimit = 0;
425 }
426 else if (_FP_strnicmp (line, "Subject:", 8) == 0) {
427 if (theheaders->subject) return theheaders;
428 variable = &theheaders->subject;
429 value = line+8;
430 delimit = 0;
431 }
432 else if (_FP_strnicmp (line, "To:", 3) == 0) {
433 if (theheaders->rcpt) return theheaders;
434 variable = &theheaders->rcpt;
435 value = line+3;
436 delimit = 0;
437 }
438 else if (_FP_strnicmp (line, "Date:", 5) == 0) {
439 if (theheaders->date) return theheaders;
440 variable = &theheaders->date;
441 value = line+5;
442 delimit = 0;
443 }
444 else if (_FP_strnicmp (line, "Mime-Version:", 13) == 0) {
445 if (theheaders->mimevers) return theheaders;
446 variable = &theheaders->mimevers;
447 value = line+13;
448 delimit = 0;
449 }
450 else if (_FP_strnicmp (line, "Content-Type:", 13) == 0) {
451 if (theheaders->ctype) return theheaders;
452 variable = &theheaders->ctype;
453 value = line+13;
454 delimit = ';';
455
456 /* we can probably extract more information */
457 if ((ptr = _FP_stristr (line, "boundary")) != NULL) {
458 if ((thenew = ParseValue (ptr))) {
459 if (theheaders->boundary) free (theheaders->boundary);
460 theheaders->boundary = _FP_strdup (thenew);
461 }
462 }
463 if ((ptr = _FP_stristr (line, "name")) != NULL) {
464 if ((thenew = ParseValue (ptr))) {
465 if (theheaders->fname) free (theheaders->fname);
466 theheaders->fname = _FP_strdup (thenew);
467 }
468 }
469 if ((ptr = _FP_stristr (line, "id")) != NULL) {
470 if ((thenew = ParseValue (ptr))) {
471 if (theheaders->mimeid) free (theheaders->mimeid);
472 theheaders->mimeid = _FP_strdup (thenew);
473 }
474 }
475 if ((ptr = _FP_stristr (line, "number")) != NULL) {
476 if ((thenew = ParseValue (ptr))) {
477 theheaders->partno = atoi (thenew);
478 }
479 }
480 if ((ptr = _FP_stristr (line, "total")) != NULL) {
481 if ((thenew = ParseValue (ptr))) {
482 theheaders->numparts = atoi (thenew);
483 }
484 }
485 }
486 else if (_FP_strnicmp (line, "Content-Transfer-Encoding:", 26) == 0) {
487 if (theheaders->ctenc) return theheaders;
488 variable = &theheaders->ctenc;
489 value = line+26;
490 delimit = ';';
491 }
492 else if (_FP_strnicmp (line, "Content-Disposition:", 20) == 0) {
493 /*
494 * Some encoders mention the original filename as parameter to
495 * Content-Type, others as parameter to Content-Disposition. We
496 * do prefer the first solution, but accept this situation, too.
497 * TODO: Read RFC1806
498 */
499 if ((ptr = _FP_stristr (line, "name")) != NULL) {
500 if (theheaders->fname == NULL && (thenew=ParseValue(ptr)) != NULL) {
501 theheaders->fname = _FP_strdup (thenew);
502 }
503 }
504 variable = NULL;
505 }
506 else {
507 /*
508 * nothing interesting
509 */
510 return theheaders;
511 }
512
513 /*
514 * okay, so extract the actual data
515 */
516 if (variable) {
517 length = 0;
518 ptr = uuscan_phtext;
519
520 while (isspace (*value))
521 value++;
522 while (*value && (delimit==0 || *value!=delimit) &&
523 *value != '\012' && *value != '\015' && length < 255) {
524 *ptr++ = *value++;
525 length++;
526 }
527 while (length && isspace(*(ptr-1))) {
528 ptr--; length--;
529 }
530 *ptr = '\0';
531
532 if ((*variable = _FP_strdup (uuscan_phtext)) == NULL)
533 return NULL;
534 }
535
536 return theheaders;
537 }
538
539 /*
540 * is this a header line we know about?
541 */
542
543 static int
IsKnownHeader(char * line)544 IsKnownHeader (char *line)
545 {
546 char **iter = knownmsgheaders;
547
548 while (iter && *iter) {
549 if (_FP_strnicmp (line, *iter, strlen (*iter)) == 0)
550 return 1;
551 iter++;
552 }
553
554 iter = knownmimeheaders;
555
556 while (iter && *iter) {
557 if (_FP_strnicmp (line, *iter, strlen (*iter)) == 0)
558 return 2;
559 iter++;
560 }
561
562 return 0;
563 }
564
565 /*
566 * Scan a header
567 */
568
569 int
UUScanHeader(FILE * datei,headers * envelope)570 UUScanHeader (FILE *datei, headers *envelope)
571 {
572 char *ptr;
573
574 while (!feof (datei)) {
575 if ((ptr = ScanHeaderLine (datei, NULL)) == NULL)
576 break;
577 if (*ptr == '\0' || *ptr == '\012' || *ptr == '\015')
578 break;
579 ParseHeader (envelope, ptr);
580 }
581 return 0;
582 }
583
584 /*
585 * Scan something for encoded data and fill the fileread* structure.
586 * If boundary is non-NULL, we stop after having read it. If Check-
587 * Headers != 0, we also stop after we've found uu_headercount recog-
588 * nized header lines.
589 * If we have a boundary, then we also don't accept Base64; MIME mails
590 * really should handle this properly.
591 * We return -1 if something went wrong, 0 if everything is normal,
592 * 1 if we found a new header and 2 if we found a boundary.
593 * In MIME message bodies (not multiparts), we also disable our reduced
594 * MIME handling.
595 */
596
597 static int
ScanData(FILE * datei,char * fname,int * errcode,char * boundary,int ismime,int checkheaders,fileread * result)598 ScanData (FILE *datei, char *fname, int *errcode,
599 char *boundary, int ismime, int checkheaders,
600 fileread *result)
601 {
602 char *line=uuscan_sdline, *bhds1=uuscan_sdbhds1, *bhds2=uuscan_sdbhds2;
603 static char *ptr, *p2, *p3=NULL, *bhdsp, bhl;
604 int islen[10], isb64[10], isuue[10], isxxe[10], isbhx[10], iscnt;
605 int cbb64, cbuue, cbxxe, cbbhx;
606 int bhflag=0, vflag, haddh=0, hadct=0;
607 int bhrpc=0, bhnf=0, c, hcount, lcount, blen=0;
608 int encoding=0, dflag=0, ctline=42;
609 int dontcare=0, hadnl=0;
610 long preheaders=0, oldposition;
611 long yefilesize=0, yepartends=0;
612 size_t dcc, bhopc;
613
614 *errcode = UURET_OK;
615 (void) UUDecodeLine (NULL, NULL, 0); /* init */
616 bhdsp = bhds2;
617
618 if (datei == NULL || feof (datei))
619 return -1;
620
621 result->startpos = ftell (datei);
622 hcount = lcount = 0;
623
624 for (iscnt=0; iscnt<10; iscnt++) {
625 isb64[iscnt] = isuue[iscnt] = isxxe[iscnt] = isbhx[iscnt] = 0;
626 islen[iscnt] = -1;
627 }
628
629 iscnt = 0;
630
631 if (boundary)
632 blen = strlen (boundary);
633
634 while (!feof (datei)) {
635 oldposition = ftell (datei);
636 if (_FP_fgets (line, 255, datei) == NULL)
637 break;
638 if (ferror (datei))
639 break;
640
641 line[255] = '\0'; /* For Safety of string functions */
642
643 /*
644 * Make Busy Polls
645 */
646
647 if (UUBUSYPOLL(ftell(datei),progress.fsize)) {
648 UUMessage (uuscan_id, __LINE__, UUMSG_NOTE,
649 uustring (S_SCAN_CANCEL));
650 *errcode = UURET_CANCEL;
651 break;
652 }
653
654 if (IsLineEmpty (line)) { /* line empty? */
655 hcount = 0;
656 hadnl = 1;
657 continue; /* then ignore */
658 }
659
660 if (checkheaders) {
661 if (IsKnownHeader (line)) {
662 (void) ScanHeaderLine (datei, line);
663
664 if (hcount == 0) {
665 preheaders = oldposition;
666 lcount = 0;
667 }
668 hcount++;
669 lcount++;
670
671 /*
672 * check for the various restart counters
673 */
674
675 if ((hcount >= hlcount.restart) ||
676 (hcount >= hlcount.afterdata && ismime == 0) ||
677 (hcount >= hlcount.afterdata && result->uudet) ||
678 (hcount >= hlcount.afternl && result->uudet && hadnl)) {
679 /*
680 * Hey, a new header starts here
681 */
682 fseek (datei, preheaders, SEEK_SET);
683 break;
684 }
685 /* continue; */
686 }
687 else if (lcount > WAITHEADER) {
688 hcount = 0;
689 lcount = 0;
690 dontcare=0;
691 }
692 else if (hcount) {
693 lcount++;
694 dontcare=1;
695 }
696 else {
697 dontcare=0;
698 }
699 }
700 else {
701 dontcare=0;
702 }
703
704 if (boundary != NULL &&
705 line[0] == '-' && line[1] == '-' &&
706 strncmp (line+2, boundary, blen) == 0) {
707 fseek (datei, oldposition, SEEK_SET);
708 break;
709 }
710 if (boundary != NULL && line[0] == 'C' && line[1] == 'o' &&
711 _FP_strnicmp (line, "Content-Type:", 13) == 0) {
712 ptr = ScanHeaderLine (datei, line);
713 p2 = (ptr)?_FP_stristr(ptr,"boundary"):NULL;
714 p3 = (p2)?ParseValue(p2):NULL;
715
716 if (p3 && strcmp (p3, boundary) == 0) {
717 fseek (datei, oldposition, SEEK_SET);
718 break;
719 }
720 else {
721 p3 = NULL;
722 }
723 }
724
725 if (strncmp (line, "begin ", 6) == 0 ||
726 _FP_strnicmp (line, "<pre>begin ", 11) == 0) {
727 if ((result->begin || result->end ||
728 result->uudet == B64ENCODED ||
729 result->uudet == BH_ENCODED) && !uu_more_mime) {
730 fseek (datei, oldposition, SEEK_SET);
731 break;
732 }
733
734 if (*line == '<')
735 ptr = line + 10;
736 else
737 ptr = line + 5;
738
739 while (*ptr == ' ') ptr++;
740 while (isdigit (*ptr))
741 result->mode = result->mode * 8 + *ptr++ - '0';
742 while (*ptr == ' ') ptr++;
743
744 /*
745 * We may have picked up a filename from a uuenview-style header
746 */
747 _FP_free (result->filename);
748 result->filename = _FP_strdup (ptr);
749 result->begin = 1;
750
751 while (isspace (result->filename[strlen(result->filename)-1]))
752 result->filename[strlen(result->filename)-1] = '\0';
753
754 continue;
755 }
756
757 if ((strncmp (line, "end", 3) == 0) &&
758 result->uudet != BH_ENCODED &&
759 result->uudet != YENC_ENCODED) {
760 if (result->uudet == B64ENCODED && result->begin)
761 result->uudet = XX_ENCODED;
762
763 if (result->uudet != B64ENCODED) {
764 result->end = 1;
765 if (dflag && encoding)
766 result->uudet = encoding;
767 continue;
768 }
769 }
770
771 hadnl = 0;
772
773 /*
774 * Detect a UUDeview-Style header
775 */
776
777 if (_FP_strnicmp (line, "_=_ Part ", 9) == 0 &&
778 result->uudet != YENC_ENCODED) {
779 if (result->uudet) {
780 fseek (datei, oldposition, SEEK_SET);
781 break;
782 }
783 result->partno = atoi (line + 8);
784 if ((ptr = _FP_stristr (line, "of file ")) != NULL) {
785 ptr += 8;
786 while (isspace (*ptr)) ptr++;
787 p2 = ptr;
788 while (isalnum(*p2) ||
789 *p2 == '.' || *p2=='_' || *p2 == '-' ||
790 *p2 == '!' || *p2=='@' || *p2 == '$')
791 p2++;
792 c = *p2; *p2 = '\0';
793 if (p2 != ptr && result->filename == NULL)
794 result->filename = _FP_strdup (ptr);
795 else if (p2 - ptr > 5 && strchr (ptr, '.') != NULL) {
796 /*
797 * This file name looks good, too. Let's use it
798 */
799 _FP_free (result->filename);
800 result->filename = _FP_strdup (ptr);
801 }
802 *p2 = c;
803 }
804 }
805
806 /*
807 * Some reduced MIME handling. Only use if boundary == NULL. Also
808 * accept the "X-Orcl-Content-Type" used by some braindead program.
809 */
810 if (boundary == NULL && !ismime && !uu_more_mime &&
811 result->uudet != YENC_ENCODED) {
812 if (_FP_strnicmp (line, "Content-Type", 12) == 0 ||
813 _FP_strnicmp (line, "X-Orcl-Content-Type", 19) == 0) {
814 /*
815 * We use Content-Type to mark a new attachment and split the file.
816 * However, we do not split if we haven't found anything encoded yet.
817 */
818 if (result->uudet) {
819 fseek (datei, oldposition, SEEK_SET);
820 break;
821 }
822 if ((ptr = strchr (line, ':')) != NULL) {
823 ptr++;
824 while (isspace (*ptr)) ptr++; p2 = ptr;
825 while (!isspace (*p2) && *p2 != ';') p2++;
826 c = *p2; *p2 = '\0';
827 if (p2 != ptr) {
828 _FP_free (result->mimetype);
829 result->mimetype = _FP_strdup (ptr);
830 }
831 *p2 = c;
832 }
833 ctline=0;
834 hadct=1;
835 }
836 if ((ptr = _FP_stristr (line, "number=")) && ctline<4) {
837 ptr += 7; if (*ptr == '"') ptr++;
838 result->partno = atoi (ptr);
839 }
840 if ((ptr = _FP_stristr (line, "total=")) && ctline<4) {
841 ptr += 6; if (*ptr == '"') ptr++;
842 result->maxpno = atoi (ptr);
843 }
844 if ((ptr = _FP_stristr (line, "name=")) && ctline<4) {
845 ptr += 5;
846 while (isspace (*ptr)) ptr++;
847 if (*ptr == '"' && *(ptr+1) && (p2 = strchr (ptr+2, '"')) != NULL) {
848 c = *p2; *p2 = '\0';
849 _FP_free (result->filename);
850 result->filename = _FP_strdup (ptr+1);
851 *p2 = c;
852 }
853 else if (*ptr=='\''&&*(ptr+1)&&(p2 = strchr(ptr+2, '\'')) != NULL) {
854 c = *p2; *p2 = '\0';
855 _FP_free (result->filename);
856 result->filename = _FP_strdup (ptr+1);
857 *p2 = c;
858 }
859 else {
860 p2 = ptr;
861 while (isalnum(*p2) ||
862 *p2 == '.' || *p2=='_' || *p2 == '-' ||
863 *p2 == '!' || *p2=='@' || *p2 == '$')
864 p2++;
865 c = *p2; *p2 = '\0';
866 if (p2 != ptr && result->filename == NULL)
867 result->filename = _FP_strdup (ptr);
868 else if (p2 - ptr > 5 && strchr (ptr, '.') != NULL) {
869 /*
870 * This file name looks good, too. Let's use it
871 */
872 _FP_free (result->filename);
873 result->filename = _FP_strdup (ptr);
874 }
875 *p2 = c;
876 }
877 }
878 if ((ptr = _FP_stristr (line, "id=")) && ctline<4) {
879 p2 = ptr += 3;
880 if (*p2 == '"') {
881 p2 = strchr (++ptr, '"');
882 }
883 else {
884 while (*p2 && isprint(*p2) && !isspace(*p2) && *p2 != ';')
885 p2++;
886 }
887 if (p2 && *p2 && p2!=ptr) {
888 c = *p2; *p2 = '\0';
889 if (result->mimeid)
890 _FP_free (result->mimeid);
891 result->mimeid = _FP_strdup (ptr);
892 *p2 = c;
893 }
894 }
895
896 /*
897 * Handling for very short Base64 files.
898 */
899 if (uu_tinyb64 && !ismime && !uu_more_mime) {
900 if (line[0] == '-' && line[1] == '-') {
901 if (dflag && (encoding==B64ENCODED || result->uudet==B64ENCODED)) {
902 if (encoding==B64ENCODED && result->uudet==0 && (haddh||hadct)) {
903 result->uudet = encoding;
904 encoding = dflag = 0;
905 }
906 haddh = 1;
907 continue;
908 }
909 hadct = 0;
910 }
911 }
912 } /* end of reduced MIME handling */
913
914 /*
915 * If we're in "freestyle" mode, have not encountered anything
916 * interesting yet, and stumble upon something that looks like
917 * a boundary, followed by a Content-* line, try to use it.
918 */
919
920 if (boundary == NULL && !ismime && !uu_more_mime && dflag <= 1 &&
921 line[0] == '-' && line[1] == '-' && strlen(line+2)>10 &&
922 (((ptr = _FP_strrstr (line+2, "--")) == NULL) ||
923 (*(ptr+2) != '\012' && *(ptr+2) != '\015')) &&
924 _FP_strstr (line+2, "_=_") != NULL) {
925 if (_FP_fgets (line, 255, datei) == NULL) {
926 break;
927 }
928 if (_FP_strnicmp (line, "Content-", 8) == 0) {
929 /*
930 * Okay, let's do it. This breaks out of ScanData. ScanPart will
931 * recognize the boundary on the next call and use it.
932 */
933 fseek (datei, oldposition, SEEK_SET);
934 break;
935 }
936 }
937
938 /*
939 * Detection for yEnc encoding
940 */
941
942 if (strncmp (line, "=ybegin ", 8) == 0 &&
943 _FP_strstr (line, " name=") != NULL) {
944 if ((result->begin || result->end || result->uudet) && !uu_more_mime) {
945 fseek (datei, oldposition, SEEK_SET);
946 break;
947 }
948
949 /*
950 * name continues to the end of the line
951 */
952
953 _FP_free (result->filename);
954 ptr = _FP_strstr (line, " name=") + 6;
955 result->filename = _FP_strdup (ptr);
956
957 while (isspace (result->filename[strlen(result->filename)-1]))
958 result->filename[strlen(result->filename)-1] = '\0';
959
960 /*
961 * Determine size
962 */
963
964 if ((ptr = _FP_strstr (line, " size=")) != NULL) {
965 ptr += 6;
966 yefilesize = atoi (ptr);
967 }
968 else {
969 yefilesize = -1;
970 }
971
972 /*
973 * check for multipart file and read =ypart line
974 */
975
976 if ((ptr = _FP_strstr (line, " part=")) != NULL) {
977 result->partno = atoi (ptr + 6);
978
979 if (result->partno == 1) {
980 result->begin = 1;
981 }
982
983 if (_FP_fgets (line, 255, datei) == NULL) {
984 break;
985 }
986
987 line[255] = '\0';
988
989 if (strncmp (line, "=ypart ", 7) != 0) {
990 break;
991 }
992
993 if ((ptr = _FP_strstr (line, " end=")) == NULL) {
994 break;
995 }
996
997 yepartends = atoi (ptr + 5);
998 }
999 else {
1000 result->partno = 1;
1001 result->begin = 1;
1002 }
1003
1004 /*
1005 * Don't want auto-detection
1006 */
1007
1008 result->uudet = YENC_ENCODED;
1009 continue;
1010 }
1011
1012 if (strncmp (line, "=yend ", 6) == 0 &&
1013 result->uudet == YENC_ENCODED) {
1014 if (yepartends == 0 || yepartends >= yefilesize) {
1015 result->end = 1;
1016 }
1017 #if 0
1018 if (!uu_more_mime)
1019 break;
1020 #endif
1021 continue;
1022 }
1023
1024 /*
1025 * if we haven't yet found anything encoded, try to find something
1026 */
1027
1028 if (!(result->uudet)) {
1029 /*
1030 * Netscape-Repair code is the same as in uunconc.c
1031 */
1032
1033 if ((vflag = UUValidData (line, 0, &bhflag)) == 0 && !ismime)
1034 vflag = UURepairData (datei, line, 0, &bhflag);
1035
1036 /*
1037 * Check data against all possible encodings
1038 */
1039
1040 islen[iscnt%10] = strlen(line);
1041 isb64[iscnt%10] = (UUValidData (line, B64ENCODED, &bhflag)==B64ENCODED);
1042 isuue[iscnt%10] = (UUValidData (line, UU_ENCODED, &bhflag)==UU_ENCODED);
1043 isxxe[iscnt%10] = (UUValidData (line, XX_ENCODED, &bhflag)==XX_ENCODED);
1044 isbhx[iscnt%10] = (UUValidData (line, BH_ENCODED, &bhflag)==BH_ENCODED);
1045
1046 /*
1047 * If we've got a first valid encoded line, we get suspicious if
1048 * it's shorter than, say, 40 characters.
1049 */
1050
1051 if (vflag == B64ENCODED &&
1052 (dflag == 0 || encoding != B64ENCODED) &&
1053 strlen (line) < 40 && !result->begin && !uu_tinyb64) {
1054 isb64[iscnt%10] = 0;
1055 vflag = 0;
1056 }
1057
1058 if ((vflag == UU_ENCODED || vflag == XX_ENCODED) &&
1059 (dflag == 0 || encoding != vflag) &&
1060 strlen (line) < 40 && !result->begin) {
1061 isuue[iscnt%10] = isxxe[iscnt%10] = 0;
1062 vflag = 0;
1063 }
1064
1065 iscnt++;
1066
1067 /*
1068 * Ah, so we got an encoded line? How interesting!
1069 */
1070
1071 if (vflag) {
1072 /*
1073 * For BinHex data, we can use the initial colon ':' as begin
1074 * and the terminating colon as ':'.
1075 * If (vflag && !bhflag), this is the last line,
1076 */
1077 if (vflag == BH_ENCODED) {
1078 if (line[0] == ':' && result->end) {
1079 fseek (datei, oldposition, SEEK_SET);
1080 break;
1081 }
1082 if (line[0] == ':')
1083 result->begin = 1;
1084 if (bhflag == 0) {
1085 result->uudet = BH_ENCODED;
1086 result->end = 1;
1087 }
1088 }
1089 /*
1090 * For BinHex files, the file name is encoded in the first encoded
1091 * data bytes. We try to extract it here
1092 */
1093 if (vflag == BH_ENCODED && bhnf == 0 && result->filename == NULL) {
1094 if (bhdsp == bhds2 ||
1095 ((bhdsp-bhds2) <= (int) bhds2[0] &&
1096 (bhdsp-bhds2) < 256)) {
1097 dcc = UUDecodeLine (line, bhds1, BH_ENCODED);
1098 UUbhdecomp (bhds1, bhdsp, &bhl, &bhrpc,
1099 dcc, 256-(bhdsp-bhds2), &bhopc);
1100 bhdsp += bhopc;
1101 }
1102 if ((bhdsp-bhds2) > (int) bhds2[0] && bhds2[0]>0 &&
1103 result->filename==NULL) {
1104 memcpy (bhds1, bhds2+1, (int) bhds2[0]);
1105 bhds1[(int)bhds2[0]]='\0';
1106 result->filename = _FP_strdup (bhds1);
1107 bhnf = 1;
1108 }
1109 else if (bhdsp-bhds2 >= 256 && bhds2[0]>0) {
1110 memcpy (bhds1, bhds2+1, 255);
1111 bhds1[255] = '\0';
1112 result->filename = _FP_strdup (bhds1);
1113 bhnf = 1;
1114 }
1115 else if (bhds2[0] <= 0)
1116 bhnf = 1;
1117 }
1118
1119 /*
1120 * We accept an encoding if it has been true for four consecutive
1121 * lines. Check the is<enc> arrays to avoid mistaking one encoding
1122 * for the other. Uuencoded data is rather easily mistaken for
1123 * Base 64. If the data matches more than one encoding, we need to
1124 * scan further.
1125 *
1126 * Since text can also rather easily be mistaken for UUencoded
1127 * data if it just happens to have 4 lines in a row that have the
1128 * correct first character for the length of the line, we also add
1129 * a check that the first 3 lines must be the same length, and the
1130 * 4th line must be less than or equal to that length. (since
1131 * uuencoders use the same length for all lines except the last,
1132 * this shouldn't increase the minimum size of UUdata we can
1133 * detect, as it would if we tested all 4 lines for being the same
1134 * length.) - Matthew Mueller, 20030109
1135 */
1136
1137 if (iscnt > 3) {
1138 cbb64 = (isb64[(iscnt-1)%10] && isb64[(iscnt-2)%10] &&
1139 isb64[(iscnt-3)%10] && isb64[(iscnt-4)%10]);
1140 cbuue = (isuue[(iscnt-1)%10] && isuue[(iscnt-2)%10] &&
1141 isuue[(iscnt-3)%10] && isuue[(iscnt-4)%10] &&
1142 islen[(iscnt-1)%10] <= islen[(iscnt-2)%10] &&
1143 islen[(iscnt-2)%10] == islen[(iscnt-3)%10] &&
1144 islen[(iscnt-3)%10] == islen[(iscnt-4)%10]);
1145 cbxxe = (isxxe[(iscnt-1)%10] && isxxe[(iscnt-2)%10] &&
1146 isxxe[(iscnt-3)%10] && isxxe[(iscnt-4)%10] &&
1147 islen[(iscnt-1)%10] <= islen[(iscnt-2)%10] &&
1148 islen[(iscnt-2)%10] == islen[(iscnt-3)%10] &&
1149 islen[(iscnt-3)%10] == islen[(iscnt-4)%10]);
1150 cbbhx = (isbhx[(iscnt-1)%10] && isbhx[(iscnt-2)%10] &&
1151 isbhx[(iscnt-3)%10] && isbhx[(iscnt-4)%10]);
1152 }
1153 else {
1154 cbb64 = cbuue = cbxxe = cbbhx = 0;
1155 }
1156
1157 if (cbb64 && !cbuue && !cbxxe && !cbbhx) {
1158 result->uudet = B64ENCODED;
1159 }
1160 else if (!cbb64 && cbuue && !cbxxe && !cbbhx) {
1161 result->uudet = UU_ENCODED;
1162 }
1163 else if (!cbb64 && !cbuue && cbxxe && !cbbhx) {
1164 result->uudet = XX_ENCODED;
1165 }
1166 else if (!cbb64 && !cbuue && !cbxxe && cbbhx) {
1167 result->uudet = BH_ENCODED;
1168 }
1169
1170 if (result->uudet) {
1171 encoding = dflag = 0;
1172
1173 /*
1174 * If we're in fast mode, we're told we don't have to look
1175 * for anything below, so we can as well break out of every-
1176 * thing
1177 * We cannot fast-scan if we have a boundary to look after.
1178 */
1179
1180 if (uu_fast_scanning && boundary == NULL)
1181 break;
1182
1183 /*
1184 * Skip the encoded data. We simply wait for a boundary, for
1185 * a header or for an empty line. But we also try to pick up
1186 * an "end"
1187 */
1188
1189 hcount = lcount = 0;
1190
1191 while (!feof (datei)) {
1192 /*
1193 * Make Busy Polls
1194 */
1195 if (UUBUSYPOLL(ftell(datei),progress.fsize)) {
1196 UUMessage (uuscan_id, __LINE__, UUMSG_NOTE,
1197 uustring (S_SCAN_CANCEL));
1198 *errcode = UURET_CANCEL;
1199 break;
1200 }
1201
1202 oldposition = ftell (datei);
1203 if (_FP_fgets (line, 255, datei) == NULL)
1204 break;
1205 if (ferror (datei))
1206 break;
1207
1208 line[255] = '\0';
1209
1210 /*
1211 * Stop scanning at an empty line or a MIME-boundary.
1212 */
1213 if (IsLineEmpty (line))
1214 break;
1215 if (boundary && line[0] == '-' && line[1] == '-' &&
1216 strncmp (line+2, boundary, blen) == 0) {
1217 fseek (datei, oldposition, SEEK_SET);
1218 break;
1219 }
1220 else if (line[0] == 'e' && (result->uudet == UU_ENCODED ||
1221 result->uudet == XX_ENCODED)) {
1222 if (strncmp (line, "end", 3) == 0) {
1223 result->end = 1;
1224 break;
1225 }
1226 }
1227 else if (line[0] == 'b') {
1228 if (strncmp (line, "begin ", 6) == 0) {
1229 fseek (datei, oldposition, SEEK_SET);
1230 break;
1231 }
1232 }
1233
1234 if (checkheaders) {
1235 if (IsKnownHeader (line)) {
1236 (void) ScanHeaderLine (datei, line);
1237 if (hcount == 0)
1238 preheaders = oldposition;
1239 hcount++;
1240 lcount++;
1241 if ((hcount >= hlcount.restart) ||
1242 (hcount >= hlcount.afterdata && result->uudet)) {
1243 /*
1244 * Hey, a new header starts here
1245 */
1246 fseek (datei, preheaders, SEEK_SET);
1247 break;
1248 }
1249 }
1250 else if (lcount > WAITHEADER) {
1251 hcount = 0;
1252 lcount = 0;
1253 }
1254 else if (hcount) {
1255 lcount++;
1256 }
1257 }
1258 if (result->uudet == BH_ENCODED) {
1259 /* pick up ``EOF'' for BinHex files. Slow :-< */
1260 if (line[0] && strchr (line+1, ':') != NULL) {
1261 result->end = 1;
1262 bhflag = 0;
1263 break;
1264 }
1265 }
1266 }
1267
1268 if (ferror (datei) || *errcode == UURET_CANCEL)
1269 break;
1270
1271 if (line[0] == '-' && line[1] == '-')
1272 haddh = 1;
1273
1274 /*
1275 * Okay, got everything we need. If we had headers or a
1276 * boundary, we break out of the outer loop immediately.
1277 */
1278
1279 if (IsKnownHeader (line) ||
1280 (boundary && line[0] == '-' && line[1] == '-' &&
1281 strncmp (line+2, boundary, blen) == 0)) {
1282 break;
1283 }
1284
1285 /*
1286 * Otherwise, we wait until finding something more interesting
1287 * in the outer loop
1288 */
1289
1290 continue;
1291 }
1292
1293 /*
1294 * Select the encoding with the best "history"
1295 */
1296
1297 cbb64 = isb64[(iscnt-1)%10];
1298 cbuue = isuue[(iscnt-1)%10];
1299 cbxxe = isxxe[(iscnt-1)%10];
1300 cbbhx = isbhx[(iscnt-1)%10];
1301 dflag = 0;
1302
1303 if (cbb64 || cbuue || cbxxe || cbbhx) {
1304 for (dflag=2; dflag<iscnt && dflag<4; dflag++) {
1305 if ((!cbb64 || !isb64[(iscnt-dflag)%10]) &&
1306 (!cbuue || !isuue[(iscnt-dflag)%10]) &&
1307 (!cbxxe || !isxxe[(iscnt-dflag)%10]) &&
1308 (!cbbhx || !isbhx[(iscnt-dflag)%10])) {
1309 dflag--;
1310 break;
1311 }
1312 cbb64 &= isb64[(iscnt-dflag)%10];
1313 cbuue &= isuue[(iscnt-dflag)%10];
1314 cbxxe &= isxxe[(iscnt-dflag)%10];
1315 cbbhx &= isbhx[(iscnt-dflag)%10];
1316 }
1317 }
1318
1319 /*
1320 * clear-cut cases
1321 */
1322
1323 if (cbb64 && !cbuue && !cbxxe && !cbbhx) {
1324 encoding = B64ENCODED;
1325 }
1326 else if (!cbb64 && cbuue && !cbxxe && !cbbhx) {
1327 encoding = UU_ENCODED;
1328 }
1329 else if (!cbb64 && !cbuue && cbxxe && !cbbhx) {
1330 encoding = XX_ENCODED;
1331 }
1332 else if (!cbb64 && !cbuue && !cbxxe && cbbhx) {
1333 encoding = BH_ENCODED;
1334 }
1335 else {
1336 encoding = 0;
1337 }
1338
1339 /*
1340 * Check a few common non-clear-cut cases
1341 */
1342
1343 if (!encoding && cbuue && result->begin) {
1344 encoding = UU_ENCODED;
1345 }
1346 else if (!encoding && cbxxe && result->begin) {
1347 encoding = XX_ENCODED;
1348 }
1349 else if (!encoding && cbb64) {
1350 encoding = B64ENCODED;
1351 }
1352 else if (!encoding && cbuue) {
1353 encoding = UU_ENCODED;
1354 }
1355 else if (!encoding && cbxxe) {
1356 encoding = XX_ENCODED;
1357 }
1358 else if (!encoding && cbbhx) {
1359 encoding = BH_ENCODED;
1360 }
1361 }
1362 else if (!dontcare) {
1363 encoding = 0;
1364 dflag = 0;
1365 haddh = 0;
1366 }
1367 } /* if (!uudet) */
1368 /*
1369 * End of scanning loop
1370 */
1371 } /* while (!feof (datei)) */
1372
1373 if (feof (datei))
1374 oldposition = ftell (datei);
1375
1376 if (dflag && encoding == B64ENCODED && haddh)
1377 result->uudet = B64ENCODED;
1378 else if (dflag && encoding == BH_ENCODED)
1379 result->uudet = BH_ENCODED;
1380
1381 /* Base64 doesn't have begin or end, so it was probably XX */
1382 if (result->uudet == B64ENCODED && result->begin && result->end)
1383 result->uudet = XX_ENCODED;
1384
1385 /* Base64 doesn't have begin or end */
1386 if (result->uudet == B64ENCODED)
1387 result->begin = result->end = 0;
1388
1389 /* Base64 and BinHex don't have a file mode */
1390 if (result->uudet == B64ENCODED || result->uudet == BH_ENCODED ||
1391 result->uudet == YENC_ENCODED)
1392 result->mode = 6*64+4*8+4;
1393
1394 /*
1395 * When strict MIME adherance is set, throw out suspicious attachments
1396 */
1397
1398 if (uu_more_mime) {
1399 /*
1400 * In a MIME message, Base64 should be appropriately tagged
1401 */
1402
1403 if (result->uudet == B64ENCODED) {
1404 result->uudet = 0;
1405 }
1406
1407 /*
1408 * Do not accept incomplete UU or XX encoded messages
1409 */
1410
1411 if ((result->uudet != 0 && result->uudet != B64ENCODED) &&
1412 (!result->begin || !result->end)) {
1413 result->uudet = 0;
1414 }
1415 }
1416
1417 /*
1418 * In fast mode, this length will yield a false value. We don't care.
1419 * This must be checked for in uunconc(), where we usually stop decoding
1420 * after reaching startpos+length
1421 */
1422
1423 if (uu_fast_scanning)
1424 result->length = progress.fsize-result->startpos;
1425 else
1426 result->length = ftell(datei)-result->startpos;
1427
1428 if (ferror (datei)) {
1429 *errcode = UURET_IOERR;
1430 uu_errno = errno;
1431 return -1;
1432 }
1433 if (*errcode != UURET_OK) {
1434 return -1;
1435 }
1436
1437 if (boundary && line[0] == '-' && line[1] == '-' &&
1438 strncmp (line+2, boundary, blen) == 0)
1439 return 2;
1440 else if (boundary && p3 &&
1441 line[0] == 'C' && line[1] == 'o' &&
1442 _FP_strnicmp (line, "Content-Type:", 13) == 0 &&
1443 strcmp (p3, boundary) == 0)
1444 return 2;
1445 else if (IsKnownHeader (line))
1446 return 1;
1447
1448 return 0;
1449 }
1450
1451 /*
1452 * This is the main scanning function.
1453 */
1454
1455 fileread *
ScanPart(FILE * datei,char * fname,int * errcode)1456 ScanPart (FILE *datei, char *fname, int *errcode)
1457 {
1458 int ecount, hcount, lcount;
1459 int bhflag, begflag, vflag, blen=0, res;
1460 long preheaders, prevpos=0, preenc, before;
1461 char *line=uuscan_spline;
1462 fileread *result;
1463 char *ptr1, *ptr2;
1464
1465 (void) UUDecodeLine (NULL, NULL, 0); /* init */
1466 if (datei == NULL || feof (datei)) {
1467 *errcode = UURET_OK;
1468 return NULL;
1469 }
1470
1471 *errcode = UURET_OK;
1472
1473 if ((result = (fileread *) malloc (sizeof (fileread))) == NULL) {
1474 *errcode = UURET_NOMEM;
1475 return NULL;
1476 }
1477 memset (result, 0, sizeof (fileread));
1478 result->startpos = ftell (datei);
1479 preheaders = result->startpos;
1480 before = result->startpos;
1481
1482 /* if this is a new file, reset our scanning state */
1483 if (sstate.source == NULL || strcmp (fname, sstate.source) != 0) {
1484 sstate.isfolder = 1; /* assume yes */
1485 sstate.ismime = 0; /* wait for MIME-Version */
1486 sstate.mimestate = MS_HEADERS; /* assume headers follow */
1487 /* mimseqno = 1; */
1488
1489 while (mssdepth) {
1490 mssdepth--;
1491 UUkillheaders (&(multistack[mssdepth].envelope));
1492 _FP_free (multistack[mssdepth].source);
1493 }
1494
1495 UUkillheaders (&sstate.envelope);
1496 memset (&sstate.envelope, 0, sizeof (headers));
1497
1498 _FP_free (sstate.source);
1499 if ((sstate.source = _FP_strdup (fname)) == NULL) {
1500 *errcode = UURET_NOMEM;
1501 _FP_free (result);
1502 return NULL;
1503 }
1504
1505 /* ignore empty lines at the beginning of a file */
1506 preheaders = ftell (datei);
1507 while (!feof (datei)) {
1508 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
1509 if (_FP_fgets (line, 255, datei) == NULL)
1510 break;
1511 line[255] = '\0';
1512 if (!IsLineEmpty (line)) {
1513 fseek (datei, preheaders, SEEK_SET);
1514 break;
1515 }
1516 preheaders = ftell (datei);
1517 }
1518 }
1519
1520 if (ferror(datei) || feof(datei)) {
1521 _FP_free (result);
1522 return NULL;
1523 }
1524
1525 /*
1526 * If we are confident that this is a mail folder and are at the
1527 * beginning of the file, expecting to read some headers, scan
1528 * the envelope.
1529 */
1530
1531 if (sstate.isfolder && sstate.mimestate == MS_HEADERS) {
1532 hcount = 0;
1533 lcount = 0;
1534 UUkillheaders (&sstate.envelope);
1535
1536 /*
1537 * clean up leftovers from invalid messages
1538 */
1539
1540 while (mssdepth) {
1541 mssdepth--;
1542 UUkillheaders (&(multistack[mssdepth].envelope));
1543 _FP_free (multistack[mssdepth].source);
1544 }
1545
1546 prevpos = ftell (datei);
1547 if (_FP_fgets (line, 255, datei) == NULL) {
1548 _FP_free (result);
1549 return NULL;
1550 }
1551 line[255] = '\0';
1552
1553 /*
1554 * Special handling for AOL folder files, which start off with a boundary.
1555 * We recognize them by a valid boundary line as the first line of a file.
1556 * Note that the rest of the scanning code becomes suspicious if a boun-
1557 * dary does never appear in a file -- this should save us from grave
1558 * false detection errors
1559 */
1560
1561 if (!feof (datei) && line[0] == '-' && line[1] == '-' && line[2]) {
1562 while (line[strlen(line)-1] == '\012' ||
1563 line[strlen(line)-1] == '\015') {
1564 line[strlen(line)-1] = '\0';
1565 }
1566
1567 sstate.ismime = 1;
1568 sstate.envelope.mimevers = _FP_strdup ("1.0");
1569 sstate.envelope.boundary = _FP_strdup (line+2);
1570 sstate.envelope.ctype = _FP_strdup ("multipart/mixed");
1571 sstate.mimestate = MS_SUBPART;
1572
1573 *errcode = UURET_CONT;
1574 _FP_free (result);
1575 return NULL;
1576 }
1577
1578 /*
1579 * Normal behavior: look for a RFC 822 header
1580 */
1581
1582 while (!feof (datei) && !IsLineEmpty (line)) {
1583 if (IsKnownHeader (line))
1584 hcount++;
1585 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
1586 if (IsHeaderLine (line)) {
1587 ptr1 = ScanHeaderLine (datei, line);
1588 if (ParseHeader (&sstate.envelope, ptr1) == NULL) {
1589 *errcode = UURET_NOMEM;
1590 _FP_free (result);
1591 return NULL;
1592 }
1593 }
1594 /*
1595 * if we've read too many lines without finding headers, then
1596 * this probably isn't a mail folder after all
1597 */
1598 lcount++;
1599 if (lcount > WAITHEADER && hcount < hlcount.afternl) {
1600 fseek (datei, prevpos, SEEK_SET);
1601 line[0] = '\0';
1602 break;
1603 }
1604
1605 if (_FP_fgets (line, 255, datei) == NULL)
1606 break;
1607 line[255] = '\0';
1608 }
1609
1610 /* skip empty lines */
1611 prevpos = ftell (datei);
1612 if (IsLineEmpty (line)) {
1613 while (!feof (datei)) {
1614 if (_FP_fgets (line, 255, datei) == NULL)
1615 break;
1616 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
1617 if (!IsLineEmpty (line)) {
1618 fseek (datei, prevpos, SEEK_SET);
1619 line[255] = '\0';
1620 break;
1621 }
1622 prevpos = ftell (datei);
1623 }
1624 }
1625
1626 /*
1627 * If we don't have all valid MIME headers yet, but the following
1628 * line is a MIME header, accept it anyway.
1629 */
1630
1631 if (!uu_more_mime &&
1632 sstate.envelope.mimevers == NULL &&
1633 sstate.envelope.ctype == NULL &&
1634 sstate.envelope.ctenc == NULL &&
1635 IsKnownHeader (line)) {
1636 /*
1637 * see above
1638 */
1639 if (_FP_fgets (line, 255, datei) == NULL) {
1640 line[0] = '\012';
1641 line[1] = '\0';
1642 }
1643 line[255] = '\0';
1644
1645 while (!feof (datei) && !IsLineEmpty (line)) {
1646 if (IsKnownHeader (line))
1647 hcount++;
1648 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
1649 ptr1 = ScanHeaderLine (datei, line);
1650 if (ParseHeader (&sstate.envelope, ptr1) == NULL) {
1651 *errcode = UURET_NOMEM;
1652 _FP_free (result);
1653 return NULL;
1654 }
1655 if (_FP_fgets (line, 255, datei) == NULL)
1656 break;
1657 line[255] = '\0';
1658 }
1659 /* skip empty lines */
1660 prevpos = ftell (datei);
1661 while (!feof (datei)) {
1662 if (_FP_fgets (line, 255, datei) == NULL)
1663 break;
1664 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
1665 if (!IsLineEmpty (line)) {
1666 fseek (datei, prevpos, SEEK_SET);
1667 line[255] = '\0';
1668 break;
1669 }
1670 prevpos = ftell (datei);
1671 }
1672 }
1673
1674 /*
1675 * A partial multipart message probably has only a Content-Type
1676 * header but nothing else. In this case, at least simulate a
1677 * MIME message
1678 * if mimevers is not set but there are other well-known MIME
1679 * headers, don't be too picky about it.
1680 */
1681 if (sstate.envelope.ctype && sstate.envelope.mimevers==NULL &&
1682 _FP_stristr (sstate.envelope.ctype, "multipart") != NULL &&
1683 sstate.envelope.boundary != NULL) {
1684 sstate.envelope.mimevers = _FP_strdup ("1.0");
1685 hcount = hlcount.afternl;
1686 }
1687 else if (sstate.envelope.mimevers==NULL && sstate.envelope.ctype &&
1688 sstate.envelope.fname && sstate.envelope.ctenc) {
1689 sstate.envelope.mimevers = _FP_strdup ("1.0");
1690 hcount = hlcount.afternl;
1691 }
1692
1693 if (sstate.envelope.mimevers != NULL) {
1694 /* this is a MIME file. check the Content-Type */
1695 sstate.ismime = 1;
1696 if (_FP_stristr (sstate.envelope.ctype, "multipart") != NULL) {
1697 if (sstate.envelope.boundary == NULL) {
1698 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
1699 uustring (S_MIME_NO_BOUNDARY));
1700 sstate.mimestate = MS_BODY;
1701 _FP_free (sstate.envelope.ctype);
1702 sstate.envelope.ctype = _FP_strdup ("text/plain");
1703 }
1704 else {
1705 sstate.mimestate = MS_PREAMBLE;
1706 }
1707 }
1708 else {
1709 sstate.mimestate = MS_BODY; /* just a `simple' message */
1710 }
1711 }
1712 else {
1713 /* not a folder after all */
1714 fseek (datei, prevpos, SEEK_SET);
1715 sstate.isfolder = 0;
1716 sstate.ismime = 0;
1717 }
1718 }
1719
1720 if (feof (datei) || ferror (datei)) { /* oops */
1721 _FP_free (result);
1722 return NULL;
1723 }
1724
1725 /*
1726 * Handle MIME stuff
1727 */
1728
1729 /*
1730 * Read Preamble. This must be ended by a sstate.envelope.boundary line.
1731 * If uu_usepreamble is set, we produce a result from this one
1732 */
1733
1734 if (sstate.ismime && sstate.mimestate == MS_PREAMBLE) {
1735 result->startpos = ftell (datei); /* remember start of preamble */
1736 prevpos = ftell (datei);
1737 preheaders = ftell (datei);
1738
1739 blen = strlen (sstate.envelope.boundary);
1740 lcount = 0;
1741
1742 while (!feof (datei)) {
1743 if (_FP_fgets (line, 255, datei) == NULL) {
1744 line[0] = '\0';
1745 break;
1746 }
1747 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
1748 if (line[0] == '-' && line[1] == '-' &&
1749 strncmp (line+2, sstate.envelope.boundary, blen) == 0)
1750 break;
1751 if (!IsLineEmpty (line))
1752 lcount++;
1753
1754 prevpos = ftell (datei);
1755 }
1756 if (feof (datei) || ferror (datei)) {
1757 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
1758 uustring (S_MIME_B_NOT_FOUND));
1759 /*
1760 * restart and try again; don't restart if uu_fast_scanning
1761 */
1762 sstate.isfolder = 0;
1763 sstate.ismime = 0;
1764 sstate.mimestate = MS_BODY;
1765
1766 if (!uu_fast_scanning) {
1767 *errcode = UURET_CONT;
1768 fseek (datei, preheaders, SEEK_SET);
1769 }
1770 _FP_free (result);
1771 return NULL;
1772 }
1773 if (line[0] == '-' && line[1] == '-' &&
1774 strncmp (line+2, sstate.envelope.boundary, blen) == 0) {
1775 ptr1 = line + 2 + blen;
1776 if (*ptr1 == '-' && *(ptr1+1) == '-') {
1777 /* Empty Multipart Message. Duh. */
1778 sstate.mimestate = MS_EPILOGUE;
1779 }
1780 else {
1781 sstate.mimestate = MS_SUBPART;
1782 }
1783 }
1784 else { /* shouldn't happen */
1785 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
1786 uustring (S_MIME_B_NOT_FOUND));
1787 /*
1788 * restart and try again; don't restart if uu_fast_scanning
1789 */
1790 sstate.isfolder = 0;
1791 sstate.ismime = 0;
1792 sstate.mimestate = MS_BODY;
1793
1794 if (!uu_fast_scanning) {
1795 *errcode = UURET_CONT;
1796 fseek (datei, preheaders, SEEK_SET);
1797 }
1798 _FP_free (result);
1799 return NULL;
1800 }
1801 /* produce result if uu_usepreamble is set */
1802 if (uu_usepreamble && lcount) {
1803 sprintf (line, "%04d.txt", ++mimseqno);
1804 result->subject = _FP_strdup (sstate.envelope.subject);
1805 result->filename = _FP_strdup (line);
1806 result->origin = _FP_strdup (sstate.envelope.from);
1807 result->mimeid = _FP_strdup (sstate.envelope.mimeid);
1808 result->mimetype = _FP_strdup ("text/plain");
1809 result->mode = 0644;
1810 result->uudet = PT_ENCODED; /* plain text */
1811 result->sfname = _FP_strdup (fname);
1812 result->flags = FL_SINGLE | FL_PROPER;
1813 /* result->startpos set from above */
1814 result->length = prevpos - result->startpos;
1815 result->partno = 1;
1816
1817 /* MIME message, let's continue */
1818 *errcode = UURET_CONT;
1819
1820 if ((sstate.envelope.subject != NULL && result->subject == NULL) ||
1821 result->filename == NULL || result->sfname == NULL) {
1822 *errcode = UURET_NOMEM;
1823 }
1824
1825 return result;
1826 }
1827 /* MIME message, let's continue */
1828 if (*errcode == UURET_OK)
1829 *errcode = UURET_CONT;
1830
1831 /* otherwise, just return NULL */
1832 _FP_free (result);
1833 return NULL;
1834 }
1835
1836 /*
1837 * Read Epilogue, the plain text after the last boundary.
1838 * This can either end with new headers from the next message of a
1839 * mail folder or with a `parent' boundary if we are inside an
1840 * encapsulated Multipart message. Oh yes, and of course the file
1841 * may also simply end :-)
1842 * Another possibility is that we might find plain encoded data
1843 * without encapsulating message. We're not _too_ flexible here,
1844 * we won't detect Base64, and require a proper `begin' line for
1845 * uuencoding and xxencoding
1846 * If uu_usepreamble is set, we produce a result from this one
1847 */
1848
1849 if (sstate.ismime && sstate.mimestate == MS_EPILOGUE) {
1850 result->startpos = ftell (datei); /* remember start of epilogue */
1851 prevpos = ftell (datei);
1852 preheaders = ftell (datei);
1853 preenc = ftell (datei);
1854 hcount = lcount = 0;
1855 ecount = bhflag = 0;
1856 begflag = vflag = 0;
1857 res = 0;
1858
1859 /*
1860 * If we are in the outermost message and uu_fast_scanning, we
1861 * know (or assume) that no more messages will follow, so there's
1862 * no need to scan the rest.
1863 */
1864 if (uu_fast_scanning && mssdepth == 0) {
1865 /*
1866 * check if the epilogue is empty
1867 */
1868 while (!feof (datei) && !ferror (datei) && lcount<10 && res==0) {
1869 if (_FP_fgets (line, 255, datei) == NULL)
1870 break;
1871 if (!IsLineEmpty (line))
1872 res++;
1873 lcount++;
1874 }
1875 if (uu_usepreamble && res) {
1876 sprintf (line, "%04d.txt", ++mimseqno);
1877 result->subject = _FP_strdup (sstate.envelope.subject);
1878 result->filename = _FP_strdup (line);
1879 result->origin = _FP_strdup (sstate.envelope.from);
1880 result->mimeid = _FP_strdup (sstate.envelope.mimeid);
1881 result->mimetype = _FP_strdup ("text/plain");
1882 result->mode = 0644;
1883 result->uudet = PT_ENCODED; /* plain text */
1884 result->sfname = _FP_strdup (fname);
1885 result->flags = FL_SINGLE | FL_PROPER | FL_TOEND;
1886 result->partno = 1;
1887 /* result->startpos set from above */
1888 result->length = progress.fsize - result->startpos;
1889
1890 if ((sstate.envelope.subject != NULL && result->subject == NULL) ||
1891 result->filename == NULL || result->sfname == NULL) {
1892 *errcode = UURET_NOMEM;
1893 }
1894
1895 return result;
1896 }
1897 _FP_free (result);
1898 return NULL;
1899 }
1900
1901 if (mssdepth > 0)
1902 blen = strlen (multistack[mssdepth-1].envelope.boundary);
1903
1904 while (!feof (datei)) {
1905 if (_FP_fgets (line, 255, datei) == NULL) {
1906 line[0] = '\0';
1907 break;
1908 }
1909 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
1910 line[255] = '\0';
1911 /* check for parent boundary */
1912 if (mssdepth > 0 && line[0] == '-' && line[1] == '-' &&
1913 strncmp (line+2,
1914 multistack[mssdepth-1].envelope.boundary, blen) == 0)
1915 break;
1916
1917 /* check for next headers only at outermost level */
1918 if (mssdepth == 0 && IsKnownHeader (line)) {
1919 (void) ScanHeaderLine (datei, line);
1920 if (hcount == 0) {
1921 preheaders = prevpos;
1922 lcount = 0;
1923 }
1924 hcount++;
1925 lcount++;
1926
1927 if (hcount >= hlcount.restart) {
1928 /* okay, new headers */
1929 break;
1930 }
1931 }
1932 else if (lcount > WAITHEADER) {
1933 hcount = 0;
1934 lcount = 0;
1935 }
1936 else if (hcount) {
1937 lcount++;
1938 }
1939 else {
1940 hcount = lcount = 0;
1941 }
1942
1943 /* check for begin and encoded data only at outermost level */
1944 if (mssdepth == 0 && !uu_more_mime) {
1945 if (strncmp (line, "begin ", 6) == 0 ||
1946 _FP_strnicmp (line, "<pre>begin ", 11) == 0) {
1947 preenc = prevpos;
1948 begflag = 1;
1949 }
1950 else if (strncmp (line, "end", 3) == 0 && begflag) {
1951 ecount = ELC_COUNT;
1952 break;
1953 }
1954 else if ((vflag = UUValidData (line, 0, &bhflag)) != 0) {
1955 if (vflag == BH_ENCODED && bhflag == 0) {
1956 /* very short BinHex file follows */
1957 preenc = prevpos;
1958 break;
1959 }
1960 /* remember that XX can easily be mistaken as Base64 */
1961 if ((vflag == UU_ENCODED || vflag == XX_ENCODED ||
1962 vflag == B64ENCODED) && begflag) {
1963 if (++ecount >= ELC_COUNT)
1964 break;
1965 }
1966 else {
1967 begflag = 0;
1968 ecount = 0;
1969 }
1970 }
1971 else {
1972 begflag = 0;
1973 ecount = 0;
1974 }
1975 }
1976
1977 if (!IsLineEmpty (line))
1978 res++;
1979
1980 prevpos = ftell (datei);
1981 }
1982
1983 if (mssdepth > 0 && line[0] == '-' && line[1] == '-' &&
1984 strncmp (line+2,
1985 multistack[mssdepth-1].envelope.boundary, blen) == 0) {
1986 /* restore previous state */
1987 mssdepth--;
1988 UUkillheaders (&sstate.envelope);
1989 _FP_free (sstate.source);
1990 memcpy (&sstate, &(multistack[mssdepth]), sizeof (scanstate));
1991
1992 ptr1 = line + 2 + strlen (sstate.envelope.boundary);
1993
1994 if (*ptr1 == '-' && *(ptr1+1) == '-') {
1995 sstate.mimestate = MS_EPILOGUE;
1996 }
1997 else {
1998 sstate.mimestate = MS_SUBPART;
1999 }
2000 result->length = prevpos - result->startpos;
2001 *errcode = UURET_CONT;
2002 }
2003 else if (mssdepth > 0) {
2004 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
2005 uustring (S_MIME_B_NOT_FOUND));
2006 /*
2007 * restart and try again; don't restart if uu_fast_scanning
2008 */
2009 sstate.isfolder = 0;
2010 sstate.ismime = 0;
2011 sstate.mimestate = MS_BODY;
2012
2013 while (mssdepth) {
2014 mssdepth--;
2015 UUkillheaders (&(multistack[mssdepth].envelope));
2016 _FP_free (multistack[mssdepth].source);
2017 }
2018
2019 if (!uu_fast_scanning) {
2020 *errcode = UURET_CONT;
2021 fseek (datei, preheaders, SEEK_SET);
2022 }
2023 _FP_free (result);
2024 return NULL;
2025 }
2026 else if (IsKnownHeader (line)) {
2027 /* new message follows */
2028 sstate.isfolder = 1;
2029 sstate.ismime = 0;
2030 sstate.mimestate = MS_HEADERS;
2031 result->length = preheaders - result->startpos;
2032 fseek (datei, preheaders, SEEK_SET);
2033 }
2034 else if (ecount >= ELC_COUNT) {
2035 /* new plain encoding */
2036 sstate.isfolder = 0;
2037 sstate.ismime = 0;
2038 sstate.mimestate = MS_BODY;
2039 result->length = preenc - result->startpos;
2040 fseek (datei, preenc, SEEK_SET);
2041 }
2042
2043 /* produce result if uu_usepreamble is set */
2044 if (uu_usepreamble && res) {
2045 sprintf (line, "%04d.txt", ++mimseqno);
2046 result->subject = _FP_strdup (sstate.envelope.subject);
2047 result->filename = _FP_strdup (line);
2048 result->origin = _FP_strdup (sstate.envelope.from);
2049 result->mimeid = _FP_strdup (sstate.envelope.mimeid);
2050 result->mimetype = _FP_strdup ("text/plain");
2051 result->mode = 0644;
2052 result->uudet = PT_ENCODED; /* plain text */
2053 result->sfname = _FP_strdup (fname);
2054 result->flags = FL_SINGLE | FL_PROPER;
2055 result->partno = 1;
2056 /* result->startpos set from above */
2057 /* result->length set from above */
2058
2059 if ((sstate.envelope.subject != NULL && result->subject == NULL) ||
2060 result->filename == NULL || result->sfname == NULL) {
2061 *errcode = UURET_NOMEM;
2062 }
2063
2064 return result;
2065 }
2066 /* otherwise, just return NULL */
2067 _FP_free (result);
2068 return NULL;
2069 }
2070
2071 /*
2072 * Scan a new part from a Multipart message. Check for a new local
2073 * envelope (which defaults to `Content-Type: text/plain') and
2074 * evaluate its Content-Type and Content-Transfer-Encoding. If this
2075 * is another Multipart/something, push the current state onto our
2076 * stack and dive into the new environment, starting with another
2077 * preamble.
2078 */
2079
2080 if (sstate.ismime && sstate.mimestate == MS_SUBPART) {
2081 memset (&localenv, 0, sizeof (headers));
2082 result->startpos = ftell (datei);
2083 prevpos = ftell (datei);
2084 hcount = 0;
2085 lcount = 0;
2086
2087 /*
2088 * Duplicate some data from outer envelope
2089 */
2090
2091 localenv.mimevers = _FP_strdup (sstate.envelope.mimevers);
2092 localenv.from = _FP_strdup (sstate.envelope.from);
2093 localenv.subject = _FP_strdup (sstate.envelope.subject);
2094 localenv.rcpt = _FP_strdup (sstate.envelope.rcpt);
2095 localenv.date = _FP_strdup (sstate.envelope.date);
2096
2097 if ((sstate.envelope.mimevers != NULL && localenv.mimevers == NULL) ||
2098 (sstate.envelope.from != NULL && localenv.from == NULL) ||
2099 (sstate.envelope.subject != NULL && localenv.subject == NULL) ||
2100 (sstate.envelope.rcpt != NULL && localenv.rcpt == NULL) ||
2101 (sstate.envelope.date != NULL && localenv.date == NULL)) {
2102
2103 while (mssdepth) {
2104 mssdepth--;
2105 UUkillheaders (&(multistack[mssdepth].envelope));
2106 _FP_free (multistack[mssdepth].source);
2107 }
2108 sstate.isfolder = 0;
2109 sstate.ismime = 0;
2110
2111 UUkillheaders (&localenv);
2112 *errcode = UURET_NOMEM;
2113 _FP_free (result);
2114 return NULL;
2115 }
2116
2117 /* Scan subheader. But what if there is no subheader? */
2118 hcount = 0;
2119 lcount = 0;
2120 preheaders = prevpos;
2121
2122 if (_FP_fgets (line, 255, datei) == NULL) {
2123 sstate.isfolder = 0;
2124 sstate.ismime = 0;
2125 while (mssdepth) {
2126 mssdepth--;
2127 UUkillheaders (&(multistack[mssdepth].envelope));
2128 _FP_free (multistack[mssdepth].source);
2129 }
2130 UUkillheaders (&localenv);
2131 _FP_free (result);
2132 return NULL;
2133 }
2134 line[255] = '\0';
2135
2136 while (!feof (datei) && !IsLineEmpty (line)) {
2137 if (IsKnownHeader (line))
2138 hcount++;
2139 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
2140 if (lcount > WAITHEADER && hcount == 0) {
2141 fseek (datei, preheaders, SEEK_SET);
2142 prevpos = preheaders;
2143 break;
2144 }
2145 ptr1 = ScanHeaderLine (datei, line);
2146 if (ParseHeader (&localenv, ptr1) == NULL)
2147 *errcode = UURET_NOMEM;
2148
2149 if (line[0] == '-' && line[1] == '-')
2150 break;
2151
2152 prevpos = ftell (datei);
2153
2154 if (_FP_fgets (line, 255, datei) == NULL)
2155 break;
2156 line[255] = '\0';
2157 lcount++;
2158 }
2159 if (line[0] == '-' && line[1] == '-') {
2160 /*
2161 * this shouldn't happen, there must always be an empty line,
2162 * but let's accept it anyway. Just skip back to before the
2163 * boundary, so that it gets handled below
2164 */
2165 fseek (datei, prevpos, SEEK_SET);
2166 }
2167
2168 if (_FP_stristr (localenv.ctype, "multipart") != NULL) {
2169 /* oh no, not again */
2170 if (mssdepth >= MSMAXDEPTH) {
2171 /* Argh, what an isane message. Treat as plain text */
2172 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
2173 uustring (S_MIME_MULTI_DEPTH));
2174 }
2175 else if (localenv.boundary == NULL) {
2176 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
2177 uustring (S_MIME_NO_BOUNDARY));
2178 }
2179 else {
2180 memcpy (&multistack[mssdepth], &sstate, sizeof (scanstate));
2181 memcpy (&sstate.envelope, &localenv, sizeof (headers));
2182 memset (&localenv, 0, sizeof (headers));
2183 sstate.mimestate = MS_PREAMBLE;
2184 if ((sstate.source = _FP_strdup (sstate.source)) == NULL)
2185 *errcode = UURET_NOMEM;
2186
2187 if (*errcode == UURET_OK)
2188 *errcode = UURET_CONT;
2189
2190 mssdepth++;
2191 /* need a restart */
2192 _FP_free (result);
2193 return NULL;
2194 }
2195 }
2196
2197 /*
2198 * So this subpart is either plain text or something else. Check
2199 * the Content-Type and Content-Transfer-Encoding. If the latter
2200 * is a defined value, we know what to do and just copy everything
2201 * up to the boundary.
2202 * If Content-Transfer-Encoding is unknown or missing, look at the
2203 * Content-Type. If it's "text/plain" or undefined, we subject the
2204 * message to our encoding detection. Otherwise, treat as plain
2205 * text.
2206 * This is done because users might `attach' a uuencoded file, which
2207 * would then be correctly typed as `text/plain'.
2208 */
2209
2210 if (_FP_stristr (localenv.ctenc, "base64") != NULL)
2211 result->uudet = B64ENCODED;
2212 else if (_FP_stristr (localenv.ctenc, "x-uue") != NULL) {
2213 result->uudet = UU_ENCODED;
2214 result->begin = result->end = 1;
2215 }
2216 else if (_FP_stristr (localenv.ctenc, "x-yenc") != NULL) {
2217 result->uudet = YENC_ENCODED;
2218 result->begin = result->end = 1;
2219 }
2220 else if (_FP_stristr (localenv.ctenc, "quoted-printable") != NULL)
2221 result->uudet = QP_ENCODED;
2222 else if (_FP_stristr (localenv.ctenc, "7bit") != NULL ||
2223 _FP_stristr (localenv.ctenc, "8bit") != NULL)
2224 result->uudet = PT_ENCODED;
2225 else if (_FP_stristr (localenv.ctype, "multipart") != NULL ||
2226 _FP_stristr (localenv.ctype, "message") != NULL)
2227 result->uudet = PT_ENCODED;
2228
2229 /*
2230 * If we're switched to MIME-only mode, handle as text
2231 */
2232
2233 if (uu_more_mime >= 2 && !result->uudet) {
2234 result->uudet = PT_ENCODED;
2235 }
2236
2237 if (result->uudet) {
2238 /*
2239 * Oh-kay, go ahead. Just read and wait for the boundary
2240 */
2241 result->startpos = ftell (datei);
2242 prevpos = ftell (datei);
2243 blen = strlen (sstate.envelope.boundary);
2244 lcount = 0;
2245
2246 while (!feof (datei)) {
2247 if (_FP_fgets (line, 255, datei) == NULL) {
2248 line[0] = '\0';
2249 break;
2250 }
2251 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
2252 line[255] = '\0';
2253 if (line[0] == '-' && line[1] == '-' &&
2254 strncmp (line+2, sstate.envelope.boundary, blen) == 0)
2255 break;
2256 /*
2257 * I've had a report of someone who tried to decode a huge file
2258 * that had an early truncated multipart message and later another
2259 * multipart message with the *same* boundary. Consequently, all
2260 * some hundred messages inbetween were ignored ...
2261 * This check here doesn't cover folded header lines, but we don't
2262 * want to slow down scanning too much. We just check for
2263 * Content-Type: multipart/... boundary="same-boundary"
2264 */
2265 if (line[0] == 'C' && line[1] == 'o' &&
2266 _FP_strnicmp (line, "Content-Type:", 13) == 0) {
2267 ptr1 = ScanHeaderLine (datei, line);
2268 ptr2 = (ptr1)?_FP_stristr(ptr1,"boundary"):NULL;
2269 ptr1 = (ptr2)?ParseValue(ptr2):NULL;
2270 if (ptr1 && strcmp (ptr1, sstate.envelope.boundary) == 0)
2271 break;
2272 for (res=0; ptr1 && res<mssdepth; res++)
2273 if (strcmp (ptr1, multistack[res].envelope.boundary) == 0)
2274 break;
2275 if (res<mssdepth)
2276 break;
2277 }
2278 if (!IsLineEmpty (line))
2279 lcount++;
2280 prevpos = ftell (datei);
2281 }
2282 if (line[0] == '-' && line[1] == '-' &&
2283 strncmp (line+2, sstate.envelope.boundary, blen) == 0) {
2284 ptr1 = line + 2 + blen;
2285 if (*ptr1 == '-' && *(ptr1+1) == '-')
2286 sstate.mimestate = MS_EPILOGUE;
2287 else
2288 sstate.mimestate = MS_SUBPART;
2289 }
2290 else {
2291 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
2292 uustring (S_MIME_B_NOT_FOUND));
2293
2294 while (mssdepth) {
2295 mssdepth--;
2296 UUkillheaders (&(multistack[mssdepth].envelope));
2297 _FP_free (multistack[mssdepth].source);
2298 }
2299 /*
2300 * Don't retry if uu_fast_scanning
2301 */
2302
2303 if (uu_fast_scanning) {
2304 UUkillheaders (&localenv);
2305 sstate.isfolder = 0;
2306 sstate.ismime = 0;
2307 sstate.mimestate = MS_BODY;
2308 _FP_free (result);
2309 return NULL;
2310 }
2311
2312 /*
2313 * Retry, but listening to headers this time
2314 */
2315 fseek (datei, result->startpos, SEEK_SET);
2316
2317 UUkillfread (result);
2318 if ((result = (fileread *) malloc (sizeof (fileread))) == NULL) {
2319 *errcode = UURET_NOMEM;
2320 sstate.isfolder = 0;
2321 sstate.ismime = 0;
2322 UUkillheaders (&localenv);
2323 return NULL;
2324 }
2325 memset (result, 0, sizeof (fileread));
2326
2327 if ((res = ScanData (datei, fname, errcode, NULL, 1, 1, result))==-1) {
2328 /* oops, something went wrong */
2329 sstate.isfolder = 0;
2330 sstate.ismime = 0;
2331 UUkillfread (result);
2332 UUkillheaders (&localenv);
2333 return NULL;
2334 }
2335 if (res == 1) {
2336 /*
2337 * new headers found
2338 */
2339 sstate.isfolder = 1;
2340 sstate.ismime = 0;
2341 sstate.mimestate = MS_HEADERS;
2342 }
2343 else {
2344 sstate.isfolder = 0;
2345 sstate.ismime = 0;
2346 }
2347 }
2348 /* produce result if uu_handletext is set */
2349 /* or if the file is explicitely named */
2350 if (result->uudet == B64ENCODED || lcount) {
2351 if (localenv.fname) {
2352 _FP_free (result->filename);
2353 if ((result->filename = _FP_strdup (localenv.fname)) == NULL)
2354 *errcode = UURET_NOMEM;
2355 }
2356 else if ((result->uudet==QP_ENCODED||result->uudet==PT_ENCODED) &&
2357 result->filename == NULL && uu_handletext) {
2358 sprintf (line, "%04d.txt", ++mimseqno);
2359 if ((result->filename = _FP_strdup (line)) == NULL)
2360 *errcode = UURET_NOMEM;
2361 }
2362 result->subject = _FP_strdup (localenv.subject);
2363 result->origin = _FP_strdup (localenv.from);
2364 result->mimeid = _FP_strdup (localenv.mimeid);
2365 result->mimetype = _FP_strdup (localenv.ctype);
2366 result->mode = 0644;
2367 result->sfname = _FP_strdup (fname);
2368 result->flags = FL_SINGLE | FL_PROPER;
2369 result->partno = 1;
2370 /* result->uudet determined above */
2371 /* result->startpos set from above */
2372 result->length = prevpos - result->startpos;
2373
2374 if ((localenv.subject != NULL && result->subject == NULL) ||
2375 result->filename == NULL || result->sfname == NULL) {
2376 *errcode = UURET_NOMEM;
2377 }
2378 }
2379 else {
2380 /* don't produce a result */
2381 _FP_free (result);
2382 result = NULL;
2383 }
2384 if (*errcode == UURET_OK)
2385 *errcode = UURET_CONT;
2386 /*
2387 * destroy local envelope
2388 */
2389 UUkillheaders (&localenv);
2390 return result;
2391 }
2392
2393 /*
2394 * we're in a subpart, but the local headers don't give us any
2395 * clue about what's to find here. So look for encoded data by
2396 * ourselves.
2397 */
2398
2399 if ((res = ScanData (datei, fname, errcode,
2400 sstate.envelope.boundary,
2401 1, 0, result)) == -1) {
2402 /* oops, something went wrong */
2403 sstate.isfolder = 0;
2404 sstate.ismime = 0;
2405 UUkillfread (result);
2406 UUkillheaders (&localenv);
2407 return NULL;
2408 }
2409 /*
2410 * we should really be at a boundary here, but check again
2411 */
2412 blen = strlen (sstate.envelope.boundary);
2413 prevpos = ftell (datei);
2414
2415 while (!feof (datei)) {
2416 if (_FP_fgets (line, 255, datei) == NULL) {
2417 line[0] = '\0';
2418 break;
2419 }
2420 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
2421 line[255] = '\0';
2422 if (line[0] == '-' && line[1] == '-' &&
2423 strncmp (line+2, sstate.envelope.boundary, blen) == 0)
2424 break;
2425 if (line[0] == 'C' && line[1] == 'o' &&
2426 _FP_strnicmp (line, "Content-Type:", 13) == 0) {
2427 ptr1 = ScanHeaderLine (datei, line);
2428 ptr2 = (ptr1)?_FP_stristr(ptr1,"boundary"):NULL;
2429 ptr1 = (ptr2)?ParseValue(ptr2):NULL;
2430 if (ptr1 && strcmp (ptr1, sstate.envelope.boundary) == 0)
2431 break;
2432 }
2433 prevpos = ftell (datei);
2434 }
2435 /*
2436 * check if this was the last subpart
2437 */
2438 if (line[0] == '-' && line[1] == '-' &&
2439 strncmp (line+2, sstate.envelope.boundary, blen) == 0) {
2440 ptr1 = line + 2 + blen;
2441 if (*ptr1 == '-' && *(ptr1+1) == '-')
2442 sstate.mimestate = MS_EPILOGUE;
2443 else
2444 sstate.mimestate = MS_SUBPART;
2445 }
2446 else {
2447 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
2448 uustring (S_MIME_B_NOT_FOUND));
2449
2450 while (mssdepth) {
2451 mssdepth--;
2452 UUkillheaders (&(multistack[mssdepth].envelope));
2453 _FP_free (multistack[mssdepth].source);
2454 }
2455
2456 if (uu_fast_scanning) {
2457 UUkillheaders (&localenv);
2458 sstate.isfolder = 0;
2459 sstate.ismime = 0;
2460 sstate.mimestate = MS_BODY;
2461 _FP_free (result);
2462 return NULL;
2463 }
2464
2465 /*
2466 * Retry, listening to headers this time
2467 */
2468 fseek (datei, result->startpos, SEEK_SET);
2469
2470 UUkillfread (result);
2471 if ((result = (fileread *) malloc (sizeof (fileread))) == NULL) {
2472 *errcode = UURET_NOMEM;
2473 sstate.isfolder = 0;
2474 sstate.ismime = 0;
2475 UUkillheaders (&localenv);
2476 return NULL;
2477 }
2478 memset (result, 0, sizeof (fileread));
2479
2480 if ((res = ScanData (datei, fname, errcode, NULL, 1, 1, result))==-1) {
2481 /* oops, something went wrong */
2482 sstate.isfolder = 0;
2483 sstate.ismime = 0;
2484 UUkillfread (result);
2485 UUkillheaders (&localenv);
2486 return NULL;
2487 }
2488 if (res == 1) {
2489 /*
2490 * new headers found
2491 */
2492 sstate.isfolder = 1;
2493 sstate.ismime = 0;
2494 sstate.mimestate = MS_HEADERS;
2495 }
2496 else {
2497 sstate.isfolder = 0;
2498 sstate.ismime = 0;
2499 }
2500 }
2501
2502 /*
2503 * If this file has been nicely MIME so far, then be very suspicious
2504 * if ScanData reports anything else. So do a double check, and if
2505 * it doesn't hold up, handle as plain text instead.
2506 */
2507
2508 if (strcmp (localenv.mimevers, "1.0") == 0 &&
2509 _FP_stristr (localenv.ctype, "text") != NULL &&
2510 sstate.ismime && sstate.mimestate == MS_SUBPART &&
2511 !uu_desperate) {
2512 if (result->uudet == UU_ENCODED && !(result->begin || result->end)) {
2513 result->uudet = 0;
2514 }
2515 }
2516
2517 /*
2518 * produce result
2519 */
2520
2521 if (result->uudet == 0) {
2522 result->uudet = PT_ENCODED; /* plain text */
2523 }
2524
2525 if (localenv.fname) {
2526 _FP_free (result->filename);
2527 if ((result->filename = _FP_strdup (localenv.fname)) == NULL)
2528 *errcode = UURET_NOMEM;
2529 }
2530 else if ((result->uudet==QP_ENCODED || result->uudet==PT_ENCODED) &&
2531 result->filename==NULL && uu_handletext) {
2532 sprintf (line, "%04d.txt", ++mimseqno);
2533 if ((result->filename = _FP_strdup (line)) == NULL)
2534 *errcode = UURET_NOMEM;
2535 }
2536 else {
2537 /* assign a filename lateron */
2538 }
2539 if (result->mimetype) _FP_free (result->mimetype);
2540 if (result->uudet) {
2541 if (_FP_stristr (localenv.ctype, "text") != NULL &&
2542 result->uudet != QP_ENCODED && result->uudet != PT_ENCODED)
2543 result->mimetype = NULL; /* better don't set it */
2544 else
2545 result->mimetype = _FP_strdup (localenv.ctype);
2546 }
2547 if (result->origin) _FP_free (result->origin);
2548 result->origin = _FP_strdup (localenv.from);
2549
2550 if (result->subject) _FP_free (result->subject);
2551 result->subject = _FP_strdup (localenv.subject);
2552
2553 if (result->sfname == NULL)
2554 if ((result->sfname = _FP_strdup (fname)) == NULL)
2555 *errcode = UURET_NOMEM;
2556
2557 result->length = prevpos - result->startpos;
2558 result->flags = FL_SINGLE | FL_PROPER;
2559 result->partno = 1;
2560
2561 if (result->mode == 0)
2562 result->mode = 0644;
2563
2564 /*
2565 * the other fields should already be set appropriately
2566 */
2567
2568 if (*errcode == UURET_OK)
2569 *errcode = UURET_CONT;
2570
2571 /*
2572 * kill local envelope
2573 */
2574 UUkillheaders (&localenv);
2575
2576 return result;
2577 }
2578
2579 /*
2580 * All right, so we're not in a Multipart message. Phew, took quite
2581 * long to figure this out. But this might still be a MIME message
2582 * body. And if it's a message/partial, we need more special handling
2583 */
2584
2585 if (sstate.isfolder && sstate.ismime && sstate.mimestate == MS_BODY &&
2586 _FP_stristr (sstate.envelope.ctype, "message") != NULL &&
2587 _FP_stristr (sstate.envelope.ctype, "partial") != NULL) {
2588
2589 result->startpos = ftell (datei);
2590
2591 if (sstate.envelope.partno == 1) {
2592 /* read local envelope */
2593 UUkillheaders (&localenv);
2594 memset (&localenv, 0, sizeof (headers));
2595
2596 /* skip over blank lines first */
2597 prevpos = ftell (datei);
2598 while (!feof (datei)) {
2599 if (_FP_fgets (line, 255, datei) == NULL)
2600 break;
2601 line[255] = '\0';
2602 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
2603 if (!IsLineEmpty (line))
2604 break;
2605 prevpos = ftell (datei);
2606 }
2607 /* Next, read header. But what if there is no subheader? */
2608 hcount = 0;
2609 lcount = 0;
2610 preheaders = prevpos;
2611
2612 while (!feof (datei) && !IsLineEmpty (line)) {
2613 if (IsKnownHeader (line))
2614 hcount++;
2615 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
2616 if (lcount > WAITHEADER && hcount == 0) {
2617 fseek (datei, preheaders, SEEK_SET);
2618 break;
2619 }
2620 ptr1 = ScanHeaderLine (datei, line);
2621 if (ParseHeader (&localenv, ptr1) == NULL)
2622 *errcode = UURET_NOMEM;
2623
2624 if (_FP_fgets (line, 255, datei) == NULL)
2625 break;
2626 line[255] = '\0';
2627 lcount++;
2628 }
2629 prevpos = ftell (datei);
2630 /*
2631 * Examine local header. We're mostly interested in the Content-Type
2632 * and the Content-Transfer-Encoding.
2633 */
2634 if (_FP_stristr (localenv.ctype, "multipart") != NULL) {
2635 UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
2636 uustring (S_MIME_PART_MULTI));
2637 }
2638 if (localenv.subject)
2639 result->subject = _FP_strdup (localenv.subject);
2640 else
2641 result->subject = _FP_strdup (sstate.envelope.subject);
2642
2643 if (localenv.from)
2644 result->origin = _FP_strdup (localenv.from);
2645 else
2646 result->origin = _FP_strdup (sstate.envelope.from);
2647
2648 if (localenv.ctype)
2649 result->mimetype = _FP_strdup (localenv.ctype);
2650 else
2651 result->mimetype = _FP_strdup ("text/plain");
2652
2653 if (_FP_stristr (localenv.ctenc, "quoted-printable") != NULL)
2654 result->uudet = QP_ENCODED;
2655 else if (_FP_stristr (localenv.ctenc, "base64") != NULL)
2656 result->uudet = B64ENCODED;
2657 else if (_FP_stristr (localenv.ctenc, "x-uue") != NULL) {
2658 result->uudet = UU_ENCODED;
2659 result->begin = result->end = 1;
2660 }
2661 else if (_FP_stristr (localenv.ctenc, "x-yenc") != NULL) {
2662 result->uudet = YENC_ENCODED;
2663 result->begin = result->end = 1;
2664 }
2665 else if (_FP_stristr (localenv.ctenc, "7bit") != NULL ||
2666 _FP_stristr (localenv.ctenc, "8bit") != NULL)
2667 result->uudet = PT_ENCODED;
2668 else if (_FP_stristr (localenv.ctype, "multipart") != NULL ||
2669 _FP_stristr (localenv.ctype, "message") != NULL)
2670 result->uudet = PT_ENCODED;
2671
2672 /*
2673 * If we're switched to MIME-only mode, handle as text
2674 */
2675
2676 if (uu_more_mime >= 2 && !result->uudet) {
2677 result->uudet = PT_ENCODED;
2678 }
2679 }
2680 else {
2681 memset (&localenv, 0, sizeof (headers));
2682 }
2683
2684 /*
2685 * If this is Quoted-Printable or Plain Text, just try looking
2686 * for the next message header. If uu_fast_scanning, and the
2687 * encoding is known, there's no need to look below. Otherwise,
2688 * we check the type of encoding first.
2689 * The encoding type is determined on the first part; in all
2690 * others, we also don't read on.
2691 * If we have a partial multipart message, scan for headers, but
2692 * do not react on standard MIME headers, as they are probably
2693 * from the subparts. However, we're stuck if there's an embedded
2694 * message/rfc822 :-(
2695 * If it is a "trivial" (non-embedded) message/rfc822, skip over
2696 * the message header and then start looking for the next header.
2697 */
2698 if (uu_fast_scanning && (result->uudet!=0||sstate.envelope.partno!=1)) {
2699 /* do nothing */
2700 res = 0;
2701 }
2702 else if (result->uudet != 0) {
2703 hcount = lcount = 0;
2704 prevpos = ftell (datei);
2705
2706 if (_FP_stristr (localenv.ctype, "message") != NULL &&
2707 _FP_stristr (localenv.ctype, "rfc822") != NULL) {
2708 /*
2709 * skip over empty lines and local header
2710 */
2711 preheaders = ftell (datei);
2712 while (!feof (datei)) {
2713 if (_FP_fgets (line, 255, datei) == NULL)
2714 break;
2715 line[255] = '\0';
2716 if (!IsLineEmpty (line)) {
2717 break;
2718 }
2719 }
2720
2721 while (!feof (datei) && !IsLineEmpty (line)) {
2722 if (IsKnownHeader (line))
2723 hcount++;
2724 lcount++;
2725 if (lcount > WAITHEADER && hcount < hlcount.afternl)
2726 break;
2727
2728 if (_FP_fgets (line, 255, datei) == NULL)
2729 break;
2730 line[255] = '\0';
2731 }
2732 if (hcount < hlcount.afternl)
2733 fseek (datei, preheaders, SEEK_SET);
2734 hcount = lcount = 0;
2735 }
2736
2737 /*
2738 * look for next header
2739 */
2740
2741 while (!feof (datei)) {
2742 if (_FP_fgets (line, 255, datei) == NULL)
2743 break;
2744 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
2745 if (ferror (datei))
2746 break;
2747 line[255] = '\0';
2748
2749 if ((vflag = IsKnownHeader (line))) {
2750 (void) ScanHeaderLine (datei, line);
2751
2752 if (result->uudet != PT_ENCODED || vflag == 1) {
2753 if (hcount == 0)
2754 preheaders = prevpos;
2755 hcount++;
2756 lcount++;
2757 if (hcount >= hlcount.restart) {
2758 /*
2759 * Hey, a new header starts here
2760 */
2761 fseek (datei, preheaders, SEEK_SET);
2762 prevpos = preheaders;
2763 break;
2764 }
2765 }
2766 }
2767 else if (lcount > WAITHEADER) {
2768 hcount = 0;
2769 lcount = 0;
2770 }
2771 else if (hcount) {
2772 lcount++;
2773 }
2774 prevpos = ftell (datei);
2775 }
2776 res = 1;
2777 }
2778 else {
2779 /*
2780 * Otherwise, let's see what we can find ourself. No
2781 * boundary (NULL) but MIME, and respect new headers.
2782 */
2783 if ((res = ScanData (datei, fname, errcode, NULL, 1, 1, result)) == -1) {
2784 /* oops, something went wrong */
2785 sstate.isfolder = 0;
2786 sstate.ismime = 0;
2787 UUkillfread (result);
2788 UUkillheaders (&localenv);
2789 return NULL;
2790 }
2791 if (result->uudet == 0 && uu_handletext)
2792 result->uudet = PT_ENCODED;
2793
2794 prevpos = ftell (datei);
2795 }
2796 /*
2797 * produce result
2798 */
2799 if (localenv.fname) {
2800 _FP_free (result->filename);
2801 if ((result->filename = _FP_strdup (localenv.fname)) == NULL)
2802 *errcode = UURET_NOMEM;
2803 }
2804 else if (sstate.envelope.fname) {
2805 _FP_free (result->filename);
2806 if ((result->filename = _FP_strdup (sstate.envelope.fname)) == NULL)
2807 *errcode = UURET_NOMEM;
2808 }
2809 else if ((result->uudet==QP_ENCODED || result->uudet==PT_ENCODED) &&
2810 result->filename == NULL) {
2811 sprintf (line, "%04d.txt", ++mimseqno);
2812 if ((result->filename = _FP_strdup (line)) == NULL)
2813 *errcode = UURET_NOMEM;
2814 }
2815 else {
2816 /* assign a filename lateron */
2817 }
2818 if (result->subject == NULL) {
2819 if (sstate.envelope.subject)
2820 result->subject = _FP_strdup (sstate.envelope.subject);
2821 }
2822 result->partno = sstate.envelope.partno;
2823 result->maxpno = sstate.envelope.numparts;
2824 result->flags = FL_PARTIAL |
2825 ((res==1 || uu_fast_scanning) ? FL_PROPER : 0) |
2826 ((uu_fast_scanning) ? FL_TOEND : 0);
2827 result->mimeid = _FP_strdup (sstate.envelope.mimeid);
2828 if (result->partno == 1)
2829 result->begin = 1;
2830
2831 if (uu_fast_scanning)
2832 result->length = progress.fsize - result->startpos;
2833 else
2834 result->length = prevpos - result->startpos;
2835
2836 if (result->sfname == NULL)
2837 result->sfname = _FP_strdup (fname);
2838
2839 if (result->mode == 0)
2840 result->mode = 0644;
2841
2842 /*
2843 * the other fields should already be set appropriately
2844 */
2845
2846 if (res == 1) {
2847 /*
2848 * new headers found
2849 */
2850 sstate.isfolder = 1;
2851 sstate.ismime = 0;
2852 sstate.mimestate = MS_HEADERS;
2853
2854 UUkillheaders (&sstate.envelope);
2855 memset (&sstate.envelope, 0, sizeof (headers));
2856 }
2857 else {
2858 /*
2859 * otherwise, this can't be a mail folder
2860 */
2861 sstate.isfolder = 0;
2862 sstate.ismime = 0;
2863 }
2864 /*
2865 * kill local envelope
2866 */
2867 UUkillheaders (&localenv);
2868 return result;
2869 }
2870
2871 /*
2872 * If this is a MIME body, honor a Content-Type different than
2873 * text/plain or a proper Content-Transfer-Encoding.
2874 * We also go in here if we have an assigned filename - this means
2875 * that we've had a Content-Disposition field, and we should probably
2876 * decode a plain-text segment with a filename.
2877 */
2878
2879 if (sstate.isfolder && sstate.ismime &&
2880 sstate.mimestate == MS_BODY &&
2881 (_FP_stristr (sstate.envelope.ctenc, "quoted-printable") != NULL ||
2882 _FP_stristr (sstate.envelope.ctenc, "base64") != NULL ||
2883 _FP_stristr (sstate.envelope.ctenc, "x-uue") != NULL ||
2884 _FP_stristr (sstate.envelope.ctenc, "x-yenc") != NULL ||
2885 _FP_stristr (sstate.envelope.ctype, "message") != NULL ||
2886 sstate.envelope.fname != NULL)) {
2887
2888 if (sstate.envelope.subject)
2889 result->subject = _FP_strdup (sstate.envelope.subject);
2890 if (sstate.envelope.from)
2891 result->origin = _FP_strdup (sstate.envelope.from);
2892
2893 if (sstate.envelope.ctype)
2894 result->mimetype = _FP_strdup (sstate.envelope.ctype);
2895 else
2896 result->mimetype = _FP_strdup ("text/plain");
2897
2898 if (_FP_stristr (sstate.envelope.ctenc, "quoted-printable") != NULL)
2899 result->uudet = QP_ENCODED;
2900 else if (_FP_stristr (sstate.envelope.ctenc, "base64") != NULL)
2901 result->uudet = B64ENCODED;
2902 else if (_FP_stristr (sstate.envelope.ctenc, "x-uue") != NULL) {
2903 result->uudet = UU_ENCODED;
2904 result->begin = result->end = 1;
2905 }
2906 else if (_FP_stristr (sstate.envelope.ctenc, "x-yenc") != NULL) {
2907 result->uudet = YENC_ENCODED;
2908 }
2909 else if (_FP_stristr (sstate.envelope.ctenc, "7bit") != NULL ||
2910 _FP_stristr (sstate.envelope.ctenc, "8bit") != NULL)
2911 result->uudet = PT_ENCODED;
2912 else if (_FP_stristr (sstate.envelope.ctype, "multipart") != NULL ||
2913 _FP_stristr (sstate.envelope.ctype, "message") != NULL ||
2914 sstate.envelope.fname != NULL)
2915 result->uudet = PT_ENCODED;
2916
2917 /*
2918 * If we're switched to MIME-only mode, handle as text
2919 */
2920
2921 if (uu_more_mime >= 2 && !result->uudet) {
2922 result->uudet = PT_ENCODED;
2923 }
2924
2925 result->startpos = prevpos = ftell (datei);
2926
2927 /*
2928 * If this is Quoted-Printable or Plain Text, just try looking
2929 * for the next message header. If uu_fast_scanning, we know
2930 * there won't be more headers.
2931 * If it is a "trivial" (non-embedded) message/rfc822, skip over
2932 * the message header and then start looking for the next header.
2933 */
2934 if (result->uudet != 0 && uu_fast_scanning) {
2935 /* do nothing */
2936 res = 0;
2937 }
2938 else if (result->uudet != 0) {
2939 hcount = lcount = 0;
2940 prevpos = ftell (datei);
2941
2942 if (_FP_stristr (sstate.envelope.ctype, "message") != NULL &&
2943 _FP_stristr (sstate.envelope.ctype, "rfc822") != NULL) {
2944 /*
2945 * skip over empty lines and local header
2946 */
2947 preheaders = ftell (datei);
2948 while (!feof (datei)) {
2949 if (_FP_fgets (line, 255, datei) == NULL)
2950 break;
2951 line[255] = '\0';
2952 if (!IsLineEmpty (line)) {
2953 break;
2954 }
2955 }
2956
2957 while (!feof (datei) && !IsLineEmpty (line)) {
2958 if (IsKnownHeader (line))
2959 hcount++;
2960 lcount++;
2961 if (lcount > WAITHEADER && hcount < hlcount.afternl)
2962 break;
2963
2964 if (_FP_fgets (line, 255, datei) == NULL)
2965 break;
2966 line[255] = '\0';
2967 }
2968 if (hcount < hlcount.afternl)
2969 fseek (datei, preheaders, SEEK_SET);
2970 hcount = lcount = 0;
2971 }
2972
2973 /*
2974 * look for next header
2975 */
2976
2977 while (!feof (datei)) {
2978 if (_FP_fgets (line, 255, datei) == NULL)
2979 break;
2980 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
2981 if (ferror (datei))
2982 break;
2983 line[255] = '\0';
2984
2985 if (IsKnownHeader (line)) {
2986 (void) ScanHeaderLine (datei, line);
2987 if (hcount == 0)
2988 preheaders = prevpos;
2989 hcount++;
2990 lcount++;
2991 if (hcount >= hlcount.restart) {
2992 /*
2993 * Hey, a new header starts here
2994 */
2995 fseek (datei, preheaders, SEEK_SET);
2996 prevpos = preheaders;
2997 break;
2998 }
2999 }
3000 else if (lcount > WAITHEADER) {
3001 hcount = 0;
3002 lcount = 0;
3003 }
3004 else if (hcount) {
3005 lcount++;
3006 }
3007 prevpos = ftell (datei);
3008 }
3009 res = 1;
3010 }
3011 else {
3012 /*
3013 * Otherwise, let's see what we can find ourself. No
3014 * boundary (NULL) but MIME, and respect new headers.
3015 */
3016 if ((res = ScanData (datei, fname, errcode, NULL, 1, 1, result)) == -1) {
3017 /* oops, something went wrong */
3018 sstate.isfolder = 0;
3019 sstate.ismime = 0;
3020 UUkillfread (result);
3021 return NULL;
3022 }
3023 if (result->uudet == 0 && uu_handletext) {
3024 result->startpos = before; /* display headers */
3025 result->uudet = PT_ENCODED;
3026 }
3027
3028 prevpos = ftell (datei);
3029 }
3030 /*
3031 * produce result
3032 */
3033 if (sstate.envelope.fname) {
3034 _FP_free (result->filename);
3035 if ((result->filename = _FP_strdup (sstate.envelope.fname)) == NULL)
3036 *errcode = UURET_NOMEM;
3037 }
3038 else if ((result->uudet==QP_ENCODED||result->uudet==PT_ENCODED) &&
3039 result->filename == NULL) {
3040 sprintf (line, "%04d.txt", ++mimseqno);
3041 if ((result->filename = _FP_strdup (line)) == NULL)
3042 *errcode = UURET_NOMEM;
3043 }
3044 else {
3045 /* assign a filename lateron */
3046 }
3047 if (result->subject == NULL) {
3048 if (sstate.envelope.subject)
3049 result->subject = _FP_strdup (sstate.envelope.subject);
3050 }
3051 result->flags = ((res==1||uu_fast_scanning)?FL_PROPER:0) |
3052 ((uu_fast_scanning) ? FL_TOEND : 0);
3053 result->mimeid = _FP_strdup (sstate.envelope.mimeid);
3054
3055 if (uu_fast_scanning)
3056 result->length = progress.fsize - result->startpos;
3057 else
3058 result->length = prevpos - result->startpos;
3059
3060 if (result->sfname == NULL)
3061 result->sfname = _FP_strdup (fname);
3062
3063 if (result->mode == 0)
3064 result->mode = 0644;
3065
3066 /*
3067 * the other fields should already be set appropriately
3068 */
3069
3070 if (res == 1) {
3071 /*
3072 * new headers found
3073 */
3074 sstate.isfolder = 1;
3075 sstate.ismime = 0;
3076 sstate.mimestate = MS_HEADERS;
3077
3078 UUkillheaders (&sstate.envelope);
3079 memset (&sstate.envelope, 0, sizeof (headers));
3080 }
3081 else {
3082 /*
3083 * otherwise, this can't be a mail folder
3084 */
3085 sstate.isfolder = 0;
3086 sstate.ismime = 0;
3087 }
3088
3089 return result;
3090 }
3091
3092 /*
3093 * Some files have reduced headers, and what should be a multipart
3094 * message is missing the proper Content-Type. If the first thing
3095 * we find after a couple of empty lines is a boundary, try it!
3096 * But make sure that this is indeed intended as being a boundary.
3097 *
3098 * Only accept it if there was indeed no Content-Type header line
3099 * and if the following line is a proper Content-Type header. BTW,
3100 * we know that sstate.envelope.boundary is NULL, or we wouldn't
3101 * be here!
3102 */
3103
3104 if ((sstate.envelope.ctype == NULL ||
3105 _FP_stristr (sstate.envelope.ctype, "multipart") != NULL) &&
3106 !uu_more_mime) {
3107 prevpos = ftell (datei);
3108 while (!feof (datei)) {
3109 if (_FP_fgets (line, 255, datei) == NULL) {
3110 line[0] = '\0';
3111 break;
3112 }
3113 if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
3114 if (!IsLineEmpty (line))
3115 break;
3116 }
3117 if (line[0] == '-' && line[1] == '-' &&
3118 !IsLineEmpty (line+2) && !feof (datei)) {
3119 ptr1 = _FP_strrstr (line+2, "--");
3120 ptr2 = ScanHeaderLine (datei, NULL);
3121 if ((ptr1 == NULL || (*(ptr1+2) != '\012' && *(ptr1+2) != '\015')) &&
3122 ptr2 && _FP_strnicmp (ptr2, "Content-", 8) == 0) {
3123 /*
3124 * hmm, okay, let's do it!
3125 */
3126 sstate.isfolder = 1;
3127 sstate.ismime = 1;
3128 sstate.mimestate = MS_PREAMBLE;
3129 /*
3130 * get boundary
3131 */
3132 ptr1 = line+2;
3133 while (*ptr1 && !isspace(*ptr1))
3134 ptr1++;
3135 *ptr1 = '\0';
3136
3137 sstate.envelope.mimevers = _FP_strdup ("1.0");
3138 sstate.envelope.boundary = _FP_strdup (line+2);
3139
3140 /*
3141 * need restart
3142 */
3143
3144 fseek (datei, prevpos, SEEK_SET);
3145
3146 _FP_free (result);
3147 return NULL;
3148 }
3149 }
3150 fseek (datei, prevpos, SEEK_SET);
3151 }
3152
3153 /*
3154 * Hmm, we're not in a ''special'' state, so it's more or less
3155 * Freestyle time. Anyway, if this seems to be a Mime message,
3156 * don't allow the minimal Base64 handling.
3157 */
3158
3159 if (sstate.envelope.subject)
3160 result->subject = _FP_strdup (sstate.envelope.subject);
3161 if (sstate.envelope.from)
3162 result->origin = _FP_strdup (sstate.envelope.from);
3163
3164 if (sstate.envelope.ctype)
3165 result->mimetype = _FP_strdup (sstate.envelope.ctype);
3166
3167 if ((res=ScanData (datei, fname, errcode, NULL,
3168 sstate.ismime, 1, result))==-1) {
3169 /* oops, something went wrong */
3170 sstate.isfolder = 0;
3171 sstate.ismime = 0;
3172 UUkillfread (result);
3173 return NULL;
3174 }
3175
3176 /*
3177 * produce result
3178 */
3179
3180 if (result->uudet == 0 && uu_handletext) {
3181 result->startpos = before; /* display headers */
3182 result->uudet = PT_ENCODED;
3183 result->partno = 1;
3184 }
3185
3186 if (result->uudet == YENC_ENCODED && result->filename != NULL) {
3187 /*
3188 * prevent replacing the filename found on the =ybegin line
3189 */
3190 }
3191 else if (sstate.envelope.fname) {
3192 _FP_free (result->filename);
3193 if ((result->filename = _FP_strdup (sstate.envelope.fname)) == NULL)
3194 *errcode = UURET_NOMEM;
3195 }
3196 else if ((result->uudet==QP_ENCODED||result->uudet==PT_ENCODED) &&
3197 result->filename == NULL) {
3198 sprintf (line, "%04d.txt", ++mimseqno);
3199 if ((result->filename = _FP_strdup (line)) == NULL)
3200 *errcode = UURET_NOMEM;
3201 }
3202 else {
3203 /* assign a filename lateron */
3204 }
3205
3206 if (result->subject == NULL) {
3207 if (sstate.envelope.subject)
3208 result->subject = _FP_strdup (sstate.envelope.subject);
3209 }
3210
3211 result->flags = (result->uudet==PT_ENCODED)?FL_SINGLE:0;
3212 result->mimeid = _FP_strdup (sstate.envelope.mimeid);
3213 result->length = ftell (datei) - result->startpos;
3214
3215 if (result->mode == 0)
3216 result->mode = 0644;
3217
3218 if (result->sfname == NULL)
3219 result->sfname = _FP_strdup (fname);
3220
3221 if (res == 1) {
3222 /*
3223 * new headers found
3224 */
3225 sstate.isfolder = 1;
3226 sstate.ismime = 0;
3227 sstate.mimestate = MS_HEADERS;
3228
3229 UUkillheaders (&sstate.envelope);
3230 memset (&sstate.envelope, 0, sizeof (headers));
3231 }
3232 else {
3233 /*
3234 * otherwise, this can't be a mail folder
3235 */
3236 sstate.isfolder = 0;
3237 sstate.ismime = 0;
3238 }
3239
3240 return result;
3241
3242 /*
3243 * Emergency handling. Set errcode before jumping here.
3244 */
3245 ScanPartEmergency:
3246 UUkillfread (result);
3247 UUkillheaders (&localenv);
3248
3249 while (mssdepth) {
3250 mssdepth--;
3251 UUkillheaders (&(multistack[mssdepth].envelope));
3252 _FP_free (multistack[mssdepth].source);
3253 }
3254
3255 return NULL;
3256 }
3257
3258