ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uucheck.c
Revision: 1.3.2.5
Committed: Sun Apr 18 19:55:46 2004 UTC (20 years, 1 month ago) by root
Content type: text/plain
Branch: UUDEVIEW
CVS Tags: UUDEVIEW-0-5-20
Changes since 1.3.2.4: +0 -0 lines
Log Message:
*** empty log message ***

File Contents

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