--- /dev/null
+++ b/README.access-list
@@ -0,0 +1,52 @@
+This version of ifmail has been patched to support access lists in iftoss.
+There are three access lists, namely packet header one, message header one
+and origin one. They are specified using the following keywords in a main
+ifmail configuration file:
+
+packetacl		<filename>
+messageacl		<filename>
+originacl		<filename>
+
+The three files are of the same format: if a line has a hash ('#') at its
+beginning then it is a comment line; otherwise a line specifies a rule and
+must have four fields delimited by whitespace characters (spaces or tabs).
+The first field specifies the action of the rule: "permit" or "deny".
+Those words must be typed in lowercase. The second field is a pattern for
+areatag. The third and the fourth ones are patterns for source and destination
+addresses, accordingly. The patterns are in the style of Unix shell ones.
+See sh(1), fnmatch(3) or fnmatch(5) for their description. No escaping with
+a backslash ('\') is available.
+
+The destination address pattern field is ignored in the origin access list
+and may be anything, but not empty.
+
+An empty line is considered a error. Use comments to beautify your access
+lists.
+
+If any of the three files is not readable or does not exist at all then
+the according access list is considered empty.
+
+Access list matching is performed in a such way that the first matching rule
+is used. If no matching rules found then the default is to permit anything.
+
+Example (for origin access list):
+#
+# Allow Alex Semenyaka and his points to post to PVT.EXCH areas
+# as he is actually in Msk
+#
+permit	PVT.EXCH.*		2:461/640	*
+permit	PVT.EXCH.*		2:461/640.*	*
+#
+# Deny posts to PVT.EXCH from R46 (on the Moderator's request)
+#
+deny	PVT.EXCH.*		2:46*/*		*
+#
+# Make this jerk and his points read-only in the famous area FOO.BAR
+# because he've got a bang [!] from the Moderator
+#
+deny	FOO.BAR			2:5020/12345	*
+deny	FOO.BAR			2:5020/12345.*	*
+#
+# This is the default action. Placed here for convenience
+#
+permit	*			*		*
--- /dev/null
+++ b/ifgate/acl.c
@@ -0,0 +1,146 @@
+#include <errno.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ftn.h"
+#include "acl.h"
+#include "lutil.h"
+#include "xutil.h"
+
+int acl_default_action = PERMIT;
+
+void tidy_acl(aclp)
+	acl *aclp;
+{
+	acl *taclp;
+
+	while (aclp != NULL) {
+		if (aclp->etag != NULL) free(aclp->etag);
+		if (aclp->from != NULL) free(aclp->from);
+		if (aclp->to != NULL) free(aclp->to);
+		taclp = aclp->next;
+		free(aclp);
+		aclp = taclp;
+	}
+}
+
+acl *read_acl(filename)
+	char *filename;
+{
+	int action;
+	int linecount;
+	FILE *fp;
+	acl *aclp, *taclp;
+	char action_s[256], etag_s[256], from_s[256], to_s[256];
+	char buf[1024];
+
+	if (filename == NULL) return NULL;
+
+	if ((fp = fopen(filename, "r")) == NULL) {
+		if (errno != ENOENT)
+			logerr("$cannot open access list \"%s\"", filename);
+		return NULL;
+	}
+
+	linecount = 1;
+	aclp = taclp = NULL;
+
+	while (fgets(buf, sizeof(buf)-1, fp) != NULL) {
+		if (buf[0] == '#') continue;
+
+		if (sscanf(buf, "%s%s%s%s",
+		    action_s, etag_s, from_s, to_s) != 4) {
+			logerr("error in access list \"%s\", line %d",
+			    filename, linecount);
+			continue;
+		}
+
+		if (strcmp(action_s, "deny") == 0)
+			action = DENY;
+		else if (strcmp(action_s, "permit") == 0)
+			action = PERMIT;
+		else {
+			logerr("bad action in access list \"%s\", line %d",
+			    filename, linecount);
+			continue;
+		}
+
+		if (taclp == NULL)
+			aclp = taclp = (acl *)xmalloc(sizeof(acl));
+		else {
+			taclp->next = (acl *)xmalloc(sizeof(acl));
+			taclp = taclp->next;
+		}
+			
+		taclp->action = action;
+		taclp->etag = xstrcpy(etag_s);
+		taclp->from = xstrcpy(from_s);
+		taclp->to = xstrcpy(to_s);
+		taclp->next = NULL;
+	}
+
+	fclose(fp);
+	return aclp;
+}
+
+int match_acl(etag, from, to, aclp)
+	char *etag;
+	faddr *from;
+	faddr *to;
+	acl *aclp;
+{
+	int action = acl_default_action;
+	char *etag_s = NULL, *from_s = NULL, *to_s = NULL;
+
+	if (etag != NULL) {
+		etag_s = xstrcpy(etag);
+		if (etag_s[strlen(etag)-1] == '\n')
+			etag_s[strlen(etag)-1] = '\0';
+	}
+
+	if (from != NULL)
+		from_s = xstrcpy(ascfnode(from, 0x0f));
+		
+	if (to != NULL)
+		to_s = xstrcpy(ascfnode(to, 0x0f));
+		
+
+	for (; aclp != NULL; aclp = aclp->next)
+		if ((etag == NULL ||
+		    fnmatch(aclp->etag, etag_s, FNM_NOESCAPE) == 0) &&
+		    (from == NULL ||
+		    fnmatch(aclp->from, from_s, FNM_NOESCAPE) == 0) &&
+		    (to == NULL ||
+		    fnmatch(aclp->to, to_s, FNM_NOESCAPE) == 0)) {
+			action = aclp->action;
+			break;
+		}
+
+	if (etag_s != NULL) free(etag_s);
+	if (from_s != NULL) free(from_s);
+	if (to_s != NULL) free(to_s);
+
+	return action;
+}
+
+#ifdef DEBUG
+
+int main(int argc, char **argv)
+{
+	int i;
+	acl *aclp;
+
+	aclp = read_acl(argv[1]);
+
+	/*for (i=0; i<1000000; i++)
+		match_acl(argv[2], argv[3], argv[4], aclp);
+	*/
+
+	printf("%d\n", match_acl(argv[2], argv[3], argv[4], aclp));
+
+	return 0;
+}
+
+#endif
--- /dev/null
+++ b/ifgate/acl.h
@@ -0,0 +1,19 @@
+#ifndef _ACL_H_
+#define _ACL_H_
+
+#define PERMIT		0
+#define DENY		1
+
+typedef struct _acl {
+	int action;
+	char *etag;
+	char *from;
+	char *to;
+	struct _acl *next;
+} acl;
+
+void	tidy_acl(acl *);
+acl *	read_acl(char *);
+int	match_acl(char *, faddr *, faddr *, acl *);
+
+#endif
--- a/ifgate/getmessage.c
+++ b/ifgate/getmessage.c
@@ -20,6 +20,7 @@
 #include "config.h"
 #include "crc.h"
 #include "charset.h"
