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 *
UUGetFileName(char * subject,char * ptonum,char * ptonend)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
UUGetPartNo(char * subject,char ** where,char ** whend)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 *
UUPreProcessPart(fileread * data,int * ret)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
UUInsertPartToList(uufile * data)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 *
UUCheckGlobalList(void)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 
1469