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 32
152886Sphk
162886Sphk/*---------------------------------------------------------------------------*/
174816Sphk/* Pass3 -- Validate the incoming CTM-file.
182886Sphk */
192886Sphk
202886Sphkint
2115456Sphksettime(const char *name, const struct timeval *times)
2215456Sphk{
2315456Sphk	if (SetTime)
2415456Sphk	    if (utimes(name,times)) {
2529526Scharnier		warn("utimes(): %s", name);
2615456Sphk		return -1;
2715456Sphk	    }
2815456Sphk	return 0;
2915456Sphk}
3015456Sphk
3115456Sphkint
322886SphkPass3(FILE *fd)
332886Sphk{
342886Sphk    u_char *p,*q,buf[BUFSIZ];
352886Sphk    MD5_CTX ctx;
362886Sphk    int i,j,sep,cnt;
372886Sphk    u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
382886Sphk    struct CTM_Syntax *sp;
392886Sphk    FILE *ed=0;
402886Sphk    struct stat st;
419491Sphk    char md5_1[33];
4217946Sphk    int match=0;
4315456Sphk    struct timeval times[2];
4417946Sphk    struct CTM_Filter *filter = NULL;
458857Srgrimes    if(Verbose>3)
462886Sphk	printf("Pass3 -- Applying the CTM-patch\n");
472886Sphk    MD5Init (&ctx);
482886Sphk
492886Sphk    GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
502886Sphk    GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
512886Sphk    GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
522886Sphk    GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
532886Sphk    GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
542886Sphk    GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
552886Sphk
5615456Sphk    /*
5715456Sphk     * This would be cleaner if mktime() worked in UTC rather than
5815456Sphk     * local time.
5915456Sphk     */
6015456Sphk    if (SetTime) {
6115456Sphk        struct tm tm;
6215456Sphk        char *tz;
6315456Sphk        char buf[5];
6415456Sphk        int i;
6515456Sphk
6615456Sphk#define SUBSTR(off,len)	strncpy(buf, &TimeStamp[off], len), buf[len] = '\0'
6715456Sphk#define WRONGDATE { fprintf(stderr, " %s failed date validation\n",\
6815456Sphk	TimeStamp); WRONG}
6915456Sphk
7015456Sphk        if (strlen(TimeStamp) != 15 || TimeStamp[14] != 'Z') WRONGDATE
7115456Sphk	for (i = 0; i < 14; i++)
7215456Sphk	    if (!isdigit(TimeStamp[i])) WRONGDATE
7315456Sphk
7415456Sphk        tz = getenv("TZ");
7515456Sphk	if (setenv("TZ", "UTC", 1) < 0) WRONG
7615456Sphk	tzset();
7715456Sphk
7815456Sphk	tm.tm_isdst = tm.tm_gmtoff = 0;
7915456Sphk
8015456Sphk        SUBSTR(0, 4);
8115456Sphk        tm.tm_year = atoi(buf) - 1900;
8215456Sphk        SUBSTR(4, 2);
8315456Sphk        tm.tm_mon = atoi(buf) - 1;
8415456Sphk        if (tm.tm_mon < 0 || tm.tm_mon > 11) WRONGDATE
8515456Sphk        SUBSTR(6, 2);
8615456Sphk        tm.tm_mday = atoi(buf);
8715456Sphk        if (tm.tm_mday < 1 || tm.tm_mday > 31) WRONG;
8815456Sphk        SUBSTR(8, 2);
8915456Sphk        tm.tm_hour = atoi(buf);
9015456Sphk        if (tm.tm_hour > 24) WRONGDATE
9115456Sphk        SUBSTR(10, 2);
9215456Sphk        tm.tm_min = atoi(buf);
9315456Sphk        if (tm.tm_min > 59) WRONGDATE
9415456Sphk        SUBSTR(12, 2);
9515456Sphk        tm.tm_sec = atoi(buf);
9615456Sphk        if (tm.tm_min > 62) WRONGDATE	/* allow leap seconds */
9715456Sphk
9815456Sphk        times[0].tv_sec = times[1].tv_sec = mktime(&tm);
9915456Sphk        if (times[0].tv_sec == -1) WRONGDATE
10015456Sphk	times[0].tv_usec = times[1].tv_usec = 0;
10115456Sphk
10215456Sphk        if (tz) {
10315456Sphk            if (setenv("TZ", tz, 1) < 0) WRONGDATE
10415456Sphk         } else {
10515456Sphk            unsetenv("TZ");
10615456Sphk        }
10715456Sphk    }
10815456Sphk
1092886Sphk    for(;;) {
11013917Sphk	Delete(md5);
11113917Sphk	Delete(uid);
11213917Sphk	Delete(gid);
11313917Sphk	Delete(mode);
11413917Sphk	Delete(md5before);
11513917Sphk	Delete(trash);
11613917Sphk	Delete(name);
1172886Sphk	cnt = -1;
1182886Sphk
1192886Sphk	GETFIELD(p,' ');
1202886Sphk
1212886Sphk	if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
1222886Sphk
1232886Sphk	if(!strcmp(p+3,"_END"))
1242886Sphk	    break;
1252886Sphk
1262886Sphk	for(sp=Syntax;sp->Key;sp++)
1272886Sphk	    if(!strcmp(p+3,sp->Key))
1282886Sphk		goto found;
1292886Sphk	WRONG
1302886Sphk    found:
1312886Sphk	for(i=0;(j = sp->List[i]);i++) {
1322886Sphk	    if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
1332886Sphk		sep = ' ';
1342886Sphk	    else
1352886Sphk		sep = '\n';
1362886Sphk
1372886Sphk	    switch (j & CTM_F_MASK) {
13813917Sphk		case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
1392886Sphk		case CTM_F_Uid:  GETFIELDCOPY(uid,sep); break;
1402886Sphk		case CTM_F_Gid:  GETFIELDCOPY(gid,sep); break;
1412886Sphk		case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
1422886Sphk		case CTM_F_MD5:
1432886Sphk		    if(j & CTM_Q_MD5_Before)
1448857Srgrimes			GETFIELDCOPY(md5before,sep);
1452886Sphk		    else
1468857Srgrimes			GETFIELDCOPY(md5,sep);
1472886Sphk		    break;
1482886Sphk		case CTM_F_Count: GETBYTECNT(cnt,sep); break;
1492886Sphk		case CTM_F_Bytes: GETDATA(trash,cnt); break;
1502886Sphk		default: WRONG
1512886Sphk		}
1522886Sphk	    }
1532948Sphk	/* XXX This should go away.  Disallow trailing '/' */
1542886Sphk	j = strlen(name)-1;
1552886Sphk	if(name[j] == '/') name[j] = '\0';
1562948Sphk
15717946Sphk	/*
15817946Sphk	 * If a filter list is specified, run thru the filter list and
15917946Sphk	 * match `name' against filters.  If the name matches, set the
16017946Sphk	 * required action to that specified in the filter.
16117946Sphk	 * The default action if no filterlist is given is to match
16217946Sphk	 * everything.
16317946Sphk	 */
16417946Sphk
16517946Sphk	match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
16617946Sphk	for (filter = FilterList; filter; filter = filter->Next) {
16717946Sphk	    if (0 == regexec(&filter->CompiledRegex, name,
16817946Sphk		0, 0, 0)) {
16917946Sphk		match = filter->Action;
17017946Sphk	    }
17117946Sphk	}
17217946Sphk
17317946Sphk	if (CTM_FILTER_DISABLE == match) /* skip file if disabled */
17417946Sphk		continue;
17517946Sphk
17617946Sphk	if (Verbose > 0)
17717946Sphk		fprintf(stderr,"> %s %s\n",sp->Key,name);
1782886Sphk	if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) {
1797372Sjoerg	    i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666);
1802886Sphk	    if(i < 0) {
18129526Scharnier		warn("%s", name);
1822948Sphk		WRONG
1832886Sphk	    }
1842886Sphk	    if(cnt != write(i,trash,cnt)) {
18529526Scharnier		warn("%s", name);
1862948Sphk		WRONG
1872886Sphk	    }
1882886Sphk	    close(i);
1899491Sphk	    if(strcmp(md5,MD5File(name,md5_1))) {
1902886Sphk		fprintf(stderr,"  %s %s MD5 didn't come out right\n",
1912886Sphk		   sp->Key,name);
1922948Sphk		WRONG
1932886Sphk	    }
19415456Sphk	    if (settime(name,times)) WRONG
1952886Sphk	    continue;
1968857Srgrimes	}
1972886Sphk	if(!strcmp(sp->Key,"FE")) {
1982971Sphk	    ed = popen("ed","w");
1992886Sphk	    if(!ed) {
2002886Sphk		WRONG
2012886Sphk	    }
2022971Sphk	    fprintf(ed,"e %s\n",name);
2032886Sphk	    if(cnt != fwrite(trash,1,cnt,ed)) {
20429526Scharnier		warn("%s", name);
2052886Sphk		pclose(ed);
2062948Sphk		WRONG
2072886Sphk	    }
2082886Sphk	    fprintf(ed,"w %s\n",name);
2092886Sphk	    if(pclose(ed)) {
21029526Scharnier		warn("ed");
2112948Sphk		WRONG
2122886Sphk	    }
2139491Sphk	    if(strcmp(md5,MD5File(name,md5_1))) {
2142886Sphk		fprintf(stderr,"  %s %s MD5 didn't come out right\n",
2152886Sphk		   sp->Key,name);
2162948Sphk		WRONG
2172886Sphk	    }
21815456Sphk	    if (settime(name,times)) WRONG
2192886Sphk	    continue;
2202886Sphk	}
2212926Sphk	if(!strcmp(sp->Key,"FN")) {
2222926Sphk	    strcpy(buf,name);
22313917Sphk	    strcat(buf,TMPSUFF);
2242926Sphk	    i = ctm_edit(trash,cnt,name,buf);
2252926Sphk	    if(i) {
2262948Sphk		fprintf(stderr," %s %s Edit failed with code %d.\n",
2272948Sphk		    sp->Key,name,i);
2282948Sphk	        WRONG
2292926Sphk	    }
23020971Sphk	    if(strcmp(md5,MD5File(buf,md5_1))) {
2312948Sphk		fprintf(stderr," %s %s Edit failed MD5 check.\n",
2322948Sphk		    sp->Key,name);
2332948Sphk	        WRONG
2342948Sphk	    }
23520971Sphk	    if (rename(buf,name) == -1)
23620971Sphk		WRONG
23715456Sphk	    if (settime(name,times)) WRONG
2382948Sphk	    continue;
2392926Sphk	}
2402886Sphk	if(!strcmp(sp->Key,"DM")) {
2417372Sjoerg	    if(0 > mkdir(name,0777)) {
2422886Sphk		sprintf(buf,"mkdir -p %s",name);
2432886Sphk		system(buf);
2442886Sphk	    }
2452886Sphk	    if(0 > stat(name,&st) || ((st.st_mode & S_IFMT) != S_IFDIR)) {
2462886Sphk		fprintf(stderr,"<%s> mkdir failed\n",name);
2472948Sphk		WRONG
2482886Sphk	    }
24915456Sphk	    if (settime(name,times)) WRONG
2502886Sphk	    continue;
2518857Srgrimes	}
2524816Sphk	if(!strcmp(sp->Key,"FR")) {
25317946Sphk	    if (KeepIt) {
25417946Sphk		if (Verbose > 1)
25517946Sphk			printf("<%s> not removed\n", name);
25617946Sphk	    }
25717946Sphk	    else if (0 != unlink(name)) {
2584947Sphk		fprintf(stderr,"<%s> unlink failed\n",name);
2596181Sphk		if (!Force)
2606181Sphk		    WRONG
2614947Sphk	    }
2624947Sphk	    continue;
2638857Srgrimes	}
2648857Srgrimes	if(!strcmp(sp->Key,"DR")) {
2654947Sphk	    /*
2664947Sphk	     * We cannot use rmdir() because we do not get the directories
2674947Sphk	     * in '-depth' order (cvs-cur.0018.gz for examples)
2684947Sphk	     */
26917946Sphk	    if (KeepIt) {
27017946Sphk		if (Verbose > 1) {
27117946Sphk			printf("<%s> not removed\n", name);
27217946Sphk		}
27317946Sphk	    } else {
27417946Sphk		    sprintf(buf,"rm -rf %s",name);
27517946Sphk		    system(buf);
27617946Sphk	    }
2772886Sphk	    continue;
2788857Srgrimes	}
2792886Sphk	WRONG
2802886Sphk    }
28113917Sphk
28213917Sphk    Delete(md5);
28313917Sphk    Delete(uid);
28413917Sphk    Delete(gid);
28513917Sphk    Delete(mode);
28613917Sphk    Delete(md5before);
28713917Sphk    Delete(trash);
28813917Sphk    Delete(name);
28913917Sphk
2909491Sphk    q = MD5End (&ctx,md5_1);
2912886Sphk    GETFIELD(p,'\n');
2922886Sphk    if(strcmp(q,p)) WRONG
2932886Sphk    if (-1 != getc(fd)) WRONG
2942886Sphk    return 0;
2952886Sphk}
296