12926Sphk/*
22926Sphk * ----------------------------------------------------------------------------
32926Sphk * "THE BEER-WARE LICENSE" (Revision 42):
493150Sphk * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
52926Sphk * can do whatever you want with this stuff. If we meet some day, and you think
62926Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
72926Sphk * ----------------------------------------------------------------------------
82926Sphk *
950479Speter * $FreeBSD$
102926Sphk *
112926Sphk */
122926Sphk
132886Sphk#include "ctm.h"
142926Sphk#define BADREAD 1
152886Sphk
162886Sphk/*---------------------------------------------------------------------------*/
174816Sphk/* Pass1 -- Validate the incoming CTM-file.
182886Sphk */
192886Sphk
202886Sphkint
216889SphkPass1(FILE *fd, unsigned applied)
222886Sphk{
232886Sphk    u_char *p,*q;
242886Sphk    MD5_CTX ctx;
252886Sphk    int i,j,sep,cnt;
2617946Sphk    u_char *md5=0,*name=0,*trash=0;
272886Sphk    struct CTM_Syntax *sp;
2817946Sphk    int slashwarn=0, match=0, total_matches=0;
296889Sphk    unsigned current;
309491Sphk    char md5_1[33];
318857Srgrimes
328857Srgrimes    if(Verbose>3)
334816Sphk	printf("Pass1 -- Checking integrity of incoming CTM-patch\n");
342886Sphk    MD5Init (&ctx);
352886Sphk
362886Sphk    GETFIELD(p,' ');				/* CTM_BEGIN */
372886Sphk    if(strcmp(p,"CTM_BEGIN")) {
382886Sphk	Fatal("Probably not a CTM-patch at all.");
398857Srgrimes	if(Verbose>3)
402886Sphk	    fprintf(stderr,"Expected \"CTM_BEGIN\" got \"%s\".\n",p);
412886Sphk	return 1;
422886Sphk    }
432886Sphk
442886Sphk    GETFIELDCOPY(Version,' ');				/* <Version> */
452886Sphk    if(strcmp(Version,VERSION)) {
462886Sphk	Fatal("CTM-patch is wrong version.");
478857Srgrimes	if(Verbose>3)
482886Sphk	    fprintf(stderr,"Expected \"%s\" got \"%s\".\n",VERSION,p);
492886Sphk	return 1;
502886Sphk    }
512886Sphk
522886Sphk    GETFIELDCOPY(Name,' ');				/* <Name> */
532886Sphk    GETFIELDCOPY(Nbr,' ');				/* <Nbr> */
542886Sphk    GETFIELDCOPY(TimeStamp,' ');			/* <TimeStamp> */
552886Sphk    GETFIELDCOPY(Prefix,'\n');				/* <Prefix> */
562886Sphk
576889Sphk    sscanf(Nbr, "%u", &current);
5817946Sphk    if (FilterList || ListIt)
5917946Sphk	current = 0;	/* ignore if -l or if filters are present */
607395Sphk    if(current && current <= applied) {
6117946Sphk	if(Verbose > 0)
626889Sphk	    fprintf(stderr,"Delta number %u is already applied; ignoring.\n",
636889Sphk		    current);
646889Sphk	return Exit_Version;
656889Sphk    }
668857Srgrimes
672886Sphk    for(;;) {
6813917Sphk	Delete(md5);
6917946Sphk	Delete(name);
7013917Sphk	Delete(trash);
712886Sphk	cnt = -1;
7217946Sphk	/* if a filter list is defined we assume that all pathnames require
7317946Sphk	   an action opposite to that requested by the first filter in the
7417946Sphk	   list.
7517946Sphk	   If no filter is defined, all pathnames are assumed to match. */
7617946Sphk	match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
772886Sphk
782886Sphk	GETFIELD(p,' ');			/* CTM_something */
792886Sphk
802886Sphk	if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') {
812886Sphk	    Fatal("Expected CTM keyword.");
822886Sphk	    fprintf(stderr,"Got [%s]\n",p);
832886Sphk	    return 1;
842886Sphk	}
852886Sphk
862886Sphk	if(!strcmp(p+3,"_END"))
872886Sphk	    break;
882886Sphk
892886Sphk	for(sp=Syntax;sp->Key;sp++)
902886Sphk	    if(!strcmp(p+3,sp->Key))
912886Sphk		goto found;
922886Sphk	Fatal("Expected CTM keyword.");
932886Sphk	fprintf(stderr,"Got [%s]\n",p);
942886Sphk	return 1;
952886Sphk    found:
962886Sphk	if(Verbose > 5)
972886Sphk	    fprintf(stderr,"%s ",sp->Key);
982886Sphk	for(i=0;(j = sp->List[i]);i++) {
992886Sphk	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
1002886Sphk		sep = ' ';
1012886Sphk	    else
1022886Sphk		sep = '\n';
1032886Sphk
10417946Sphk	    if(Verbose > 5)
10517946Sphk	        fprintf(stderr," %x(%d)",sp->List[i],sep);
10617946Sphk
1072886Sphk	    switch (j & CTM_F_MASK) {
1082886Sphk		case CTM_F_Name: /* XXX check for garbage and .. */
10917946Sphk		    GETFIELDCOPY(name,sep);
11017946Sphk		    j = strlen(name);
11117946Sphk		    if(name[j-1] == '/' && !slashwarn)  {
1122948Sphk			fprintf(stderr,"Warning: contains trailing slash\n");
1132948Sphk			slashwarn++;
1142948Sphk		    }
11517946Sphk		    if (name[0] == '/') {
1166181Sphk			Fatal("Absolute paths are illegal.");
1176181Sphk			return Exit_Mess;
1186181Sphk		    }
11917946Sphk		    q = name;
1206694Sphk		    for (;;) {
12117946Sphk			if (q[0] == '.' && q[1] == '.')
12217946Sphk			    if (q[2] == '/' || q[2] == '\0') {
1236694Sphk				Fatal("Paths containing '..' are illegal.");
1246694Sphk				return Exit_Mess;
1256694Sphk			    }
12617946Sphk			if ((q = strchr(q, '/')) == NULL)
1276694Sphk			    break;
12817946Sphk			q++;
1296694Sphk		    }
13017946Sphk
13117946Sphk		    /* if we have been asked to `keep' files then skip
13217946Sphk		       removes; i.e. we don't match these entries at
13317946Sphk		       all. */
13417946Sphk		    if (KeepIt &&
13517946Sphk			(!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) {
13617946Sphk			match = CTM_FILTER_DISABLE;
13717946Sphk			break;
13817946Sphk		    }
13917946Sphk
14017946Sphk		    /* If filter expression have been defined, match the
14117946Sphk		       path name against the expression list.  */
14217946Sphk
14317946Sphk		    if (FilterList) {
14417946Sphk			struct CTM_Filter *filter;
14517946Sphk
14617946Sphk			for (filter = FilterList; filter;
14717946Sphk			     filter = filter->Next) {
14817946Sphk				if (0 == regexec(&filter->CompiledRegex, name,
14917946Sphk					0, 0, 0))
15017946Sphk					/* if the name matches, adopt the
15117946Sphk					   action */
15217946Sphk					match = filter->Action;
15317946Sphk			}
15417946Sphk		    }
15517946Sphk
15617946Sphk		    /* Add up the total number of matches */
15717946Sphk		    total_matches += match;
1582886Sphk		    break;
1592948Sphk		case CTM_F_Uid:
1602948Sphk		    GETFIELD(p,sep);
1612948Sphk		    while(*p) {
1622948Sphk			if(!isdigit(*p)) {
1632948Sphk			    Fatal("Non-digit in uid.");
1642948Sphk			    return 32;
1652948Sphk			}
1662948Sphk			p++;
1672948Sphk		    }
1682948Sphk		    break;
1692948Sphk		case CTM_F_Gid:
1702948Sphk		    GETFIELD(p,sep);
1712948Sphk		    while(*p) {
1722948Sphk			if(!isdigit(*p)) {
1732948Sphk			    Fatal("Non-digit in gid.");
1742948Sphk			    return 32;
1752948Sphk			}
1762948Sphk			p++;
1772948Sphk		    }
1782948Sphk		    break;
1792948Sphk		case CTM_F_Mode:
1802948Sphk		    GETFIELD(p,sep);
1812948Sphk		    while(*p) {
1822948Sphk			if(!isdigit(*p)) {
1832948Sphk			    Fatal("Non-digit in mode.");
1842948Sphk			    return 32;
1852948Sphk			}
1862948Sphk			p++;
1872948Sphk		    }
1882948Sphk		    break;
1892886Sphk		case CTM_F_MD5:
1902948Sphk		    if(j & CTM_Q_MD5_Chunk) {
1912886Sphk			GETFIELDCOPY(md5,sep);  /* XXX check for garbage */
1922948Sphk		    } else if(j & CTM_Q_MD5_Before) {
1932886Sphk			GETFIELD(p,sep);  /* XXX check for garbage */
1942948Sphk		    } else if(j & CTM_Q_MD5_After) {
1952948Sphk			GETFIELD(p,sep);  /* XXX check for garbage */
1962948Sphk		    } else {
1972948Sphk			fprintf(stderr,"List = 0x%x\n",j);
1982948Sphk			Fatal("Unqualified MD5.");
1992948Sphk			return 32;
2002948Sphk		    }
2012886Sphk		    break;
2022886Sphk		case CTM_F_Count:
2032948Sphk		    GETBYTECNT(cnt,sep);
2042886Sphk		    break;
2052886Sphk		case CTM_F_Bytes:
2062886Sphk		    if(cnt < 0) WRONG
2072886Sphk		    GETDATA(trash,cnt);
2089491Sphk		    p = MD5Data(trash,cnt,md5_1);
2092886Sphk		    if(md5 && strcmp(md5,p)) {
2102886Sphk			Fatal("Internal MD5 failed.");
21117946Sphk			return Exit_Garbage;
2122886Sphk		default:
2132886Sphk			fprintf(stderr,"List = 0x%x\n",j);
2142886Sphk			Fatal("List had garbage.");
21517946Sphk			return Exit_Garbage;
2162886Sphk		    }
2172886Sphk	    }
21817946Sphk	}
2192886Sphk	if(Verbose > 5)
2202886Sphk	    putc('\n',stderr);
22117946Sphk	if(ListIt && match)
22217946Sphk	    printf("> %s %s\n", sp->Key, name);
2232886Sphk    }
22413917Sphk
22513917Sphk    Delete(md5);
22617946Sphk    Delete(name);
22713917Sphk    Delete(trash);
22813917Sphk
2299491Sphk    q = MD5End (&ctx,md5_1);
2302886Sphk    if(Verbose > 2)
2312886Sphk	printf("Expecting Global MD5 <%s>\n",q);
2322886Sphk    GETFIELD(p,'\n');			/* <MD5> */
2332886Sphk    if(Verbose > 2)
2342886Sphk	printf("Reference Global MD5 <%s>\n",p);
2352886Sphk    if(strcmp(q,p)) {
2362886Sphk	Fatal("MD5 sum doesn't match.");
2372886Sphk	fprintf(stderr,"\tI have:<%s>\n",q);
2382886Sphk	fprintf(stderr,"\tShould have been:<%s>\n",p);
23917946Sphk	return Exit_Garbage;
2402886Sphk    }
2412886Sphk    if (-1 != getc(fd)) {
2422926Sphk	if(!Force) {
2432926Sphk	    Fatal("Trailing junk in CTM-file.  Can Force with -F.");
2442926Sphk	    return 16;
2452926Sphk	}
2462886Sphk    }
24717946Sphk    if ((Verbose > 1) && (0 == total_matches))
24817946Sphk	printf("No matches in \"%s\"\n", FileName);
24917946Sphk    return (total_matches ? Exit_OK : Exit_NoMatch);
2502886Sphk}
251