+#include "acl.h"
 
 #define KWDCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
 		"abcdefghijklmnopqrstuvwxyz" \
@@ -30,6 +31,8 @@ extern char *rfcdate(time_t);
 
 int pgpsigned;
 
+extern acl *packet_acl, *message_acl, *origin_acl;
+
 static time_t parsefdate(char *,void *);
 static time_t parsefdate(str,now)
 char *str;
@@ -59,6 +62,7 @@ faddr *p_from,*p_to;
 	rfcmsg *kmsg=NULL,**tmsg;
 	int tmp,rc,maxrc=0;
 	faddr f,t,*o;
+	faddr m_from, m_to;
 	int flags;
 	int waskluge,badkludge;
 	time_t mdate=0L;
@@ -153,6 +157,10 @@ faddr *p_from,*p_to;
 	debug(5,"message subj \"%s\"",S(subj));
 	debug(5,"message date \"%s\"",rfcdate(mdate));
 
+	/* We need only numeric fields */
+	m_from=f;
+	m_to=t;
+
 	tear_off=0L;
 	orig_off=0L;
 	via_off=0L;
@@ -336,11 +344,37 @@ faddr *p_from,*p_to;
 	if ((via_off) && (via_off < endmsg_off)) endmsg_off=via_off;
 	debug(5,"end message offset %ld",(long)endmsg_off);
 	
+
+	if (kmsg && !strcmp(kmsg->key,"AREA"))
+	{
+		if (match_acl(kmsg->val, p_from, p_to, packet_acl) == DENY) 
+		{
+			loginf("message from %s in area %s denied by packet header filter",
+			    ascfnode(p_from, 0xf), kmsg->val);
+			goto skip;
+		}
+		if (match_acl(kmsg->val, &m_from, &m_to, message_acl) == DENY)
+		{
+			loginf("message from %s in area %s denied by message header filter",
+			    ascfnode(&m_from, 0xf), kmsg->val);
+			goto skip;
+		}
+
+		if (match_acl(kmsg->val, &f, NULL, origin_acl) == DENY)
+		{
+			loginf("message from %s in area %s denied by origin filter",
+			    ascfnode(&f, 0xf), kmsg->val);
+			goto skip;
+		}
+
+	}
+
 	rewind(fp);
 	rc=mkrfcmsg(&f,&t,subj,orig,mdate,flags,fp,endmsg_off,kmsg);
 	if (rc) rc+=10;
 	if (rc > maxrc) maxrc=rc;
 
