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