ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uucheck.c
Revision: 1.26
Committed: Sat Dec 12 10:48:58 2020 UTC (3 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.25: +0 -7 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$";
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 static char *
106 UUGetFileName (char *subject, char *ptonum, char *ptonend)
107 {
108 char *ptr = subject, *iter, *result, *part;
109 int count, length=0, alflag=0;
110
111 /*
112 * If this file has no subject line, assume it is the next part of the
113 * previous file (this is done in UUPreProcessPart)
114 **/
115
116 if (subject == NULL)
117 return NULL;
118
119 /*
120 * If the subject starts with 'Re', it is ignored
121 * REPosts or RETries are not ignored!
122 **/
123
124 if (uu_ignreply &&
125 (subject[0] == 'R' || subject[0] == 'r') &&
126 (subject[1] == 'E' || subject[1] == 'e') &&
127 (subject[2] == ':' || subject[2] == ' ')) {
128 return uu_FileNameCallback ? uu_FileNameCallback(uu_FNCBArg, subject, NULL) : NULL;
129 }
130
131 /*
132 * Ignore a "Repost" prefix of the subject line. We don't want to get
133 * a file named "Repost" :-)
134 **/
135
136 if (FP_strnicmp (subject, "repost", 6) == 0)
137 subject += 6;
138 if (FP_strnicmp (subject, "re:", 3) == 0)
139 subject += 3;
140
141 while (*subject == ' ' || *subject == ':') subject++;
142
143 part = FP_stristr (subject, "part");
144 if (part == subject) {
145 subject += 4;
146 while (*subject == ' ') subject++;
147 }
148
149 /*
150 * If the file was encoded by uuenview, then the filename is enclosed
151 * in [brackets]. But check what's inside these bracket's, try not to
152 * fall for something other than a filename
153 */
154
155 ptr = subject;
156 while ((iter = strchr (ptr, '[')) != NULL) {
157 if (strchr (iter, ']') == NULL) {
158 ptr = iter + 1;
159 continue;
160 }
161 iter++;
162 while (isspace (*iter))
163 iter++;
164 count = length = alflag = 0;
165 while (iter[count] &&
166 (isalnum (iter[count]) || strchr (fnchars, iter[count])!=NULL)) {
167 if (isalpha (iter[count]))
168 alflag++;
169 count++;
170 }
171 if (count<4 || alflag==0) {
172 ptr = iter + 1;
173 continue;
174 }
175 length = count;
176 while (isspace (iter[count]))
177 count++;
178 if (iter[count] == ']') {
179 ptr = iter;
180 break;
181 }
182 length = 0;
183 ptr = iter + 1;
184 }
185
186 /*
187 * new filename detection routine, fists mostly for files by ftp-by-email
188 * servers that create subject lines with ftp.host.address:/full/path/file
189 * on them. We look for slashes and take the filename from after the last
190 * one ... or at least we try to.
191 */
192
193 if (length == 0) {
194 ptr = subject;
195 while ((iter = strchr (ptr, '/')) != NULL) {
196 if (iter >= ptonum && iter <= ptonend) {
197 ptr = iter + 1;
198 continue;
199 }
200 count = length = 0;
201 iter++;
202 while (iter[count] &&
203 (isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL))
204 count++;
205 if (iter[count] == ' ' && length > 4) {
206 length = count;
207 break;
208 }
209 ptr = iter + ((count)?count:1);
210 }
211 }
212
213 /*
214 * Look for two alphanumeric strings separated by a '.'
215 * (That's most likely a filename)
216 **/
217
218 if (length == 0) {
219 ptr = subject;
220 /* #warning another experimental change */
221 /*while (*ptr && *ptr != 0x0a && *ptr != 0x0d && ptr != part) {*/
222 while (*ptr && *ptr != 0x0a && *ptr != 0x0d) {
223 iter = ptr;
224 count = length = alflag = 0;
225
226 if (FP_strnicmp (ptr, "ftp", 3) == 0) {
227 /* hey, that's an ftp address */
228 while (isalpha (*ptr) || isdigit (*ptr) || *ptr == '.')
229 ptr++;
230 continue;
231 }
232
233 while ((isalnum(*iter)||strchr(fnchars, *iter)!=NULL||
234 *iter=='/') && *iter && iter != ptonum && *iter != '.') {
235 if (isalpha (*iter))
236 alflag = 1;
237
238 count++; iter++;
239 }
240 if (*iter == '\0' || iter == ptonum) {
241 if (iter == ptonum)
242 ptr = ptonend;
243 else
244 ptr = iter;
245
246 length = 0;
247 continue;
248 }
249
250 /* #warning multi-part change experimental, make pluggable */
251 /* if (*iter++ != '.' || count > 32 || alflag == 0) { */
252 if (*iter++ != '.' || count > 32) {
253 ptr = iter;
254 length = 0;
255 continue;
256 }
257
258 /* a dot followed by sth. unesthetically doesn't look right */
259 if (*iter < '0') {
260 ptr = iter + 1;
261 length = 0;
262 continue;
263 }
264
265 if (FP_strnicmp (iter, "edu", 3) == 0 ||
266 FP_strnicmp (iter, "gov", 3) == 0) {
267 /* hey, that's an ftp address */
268 while (isalpha (*iter) || isdigit (*iter) || *iter == '.')
269 iter++;
270 ptr = iter;
271 length = 0;
272 continue;
273 }
274
275 length += count + 1;
276 count = 0;
277
278 while ((isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL||
279 iter[count]=='/') && iter[count] && iter[count] != '.')
280 count++;
281
282 if (iter[count]==':' && iter[count+1]=='/') {
283 /* looks like stuff from a mail server */
284 ptr = iter + 1;
285 length = 0;
286 continue;
287 }
288
289 if (count > 8 || iter == ptonum) {
290 ptr = iter;
291 length = 0;
292 continue;
293 }
294
295 if (iter[count] != '.') {
296 length += count;
297 break;
298 }
299
300 while (iter[count] &&
301 (isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL||
302 iter[count]=='/'))
303 count++;
304
305 if (iter[count]==':' && iter[count+1]=='/') {
306 /* looks like stuff from a mail server */
307 ptr = iter + 1;
308 length = 0;
309 continue;
310 }
311
312 if (count < 12 && iter != ptonum) {
313 length += count;
314 break;
315 }
316
317 ptr = iter;
318 length = 0;
319 }
320 }
321
322 if (length == 0) { /* No filename found, use subject line for ident */
323 ptr = subject;
324
325 while (*ptr && !isalpha (*ptr))
326 ptr++;
327
328 while ((isalnum(ptr[length])||strchr(fnchars,ptr[length])!=NULL||
329 ptr[length] == '/') &&
330 ptr[length] && ptr+length!=part && ptr+length!=ptonum)
331 length++;
332
333 if (length) {
334 if (ptr[length] == '\0' || ptr[length] == 0x0a || ptr[length] == 0x0d) {
335 length--;
336
337 /*
338 * I used to cut off digits from the end of the string, but
339 * let's try to live without. We want to distinguish
340 * DUTCH951 from DUTCH952
341 *
342 * while ((ptr[length] == ' ' || isdigit (ptr[length])) && length > 0)
343 * length--;
344 */
345 }
346 else {
347 length--;
348
349 while (ptr[length] == ' ' && length > 0)
350 length--;
351 }
352 length++;
353 }
354 }
355
356 if (length == 0) { /* Still found nothing? We need *something*! */
357 ptr = nofname;
358 length = strlen (nofname);
359 }
360
361 if ((result = (char *) malloc (length + 1)) == NULL) {
362 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
363 uustring (S_OUT_OF_MEMORY), length+1);
364 return NULL;
365 }
366
367 memcpy (result, ptr, length);
368 result[length] = '\0';
369
370 return uu_FileNameCallback ? uu_FileNameCallback(uu_FNCBArg, subject, result) : result;
371 }
372
373 /*
374 * Extract the Part Number from the subject line.
375 * We look first for numbers in (#/#)'s, then for numbers in [#/#]'s
376 * and then for digits that are not part of a string.
377 * If we cannot find anything, assume it is the next part of the
378 * previous file.
379 * If we find a part number, we put a pointer to it in *where. This is
380 * done so that the UUGetFileName function doesn't accidentally use the
381 * part number as the file name. *whend points to the end of this part
382 * number.
383 **/
384
385 static int
386 UUGetPartNo (char *subject, char **where, char **whend)
387 {
388 char *ptr = subject, *iter, *delim, bdel[2]=" ";
389 int count, length=0, bpc;
390
391 *where = NULL; bdel[0] = ' ';
392 *whend = NULL; bdel[1] = '\0';
393
394 iter = NULL;
395 delim = "";
396
397 if (subject == NULL)
398 return -1;
399
400 if (uu_ignreply &&
401 (subject[0] == 'R' || subject[0] == 'r') && /* Ignore replies, but not */
402 (subject[1] == 'E' || subject[1] == 'e') && /* reposts */
403 (subject[2] == ':' || subject[2] == ' '))
404 return -2;
405
406 /*
407 * First try numbers in () or [] (or vice versa, according to bracket
408 * policy)
409 * For multiple occurences, give priority to the bracket with a slash
410 * or the last one, whichever is "found" first.
411 */
412
413 for (bpc=0, length=0; brackchr[uu_bracket_policy][bpc]; bpc+=2) {
414 iter = subject;
415 length = 0;
416 while ((iter = strchr (iter, brackchr[uu_bracket_policy][bpc])) != NULL) {
417 int plength;
418
419 count = 0; iter++;
420
421 while (*iter == ' ' || *iter == '#')
422 iter++;
423
424 if (!isdigit (*iter)) {
425 continue;
426 }
427 while (isdigit (iter[count]))
428 count++;
429
430 if (iter[count] == '\0') {
431 iter += count;
432 break;
433 }
434
435 plength = count;
436
437 if (iter[count] == brackchr[uu_bracket_policy][bpc+1]) {
438 *where = iter;
439 bdel[0] = brackchr[uu_bracket_policy][bpc+1];
440 delim = bdel;
441 length = plength;
442 continue;
443 }
444
445 while (iter[count] == ' ' || iter[count] == '#' ||
446 iter[count] == '/' || iter[count] == '\\') count++;
447
448 if (FP_strnicmp (iter + count, "of", 2) == 0)
449 count += 2;
450
451 while (iter[count] == ' ') count++;
452 while (isdigit (iter[count])) count++;
453 while (iter[count] == ' ') count++;
454
455 if (iter[count] == brackchr[uu_bracket_policy][bpc+1]) {
456 *where = iter;
457 bdel[0] = brackchr[uu_bracket_policy][bpc+1];
458 delim = bdel;
459 length = plength;
460 break;
461 }
462 }
463 if (length)
464 {
465 iter = *where; /* strange control flow, but less changes == less hassle */
466 break;
467 }
468 }
469
470 /*
471 * look for the string "part " followed by a number
472 */
473
474 if (length == 0) {
475 if ((iter = FP_stristr (subject, "part ")) != NULL) {
476 iter += 5;
477
478 while (isspace (*iter) || *iter == '.' || *iter == '-')
479 iter++;
480
481 while (isdigit (iter[length]))
482 length++;
483
484 if (length == 0) {
485 if (FP_strnicmp (iter, "one", 3) == 0) length = 1;
486 else if (FP_strnicmp (iter, "two", 3) == 0) length = 2;
487 else if (FP_strnicmp (iter, "three", 5) == 0) length = 3;
488 else if (FP_strnicmp (iter, "four", 4) == 0) length = 4;
489 else if (FP_strnicmp (iter, "five", 4) == 0) length = 5;
490 else if (FP_strnicmp (iter, "six", 3) == 0) length = 6;
491 else if (FP_strnicmp (iter, "seven", 5) == 0) length = 7;
492 else if (FP_strnicmp (iter, "eight", 5) == 0) length = 8;
493 else if (FP_strnicmp (iter, "nine", 4) == 0) length = 9;
494 else if (FP_strnicmp (iter, "ten", 3) == 0) length = 10;
495
496 if (length && (*whend = strchr (iter, ' '))) {
497 *where = iter;
498 return length;
499 }
500 else
501 length = 0;
502 }
503 else {
504 *where = iter;
505 delim = "of";
506 }
507 }
508 }
509
510 /*
511 * look for the string "part" followed by a number
512 */
513
514 if (length == 0) {
515 if ((iter = FP_stristr (subject, "part")) != NULL) {
516 iter += 4;
517
518 while (isspace (*iter) || *iter == '.' || *iter == '-')
519 iter++;
520
521 while (isdigit (iter[length]))
522 length++;
523
524 if (length == 0) {
525 if (FP_strnicmp (iter, "one", 3) == 0) length = 1;
526 else if (FP_strnicmp (iter, "two", 3) == 0) length = 2;
527 else if (FP_strnicmp (iter, "three", 5) == 0) length = 3;
528 else if (FP_strnicmp (iter, "four", 4) == 0) length = 4;
529 else if (FP_strnicmp (iter, "five", 4) == 0) length = 5;
530 else if (FP_strnicmp (iter, "six", 3) == 0) length = 6;
531 else if (FP_strnicmp (iter, "seven", 5) == 0) length = 7;
532 else if (FP_strnicmp (iter, "eight", 5) == 0) length = 8;
533 else if (FP_strnicmp (iter, "nine", 4) == 0) length = 9;
534 else if (FP_strnicmp (iter, "ten", 3) == 0) length = 10;
535
536 if (length && (*whend = strchr (iter, ' '))) {
537 *where = iter;
538 return length;
539 }
540 else
541 length = 0;
542 }
543 else {
544 *where = iter;
545 delim = "of";
546 }
547 }
548 }
549
550 /*
551 * look for [0-9]* "of" [0-9]*
552 */
553
554 if (length == 0) {
555 if ((iter = FP_strirstr (subject, "of")) != NULL) {
556 while (iter>subject && isspace (*(iter-1)))
557 iter--;
558 if (isdigit(*(iter-1))) {
559 while (iter>subject && isdigit (*(iter-1)))
560 iter--;
561 if (!isdigit (*iter) && !isalpha (*iter) && *iter != '.')
562 iter++;
563 ptr = iter;
564
565 while (isdigit (*ptr)) {
566 ptr++; length++;
567 }
568 *where = iter;
569 delim = "of";
570 }
571 }
572 }
573
574 /*
575 * look for whitespace-separated (or '/'-separated) digits
576 */
577
578 if (length == 0) {
579 ptr = subject;
580
581 while (*ptr && length==0) {
582 while (*ptr && !isdigit (*ptr))
583 ptr++;
584 if (isdigit (*ptr) && (ptr==subject || *ptr==' ' || *ptr=='/')) {
585 while (isdigit (ptr[length]))
586 length++;
587 if (ptr[length]!='\0' && ptr[length]!=' ' && ptr[length]!='/') {
588 ptr += length;
589 length = 0;
590 }
591 else {
592 iter = ptr;
593 bdel[0] = ptr[length];
594 delim = bdel;
595 }
596 }
597 else {
598 while (isdigit (*ptr))
599 ptr++;
600 }
601 }
602 }
603
604 /*
605 * look for _any_ digits -- currently disabled, because it also fell
606 * for "part numbers" in file names
607 */
608
609 #if 0
610 if (length == 0) {
611 count = strlen(subject) - 1;
612 ptr = subject;
613
614 while (count > 0) {
615 if (!isdigit(ptr[count])||isalpha(ptr[count+1])||ptr[count+1] == '.') {
616 count--;
617 continue;
618 }
619 length = 0;
620
621 while (count >= 0 && isdigit (ptr[count])) {
622 count--; length++;
623 }
624 if (count>=0 && ((isalpha (ptr[count]) &&
625 (ptr[count] != 's' || ptr[count+1] != 't') &&
626 (ptr[count] != 'n' || ptr[count+1] != 'd')) ||
627 ptr[count] == '/' || ptr[count] == '.' ||
628 ptr[count] == '-' || ptr[count] == '_')) {
629 length = 0;
630 continue;
631 }
632 count++;
633 iter = ptr + count;
634
635 if (length > 4) {
636 length = 0;
637 continue;
638 }
639 *where = iter;
640 delim = "of";
641 break;
642 }
643 }
644 #endif
645
646 /*
647 * look for part numbering as string
648 */
649
650 if (length == 0) {
651 /*
652 * some people use the strangest things, including spelling mistakes :-)
653 */
654 if ((iter = FP_stristr (subject, "first")) != NULL) length = 1;
655 else if ((iter = FP_stristr (subject, "second")) != NULL) length = 2;
656 else if ((iter = FP_stristr (subject, "third")) != NULL) length = 3;
657 else if ((iter = FP_stristr (subject, "forth")) != NULL) length = 4;
658 else if ((iter = FP_stristr (subject, "fourth")) != NULL) length = 4;
659 else if ((iter = FP_stristr (subject, "fifth")) != NULL) length = 5;
660 else if ((iter = FP_stristr (subject, "sixth")) != NULL) length = 6;
661 else if ((iter = FP_stristr (subject, "seventh")) != NULL) length = 7;
662 else if ((iter = FP_stristr (subject, "eigth")) != NULL) length = 8;
663 else if ((iter = FP_stristr (subject, "eighth")) != NULL) length = 8;
664 else if ((iter = FP_stristr (subject, "nineth")) != NULL) length = 9;
665 else if ((iter = FP_stristr (subject, "ninth")) != NULL) length = 9;
666 else if ((iter = FP_stristr (subject, "tenth")) != NULL) length = 10;
667 else iter = NULL;
668
669 if (length && iter && (*whend = strchr (iter, ' '))) {
670 *where = iter;
671 return length;
672 }
673 else
674 length = 0;
675 }
676
677 if (iter == NULL || length == 0) /* should be equivalent */
678 return -1;
679
680 *where = iter;
681
682 if (delim && delim[0]) {
683 if ((*whend=FP_stristr (iter, delim)) != NULL && (*whend - *where) < 12) {
684 ptr = (*whend += strlen (delim));
685
686 while (*ptr == ' ')
687 ptr++;
688
689 if (isdigit (*ptr)) {
690 *whend = ptr;
691 while (isdigit (**whend))
692 *whend += 1;
693 }
694 }
695 else {
696 *whend = iter + length;
697 }
698 }
699 else {
700 *whend = iter + length;
701 }
702
703 return atoi (iter);
704 }
705
706 /*
707 * Obtain and process some information about the data.
708 **/
709
710 uufile *
711 UUPreProcessPart (fileread *data, int *ret)
712 {
713 char *where, *whend, temp[80], *ptr, *p2;
714 uufile *result;
715
716 if ((result = (uufile *) malloc (sizeof (uufile))) == NULL) {
717 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
718 uustring (S_OUT_OF_MEMORY), sizeof (uufile));
719 *ret = UURET_NOMEM;
720 return NULL;
721 }
722 memset (result, 0, sizeof (uufile));
723
724 if (data->partno) {
725 where = whend = NULL;
726 result->partno = data->partno;
727 }
728 else if (uu_dumbness) {
729 result->partno = -1;
730 where = whend = NULL;
731 }
732 else if ((result->partno=UUGetPartNo(data->subject,&where,&whend)) == -2) {
733 *ret = UURET_NODATA;
734 UUkillfile (result);
735 return NULL;
736 }
737
738 if (data->filename != NULL) {
739 if ((result->filename = FP_strdup (data->filename)) == NULL) {
740 UUMessage (uucheck_id, __LINE__, UUMSG_ERROR,
741 uustring (S_OUT_OF_MEMORY),
742 strlen (data->filename)+1);
743 *ret = UURET_NOMEM;
744 UUkillfile (result);
745 return NULL;
746 }
747 }
748 else
749 result->filename = NULL;
750
751 if (uu_dumbness <= 1)
752 result->subfname = 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 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
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, or
1014 */
1015
1016 /* TODO: walk two hash chains looked up by filename_hash and mimeid_hash instead */
1017 /* may have to be be two separate chains, unfortunately, due to the way mimeids are compared? */
1018
1019 /*
1020 * check if this part wants to be left alone. If so, don't bother
1021 * to do all the checks
1022 */
1023 if (data_data->flags & FL_SINGLE)
1024 goto newpart;
1025
1026 while (iter) {
1027 if (
1028 (
1029 1
1030 && filename_hash == iter->filename_hash
1031 && (data->filename && iter->filename && strcmp (data->filename, iter->filename) == 0)
1032 && !(iter->begin && data_data->begin)
1033 && !(iter->end && data_data->end)
1034 && !(iter->flags & FL_SINGLE)
1035 && !(iter->thisfile && iter->thisfile->yefilesize != data->yefilesize)
1036 && FP_stricmp (data->subfname, iter->subfname) == 0
1037 && mimeid_hash == iter->mimeid_hash
1038 && !(data->mimeid && iter->mimeid && strcmp (data->mimeid, iter->mimeid) != 0)
1039 )
1040 || (mimeid_hash == iter->mimeid_hash && data->mimeid && iter->mimeid && strcmp (data->mimeid, iter->mimeid) == 0)
1041 ) {
1042
1043 /*
1044 * Don't insert a part that is already there.
1045 *
1046 * Also don't add a part beyond the "end" marker (unless we
1047 * have a mimeid, which screws up the marker).
1048 */
1049
1050 for (fiter=iter->thisfile; fiter; fiter=fiter->NEXT) {
1051 if (data->partno == fiter->partno)
1052 goto goahead;
1053 if (!iter->mimeid) {
1054 if (data->partno > fiter->partno && fiter->data->end) {
1055 goto goahead;
1056 }
1057 }
1058 }
1059
1060 if (iter->filename == NULL && data->filename != NULL) {
1061 if ((iter->filename = FP_strdup (data->filename)) == NULL)
1062 return UURET_NOMEM;
1063 }
1064
1065 /*
1066 * special case when we might have tagged a part as Base64 when the
1067 * file was really XX
1068 */
1069
1070 if (data_data->uudet == B64ENCODED &&
1071 iter->uudet == XX_ENCODED && iter->begin) {
1072 data_data->uudet = XX_ENCODED;
1073 }
1074 else if (data_data->uudet == XX_ENCODED && data_data->begin &&
1075 iter->uudet == B64ENCODED) {
1076 iter->uudet = XX_ENCODED;
1077
1078 fiter = iter->thisfile;
1079 while (fiter) {
1080 fiter->data->uudet = XX_ENCODED;
1081 fiter = fiter->NEXT;
1082 }
1083 }
1084
1085 /*
1086 * If this is from a Message/Partial, we believe only the
1087 * iter->uudet from the first part
1088 */
1089 if (data_data->flags & FL_PARTIAL) {
1090 if (data->partno == 1) {
1091 iter->uudet = data_data->uudet;
1092 iter->flags = data_data->flags;
1093 }
1094 }
1095 else {
1096 if (data_data->uudet) iter->uudet = data_data->uudet;
1097 if (data_data->flags) iter->flags = data_data->flags;
1098 }
1099
1100 if (iter->mode == 0 && data_data->mode != 0)
1101 iter->mode = data_data->mode;
1102
1103 if (data_data->begin) iter->begin = data->partno ? data->partno : 1;
1104 if (data_data->end) iter->end = data->partno ? data->partno : 1;
1105
1106 if (data->mimetype) {
1107 FP_free (iter->mimetype);
1108 iter->mimetype = FP_strdup (data->mimetype);
1109 }
1110
1111 /*
1112 * insert part at the beginning
1113 */
1114
1115 if (data->partno != -1 && data->partno < iter->thisfile->partno) {
1116 iter->state = UUFILE_READ;
1117 data->NEXT = iter->thisfile;
1118 iter->thisfile = data;
1119 return UURET_OK;
1120 }
1121
1122 /*
1123 * insert part somewhere else
1124 */
1125
1126 iter->state = UUFILE_READ; /* prepare for re-checking */
1127 fiter = iter->thisfile;
1128 last = NULL;
1129
1130 while (fiter) {
1131 /*
1132 * if we find the same part no again, check which one looks better
1133 */
1134 if (data->partno == fiter->partno) {
1135 if (fiter->data->subject == NULL)
1136 return UURET_NODATA;
1137 else if (FP_stristr (fiter->data->subject, "repost") != NULL &&
1138 FP_stristr (data_data->subject, "repost") == NULL)
1139 return UURET_NODATA;
1140 else if (fiter->data->uudet && !data_data->uudet)
1141 return UURET_NODATA;
1142 else {
1143 /*
1144 * replace
1145 */
1146 data->NEXT = fiter->NEXT;
1147 fiter->NEXT = NULL;
1148 UUkillfile (fiter);
1149
1150 if (last == NULL)
1151 iter->thisfile = data;
1152 else
1153 last->NEXT = data;
1154
1155 return UURET_OK;
1156 }
1157 }
1158
1159 /*
1160 * if at the end of the part list, add it
1161 */
1162
1163 if (fiter->NEXT == NULL ||
1164 (data->partno != -1 && data->partno < fiter->NEXT->partno)) {
1165 data->NEXT = fiter->NEXT;
1166 fiter->NEXT = data;
1167
1168 if (data->partno == -1)
1169 data->partno = fiter->partno + 1;
1170
1171 return UURET_OK;
1172 }
1173
1174 last = fiter;
1175 fiter = fiter->NEXT;
1176 }
1177
1178 return UURET_OK; /* Shouldn't get here */
1179 }
1180
1181 goahead:
1182 iter = iter->NEXT;
1183
1184 if (!iter)
1185 break;
1186 }
1187
1188 newpart:
1189 /*
1190 * handle new entry
1191 */
1192
1193 if (data->partno == -1) {
1194 /*
1195 * if it's got no part no, and it's MIME mail, then assume this is
1196 * part no. 1. If it's not MIME, then we can't handle it; if it
1197 * had a 'begin', it'd have got a part number assigned by
1198 * UUPreProcessPart().
1199 */
1200 if (data_data->uudet == B64ENCODED || data_data->uudet == BH_ENCODED)
1201 data->partno = 1;
1202 else
1203 return UURET_NODATA;
1204 }
1205
1206 if ((unew = (uulist *)malloc (sizeof (uulist))) == NULL) {
1207 return UURET_NOMEM;
1208 }
1209
1210 {
1211 const uulist uulist_new = { 0 };
1212 *unew = uulist_new; /* zero-initialise the structure */
1213 }
1214
1215 if ((unew->subfname = FP_strdup (data->subfname)) == NULL) {
1216 FP_free (unew);
1217 return UURET_NOMEM;
1218 }
1219
1220 unew->filename_hash = filename_hash;
1221 if (data->filename != NULL) {
1222 if ((unew->filename = FP_strdup (data->filename)) == NULL) {
1223 FP_free (unew->subfname);
1224 FP_free (unew);
1225 return UURET_NOMEM;
1226 }
1227 }
1228 else
1229 unew->filename = NULL;
1230
1231 unew->mimeid_hash = mimeid_hash;
1232 if (data->mimeid != NULL) {
1233 if ((unew->mimeid = FP_strdup (data->mimeid)) == NULL) {
1234 FP_free (unew->subfname);
1235 FP_free (unew->filename);
1236 FP_free (unew);
1237 return UURET_NOMEM;
1238 }
1239 }
1240 else
1241 unew->mimeid = NULL;
1242
1243 if (data->mimetype != NULL) {
1244 if ((unew->mimetype = FP_strdup (data->mimetype)) == NULL) {
1245 FP_free (unew->mimeid);
1246 FP_free (unew->subfname);
1247 FP_free (unew->filename);
1248 FP_free (unew);
1249 return UURET_NOMEM;
1250 }
1251 }
1252 else
1253 unew->mimetype = NULL;
1254
1255 unew->state = UUFILE_READ;
1256 unew->thisfile = data;
1257 unew->mode = data_data->mode;
1258 unew->uudet = data_data->uudet;
1259 unew->flags = data_data->flags;
1260 unew->begin = data_data->begin ? (data->partno ? data->partno : 1) : 0;
1261 unew->end = data_data->end ? (data->partno ? data->partno : 1) : 0;
1262
1263 /* insert at the beginning, as parts often show up in bursts */
1264 unew->NEXT = UUGlobalFileList;
1265 UUGlobalFileList = unew;
1266
1267 return UURET_OK;
1268 }
1269
1270 /*
1271 * At this point, all files are read in and stored in the
1272 * "UUGlobalFileList". Do some checking. All parts there?
1273 **/
1274
1275 void UUEXPORT
1276 UUCheckGlobalList (void)
1277 {
1278 int misparts[MAXPLIST], haveparts[MAXPLIST];
1279 int miscount, havecount, count, flag, part;
1280 uulist *liter=UUGlobalFileList, *prev;
1281 uufile *fiter;
1282 long thesize;
1283
1284 while (liter) {
1285 miscount = 0;
1286 thesize = 0;
1287
1288 if (liter->state & UUFILE_OK) {
1289 liter = liter->NEXT;
1290 continue;
1291 }
1292 else if ((liter->uudet == QP_ENCODED ||
1293 liter->uudet == PT_ENCODED) &&
1294 (liter->flags & FL_SINGLE)) {
1295 if ((liter->flags&FL_PROPER)==0)
1296 liter->size = -1;
1297 else
1298 liter->size = liter->thisfile->data->length;
1299
1300 liter->state = UUFILE_OK;
1301 continue;
1302 }
1303 else if ((fiter = liter->thisfile) == NULL) {
1304 liter->state = UUFILE_NODATA;
1305 liter = liter->NEXT;
1306 continue;
1307 }
1308
1309 /*
1310 * Re-Check this file
1311 */
1312
1313 flag = 0;
1314 miscount = 0;
1315 havecount = 0;
1316 thesize = 0;
1317 liter->state = UUFILE_READ;
1318
1319 /*
1320 * search encoded data
1321 */
1322
1323 while (fiter && !fiter->data->uudet) {
1324 if (havecount<MAXPLIST) {
1325 haveparts[havecount++] = fiter->partno;
1326 }
1327 fiter = fiter->NEXT;
1328 }
1329
1330 if (fiter == NULL) {
1331 liter->state = UUFILE_NODATA;
1332 liter = liter->NEXT;
1333 continue;
1334 }
1335
1336 if (havecount<MAXPLIST) {
1337 haveparts[havecount++] = fiter->partno;
1338 }
1339
1340 if ((part = fiter->partno) > 1) {
1341 if (!fiter->data->begin) {
1342 for (count=1; count < part && miscount < MAXPLIST; count++)
1343 misparts[miscount++] = count;
1344 }
1345 }
1346
1347 /*
1348 * don't care if so many parts are missing
1349 */
1350
1351 if (miscount >= MAXPLIST) {
1352 liter->state = UUFILE_MISPART;
1353 liter = liter->NEXT;
1354 continue;
1355 }
1356
1357 if (liter->uudet == B64ENCODED ||
1358 liter->uudet == QP_ENCODED ||
1359 liter->uudet == PT_ENCODED)
1360 flag |= 3; /* Don't need begin or end with Base64 or plain text*/
1361
1362 if (fiter->data->begin) flag |= 1;
1363 if (fiter->data->end) flag |= 2;
1364 if (fiter->data->uudet) flag |= 4;
1365
1366 /*
1367 * guess size of part
1368 */
1369
1370 switch (fiter->data->uudet) {
1371 case UU_ENCODED:
1372 case XX_ENCODED:
1373 thesize += 3*fiter->data->length/4;
1374 thesize -= 3*fiter->data->length/124; /* substract 2 of 62 chars */
1375 break;
1376 case B64ENCODED:
1377 thesize += 3*fiter->data->length/4;
1378 thesize -= fiter->data->length/52; /* substract 2 of 78 chars */
1379 break;
1380 case QP_ENCODED:
1381 case PT_ENCODED:
1382 thesize += fiter->data->length;
1383 break;
1384 }
1385
1386 fiter = fiter->NEXT;
1387
1388 while (fiter != NULL) {
1389 for (count = part+1; count < fiter->partno && miscount < MAXPLIST; count++)
1390 misparts[miscount++] = count;
1391
1392 part = fiter->partno;
1393
1394 if (havecount < MAXPLIST)
1395 haveparts[havecount++] = part;
1396
1397 if (fiter->data->begin) flag |= 1;
1398 if (fiter->data->end) flag |= 2;
1399 if (fiter->data->uudet) flag |= 4;
1400
1401 switch (fiter->data->uudet) {
1402 case UU_ENCODED:
1403 case XX_ENCODED:
1404 thesize += 3*fiter->data->length/4;
1405 thesize -= 3*fiter->data->length/124; /* substract 2 of 62 chars */
1406 break;
1407 case B64ENCODED:
1408 thesize += 3*fiter->data->length/4;
1409 thesize -= fiter->data->length/52; /* substract 2 of 78 chars */
1410 break;
1411 case QP_ENCODED:
1412 case PT_ENCODED:
1413 thesize += fiter->data->length;
1414 break;
1415 }
1416
1417 if (fiter->data->end)
1418 break;
1419
1420 fiter = fiter->NEXT;
1421 }
1422
1423 /*
1424 * if in fast mode, we don't notice an 'end'. So if its uu or xx
1425 * encoded, there's a begin line and encoded data, assume it's
1426 * there.
1427 */
1428
1429 if (uu_fast_scanning && (flag & 0x01) && (flag & 0x04) &&
1430 (liter->uudet == UU_ENCODED || liter->uudet == XX_ENCODED))
1431 flag |= 2;
1432
1433 /*
1434 * Set the parts we have and/or missing
1435 */
1436
1437 FP_free (liter->haveparts);
1438 FP_free (liter->misparts);
1439
1440 liter->haveparts = NULL;
1441 liter->misparts = NULL;
1442
1443 if (havecount) {
1444 if ((liter->haveparts=(int*)malloc((havecount+1)*sizeof(int)))!=NULL) {
1445 memcpy (liter->haveparts, haveparts, havecount*sizeof(int));
1446 liter->haveparts[havecount] = 0;
1447 }
1448 }
1449
1450 if (miscount) {
1451 if ((liter->misparts=(int*)malloc((miscount+1)*sizeof(int)))!=NULL) {
1452 memcpy (liter->misparts, misparts, miscount*sizeof(int));
1453 liter->misparts[miscount] = 0;
1454 }
1455 liter->state |= UUFILE_MISPART;
1456 }
1457
1458 /*
1459 * Finalize checking
1460 */
1461
1462 if ((flag & 1) == 0) liter->state |= UUFILE_NOBEGIN;
1463 if ((flag & 2) == 0) liter->state |= UUFILE_NOEND;
1464 if ((flag & 4) == 0) liter->state |= UUFILE_NODATA;
1465
1466 if ((flag & 7) == 7 && miscount==0) {
1467 liter->state = UUFILE_OK;
1468 }
1469
1470 if ((uu_fast_scanning && (liter->flags&FL_PROPER)==0) || thesize<=0)
1471 liter->size = -1;
1472 else
1473 liter->size = thesize;
1474
1475 if (liter->state==UUFILE_OK &&
1476 (liter->filename==NULL || liter->filename[0]=='\0')) {
1477 /*
1478 * Emergency backup if the file does not have a filename
1479 */
1480 FP_free (liter->filename);
1481 if (liter->subfname && liter->subfname[0] &&
1482 FP_strpbrk (liter->subfname, "()[];: ") == NULL)
1483 liter->filename = FP_strdup (liter->subfname);
1484 else {
1485 sprintf (uucheck_tempname, "%s.%03d", nofname, ++nofnum);
1486 liter->filename = FP_strdup (uucheck_tempname);
1487 }
1488 }
1489 liter = liter->NEXT;
1490 }
1491
1492 /*
1493 * Sets back (PREV) links
1494 */
1495
1496 liter = UUGlobalFileList;
1497 prev = NULL;
1498
1499 while (liter) {
1500 liter->PREV = prev;
1501 prev = liter;
1502 liter = liter->NEXT;
1503 }
1504 }
1505