| #include "common.h" | 
 | #include "send.h" | 
 |  | 
 | extern int debug; | 
 |  | 
 | /*  | 
 |  *	Routines for dealing with the rewrite rules. | 
 |  */ | 
 |  | 
 | /* globals */ | 
 | typedef struct rule rule; | 
 |  | 
 | #define NSUBEXP 10 | 
 | struct rule { | 
 | 	String *matchre;	/* address match */ | 
 | 	String *repl1;		/* first replacement String */ | 
 | 	String *repl2;		/* second replacement String */ | 
 | 	d_status type;		/* type of rule */ | 
 | 	Reprog *program; | 
 | 	Resub subexp[NSUBEXP]; | 
 | 	rule *next; | 
 | }; | 
 | static rule *rulep; | 
 | static rule *rlastp; | 
 |  | 
 | /* predeclared */ | 
 | static String *substitute(String *, Resub *, message *); | 
 | static rule *findrule(String *, int); | 
 |  | 
 |  | 
 | /* | 
 |  *  Get the next token from `line'.  The symbol `\l' is replaced by | 
 |  *  the name of the local system. | 
 |  */ | 
 | extern String * | 
 | rule_parse(String *line, char *system, int *backl) | 
 | { | 
 | 	String *token; | 
 | 	String *expanded; | 
 | 	char *cp; | 
 |  | 
 | 	token = s_parse(line, 0); | 
 | 	if(token == 0) | 
 | 		return(token); | 
 | 	if(strchr(s_to_c(token), '\\')==0) | 
 | 		return(token); | 
 | 	expanded = s_new(); | 
 | 	for(cp = s_to_c(token); *cp; cp++) { | 
 | 		if(*cp == '\\') switch(*++cp) { | 
 | 		case 'l': | 
 | 			s_append(expanded, system); | 
 | 			*backl = 1; | 
 | 			break; | 
 | 		case '\\': | 
 | 			s_putc(expanded, '\\'); | 
 | 			break; | 
 | 		default: | 
 | 			s_putc(expanded, '\\'); | 
 | 			s_putc(expanded, *cp); | 
 | 			break; | 
 | 		} else | 
 | 			s_putc(expanded, *cp); | 
 | 	} | 
 | 	s_free(token); | 
 | 	s_terminate(expanded); | 
 | 	return(expanded); | 
 | } | 
 |  | 
 | static int | 
 | getrule(String *line, String *type, char *system) | 
 | { | 
 | 	rule	*rp; | 
 | 	String	*re; | 
 | 	int	backl; | 
 |  | 
 | 	backl = 0; | 
 |  | 
 | 	/* get a rule */ | 
 | 	re = rule_parse(s_restart(line), system, &backl); | 
 | 	if(re == 0) | 
 | 		return 0; | 
 | 	rp = (rule *)malloc(sizeof(rule)); | 
 | 	if(rp == 0) { | 
 | 		perror("getrules:"); | 
 | 		exit(1); | 
 | 	} | 
 | 	rp->next = 0; | 
 | 	s_tolower(re); | 
 | 	rp->matchre = s_new(); | 
 | 	s_append(rp->matchre, s_to_c(re)); | 
 | 	s_restart(rp->matchre); | 
 | 	s_free(re); | 
 | 	s_parse(line, s_restart(type)); | 
 | 	rp->repl1 = rule_parse(line, system, &backl); | 
 | 	rp->repl2 = rule_parse(line, system, &backl); | 
 | 	rp->program = 0; | 
 | 	if(strcmp(s_to_c(type), "|") == 0) | 
 | 		rp->type = d_pipe; | 
 | 	else if(strcmp(s_to_c(type), ">>") == 0) | 
 | 		rp->type = d_cat; | 
 | 	else if(strcmp(s_to_c(type), "alias") == 0) | 
 | 		rp->type = d_alias; | 
 | 	else if(strcmp(s_to_c(type), "translate") == 0) | 
 | 		rp->type = d_translate; | 
 | 	else if(strcmp(s_to_c(type), "auth") == 0) | 
 | 		rp->type = d_auth; | 
 | 	else { | 
 | 		s_free(rp->matchre); | 
 | 		s_free(rp->repl1); | 
 | 		s_free(rp->repl2); | 
 | 		free((char *)rp); | 
 | 		fprint(2,"illegal rewrite rule: %s\n", s_to_c(line)); | 
 | 		return 0; | 
 | 	} | 
 | 	if(rulep == 0) | 
 | 		rulep = rlastp = rp; | 
 | 	else | 
 | 		rlastp = rlastp->next = rp; | 
 | 	return backl; | 
 | } | 
 |  | 
 | /* | 
 |  *  rules are of the form: | 
 |  *	<reg exp> <String> <repl exp> [<repl exp>] | 
 |  */ | 
 | extern int | 
 | getrules(void) | 
 | { | 
 | 	Biobuf	*rfp; | 
 | 	String	*line; | 
 | 	String	*type; | 
 | 	String	*file; | 
 |  | 
 | 	file = abspath("rewrite", UPASLIB, (String *)0); | 
 | 	rfp = sysopen(s_to_c(file), "r", 0); | 
 | 	if(rfp == 0) { | 
 | 		rulep = 0; | 
 | 		return -1; | 
 | 	} | 
 | 	rlastp = 0; | 
 | 	line = s_new(); | 
 | 	type = s_new(); | 
 | 	while(s_getline(rfp, s_restart(line))) | 
 | 		if(getrule(line, type, thissys) && altthissys) | 
 | 			getrule(s_restart(line), type, altthissys); | 
 | 	s_free(type); | 
 | 	s_free(line); | 
 | 	s_free(file); | 
 | 	sysclose(rfp); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* look up a matching rule */ | 
 | static rule * | 
 | findrule(String *addrp, int authorized) | 
 | { | 
 | 	rule *rp; | 
 | 	static rule defaultrule; | 
 |  | 
 | 	if(rulep == 0) | 
 | 		return &defaultrule; | 
 | 	for (rp = rulep; rp != 0; rp = rp->next) { | 
 | 		if(rp->type==d_auth && authorized) | 
 | 			continue; | 
 | 		if(rp->program == 0) | 
 | 			rp->program = regcomp(rp->matchre->base); | 
 | 		if(rp->program == 0) | 
 | 			continue; | 
 | 		memset(rp->subexp, 0, sizeof(rp->subexp)); | 
 | 		if(debug) | 
 | 			fprint(2, "matching %s aginst %s\n", s_to_c(addrp), rp->matchre->base); | 
 | 		if(regexec(rp->program, s_to_c(addrp), rp->subexp, NSUBEXP)) | 
 | 		if(s_to_c(addrp) == rp->subexp[0].s.sp) | 
 | 		if((s_to_c(addrp) + strlen(s_to_c(addrp))) == rp->subexp[0].e.ep) | 
 | 			return rp; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /*  Transforms the address into a command. | 
 |  *  Returns:	-1 ifaddress not matched by reules | 
 |  *		 0 ifaddress matched and ok to forward | 
 |  *		 1 ifaddress matched and not ok to forward | 
 |  */ | 
 | extern int | 
 | rewrite(dest *dp, message *mp) | 
 | { | 
 | 	rule *rp;		/* rewriting rule */ | 
 | 	String *lower;		/* lower case version of destination */ | 
 |  | 
 | 	/* | 
 | 	 *  Rewrite the address.  Matching is case insensitive. | 
 | 	 */ | 
 | 	lower = s_clone(dp->addr); | 
 | 	s_tolower(s_restart(lower)); | 
 | 	rp = findrule(lower, dp->authorized); | 
 | 	if(rp == 0){ | 
 | 		s_free(lower); | 
 | 		return -1; | 
 | 	} | 
 | 	strcpy(s_to_c(lower), s_to_c(dp->addr)); | 
 | 	dp->repl1 = substitute(rp->repl1, rp->subexp, mp); | 
 | 	dp->repl2 = substitute(rp->repl2, rp->subexp, mp); | 
 | 	dp->status = rp->type; | 
 | 	if(debug){ | 
 | 		fprint(2, "\t->"); | 
 | 		if(dp->repl1) | 
 | 			fprint(2, "%s", s_to_c(dp->repl1)); | 
 | 		if(dp->repl2) | 
 | 			fprint(2, "%s", s_to_c(dp->repl2)); | 
 | 		fprint(2, "\n"); | 
 | 	} | 
 | 	s_free(lower); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* stolen from rc/lex.c */ | 
 | static int | 
 | idchr(int c) | 
 | { | 
 | 	return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); | 
 | } | 
 |  | 
 | static char* | 
 | getrcvar(char* p, char** rv) | 
 | { | 
 | 	char* p0; | 
 | 	char buf[128]; | 
 | 	char* bufe; | 
 |  | 
 | 	*rv = 0; | 
 | 	p0=p; | 
 | 	bufe=buf+sizeof buf-1; | 
 | 	while(p<bufe && idchr(*p)) | 
 | 		p++; | 
 |  | 
 | 	memcpy(buf, p0, p-p0); | 
 | 	buf[p-p0]=0; | 
 | 	*rv = getenv(buf); | 
 | 	if (debug) | 
 | 		fprint(2, "varsubst: %s → %s\n", buf, *rv); | 
 | 	return p; | 
 | } | 
 |  | 
 | static String * | 
 | substitute(String *source, Resub *subexp, message *mp) | 
 | { | 
 | 	int i; | 
 | 	char *s; | 
 | 	char *sp; | 
 | 	String *stp; | 
 | 	 | 
 | 	if(source == 0) | 
 | 		return 0; | 
 | 	sp = s_to_c(source); | 
 |  | 
 | 	/* someplace to put it */ | 
 | 	stp = s_new(); | 
 |  | 
 | 	/* do the substitution */ | 
 | 	while (*sp != '\0') { | 
 | 		if(*sp == '\\') { | 
 | 			switch (*++sp) { | 
 | 			case '0': case '1': case '2': case '3': case '4': | 
 | 			case '5': case '6': case '7': case '8': case '9': | 
 | 				i = *sp-'0'; | 
 | 				if(subexp[i].s.sp != 0) | 
 | 					for (s = subexp[i].s.sp; | 
 | 					     s < subexp[i].e.ep; | 
 | 					     s++) | 
 | 						s_putc(stp, *s); | 
 | 				break; | 
 | 			case '\\': | 
 | 				s_putc(stp, '\\'); | 
 | 				break; | 
 | 			case '\0': | 
 | 				sp--; | 
 | 				break; | 
 | 			case 's': | 
 | 				for(s = s_to_c(mp->replyaddr); *s; s++) | 
 | 					s_putc(stp, *s); | 
 | 				break; | 
 | 			case 'p': | 
 | 				if(mp->bulk) | 
 | 					s = "bulk"; | 
 | 				else | 
 | 					s = "normal"; | 
 | 				for(;*s; s++) | 
 | 					s_putc(stp, *s); | 
 | 				break; | 
 | 			default: | 
 | 				s_putc(stp, *sp); | 
 | 				break; | 
 | 			} | 
 | 		} else if(*sp == '&') {			 | 
 | 			if(subexp[0].s.sp != 0) | 
 | 				for (s = subexp[0].s.sp; | 
 | 				     s < subexp[0].e.ep; s++) | 
 | 					s_putc(stp, *s); | 
 | 		} else if(*sp == '$') { | 
 | 			sp = getrcvar(sp+1, &s); | 
 | 			s_append(stp, s); | 
 | 			free(s); | 
 | 			sp--;	/* counter sp++ below */ | 
 | 		} else | 
 | 			s_putc(stp, *sp); | 
 | 		sp++; | 
 | 	} | 
 | 	s_terminate(stp); | 
 |  | 
 | 	return s_restart(stp); | 
 | } | 
 |  | 
 | extern void | 
 | regerror(char* s) | 
 | { | 
 | 	fprint(2, "rewrite: %s\n", s); | 
 | } | 
 |  | 
 | extern void | 
 | dumprules(void) | 
 | { | 
 | 	rule *rp; | 
 |  | 
 | 	for (rp = rulep; rp != 0; rp = rp->next) { | 
 | 		fprint(2, "'%s'", rp->matchre->base); | 
 | 		switch (rp->type) { | 
 | 		case d_pipe: | 
 | 			fprint(2, " |"); | 
 | 			break; | 
 | 		case d_cat: | 
 | 			fprint(2, " >>"); | 
 | 			break; | 
 | 		case d_alias: | 
 | 			fprint(2, " alias"); | 
 | 			break; | 
 | 		case d_translate: | 
 | 			fprint(2, " translate"); | 
 | 			break; | 
 | 		default: | 
 | 			fprint(2, " UNKNOWN"); | 
 | 			break; | 
 | 		} | 
 | 		fprint(2, " '%s'", rp->repl1 ? rp->repl1->base:"..."); | 
 | 		fprint(2, " '%s'\n", rp->repl2 ? rp->repl2->base:"..."); | 
 | 	} | 
 | } | 
 |  |