ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uucheck.c
Revision: 1.3
Committed: Sun Feb 10 22:47:18 2002 UTC (22 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Branch point for: UUDEVIEW
Changes since 1.2: +16 -3 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 /*
2 * This file is part of uudeview, the simple and friendly multi-part multi-
3 * file uudecoder program (c) 1994-2001 by Frank Pilhofer. The author may
4 * be contacted at fp@fpx.de
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #ifdef SYSTEM_WINDLL
22 #include <windows.h>
23 #endif
24 #ifdef SYSTEM_OS2
25 #include <os2.h>
26 #endif
27
28 /*
29 * uucheck.c
30 *
31 * Various checking and processing of one input part
32 **/
33
34 #include <stdio.h>
35 #include <ctype.h>
36
37 #ifdef STDC_HEADERS
38 #include <stdlib.h>
39 #include <string.h>
40 #endif
41 #ifdef HAVE_MALLOC_H
42 #include <malloc.h>
43 #endif
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #ifdef HAVE_MEMORY_H
48 #include <memory.h>
49 #endif
50
51 #include <uudeview.h>
52 #include <uuint.h>
53 #include <fptools.h>
54 #include <uustring.h>
55
56 char * uucheck_id = "$Id: uucheck.c,v 1.2 2001/06/11 20:42:36 root Exp $";
57
58 /*
59 * Arbitrary number. This is the maximum number of part numbers we
60 * store for our have-parts and missing-parts lists
61 */
62
63 #define MAXPLIST 256
64
65
66 /*
67 * forward declarations of local functions
68 */
69
70 static char * UUGetFileName (char *, char *, char *);
71 static int UUGetPartNo (char *, char **, char **);
72
73 /*
74 * State of Scanner function and PreProcessPart
75 */
76
77 int lastvalid, lastenc, nofnum;
78 char *uucheck_lastname;
79 char *uucheck_tempname;
80 static int lastpart = 0;
81 static char *nofname = "UNKNOWN";
82
83 /*
84 * special characters we allow an unquoted filename to have
85 */
86
87 static char *fnchars = "._-~!";
88
89 /*
90 * Policy for extracting a part number from the subject line.
91 * usually, look for part numbers in () brackets first, then in []
92 */
93
94 static char *brackchr[] = {
95 "()[]", "[]()"
96 };
97
98 /*
99 * Extract a filename from the subject line. We need anything to identify
100 * the name of the program for sorting. If a nice filename cannot be found,
101 * the subject line itself is used
102 * ptonum is, if not NULL, a pointer to the part number in the subject line,
103 * so that it won't be used as filename.
104 **/
105
106 static char *
107 UUGetFileName (char *subject, char *ptonum, char *ptonend)
108 {
109 char *ptr = subject, *iter, *result, *part;
110 int count, length=0, alflag=0;
111
112 /*
113 * If this file has no subject line, assume it is the next part of the
114 * previous file (this is done in UUPreProcessPart)
115 **/
116
117 if (subject == NULL)
118 return NULL;
119
120 /*
121 * If the subject starts with 'Re', it is ignored
122 * REPosts or RETries are not ignored!
123 **/
124
125 if (uu_ignreply &&
126 (subject[0] == 'R' || subject[0] == 'r') &&
127 (subject[1] == 'E' || subject[1] == 'e') &&
128 (subject[2] == ':' || subject[2] == ' ')) {
129 return NULL;
130 }
131
132 /*
133 * Ignore a "Repost" prefix of the subject line. We don't want to get
134 * a file named "Repost" :-)
135 **/
136
137 if (FP_strnicmp (subject, "repost", 6) == 0)
138 subject += 6;
139 if (FP_strnicmp (subject, "re:", 3) == 0)
140 subject += 3;
141
142 while (*subject == ' ' || *subject == ':') subject++;
143
144 part = FP_stristr (subject, "part");
145 if (part == subject) {
146 subject += 4;
147 while (*subject == ' ') subject++;
148 }
149
150 /*
151 * If the file was encoded by uuenview, then the filename is enclosed
152 * in [brackets]. But check what's inside these bracket's, try not to
153 * fall for something other than a filename
154 */
155
156 ptr = subject;
157 while ((iter = strchr (ptr, '[')) != NULL) {
158 if (strchr (iter, ']') == NULL) {
159 ptr = iter + 1;
160 continue;
161 }
162 iter++;
163 while (isspace (*iter))
164 iter++;
165 count = length = alflag = 0;
166 while (iter[count] &&
167 (isalnum (iter[count]) || strchr (fnchars, iter[count])!=NULL)) {
168 if (isalpha (iter[count]))
169 alflag++;
170 count++;
171 }
172 if (count<4 || alflag==0) {
173 ptr = iter + 1;
174 continue;
175 }
176 length = count;
177 while (isspace (iter[count]))
178 count++;
179 if (iter[count] == ']') {
180 ptr = iter;
181 break;
182 }
183 length = 0;
184 ptr = iter + 1;
185 }
186
187 /*
188 * new filename detection routine, fists mostly for files by ftp-by-email
189 * servers that create subject lines with ftp.host.address:/full/path/file
190 * on them. We look for slashes and take the filename from after the last
191 * one ... or at least we try to.
192 */
193
194 if (length == 0) {
195 ptr = subject;
196 while ((iter = strchr (ptr, '/')) != NULL) {
197 if (iter >= ptonum && iter <= ptonend) {
198 ptr = iter + 1;
199 continue;
200 }
201 count = length = 0;
202 iter++;
203 while (iter[count] &&
204 (isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL))
205 count++;
206 if (iter[count] == ' ' && length > 4) {
207 length = count;
208 break;
209 }
210 ptr = iter + ((count)?count:1);
211 }
212 }
213
214 /*
215 * Look for two alphanumeric strings separated by a '.'
216 * (That's most likely a filename)
217 **/
218
219 if (length == 0) {
220 ptr = subject;
221 /* #warning another experimental change */
222 /*while (*ptr && *ptr != 0x0a && *ptr != 0x0d && ptr != part) {*/
223 while (*ptr && *ptr != 0x0a && *ptr != 0x0d) {
224 iter = ptr;
225 count = length = alflag = 0;
226
227 if (FP_strnicmp (ptr, "ftp", 3) == 0) {
228 /* hey, that's an ftp address */
229 while (isalpha (*ptr) || isdigit (*ptr) || *ptr == '.')
230 ptr++;
231 continue;
232 }
233
234 while ((isalnum(*iter)||strchr(fnchars, *iter)!=NULL||
235 *iter=='/') && *iter && iter != ptonum && *iter != '.') {
236 if (isalpha (*iter))
237 alflag = 1;
238
239 count++; iter++;
240 }
241 if (*iter == '\0' || iter == ptonum) {
242 if (iter == ptonum)
243 ptr = ptonend;
244 else
245 ptr = iter;
246
247 length = 0;
248 continue;
249 }
250
251 /* #warning multi-part change experimental, make pluggable */
252 /* if (*iter++ != '.' || count > 32 || alflag == 0) { */
253 if (*iter++ != '.' || count > 32) {
254 ptr = iter;
255 length = 0;
256 continue;
257 }
258
259 /* two consecutive dots don't look correct */
260 if (*iter == '.') {
261 ptr = iter + 1;
262 length = 0;
263 continue;
264 }
265
266 if (FP_strnicmp (iter, "edu", 3) == 0 ||
267 FP_strnicmp (iter, "gov", 3) == 0) {
268 /* hey, that's an ftp address */
269 while (isalpha (*iter) || isdigit (*iter) || *iter == '.')
270 iter++;
271 ptr = iter;
272 length = 0;
273 continue;
274 }
275
276 length += count + 1;
277 count = 0;
278
279 while ((isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL||
280 iter[count]=='/') && iter[count] && iter[count] != '.')
281 count++;
282
283 if (iter[count]==':' && iter[count+1]=='/') {
284 /* looks like stuff from a mail server */
285 ptr = iter + 1;
286 length = 0;
287 continue;
288 }
289
290 if (count > 8 || iter == ptonum) {
291 ptr = iter;
292 length = 0;
293 continue;
294 }
295
296 if (iter[count] != '.') {
297 length += count;
298 break;
299 }
300
301 while (iter[count] &&
302 (isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL||
303 iter[count]=='/'))
304 count++;
305
306 if (iter[count]==':' && iter[count+1]=='/') {
307 /* looks like stuff from a mail server */
308 ptr = iter + 1;
309 length = 0;
310 continue;
311 }
312
313 if (count < 12 && iter != ptonum) {
314 length += count;
315 break;
316 }
317
318 ptr = iter;
319 length = 0;
320 }
321 }
322
323 if (length == 0) { /* No filename found, use subject line for ident */
324 ptr = subject;
325
326 while (*ptr && !isalpha (*ptr))
327 ptr++;
328
329 while ((isalnum(ptr[length])||strchr(fnchars,ptr[length])!=NULL||
330 ptr[length] == '/') &&
331 ptr[length] && ptr+length!=part && ptr+length!=ptonum)
332 length++;
333
334 if (length) {
335 if (ptr[length] == '\0' || ptr[length] == 0x0a || ptr[length] == 0x0d) {
336 length--;
337
338 /*
339 * I used to cut off digits from the end of the string, but
340 * let's try to live without. We want to distinguish
341 * DUTCH951 from DUTCH952
342 *
343 * while ((ptr[length] == ' ' || isdigit (ptr[length])) && length > 0)
344 * length--;
345 */
346 }
347 else {
348 length--;
349
350 while (ptr[length] == ' ' && length > 0)
351 length--;
352 }
353 length++;
354 }
355 }
356
357 if (length == 0) { /* Still found nothing? We need *something*! */
358 ptr = nofname;
359 length = strlen (nofname);
360 }
361
362 if ((result = (char *) malloc (length + 1)) == NULL) {
363 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
364 uustring (S_OUT_OF_MEMORY), length+1);
365 return NULL;
366 }
367
368 memcpy (result, ptr, length);
369 result[length] = '\0';
370
371 return result;
372 }
373
374 /*
375 * Extract the Part Number from the subject line.
376 * We look first for numbers in (#/#)'s, then for numbers in [#/#]'s
377 * and then for digits that are not part of a string.
378 * If we cannot find anything, assume it is the next part of the
379 * previous file.
380 * If we find a part number, we put a pointer to it in *where. This is
381 * done so that the UUGetFileName function doesn't accidentally use the
382 * part number as the file name. *whend points to the end of this part
383 * number.
384 **/
385
386 static int
387 UUGetPartNo (char *subject, char **where, char **whend)
388 {
389 char *ptr = subject, *iter, *delim, bdel[2]=" ";
390 int count, length=0, bpc;
391
392 *where = NULL; bdel[0] = ' ';
393 *whend = NULL; bdel[1] = '\0';
394
395 iter = NULL;
396 delim = "";
397
398 if (subject == NULL)
399 return -1;
400
401 if (uu_ignreply &&
402 (subject[0] == 'R' || subject[0] == 'r') && /* Ignore replies, but not */
403 (subject[1] == 'E' || subject[1] == 'e') && /* reposts */
404 (subject[2] == ':' || subject[2] == ' '))
405 return -2;
406
407 /*
408 * First try numbers in () or [] (or vice versa, according to bracket
409 * policy)
410 */
411
412 for (bpc=0, length=0; brackchr[uu_bracket_policy][bpc]; bpc+=2) {
413 ptr = subject;
414 while ((iter = strchr (ptr, brackchr[uu_bracket_policy][bpc])) != NULL) {
415 count = length = 0; iter++;
416
417 while (*iter == ' ' || *iter == '#')
418 iter++;
419
420 if (!isdigit (*iter)) {
421 ptr = iter;
422 continue;
423 }
424 while (isdigit (iter[count]))
425 count++;
426 length = count;
427
428 if (iter[count] == '\0' || iter[count+1] == '\0') {
429 iter += count;
430 length = 0;
431 break;
432 }
433 if (iter[count] == brackchr[uu_bracket_policy][bpc+1]) {
434 *where = iter;
435 bdel[0] = brackchr[uu_bracket_policy][bpc+1];
436 delim = bdel;
437 break;
438 }
439
440 while (iter[count] == ' ' || iter[count] == '#' ||
441 iter[count] == '/' || iter[count] == '\\') count++;
442
443 if (FP_strnicmp (iter + count, "of", 2) == 0)
444 count += 2;
445
446 while (iter[count] == ' ') count++;
447 while (isdigit (iter[count])) count++;
448 while (iter[count] == ' ') count++;
449
450 if (iter[count] == brackchr[uu_bracket_policy][bpc+1]) {
451 *where = iter;
452 bdel[0] = brackchr[uu_bracket_policy][bpc+1];
453 delim = bdel;
454 break;
455 }
456
457 length = 0;
458 ptr = iter;
459 }
460 if (length)
461 break;
462 }
463
464 /*
465 * look for the string "part " followed by a number
466 */
467
468 if (length == 0) {
469 if ((iter = FP_stristr (subject, "part ")) != NULL) {
470 iter += 5;
471
472 while (isspace (*iter) || *iter == '.' || *iter == '-')
473 iter++;
474
475 while (isdigit (iter[length]))
476 length++;
477
478 if (length == 0) {
479 if (FP_strnicmp (iter, "one", 3) == 0) length = 1;
480 else if (FP_strnicmp (iter, "two", 3) == 0) length = 2;
481 else if (FP_strnicmp (iter, "three", 5) == 0) length = 3;
482 else if (FP_strnicmp (iter, "four", 4) == 0) length = 4;
483 else if (FP_strnicmp (iter, "five", 4) == 0) length = 5;
484 else if (FP_strnicmp (iter, "six", 3) == 0) length = 6;
485 else if (FP_strnicmp (iter, "seven", 5) == 0) length = 7;
486 else if (FP_strnicmp (iter, "eight", 5) == 0) length = 8;
487 else if (FP_strnicmp (iter, "nine", 4) == 0) length = 9;
488 else if (FP_strnicmp (iter, "ten", 3) == 0) length = 10;
489
490 if (length && (*whend = strchr (iter, ' '))) {
491 *where = iter;
492 return length;
493 }
494 else
495 length = 0;
496 }
497 else {
498 *where = iter;
499 delim = "of";
500 }
501 }
502 }
503
504 /*
505 * look for the string "part" followed by a number
506 */
507
508 if (length == 0) {
509 if ((iter = FP_stristr (subject, "part")) != NULL) {
510 iter += 4;
511
512 while (isspace (*iter) || *iter == '.' || *iter == '-')
513 iter++;
514
515 while (isdigit (iter[length]))
516 length++;
517
518 if (length == 0) {
519 if (FP_strnicmp (iter, "one", 3) == 0) length = 1;
520 else if (FP_strnicmp (iter, "two", 3) == 0) length = 2;
521 else if (FP_strnicmp (iter, "three", 5) == 0) length = 3;
522 else if (FP_strnicmp (iter, "four", 4) == 0) length = 4;
523 else if (FP_strnicmp (iter, "five", 4) == 0) length = 5;
524 else if (FP_strnicmp (iter, "six", 3) == 0) length = 6;
525 else if (FP_strnicmp (iter, "seven", 5) == 0) length = 7;
526 else if (FP_strnicmp (iter, "eight", 5) == 0) length = 8;
527 else if (FP_strnicmp (iter, "nine", 4) == 0) length = 9;
528 else if (FP_strnicmp (iter, "ten", 3) == 0) length = 10;
529
530 if (length && (*whend = strchr (iter, ' '))) {
531 *where = iter;
532 return length;
533 }
534 else
535 length = 0;
536 }
537 else {
538 *where = iter;
539 delim = "of";
540 }
541 }
542 }
543
544 /*
545 * look for [0-9]* "of" [0-9]*
546 */
547
548 if (length == 0) {
549 if ((iter = FP_strirstr (subject, "of")) != NULL) {
550 while (iter>subject && isspace (*(iter-1)))
551 iter--;
552 if (isdigit(*(iter-1))) {
553 while (iter>subject && isdigit (*(iter-1)))
554 iter--;
555 if (!isdigit (*iter) && !isalpha (*iter) && *iter != '.')
556 iter++;
557 ptr = iter;
558
559 while (isdigit (*ptr)) {
560 ptr++; length++;
561 }
562 *where = iter;
563 delim = "of";
564 }
565 }
566 }
567
568 /*
569 * look for whitespace-separated (or '/'-separated) digits
570 */
571
572 if (length == 0) {
573 ptr = subject;
574
575 while (*ptr && length==0) {
576 while (*ptr && !isdigit (*ptr))
577 ptr++;
578 if (isdigit (*ptr) && (ptr==subject || *ptr==' ' || *ptr=='/')) {
579 while (isdigit (ptr[length]))
580 length++;
581 if (ptr[length]!='\0' && ptr[length]!=' ' && ptr[length]!='/') {
582 ptr += length;
583 length = 0;
584 }
585 else {
586 iter = ptr;
587 bdel[0] = ptr[length];
588 delim = bdel;
589 }
590 }
591 else {
592 while (isdigit (*ptr))
593 ptr++;
594 }
595 }
596 }
597
598 /*
599 * look for _any_ digits -- currently disabled, because it also fell
600 * for "part numbers" in file names
601 */
602
603 #if 0
604 if (length == 0) {
605 count = strlen(subject) - 1;
606 ptr = subject;
607
608 while (count > 0) {
609 if (!isdigit(ptr[count])||isalpha(ptr[count+1])||ptr[count+1] == '.') {
610 count--;
611 continue;
612 }
613 length = 0;
614
615 while (count >= 0 && isdigit (ptr[count])) {
616 count--; length++;
617 }
618 if (count>=0 && ((isalpha (ptr[count]) &&
619 (ptr[count] != 's' || ptr[count+1] != 't') &&
620 (ptr[count] != 'n' || ptr[count+1] != 'd')) ||
621 ptr[count] == '/' || ptr[count] == '.' ||
622 ptr[count] == '-' || ptr[count] == '_')) {
623 length = 0;
624 continue;
625 }
626 count++;
627 iter = ptr + count;
628
629 if (length > 4) {
630 length = 0;
631 continue;
632 }
633 *where = iter;
634 delim = "of";
635 break;
636 }
637 }
638 #endif
639
640 /*
641 * look for part numbering as string
642 */
643
644 if (length == 0) {
645 /*
646 * some people use the strangest things, including spelling mistakes :-)
647 */
648 if ((iter = FP_stristr (subject, "first")) != NULL) length = 1;
649 else if ((iter = FP_stristr (subject, "second")) != NULL) length = 2;
650 else if ((iter = FP_stristr (subject, "third")) != NULL) length = 3;
651 else if ((iter = FP_stristr (subject, "forth")) != NULL) length = 4;
652 else if ((iter = FP_stristr (subject, "fourth")) != NULL) length = 4;
653 else if ((iter = FP_stristr (subject, "fifth")) != NULL) length = 5;
654 else if ((iter = FP_stristr (subject, "sixth")) != NULL) length = 6;
655 else if ((iter = FP_stristr (subject, "seventh")) != NULL) length = 7;
656 else if ((iter = FP_stristr (subject, "eigth")) != NULL) length = 8;
657 else if ((iter = FP_stristr (subject, "eighth")) != NULL) length = 8;
658 else if ((iter = FP_stristr (subject, "nineth")) != NULL) length = 9;
659 else if ((iter = FP_stristr (subject, "ninth")) != NULL) length = 9;
660 else if ((iter = FP_stristr (subject, "tenth")) != NULL) length = 10;
661 else iter = NULL;
662
663 if (length && iter && (*whend = strchr (iter, ' '))) {
664 *where = iter;
665 return length;
666 }
667 else
668 length = 0;
669 }
670
671 if (iter == NULL || length == 0) /* should be equivalent */
672 return -1;
673
674 *where = iter;
675
676 if (delim && delim[0]) {
677 if ((*whend=FP_stristr (iter, delim)) != NULL && (*whend - *where) < 12) {
678 ptr = (*whend += strlen (delim));
679
680 while (*ptr == ' ')
681 ptr++;
682
683 if (isdigit (*ptr)) {
684 *whend = ptr;
685 while (isdigit (**whend))
686 *whend += 1;
687 }
688 }
689 else {
690 *whend = iter + length;
691 }
692 }
693 else {
694 *whend = iter + length;
695 }
696
697 return atoi (iter);
698 }
699
700 /*
701 * Obtain and process some information about the data.
702 **/
703
704 uufile *
705 UUPreProcessPart (fileread *data, int *ret)
706 {
707 char *where, *whend, temp[80], *ptr, *p2;
708 uufile *result;
709
710 if ((result = (uufile *) malloc (sizeof (uufile))) == NULL) {
711 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
712 uustring (S_OUT_OF_MEMORY), sizeof (uufile));
713 *ret = UURET_NOMEM;
714 return NULL;
715 }
716 memset (result, 0, sizeof (uufile));
717
718 if (data->partno) {
719 where = whend = NULL;
720 result->partno = data->partno;
721 }
722 else if (uu_dumbness) {
723 result->partno = -1;
724 where = whend = NULL;
725 }
726 else if ((result->partno=UUGetPartNo(data->subject,&where,&whend)) == -2) {
727 *ret = UURET_NODATA;
728 UUkillfile (result);
729 return NULL;
730 }
731
732 if (data->filename != NULL) {
733 if ((result->filename = FP_strdup (data->filename)) == NULL) {
734 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
735 uustring (S_OUT_OF_MEMORY),
736 strlen (data->filename)+1);
737 *ret = UURET_NOMEM;
738 UUkillfile (result);
739 return NULL;
740 }
741 }
742 else
743 result->filename = NULL;
744
745 if (uu_dumbness <= 1)
746 result->subfname = UUGetFileName (data->subject, where, whend);
747 else
748 result->subfname = NULL;
749
750 result->mimeid = FP_strdup (data->mimeid);
751 result->mimetype = FP_strdup (data->mimetype);
752
753 if (result->partno == -1 &&
754 (data->uudet == PT_ENCODED || data->uudet == QP_ENCODED))
755 result->partno = 1;
756
757 if (data->flags & FL_SINGLE) {
758 /*
759 * Don't touch this part. But it should really have a filename
760 */
761 if (result->filename == NULL) {
762 sprintf (temp, "%s.%03d", nofname, ++nofnum);
763 result->filename = FP_strdup (temp);
764 }
765 if (result->subfname == NULL)
766 result->subfname = FP_strdup (result->filename);
767
768 if (result->filename == NULL ||
769 result->subfname == NULL) {
770 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
771 uustring (S_OUT_OF_MEMORY),
772 (result->filename==NULL)?
773 (strlen(temp)+1):(strlen(result->filename)+1));
774 *ret = UURET_NOMEM;
775 UUkillfile(result);
776 return NULL;
777 }
778 if (result->partno == -1)
779 result->partno = 1;
780 }
781 else if (result->subfname == NULL && data->uudet &&
782 (data->begin || result->partno == 1 ||
783 (!uu_dumbness && result->partno == -1 &&
784 (data->subject != NULL || result->filename != NULL)))) {
785 /*
786 * If it's the first part of something and has some valid data, but
787 * no subject or anything, initialize lastvalid
788 */
789 /*
790 * in this case, it really _should_ have a filename somewhere
791 */
792 if (result->filename != NULL)
793 result->subfname = FP_strdup (result->filename);
794 else { /* if not, escape to UNKNOWN. We need to fill subfname */
795 sprintf (temp, "%s.%03d", nofname, ++nofnum);
796 result->subfname = FP_strdup (temp);
797 }
798 /*
799 * in case the strdup failed
800 */
801 if (result->subfname == NULL) {
802 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
803 uustring (S_OUT_OF_MEMORY),
804 (result->filename)?
805 (strlen(result->filename)+1):(strlen(temp)+1));
806 *ret = UURET_NOMEM;
807 UUkillfile (result);
808 return NULL;
809 }
810 /*
811 * if it's also got an 'end', or is the last part in a MIME-Mail,
812 * then don't set lastvalid
813 */
814 if (!data->end && (!data->partno || data->partno != data->maxpno)) {
815 /*
816 * initialize lastvalid
817 */
818 lastvalid = 1;
819 lastenc = data->uudet;
820 lastpart = result->partno = 1;
821 FP_strncpy (uucheck_lastname, result->subfname, 256);
822 }
823 else
824 result->partno = 1;
825 }
826 else if (result->subfname == NULL && data->uudet && data->mimeid) {
827 /*
828 * if it's got a file name, use it. Else use the mime-id for identifying
829 * this part, and hope there's no other files encoded in the same message
830 * under the same id.
831 */
832 if (result->filename)
833 result->subfname = FP_strdup (result->filename);
834 else
835 result->subfname = FP_strdup (result->mimeid);
836 }
837 else if (result->subfname == NULL && data->uudet) {
838 /*
839 * ff we have lastvalid, use it. Make an exception for
840 * Base64-encoded files.
841 */
842 if (data->uudet == B64ENCODED) {
843 /*
844 * Assume it's the first part. I wonder why it's got no part number?
845 */
846 if (result->filename != NULL)
847 result->subfname = FP_strdup (result->filename);
848 else { /* if not, escape to UNKNOWN. We need to fill subfname */
849 sprintf (temp, "%s.%03d", nofname, ++nofnum);
850 result->subfname = FP_strdup (temp);
851 }
852 if (result->subfname == NULL) {
853 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
854 uustring (S_OUT_OF_MEMORY),
855 (result->filename)?
856 (strlen(result->filename)+1):(strlen(temp)+1));
857 *ret = UURET_NOMEM;
858 UUkillfile (result);
859 return NULL;
860 }
861 lastvalid = 0;
862 }
863 else if (lastvalid && data->uudet == lastenc) {
864 result->subfname = FP_strdup (uucheck_lastname);
865 result->partno = ++lastpart;
866
867 /*
868 * if it's the last part, invalidate lastvalid
869 */
870 if (data->end || (data->partno && data->partno == data->maxpno))
871 lastvalid = 0;
872 }
873 else {
874 /*
875 * it's got no info, it's got no begin, and we don't know anything
876 * about this part. Let's forget all about it.
877 */
878 *ret = UURET_NODATA;
879 UUkillfile (result);
880 return NULL;
881 }
882 }
883 else if (result->subfname == NULL && result->partno == -1) {
884 /*
885 * This, too, is a part without any useful information that we
886 * should forget about.
887 */
888 *ret = UURET_NODATA;
889 UUkillfile (result);
890 return NULL;
891 }
892 else if (result->subfname == NULL) {
893 /*
894 * This is a part without useful subject name, a valid part number
895 * but no encoded data. It *could* be the zeroeth part of something,
896 * but we don't care here. Just forget it.
897 */
898 *ret = UURET_NODATA;
899 UUkillfile (result);
900 return NULL;
901 }
902
903 /*
904 * now, handle some cases where we have a useful subject but no
905 * useful part number
906 */
907
908 if (result->partno == -1 && data->begin) {
909 /*
910 * hmm, this is reason enough to initialize lastvalid, at least
911 * if we have no end
912 */
913 if (!data->end) {
914 FP_strncpy (uucheck_lastname, result->subfname, 256);
915 result->partno = lastpart = 1;
916 lastenc = data->uudet;
917 lastvalid = 1;
918 }
919 else
920 result->partno = 1;
921 }
922 else if (result->partno == -1 && data->uudet) {
923 if (lastvalid && FP_stricmp (uucheck_lastname, result->subfname) == 0) {
924 /*
925 * if the subject filename is the same as last time, use part no
926 * of lastvalid. If at end, invalidate lastvalid
927 */
928 result->partno = ++lastpart;
929
930 if (data->end)
931 lastvalid = 0;
932 }
933 else {
934 /*
935 * data but no part no. It's something UUInsertPartToList() should
936 * handle
937 */
938 goto skipcheck;
939 }
940 }
941 else if (result->partno == -1) {
942 /*
943 * it's got no data, so why should we need this one anyway?
944 */
945 *ret = UURET_NODATA;
946 UUkillfile (result);
947 return NULL;
948 }
949
950 /*
951 * at this point, the part should have a valid subfname and a valid
952 * part number. If it doesn't, then fail.
953 */
954 if (result->subfname == NULL || result->partno == -1) {
955 *ret = UURET_NODATA;
956 UUkillfile (result);
957 return NULL;
958 }
959
960 skipcheck:
961
962 if (result->filename) {
963 if (*(ptr = FP_cutdir (result->filename))) {
964 p2 = FP_strdup (ptr);
965 FP_free (result->filename);
966 result->filename = p2;
967 }
968 }
969
970 result->data = data;
971 result->NEXT = NULL;
972
973 *ret = UURET_OK;
974
975 return result;
976 }
977
978 /*
979 * Insert one part of a file into the global list
980 **/
981
982 int
983 UUInsertPartToList (uufile *data)
984 {
985 uulist *iter = UUGlobalFileList, *unew;
986 uufile *fiter, *last;
987
988 /*
989 * Part belongs together, if
990 * (a) The file name received from the subject lines match _or_
991 * the MIME-IDs match,
992 * (b) Not both parts have a begin line
993 * (c) Not both parts have an end line
994 * (d) Both parts don't have different MIME-IDs
995 * (e) Both parts don't encode different files
996 * (f) The other part wants to stay alone (FL_SINGLE)
997 */
998
999 /*
1000 * check if this part wants to be left alone. If so, don't bother
1001 * to do all the checks
1002 */
1003
1004 while (iter) {
1005 if (data->data->flags & FL_SINGLE) {
1006 /* this space intentionally left blank */
1007 }
1008 else if ((FP_stricmp (data->subfname, iter->subfname) == 0 ||
1009 (data->mimeid && iter->mimeid &&
1010 strcmp (data->mimeid, iter->mimeid) == 0)) &&
1011 !(iter->begin && data->data->begin) &&
1012 !(iter->end && data->data->end) &&
1013 !(data->mimeid && iter->mimeid &&
1014 strcmp (data->mimeid, iter->mimeid) != 0) &&
1015 !(data->filename && iter->filename &&
1016 strcmp (data->filename, iter->filename) != 0) &&
1017 !(iter->flags & FL_SINGLE)) {
1018
1019 /*
1020 * if we already have this part, don't try to insert it
1021 */
1022
1023 for (fiter=iter->thisfile;
1024 fiter && (data->partno>fiter->partno) && !fiter->data->end;
1025 fiter=fiter->NEXT)
1026 /* empty loop */ ;
1027 if (fiter &&
1028 (data->partno==fiter->partno ||
1029 (data->partno > fiter->partno && fiter->data->end)))
1030 goto goahead;
1031
1032 if (iter->filename == NULL && data->filename != NULL) {
1033 if ((iter->filename = FP_strdup (data->filename)) == NULL)
1034 return UURET_NOMEM;
1035 }
1036
1037 /*
1038 * special case when we might have tagged a part as Base64 when the
1039 * file was really XX
1040 */
1041
1042 if (data->data->uudet == B64ENCODED &&
1043 iter->uudet == XX_ENCODED && iter->begin) {
1044 data->data->uudet = XX_ENCODED;
1045 }
1046 else if (data->data->uudet == XX_ENCODED && data->data->begin &&
1047 iter->uudet == B64ENCODED) {
1048 iter->uudet = XX_ENCODED;
1049
1050 fiter = iter->thisfile;
1051 while (fiter) {
1052 fiter->data->uudet = XX_ENCODED;
1053 fiter = fiter->NEXT;
1054 }
1055 }
1056
1057 /*
1058 * If this is from a Message/Partial, we believe only the
1059 * iter->uudet from the first part
1060 */
1061 if (data->data->flags & FL_PARTIAL) {
1062 if (data->partno == 1) {
1063 iter->uudet = data->data->uudet;
1064 iter->flags = data->data->flags;
1065 }
1066 }
1067 else {
1068 if (data->data->uudet) iter->uudet = data->data->uudet;
1069 if (data->data->flags) iter->flags = data->data->flags;
1070 }
1071
1072 if (iter->mode == 0 && data->data->mode != 0)
1073 iter->mode = data->data->mode;
1074 if (data->data->begin) iter->begin = (data->partno)?data->partno:1;
1075 if (data->data->end) iter->end = (data->partno)?data->partno:1;
1076
1077 if (data->mimetype) {
1078 FP_free (iter->mimetype);
1079 iter->mimetype = FP_strdup (data->mimetype);
1080 }
1081
1082 /*
1083 * insert part at the beginning
1084 */
1085
1086 if (data->partno != -1 && data->partno < iter->thisfile->partno) {
1087 iter->state = UUFILE_READ;
1088 data->NEXT = iter->thisfile;
1089 iter->thisfile = data;
1090 return UURET_OK;
1091 }
1092
1093 /*
1094 * insert part somewhere else
1095 */
1096
1097 iter->state = UUFILE_READ; /* prepare for re-checking */
1098 fiter = iter->thisfile;
1099 last = NULL;
1100
1101 while (fiter) {
1102 /*
1103 * if we find the same part no again, check which one looks better
1104 */
1105 if (data->partno == fiter->partno) {
1106 if (fiter->data->subject == NULL)
1107 return UURET_NODATA;
1108 else if (FP_stristr (fiter->data->subject, "repost") != NULL &&
1109 FP_stristr (data->data->subject, "repost") == NULL)
1110 return UURET_NODATA;
1111 else if (fiter->data->uudet && !data->data->uudet)
1112 return UURET_NODATA;
1113 else {
1114 /*
1115 * replace
1116 */
1117 data->NEXT = fiter->NEXT;
1118 fiter->NEXT = NULL;
1119 UUkillfile (fiter);
1120
1121 if (last == NULL)
1122 iter->thisfile = data;
1123 else
1124 last->NEXT = data;
1125
1126 return UURET_OK;
1127 }
1128 }
1129
1130 /*
1131 * if at the end of the part list, add it
1132 */
1133
1134 if (fiter->NEXT == NULL ||
1135 (data->partno != -1 && data->partno < fiter->NEXT->partno)) {
1136 data->NEXT = fiter->NEXT;
1137 fiter->NEXT = data;
1138
1139 if (data->partno == -1)
1140 data->partno = fiter->partno + 1;
1141
1142 return UURET_OK;
1143 }
1144 last = fiter;
1145 fiter = fiter->NEXT;
1146 }
1147
1148 return UURET_OK; /* Shouldn't get here */
1149 }
1150 goahead:
1151 /*
1152 * we need iter below
1153 */
1154 if (iter->NEXT == NULL)
1155 break;
1156
1157 iter = iter->NEXT;
1158 }
1159 /*
1160 * handle new entry
1161 */
1162
1163 if (data->partno == -1) {
1164 /*
1165 * if it's got no part no, and it's MIME mail, then assume this is
1166 * part no. 1. If it's not MIME, then we can't handle it; if it
1167 * had a 'begin', it'd have got a part number assigned by
1168 * UUPreProcessPart().
1169 */
1170 if (data->data->uudet == B64ENCODED || data->data->uudet == BH_ENCODED)
1171 data->partno = 1;
1172 else
1173 return UURET_NODATA;
1174 }
1175
1176 if ((unew = (uulist *) malloc (sizeof (uulist))) == NULL) {
1177 return UURET_NOMEM;
1178 }
1179
1180 if ((unew->subfname = FP_strdup (data->subfname)) == NULL) {
1181 FP_free (unew);
1182 return UURET_NOMEM;
1183 }
1184
1185 if (data->filename != NULL) {
1186 if ((unew->filename = FP_strdup (data->filename)) == NULL) {
1187 FP_free (unew->subfname);
1188 FP_free (unew);
1189 return UURET_NOMEM;
1190 }
1191 }
1192 else
1193 unew->filename = NULL;
1194
1195 if (data->mimeid != NULL) {
1196 if ((unew->mimeid = FP_strdup (data->mimeid)) == NULL) {
1197 FP_free (unew->subfname);
1198 FP_free (unew->filename);
1199 FP_free (unew);
1200 return UURET_NOMEM;
1201 }
1202 }
1203 else
1204 unew->mimeid = NULL;
1205
1206 if (data->mimetype != NULL) {
1207 if ((unew->mimetype = FP_strdup (data->mimetype)) == NULL) {
1208 FP_free (unew->mimeid);
1209 FP_free (unew->subfname);
1210 FP_free (unew->filename);
1211 FP_free (unew);
1212 return UURET_NOMEM;
1213 }
1214 }
1215 else
1216 unew->mimetype = NULL;
1217
1218 unew->state = UUFILE_READ;
1219 unew->binfile = NULL;
1220 unew->thisfile = data;
1221 unew->mode = data->data->mode;
1222 unew->uudet = data->data->uudet;
1223 unew->flags = data->data->flags;
1224 unew->begin = (data->data->begin) ? ((data->partno)?data->partno:1) : 0;
1225 unew->end = (data->data->end) ? ((data->partno)?data->partno:1) : 0;
1226 unew->misparts = NULL;
1227 unew->haveparts = NULL;
1228 unew->NEXT = NULL;
1229
1230 if (iter == NULL)
1231 UUGlobalFileList = unew;
1232 else
1233 iter->NEXT = unew;
1234
1235 return UURET_OK;
1236 }
1237
1238 /*
1239 * At this point, all files are read in and stored in the
1240 * "UUGlobalFileList". Do some checking. All parts there?
1241 **/
1242
1243 uulist *
1244 UUCheckGlobalList (void)
1245 {
1246 int misparts[MAXPLIST], haveparts[MAXPLIST];
1247 int miscount, havecount, count, flag, part;
1248 uulist *liter=UUGlobalFileList, *prev;
1249 uufile *fiter;
1250 long thesize;
1251
1252 while (liter) {
1253 miscount = 0;
1254 thesize = 0;
1255
1256 if (liter->state & UUFILE_OK) {
1257 liter = liter->NEXT;
1258 continue;
1259 }
1260 else if ((liter->uudet == QP_ENCODED ||
1261 liter->uudet == PT_ENCODED) &&
1262 (liter->flags & FL_SINGLE)) {
1263 if ((liter->flags&FL_PROPER)==0)
1264 liter->size = -1;
1265 else
1266 liter->size = liter->thisfile->data->length;
1267
1268 liter->state = UUFILE_OK;
1269 continue;
1270 }
1271 else if ((fiter = liter->thisfile) == NULL) {
1272 liter->state = UUFILE_NODATA;
1273 liter = liter->NEXT;
1274 continue;
1275 }
1276
1277 /*
1278 * Re-Check this file
1279 */
1280
1281 flag = 0;
1282 miscount = 0;
1283 havecount = 0;
1284 thesize = 0;
1285 liter->state = UUFILE_READ;
1286
1287 /*
1288 * search encoded data
1289 */
1290
1291 while (fiter && !fiter->data->uudet) {
1292 if (havecount<MAXPLIST) {
1293 haveparts[havecount++] = fiter->partno;
1294 }
1295 fiter = fiter->NEXT;
1296 }
1297
1298 if (fiter == NULL) {
1299 liter->state = UUFILE_NODATA;
1300 liter = liter->NEXT;
1301 continue;
1302 }
1303
1304 if (havecount<MAXPLIST) {
1305 haveparts[havecount++] = fiter->partno;
1306 }
1307
1308 if ((part = fiter->partno) > 1) {
1309 if (!fiter->data->begin) {
1310 for (count=1; count < part && miscount < MAXPLIST; count++)
1311 misparts[miscount++] = count;
1312 }
1313 }
1314
1315 /*
1316 * don't care if so many parts are missing
1317 */
1318
1319 if (miscount >= MAXPLIST) {
1320 liter->state = UUFILE_MISPART;
1321 liter = liter->NEXT;
1322 continue;
1323 }
1324
1325 if (liter->uudet == B64ENCODED ||
1326 liter->uudet == QP_ENCODED ||
1327 liter->uudet == PT_ENCODED)
1328 flag |= 3; /* Don't need begin or end with Base64 or plain text*/
1329
1330 if (fiter->data->begin) flag |= 1;
1331 if (fiter->data->end) flag |= 2;
1332 if (fiter->data->uudet) flag |= 4;
1333
1334 /*
1335 * guess size of part
1336 */
1337
1338 switch (fiter->data->uudet) {
1339 case UU_ENCODED:
1340 case XX_ENCODED:
1341 thesize += 3*fiter->data->length/4;
1342 thesize -= 3*fiter->data->length/124; /* substract 2 of 62 chars */
1343 break;
1344 case B64ENCODED:
1345 thesize += 3*fiter->data->length/4;
1346 thesize -= fiter->data->length/52; /* substract 2 of 78 chars */
1347 break;
1348 case QP_ENCODED:
1349 case PT_ENCODED:
1350 thesize += fiter->data->length;
1351 break;
1352 }
1353
1354 fiter = fiter->NEXT;
1355
1356 while (fiter != NULL) {
1357 for (count=part+1; count<fiter->partno && miscount<MAXPLIST; count++)
1358 misparts[miscount++] = count;
1359
1360 part = fiter->partno;
1361
1362 if (havecount<MAXPLIST)
1363 haveparts[havecount++]=part;
1364
1365 if (fiter->data->begin) flag |= 1;
1366 if (fiter->data->end) flag |= 2;
1367 if (fiter->data->uudet) flag |= 4;
1368
1369 switch (fiter->data->uudet) {
1370 case UU_ENCODED:
1371 case XX_ENCODED:
1372 thesize += 3*fiter->data->length/4;
1373 thesize -= 3*fiter->data->length/124; /* substract 2 of 62 chars */
1374 break;
1375 case B64ENCODED:
1376 thesize += 3*fiter->data->length/4;
1377 thesize -= fiter->data->length/52; /* substract 2 of 78 chars */
1378 break;
1379 case QP_ENCODED:
1380 case PT_ENCODED:
1381 thesize += fiter->data->length;
1382 break;
1383 }
1384
1385 if (fiter->data->end)
1386 break;
1387
1388 fiter = fiter->NEXT;
1389 }
1390
1391 /*
1392 * if in fast mode, we don't notice an 'end'. So if its uu or xx
1393 * encoded, there's a begin line and encoded data, assume it's
1394 * there.
1395 */
1396
1397 if (uu_fast_scanning && (flag & 0x01) && (flag & 0x04) &&
1398 (liter->uudet == UU_ENCODED || liter->uudet == XX_ENCODED))
1399 flag |= 2;
1400
1401 /*
1402 * Set the parts we have and/or missing
1403 */
1404
1405 FP_free (liter->haveparts);
1406 FP_free (liter->misparts);
1407
1408 liter->haveparts = NULL;
1409 liter->misparts = NULL;
1410
1411 if (havecount) {
1412 if ((liter->haveparts=(int*)malloc((havecount+1)*sizeof(int)))!=NULL) {
1413 memcpy (liter->haveparts, haveparts, havecount*sizeof(int));
1414 liter->haveparts[havecount] = 0;
1415 }
1416 }
1417
1418 if (miscount) {
1419 if ((liter->misparts=(int*)malloc((miscount+1)*sizeof(int)))!=NULL) {
1420 memcpy (liter->misparts, misparts, miscount*sizeof(int));
1421 liter->misparts[miscount] = 0;
1422 }
1423 liter->state |= UUFILE_MISPART;
1424 }
1425
1426 /*
1427 * Finalize checking
1428 */
1429
1430 if ((flag & 4) == 0) liter->state |= UUFILE_NODATA;
1431 if ((flag & 1) == 0) liter->state |= UUFILE_NOBEGIN;
1432 if ((flag & 2) == 0) liter->state |= UUFILE_NOEND;
1433
1434 if ((flag & 7) == 7 && miscount==0) {
1435 liter->state = UUFILE_OK;
1436 }
1437
1438 if ((uu_fast_scanning && (liter->flags&FL_PROPER)==0) || thesize<=0)
1439 liter->size = -1;
1440 else
1441 liter->size = thesize;
1442
1443 if (liter->state==UUFILE_OK &&
1444 (liter->filename==NULL || liter->filename[0]=='\0')) {
1445 /*
1446 * Emergency backup if the file does not have a filename
1447 */
1448 FP_free (liter->filename);
1449 if (liter->subfname && liter->subfname[0] &&
1450 FP_strpbrk (liter->subfname, "()[];: ") == NULL)
1451 liter->filename = FP_strdup (liter->subfname);
1452 else {
1453 sprintf (uucheck_tempname, "%s.%03d", nofname, ++nofnum);
1454 liter->filename = FP_strdup (uucheck_tempname);
1455 }
1456 }
1457 liter = liter->NEXT;
1458 }
1459
1460 /*
1461 * Sets back (PREV) links
1462 */
1463
1464 liter = UUGlobalFileList;
1465 prev = NULL;
1466
1467 while (liter) {
1468 liter->PREV = prev;
1469 prev = liter;
1470 liter = liter->NEXT;
1471 }
1472
1473 return UUGlobalFileList;
1474 }
1475