ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uucheck.c
Revision: 1.3.2.4
Committed: Thu Nov 6 13:08:23 2003 UTC (20 years, 6 months ago) by root
Content type: text/plain
Branch: UUDEVIEW
CVS Tags: UUDEVIEW-0-5-19
Changes since 1.3.2.3: +33 -29 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /*
2     * This file is part of uudeview, the simple and friendly multi-part multi-
3 root 1.2 * file uudecoder program (c) 1994-2001 by Frank Pilhofer. The author may
4     * be contacted at fp@fpx.de
5 root 1.1 *
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 root 1.2 #include <uudeview.h>
52 root 1.1 #include <uuint.h>
53     #include <fptools.h>
54     #include <uustring.h>
55    
56 root 1.3.2.4 char * uucheck_id = "$Id: uucheck.c,v 1.15 2003/04/13 15:41:55 fp Exp $";
57 root 1.1
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 root 1.3.2.1 static char * UUGetFileName _ANSI_ARGS_((char *, char *, char *));
71     static int UUGetPartNo _ANSI_ARGS_((char *, char **, char **));
72 root 1.1
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 root 1.3.2.1 if (_FP_strnicmp (subject, "repost", 6) == 0)
138 root 1.1 subject += 6;
139 root 1.3.2.1 if (_FP_strnicmp (subject, "re:", 3) == 0)
140 root 1.1 subject += 3;
141    
142     while (*subject == ' ' || *subject == ':') subject++;
143    
144 root 1.3.2.1 part = _FP_stristr (subject, "part");
145 root 1.1 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 root 1.3.2.1 while (*ptr && *ptr != 0x0a && *ptr != 0x0d && ptr != part) {
222 root 1.1 iter = ptr;
223     count = length = alflag = 0;
224    
225 root 1.3.2.1 if (_FP_strnicmp (ptr, "ftp", 3) == 0) {
226 root 1.1 /* 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 root 1.3.2.1 if (*iter++ != '.' || count > 32 || alflag == 0) {
249 root 1.1 ptr = iter;
250     length = 0;
251     continue;
252     }
253 root 1.3.2.1 if (_FP_strnicmp (iter, "edu", 3) == 0 ||
254     _FP_strnicmp (iter, "gov", 3) == 0) {
255 root 1.1 /* 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 root 1.3.2.1 if (_FP_strnicmp (iter + count, "of", 2) == 0)
431 root 1.1 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 root 1.3.2.1 if ((iter = _FP_stristr (subject, "part ")) != NULL) {
457 root 1.1 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 root 1.3.2.1 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 root 1.1
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 root 1.3.2.1 if ((iter = _FP_stristr (subject, "part")) != NULL) {
497 root 1.1 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 root 1.3.2.1 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 root 1.1
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 root 1.3.2.1 if ((iter = _FP_strirstr (subject, "of")) != NULL) {
537 root 1.1 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 root 1.3.2.1 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 root 1.1 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 root 1.3.2.1 if ((*whend=_FP_stristr (iter, delim)) != NULL && (*whend - *where) < 12) {
664 root 1.1 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 root 1.3.2.1 if ((result->filename = _FP_strdup (data->filename)) == NULL) {
720 root 1.1 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 root 1.3.2.1 result->mimeid = _FP_strdup (data->mimeid);
737     result->mimetype = _FP_strdup (data->mimetype);
738 root 1.1
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 root 1.3.2.1 result->filename = _FP_strdup (temp);
750 root 1.1 }
751     if (result->subfname == NULL)
752 root 1.3.2.1 result->subfname = _FP_strdup (result->filename);
753 root 1.1
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 root 1.3.2.4 if (result->filename != NULL && *result->filename)
779 root 1.3.2.1 result->subfname = _FP_strdup (result->filename);
780 root 1.1 else { /* if not, escape to UNKNOWN. We need to fill subfname */
781     sprintf (temp, "%s.%03d", nofname, ++nofnum);
782 root 1.3.2.1 result->subfname = _FP_strdup (temp);
783 root 1.1 }
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 root 1.3.2.1 _FP_strncpy (uucheck_lastname, result->subfname, 256);
808 root 1.1 }
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 root 1.3.2.1 result->subfname = _FP_strdup (result->filename);
820 root 1.1 else
821 root 1.3.2.1 result->subfname = _FP_strdup (result->mimeid);
822 root 1.1 }
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 root 1.3.2.4 if (result->filename != NULL && *result->filename)
833 root 1.3.2.1 result->subfname = _FP_strdup (result->filename);
834 root 1.1 else { /* if not, escape to UNKNOWN. We need to fill subfname */
835     sprintf (temp, "%s.%03d", nofname, ++nofnum);
836 root 1.3.2.1 result->subfname = _FP_strdup (temp);
837 root 1.1 }
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 root 1.3.2.2 else if (lastvalid && data->uudet == lastenc && result->partno == -1) {
850 root 1.3.2.1 result->subfname = _FP_strdup (uucheck_lastname);
851 root 1.1 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 root 1.3.2.2 }
859     else if (data->partno != -1 && result->filename) {
860     result->subfname = _FP_strdup (result->filename);
861 root 1.1 }
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 root 1.3.2.1 _FP_strncpy (uucheck_lastname, result->subfname, 256);
904 root 1.1 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 root 1.3.2.1 if (lastvalid && _FP_stricmp (uucheck_lastname, result->subfname) == 0) {
913 root 1.1 /*
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 root 1.3.2.1 if (*(ptr = _FP_cutdir (result->filename))) {
953     p2 = _FP_strdup (ptr);
954     _FP_free (result->filename);
955 root 1.1 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 root 1.3.2.4 * (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 root 1.1 */
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 root 1.3.2.4 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 root 1.1
1008     /*
1009 root 1.3.2.4 * 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 root 1.1 */
1014    
1015 root 1.3.2.4 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 root 1.1
1025     if (iter->filename == NULL && data->filename != NULL) {
1026 root 1.3.2.1 if ((iter->filename = _FP_strdup (data->filename)) == NULL)
1027 root 1.1 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 root 1.3.2.1 _FP_free (iter->mimetype);
1072     iter->mimetype = _FP_strdup (data->mimetype);
1073 root 1.1 }
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 root 1.3.2.1 else if (_FP_stristr (fiter->data->subject, "repost") != NULL &&
1102     _FP_stristr (data->data->subject, "repost") == NULL)
1103 root 1.1 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 root 1.3.2.1 if ((unew->subfname = _FP_strdup (data->subfname)) == NULL) {
1174     _FP_free (unew);
1175 root 1.1 return UURET_NOMEM;
1176     }
1177    
1178     if (data->filename != NULL) {
1179 root 1.3.2.1 if ((unew->filename = _FP_strdup (data->filename)) == NULL) {
1180     _FP_free (unew->subfname);
1181     _FP_free (unew);
1182 root 1.1 return UURET_NOMEM;
1183     }
1184     }
1185     else
1186     unew->filename = NULL;
1187    
1188     if (data->mimeid != NULL) {
1189 root 1.3.2.1 if ((unew->mimeid = _FP_strdup (data->mimeid)) == NULL) {
1190     _FP_free (unew->subfname);
1191     _FP_free (unew->filename);
1192     _FP_free (unew);
1193 root 1.1 return UURET_NOMEM;
1194     }
1195     }
1196     else
1197     unew->mimeid = NULL;
1198    
1199     if (data->mimetype != NULL) {
1200 root 1.3.2.1 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 root 1.1 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 root 1.3.2.1 _FP_free (liter->haveparts);
1399     _FP_free (liter->misparts);
1400 root 1.1
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 root 1.3.2.1 _FP_free (liter->filename);
1442 root 1.1 if (liter->subfname && liter->subfname[0] &&
1443 root 1.3.2.1 _FP_strpbrk (liter->subfname, "()[];: ") == NULL)
1444     liter->filename = _FP_strdup (liter->subfname);
1445 root 1.1 else {
1446     sprintf (uucheck_tempname, "%s.%03d", nofname, ++nofnum);
1447 root 1.3.2.1 liter->filename = _FP_strdup (uucheck_tempname);
1448 root 1.1 }
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