ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uucheck.c
Revision: 1.36
Committed: Tue Nov 29 05:21:02 2022 UTC (17 months, 2 weeks ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.35: +3 -2 lines
Log Message:
*** empty log message ***

File Contents

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