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