+skip:
 	fclose(fp);
 	tidyrfc(kmsg);
 	if(f.name) free(f.name); f.name=NULL;
--- a/ifgate/iftoss.c
+++ b/ifgate/iftoss.c
@@ -18,6 +18,7 @@
 #include "ftn.h"
 #include "getheader.h"
 #include "trap.h"
+#include "acl.h"
 #ifdef DIRTY_CHRS
 #include "charset.h"
 int dirtyoutcode;
@@ -41,6 +42,9 @@ extern void readalias(char *);
 extern int exclose(FILE *);
 
 extern int num_echo,num_mail;
+extern long paranoid;
+
+acl *packet_acl, *message_acl, *origin_acl;
 
 int usetmp=1; /* to tell bgets that we do not use batch mode */
 int notransports=0;
@@ -113,23 +117,34 @@ char *argv[];
 #endif
 	if (aliasfile) readalias(aliasfile);
 
+	packet_acl=read_acl(pktaclfile);
+	message_acl=read_acl(msgaclfile);
+	origin_acl=read_acl(orgaclfile);
+
 	if (notransports)
 	{
 		mkdir(FAKEDIR,0777);
 		loginf("messages/newsbatches will go to %s",FAKEDIR);
 	}
 
-#ifdef PARANOID
-	if (((rc=getheader(&from,&to,stdin)) != 0) &&
-	    ((rc != 3) || (!relaxed)))
-#else
-	if (((rc=getheader(&from,&to,stdin)) != 0) &&
-	    ((rc != 3) || (!relaxed)) &&
-	    (rc != 4))
-#endif
-	{
-		logerr("%s, aborting",(rc==3)?"packet not to this node":
-			(rc==4)?"bad password":"bad packet");
+	switch ((rc=getheader(&from,&to,stdin))) {
+	    case 0:
+		break;
+	    case 2:
+		logerr("bad packet, aborting");
+		exit(rc);
+	    case 3:
+		if (relaxed)
+		    break;
+		logerr("packet not to this node, aborting");
+		exit(rc);
+	    case 4:
+		if (!paranoid)
+		    break;
+		logerr("bad password, aborting");
+		exit(rc);
+	    default:
+		logerr("can't happen: getheader returned %d", rc);
 		exit(rc);
 	}
 #ifdef AREAS_HACKING
--- a/ifgate/Makefile
+++ b/ifgate/Makefile
@@ -18,7 +18,7 @@ OBJMAIL = version.o ifmail.o rfcmsg.o me
 		charconv.o charconv_jp.o charconv_hz.o charconv_utf.o
 OBJTOSS = version.o iftoss.o areas.o \
 		getmessage.o mkrfcmsg.o rfcmsg.o batchrd.o \
-		ifdbm.o backalias.o msgflags.o \
+		ifdbm.o backalias.o msgflags.o acl.o \
 		charconv.o charconv_jp.o charconv_hz.o charconv_utf.o
 OBJUNPACK = version.o ifunpack.o unpacker.o flock.o
 OBJPACK = version.o ifpack.o flock.o
@@ -28,10 +28,10 @@ SRCS = ifmail.c rfcmsg.c message.c mkftn
 		iftoss.c getmessage.c mkrfcmsg.c \
 		nlindex.c nodecheck.c \
 		ifunpack.c unpacker.c ifpack.c flock.c \
-		backalias.c msgidbm.c attach.c ifstat.c lastmtime.c \
+		backalias.c msgidbm.c attach.c ifstat.c lastmtime.c acl.c \
 		body.c charconv.c charconv_jp.c charconv_hz.c charconv_utf.c
 HDRS = areas.h mkrfcmsg.h mkftnhdr.h nlindex.h nodecheck.h \
-		charconv.h charconv_jp.h charconv_hz.h
+		charconv.h charconv_jp.h charconv_hz.h acl.h
 OTHER = README Makefile testmail newsin pkt ifmail.8 iftoss.8
 ALL = ifmail ifnews iftoss ifunpack ifpack ifstat
 
--- a/iflib/config.h
+++ b/iflib/config.h
@@ -139,6 +139,10 @@ extern int  defaultrfcchar;
 extern int  defaultftnchar;
 extern int  toftnchar;
 
+extern char *pktaclfile;
+extern char *msgaclfile;
+extern char *orgaclfile;
+
 int readconfig(void);
 int confopt(int,char*);
 void confusage(char*);
