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 32
15
16/*---------------------------------------------------------------------------*/
17/* Pass2 -- Validate the incoming CTM-file.
18 */
19
20int
21Pass2(FILE *fd)
22{
23    u_char *p,*q,*md5=0;
24    MD5_CTX ctx;
25    int i,j,sep,cnt,fdesc;
26    u_char *trash=0,*name=0;
27    struct CTM_Syntax *sp;
28    struct stat st;
29    int ret = 0;
30    int match = 0;
31    char md5_1[33];
32    struct CTM_Filter *filter;
33    FILE *ed = NULL;
34    static char *template = NULL;
35
36    if(Verbose>3)
37	printf("Pass2 -- Checking if CTM-patch will apply\n");
38    MD5Init (&ctx);
39
40    GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
41    GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
42    GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
43    /* XXX Lookup name in /etc/ctm,conf, read stuff */
44    GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
45    /* XXX Verify that this is the next patch to apply */
46    GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
47    GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
48    /* XXX drop or use ? */
49
50    for(;;) {
51	Delete(trash);
52	Delete(name);
53	Delete(md5);
54	cnt = -1;
55
56	/* if a filter list was specified, check file name against
57	   the filters specified
58	   if no filter was given operate on all files. */
59	match = (FilterList ?
60		    !(FilterList->Action) : CTM_FILTER_ENABLE);
61
62	GETFIELD(p,' ');
63
64	if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
65
66	if(!strcmp(p+3,"_END"))
67	    break;
68
69	for(sp=Syntax;sp->Key;sp++)
70	    if(!strcmp(p+3,sp->Key))
71		goto found;
72	WRONG
73    found:
74	for(i=0;(j = sp->List[i]);i++) {
75	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
76		sep = ' ';
77	    else
78		sep = '\n';
79
80	    switch (j & CTM_F_MASK) {
81		case CTM_F_Name:
82		    GETNAMECOPY(name,sep,j,0);
83		    /* If `keep' was specified, we won't remove any files,
84		       so don't check if the file exists */
85		    if (KeepIt &&
86			(!strcmp(sp->Key,"FR") || !strcmp(sp->Key,"DR"))) {
87			match = CTM_FILTER_DISABLE;
88			break;
89		    }
90
91		    for (filter = FilterList; filter; filter = filter->Next)				if (0 == regexec(&filter->CompiledRegex, name,
92				    0, 0, 0)) {
93				    match = filter->Action;
94			    }
95
96		    if (CTM_FILTER_DISABLE == match)
97			    break;	/* should ignore this file */
98
99		    /* XXX Check DR DM rec's for parent-dir */
100		    if(j & CTM_Q_Name_New) {
101			/* XXX Check DR FR rec's for item */
102			if(-1 != stat(name,&st)) {
103			    fprintf(stderr,"  %s: %s exists.\n",
104				sp->Key,name);
105			    ret |= Exit_Forcible;
106			}
107			break;
108		    }
109		    if(-1 == stat(name,&st)) {
110			fprintf(stderr,"  %s: %s doesn't exist.\n",
111			    sp->Key,name);
112		        if (sp->Key[1] == 'R')
113			    ret |= Exit_Forcible;
114			else
115			    ret |= Exit_NotOK;
116			break;
117		    }
118		    if (SetTime && getuid() && (getuid() != st.st_uid)) {
119			    fprintf(stderr,
120				"  %s: %s not mine, cannot set time.\n",
121				sp->Key,name);
122			    ret |= Exit_NotOK;
123		    }
124		    if (j & CTM_Q_Name_Dir) {
125			if((st.st_mode & S_IFMT) != S_IFDIR) {
126			    fprintf(stderr,
127				"  %s: %s exist, but isn't dir.\n",
128				sp->Key,name);
129			    ret |= Exit_NotOK;
130			}
131			break;
132		    }
133		    if (j & CTM_Q_Name_File) {
134			if((st.st_mode & S_IFMT) != S_IFREG) {
135			    fprintf(stderr,
136				"  %s: %s exist, but isn't file.\n",
137				sp->Key,name);
138			    ret |= Exit_NotOK;
139			}
140			break;
141		    }
142		    break;
143		case CTM_F_Uid:
144		case CTM_F_Gid:
145		case CTM_F_Mode:
146		    GETFIELD(p,sep);
147		    break;
148		case CTM_F_MD5:
149		    if(!name) WRONG
150		    if(j & CTM_Q_MD5_Before) {
151		        char *tmp;
152			GETFIELD(p,sep);
153			if(match && (st.st_mode & S_IFMT) == S_IFREG &&
154			  (tmp = MD5File(name,md5_1)) != NULL &&
155			  strcmp(tmp,p)) {
156			    fprintf(stderr,"  %s: %s md5 mismatch.\n",
157				sp->Key,name);
158			    GETFIELDCOPY(md5,sep);
159			    if(md5 != NULL && strcmp(tmp,md5) == 0) {
160				fprintf(stderr,"  %s: %s already applied.\n",
161					sp->Key,name);
162				match = CTM_FILTER_DISABLE;
163			    } else if(j & CTM_Q_MD5_Force) {
164				if(Force)
165				    fprintf(stderr,"  Can and will force.\n");
166				else
167				    fprintf(stderr,"  Could have forced.\n");
168				ret |= Exit_Forcible;
169			    } else {
170				ret |= Exit_NotOK;
171			    }
172			}
173			break;
174		    } else if(j & CTM_Q_MD5_After) {
175			if(md5 == NULL) {
176			    GETFIELDCOPY(md5,sep);
177			}
178			break;
179		    }
180		    /* Unqualified MD5 */
181		    WRONG
182		    break;
183		case CTM_F_Count:
184		    GETBYTECNT(cnt,sep);
185		    break;
186		case CTM_F_Bytes:
187		    if(cnt < 0) WRONG
188		    GETDATA(trash,cnt);
189		    if (!match)
190			break;
191		    if (!template) {
192			if (asprintf(&template, "%s/CTMclientXXXXXX",
193				TmpDir) == -1) {
194			    fprintf(stderr, "  %s: malloc failed.\n",
195				sp->Key);
196			    ret |= Exit_Mess;
197			    return ret;
198		        }
199		    }
200		    if(!strcmp(sp->Key,"FN")) {
201			if ((p = strdup(template)) == NULL) {
202			    fprintf(stderr, "  %s: malloc failed.\n",
203				sp->Key);
204			    ret |= Exit_Mess;
205			    return ret;
206			}
207			if ((fdesc = mkstemp(p)) == -1) {
208			    fprintf(stderr, "  %s: mkstemp failed.\n",
209				sp->Key);
210			    ret |= Exit_Mess;
211			    Free(p);
212			    return ret;
213			}
214			if (close(fdesc) == -1) {
215			    fprintf(stderr, "  %s: close failed.\n",
216				sp->Key);
217			    ret |= Exit_Mess;
218			    unlink(p);
219			    Free(p);
220			    return ret;
221			}
222			j = ctm_edit(trash,cnt,name,p);
223			if(j) {
224			    fprintf(stderr,"  %s: %s edit returned %d.\n",
225				sp->Key,name,j);
226			    ret |= j;
227			    unlink(p);
228			    Free(p);
229			    return ret;
230			} else if(strcmp(md5,MD5File(p,md5_1))) {
231			    fprintf(stderr,"  %s: %s edit fails.\n",
232				sp->Key,name);
233			    ret |= Exit_Mess;
234			    unlink(p);
235			    Free(p);
236			    return ret;
237			}
238		        unlink(p);
239			Free(p);
240		    } else if (!strcmp(sp->Key,"FE")) {
241			if ((p = strdup(template)) == NULL) {
242			    fprintf(stderr, "  %s: malloc failed.\n",
243				sp->Key);
244			    ret |= Exit_Mess;
245			    return ret;
246			}
247			if ((fdesc = mkstemp(p)) == -1) {
248			    fprintf(stderr, "  %s: mkstemp failed.\n",
249				sp->Key);
250			    ret |= Exit_Mess;
251			    Free(p);
252			    return ret;
253			}
254			if (close(fdesc) == -1) {
255			    fprintf(stderr, "  %s: close failed.\n",
256				sp->Key);
257			    ret |= Exit_Mess;
258			    unlink(p);
259			    Free(p);
260			    return ret;
261			}
262			ed = popen("ed","w");
263			if (!ed) {
264			    WRONG
265			}
266			fprintf(ed,"e %s\n", name);
267			if (cnt != fwrite(trash,1,cnt,ed)) {
268			    warn("%s", name);
269			    pclose(ed);
270			    WRONG
271			}
272			fprintf(ed,"w %s\n",p);
273			if (pclose(ed)) {
274			    warn("%s", p);
275			    WRONG
276			}
277			if(strcmp(md5,MD5File(p,md5_1))) {
278			    fprintf(stderr,"%s %s MD5 didn't come out right\n",
279				sp->Key, name);
280			    WRONG
281			}
282		        unlink(p);
283			Free(p);
284		    }
285
286		    break;
287		default: WRONG
288	    }
289        }
290    }
291
292    Delete(trash);
293    Delete(name);
294    Delete(md5);
295
296    q = MD5End (&ctx,md5_1);
297    GETFIELD(p,'\n');			/* <MD5> */
298    if(strcmp(q,p)) WRONG
299    if (-1 != getc(fd)) WRONG
300    return ret;
301}
302