1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD$
10 *
11 */
12
13#include "ctm.h"
14#define BADREAD 1
15
16/*---------------------------------------------------------------------------*/
17/* Pass1 -- Validate the incoming CTM-file.
18 */
19
20int
21Pass1(FILE *fd, unsigned applied)
22{
23    u_char *p,*q;
24    MD5_CTX ctx;
25    int i,j,sep,cnt;
26    u_char *md5=0,*name=0,*trash=0;
27    struct CTM_Syntax *sp;
28    int slashwarn=0, match=0, total_matches=0;
29    unsigned current;
30    char md5_1[33];
31
32    if(Verbose>3)
33	printf("Pass1 -- Checking integrity of incoming CTM-patch\n");
34    MD5Init (&ctx);
35
36    GETFIELD(p,' ');				/* CTM_BEGIN */
37    if(strcmp(p,"CTM_BEGIN")) {
38	Fatal("Probably not a CTM-patch at all.");
39	if(Verbose>3)
40	    fprintf(stderr,"Expected \"CTM_BEGIN\" got \"%s\".\n",p);
41	return 1;
42    }
43
44    GETFIELDCOPY(Version,' ');				/* <Version> */
45    if(strcmp(Version,VERSION)) {
46	Fatal("CTM-patch is wrong version.");
47	if(Verbose>3)
48	    fprintf(stderr,"Expected \"%s\" got \"%s\".\n",VERSION,p);
49	return 1;
50    }
51
52    GETFIELDCOPY(Name,' ');				/* <Name> */
53    GETFIELDCOPY(Nbr,' ');				/* <Nbr> */
54    GETFIELDCOPY(TimeStamp,' ');			/* <TimeStamp> */
55    GETFIELDCOPY(Prefix,'\n');				/* <Prefix> */
56
57    sscanf(Nbr, "%u", &current);
58    if (FilterList || ListIt)
59	current = 0;	/* ignore if -l or if filters are present */
60    if(current && current <= applied) {
61	if(Verbose > 0)
62	    fprintf(stderr,"Delta number %u is already applied; ignoring.\n",
63		    current);
64	return Exit_Version;
65    }
66
67    for(;;) {
68	Delete(md5);
69	Delete(name);
70	Delete(trash);
71	cnt = -1;
72	/* if a filter list is defined we assume that all pathnames require
73	   an action opposite to that requested by the first filter in the
74	   list.
75	   If no filter is defined, all pathnames are assumed to match. */
76	match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
77
78	GETFIELD(p,' ');			/* CTM_something */
79
80	if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') {
81	    Fatal("Expected CTM keyword.");
82	    fprintf(stderr,"Got [%s]\n",p);
83	    return 1;
84	}
85
86	if(!strcmp(p+3,"_END"))
87	    break;
88
89	for(sp=Syntax;sp->Key;sp++)
90	    if(!strcmp(p+3,sp->Key))
91		goto found;
92	Fatal("Expected CTM keyword.");
93	fprintf(stderr,"Got [%s]\n",p);
94	return 1;
95    found:
96	if(Verbose > 5)
97	    fprintf(stderr,"%s ",sp->Key);
98	for(i=0;(j = sp->List[i]);i++) {
99	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
100		sep = ' ';
101	    else
102		sep = '\n';
103
104	    if(Verbose > 5)
105	        fprintf(stderr," %x(%d)",sp->List[i],sep);
106
107	    switch (j & CTM_F_MASK) {
108		case CTM_F_Name: /* XXX check for garbage and .. */
109		    GETFIELDCOPY(name,sep);
110		    j = strlen(name);
111		    if(name[j-1] == '/' && !slashwarn)  {
112			fprintf(stderr,"Warning: contains trailing slash\n");
113			slashwarn++;
114		    }
115		    if (name[0] == '/') {
116			Fatal("Absolute paths are illegal.");
117			return Exit_Mess;
118		    }
119		    q = name;
120		    for (;;) {
121			if (q[0] == '.' && q[1] == '.')
122			    if (q[2] == '/' || q[2] == '\0') {
123				Fatal("Paths containing '..' are illegal.");
124				return Exit_Mess;
125			    }
126			if ((q = strchr(q, '/')) == NULL)
127			    break;
128			q++;
129		    }
130
131		    /* if we have been asked to `keep' files then skip
132		       removes; i.e. we don't match these entries at
133		       all. */
134		    if (KeepIt &&
135			(!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) {
136			match = CTM_FILTER_DISABLE;
137			break;
138		    }
139
140		    /* If filter expression have been defined, match the
141		       path name against the expression list.  */
142
143		    if (FilterList) {
144			struct CTM_Filter *filter;
145
146			for (filter = FilterList; filter;
147			     filter = filter->Next) {
148				if (0 == regexec(&filter->CompiledRegex, name,
149					0, 0, 0))
150					/* if the name matches, adopt the
151					   action */
152					match = filter->Action;
153			}
154		    }
155
156		    /* Add up the total number of matches */
157		    total_matches += match;
158		    break;
159		case CTM_F_Uid:
160		    GETFIELD(p,sep);
161		    while(*p) {
162			if(!isdigit(*p)) {
163			    Fatal("Non-digit in uid.");
164			    return 32;
165			}
166			p++;
167		    }
168		    break;
169		case CTM_F_Gid:
170		    GETFIELD(p,sep);
171		    while(*p) {
172			if(!isdigit(*p)) {
173			    Fatal("Non-digit in gid.");
174			    return 32;
175			}
176			p++;
177		    }
178		    break;
179		case CTM_F_Mode:
180		    GETFIELD(p,sep);
181		    while(*p) {
182			if(!isdigit(*p)) {
183			    Fatal("Non-digit in mode.");
184			    return 32;
185			}
186			p++;
187		    }
188		    break;
189		case CTM_F_MD5:
190		    if(j & CTM_Q_MD5_Chunk) {
191			GETFIELDCOPY(md5,sep);  /* XXX check for garbage */
192		    } else if(j & CTM_Q_MD5_Before) {
193			GETFIELD(p,sep);  /* XXX check for garbage */
194		    } else if(j & CTM_Q_MD5_After) {
195			GETFIELD(p,sep);  /* XXX check for garbage */
196		    } else {
197			fprintf(stderr,"List = 0x%x\n",j);
198			Fatal("Unqualified MD5.");
199			return 32;
200		    }
201		    break;
202		case CTM_F_Count:
203		    GETBYTECNT(cnt,sep);
204		    break;
205		case CTM_F_Bytes:
206		    if(cnt < 0) WRONG
207		    GETDATA(trash,cnt);
208		    p = MD5Data(trash,cnt,md5_1);
209		    if(md5 && strcmp(md5,p)) {
210			Fatal("Internal MD5 failed.");
211			return Exit_Garbage;
212		default:
213			fprintf(stderr,"List = 0x%x\n",j);
214			Fatal("List had garbage.");
215			return Exit_Garbage;
216		    }
217	    }
218	}
219	if(Verbose > 5)
220	    putc('\n',stderr);
221	if(ListIt && match)
222	    printf("> %s %s\n", sp->Key, name);
223    }
224
225    Delete(md5);
226    Delete(name);
227    Delete(trash);
228
229    q = MD5End (&ctx,md5_1);
230    if(Verbose > 2)
231	printf("Expecting Global MD5 <%s>\n",q);
232    GETFIELD(p,'\n');			/* <MD5> */
233    if(Verbose > 2)
234	printf("Reference Global MD5 <%s>\n",p);
235    if(strcmp(q,p)) {
236	Fatal("MD5 sum doesn't match.");
237	fprintf(stderr,"\tI have:<%s>\n",q);
238	fprintf(stderr,"\tShould have been:<%s>\n",p);
239	return Exit_Garbage;
240    }
241    if (-1 != getc(fd)) {
242	if(!Force) {
243	    Fatal("Trailing junk in CTM-file.  Can Force with -F.");
244	    return 16;
245	}
246    }
247    if ((Verbose > 1) && (0 == total_matches))
248	printf("No matches in \"%s\"\n", FileName);
249    return (total_matches ? Exit_OK : Exit_NoMatch);
250}
251