summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2004-01-26 02:01:49 +0000
committerIan C <ianc@noddybox.co.uk>2004-01-26 02:01:49 +0000
commitc378e8f900d85d59a8a616bf0b8b14e426d898e1 (patch)
tree0dbf84eab213d73e58b8041c7d0b2057ba5bcb0f
parentf3a485b283141787667ea98b8e0a8687f07d9062 (diff)
Added allow_to in domain; Added include; Update docs; Fixed folding header lines
-rw-r--r--doc/INSTRUCTION76
-rw-r--r--src/Makefile51
-rw-r--r--src/config.c81
-rw-r--r--src/config.h5
-rw-r--r--src/dbase.c43
-rw-r--r--src/dbase.h6
-rw-r--r--src/kbs.c2
-rw-r--r--src/kbsrc9
-rw-r--r--src/pop3.c94
9 files changed, 322 insertions, 45 deletions
diff --git a/doc/INSTRUCTION b/doc/INSTRUCTION
index 40d91b1..1ac9e8e 100644
--- a/doc/INSTRUCTION
+++ b/doc/INSTRUCTION
@@ -2,7 +2,9 @@
Usage
=====
- kbs
+ kbs [config-file]
+
+If config-file is not provided, a default of $HOME/.kbsrc is used.
Description
@@ -16,11 +18,13 @@ a special keyword in a subject header to be used.
Config file format
==================
-The kbs config file is read from $HOME/.kbsrc and has sections in the suggested
-order:
+The kbs config file is read from $HOME/.kbsrc (or the parameter supplied) and
+has sections in the suggested order:
[config settings]
+[include]
+
[trusted settings]
[blacklist settings]
@@ -29,7 +33,7 @@ order:
[domain settings]
-It is *highly* recommended to set .kbsrc to be only readable by the user - this
+It is *stringly* suggested to set .kbsrc to be only readable by the user - this
is as passwords are stored in there.
Blank lines and text proceeded with a hash (#) are ignored.
@@ -39,7 +43,7 @@ includes spaces, simple quote them (with either single or double quotes).
Escapes aren't used (to make regular expression writing easier), so if you want
to include a quote in a string, simply use the other sort of quote. If you
-want both, sorry, you're stuck!
+want both, sorry!
To see an example kbsrc file, see kbsrc in the src directory.
@@ -98,7 +102,10 @@ And understand the following variables:
blockhtml <on|off> Whether messages that are pure HTML (content part just
reported as "text/html" are blocked. Also blocks
- messages with an empty, or missing, content type.
+ messages with an empty, or missing, content type
+ WARNING: this will delete mail sent with old command
+ line mailers as they don't set a content-type - however,
+ plenty of spam has no valid content-type either.
Defaults to off.
testmode <on|off> Whether things will be really deleted, or just
@@ -109,6 +116,33 @@ And understand the following variables:
(or too easy for that matter).
+Include files
+=============
+
+Include files are useful if you want to scan multiple hosts buy using mutiple
+config files, eg.
+
+In file 1:
+
+set host host1
+set username user1
+set password pass1
+
+include "/etc/kbsrc"
+
+
+In file 2:
+
+set host host2
+set username user2
+set password pass2
+
+include "/etc/kbsrc"
+
+
+Then put all your rules as needed in /etc/kbsrc.
+
+
Trusted settings
================
@@ -191,6 +225,7 @@ will be used.
{
[default block|allow]
[block_user <username>]
+ [allow_to <regular expression>]
[allow_subject <regular expression>]
[block_subject <regular expression>]
}
@@ -202,6 +237,13 @@ The block user allows a specific username to be blocked. For instance, I've
noticed that spammers have a great love of emailing from your username at
a different domain.
+The allow_to sets a regular expression that the 'To: ' line in the header
+data must match. This can be handy as for some reason, even though spam may
+be directed to your inbox, the 'To:' line will actually read a nonsense name
+for your host. Note that it's not recommended to anchor the regular expression
+as if the mail has been sent to multiple recipients it is not guaranteed
+where your name will appear in the list of users.
+
The allow_subject means that subjects that match that regular expression are
always let through.
@@ -213,17 +255,19 @@ Multiple allow and block commands can be in one domain.
The commands inside a domain can appear in any order, but the checks are always
done in this order:
-1. If a trusted domain, allow message.
-2. If a trusted user, allow message.
-3. If an HTML message and these are blocked, delete message.
-4. If the domain is blacklisted, delete message.
-5. If the domain is not matched in a domain command, allow message.
-6. If the subject is allowed for the domain, allow message.
-7. If the username is blocked for the domain, delete message.
-8. If the subject is disallowed for the domain, delete message.
-9. Delete the message if the default is to block, otherwise allow.
+ 1. If a trusted domain, allow message.
+ 2. If a trusted user, allow message.
+ 3. If an HTML message and these are blocked, delete message.
+ 4. If the domain is blacklisted, delete message.
+ 5. If the domain is not matched in a domain command, allow message.
+ 6. If the subject is allowed for the domain, allow message.
+ 7. If an allow_to has been set for the domain, and it doesn't match,
+ delete message.
+ 8. If the username is blocked for the domain, delete message.
+ 9. If the subject is disallowed for the domain, delete message.
+10. Delete the message if the default is to block, otherwise allow.
-------------------------------------------------------------------------------
-$Id: INSTRUCTION,v 1.6 2004-01-01 01:25:04 ianc Exp $
+$Id: INSTRUCTION,v 1.7 2004-01-26 02:01:49 ianc Exp $
diff --git a/src/Makefile b/src/Makefile
index e1ef0ce..15b4775 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -18,7 +18,7 @@
#
# -------------------------------------------------------------------------
#
-# $Id: Makefile,v 1.6 2003-12-12 01:15:38 ianc Exp $
+# $Id: Makefile,v 1.7 2004-01-26 02:01:49 ianc Exp $
#
# CFLAGS assumes that gcc is being used - simply comment out if required
@@ -54,3 +54,52 @@ depend:
if test -e Makefile ; then rm -f Makefile.bak ; fi
# DO NOT DELETE THIS LINE -- make depend depends on it
+
+kbs.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
+kbs.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+kbs.o: /usr/include/limits.h /usr/include/sys/limits.h
+kbs.o: /usr/include/machine/_limits.h /usr/include/sys/syslimits.h
+kbs.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h
+kbs.o: /usr/include/stdarg.h /usr/include/time.h /usr/include/sys/timespec.h
+kbs.o: /usr/include/errno.h global.h config.h pop3.h msg.h dbase.h rexp.h
+kbs.o: dstring.h util.h
+pop3.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
+pop3.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+pop3.o: /usr/include/string.h /usr/include/strings.h /usr/include/stdio.h
+pop3.o: /usr/include/stdarg.h /usr/include/ctype.h /usr/include/runetype.h
+pop3.o: /usr/include/unistd.h /usr/include/sys/types.h
+pop3.o: /usr/include/machine/endian.h /usr/include/sys/select.h
+pop3.o: /usr/include/sys/_sigset.h /usr/include/sys/_timeval.h
+pop3.o: /usr/include/sys/timespec.h /usr/include/sys/unistd.h
+pop3.o: /usr/include/sys/socket.h /usr/include/sys/_iovec.h
+pop3.o: /usr/include/machine/param.h /usr/include/netinet/in.h
+pop3.o: /usr/include/netinet6/in6.h /usr/include/netinet/tcp.h
+pop3.o: /usr/include/netdb.h global.h pop3.h msg.h dstring.h util.h
+config.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
+config.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+config.o: /usr/include/string.h /usr/include/strings.h /usr/include/stdio.h
+config.o: /usr/include/errno.h /usr/include/ctype.h /usr/include/runetype.h
+config.o: global.h config.h dstring.h dbase.h msg.h rexp.h util.h
+rexp.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
+rexp.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+rexp.o: /usr/include/regex.h global.h rexp.h config.h util.h
+dbase.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
+dbase.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+dbase.o: /usr/include/string.h /usr/include/strings.h /usr/include/stdio.h
+dbase.o: /usr/include/ctype.h /usr/include/runetype.h global.h dbase.h msg.h
+dbase.o: rexp.h dstring.h config.h util.h
+dstring.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
+dstring.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+dstring.o: /usr/include/string.h /usr/include/strings.h /usr/include/stdio.h
+dstring.o: /usr/include/stdarg.h /usr/include/ctype.h /usr/include/runetype.h
+dstring.o: /usr/include/sys/types.h /usr/include/machine/endian.h
+dstring.o: /usr/include/sys/select.h /usr/include/sys/_sigset.h
+dstring.o: /usr/include/sys/_timeval.h /usr/include/sys/timespec.h
+dstring.o: /usr/include/sys/socket.h /usr/include/sys/_iovec.h
+dstring.o: /usr/include/machine/param.h /usr/include/netinet/in.h
+dstring.o: /usr/include/netinet6/in6.h /usr/include/netinet/tcp.h
+dstring.o: /usr/include/netdb.h global.h dstring.h util.h
+util.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
+util.o: /usr/include/sys/_types.h /usr/include/machine/_types.h
+util.o: /usr/include/string.h /usr/include/strings.h /usr/include/stdio.h
+util.o: global.h util.h
diff --git a/src/config.c b/src/config.c
index 1c10591..66ea944 100644
--- a/src/config.c
+++ b/src/config.c
@@ -57,6 +57,8 @@ typedef enum {
TOK_BlockSubject,
TOK_SubjectMacro,
TOK_Blacklist,
+ TOK_Include,
+ TOK_AllowTo,
TOK_VarHostname,
TOK_VarPort,
TOK_VarUsername,
@@ -128,6 +130,8 @@ static const Command cmd_table[]=
{"block_subject", TOK_BlockSubject},
{"subject_macro", TOK_SubjectMacro},
{"blacklist", TOK_Blacklist},
+ {"include", TOK_Include},
+ {"allow_to", TOK_AllowTo},
/* Variables
*/
@@ -160,6 +164,7 @@ static const Command cmd_table[]=
/* ---------------------------------------- REQUIRED PROTOS
*/
static Token GetToken(FILE *fp, DString *ret);
+static int Parse(FILE *fp);
/* ---------------------------------------- COMMAND HANDLER UTILS
@@ -417,6 +422,22 @@ static int DoDomain(FILE *fp)
DBBlockSubject(domain,re);
break;
+ case TOK_AllowTo:
+ GetToken(fp,&ds);
+
+ ds=ExpandRE(ds);
+
+ if (!(re=RECompile(ds->text)))
+ {
+ DSAddCP(error,"Bad regular expression: ");
+ DSAddDS(error,ds);
+ DSFree(ds);
+ return FALSE;
+ }
+
+ DBAllowTo(domain,re);
+ break;
+
default:
DSAddCP(error,"Unexpected string in domain command: ");
DSAddDS(error,ds);
@@ -576,6 +597,42 @@ static int DoBlacklist(FILE *fp)
}
+static int DoInclude(FILE *fp)
+{
+ FILE *inc;
+ DString ds;
+ Token tok;
+ int ret;
+
+ ds=DSInit();
+
+ tok=GetToken(fp,&ds);
+
+ if (tok==TOK_EOF)
+ {
+ DSAddCP(error,"Unexpected EOF in include command");
+ DSFree(ds);
+ return FALSE;
+ }
+
+ if (!(inc=fopen(ds->text,"r")))
+ {
+ DSAddCP(error,"Couldn't open include file ");
+ DSAddDS(error,ds);
+ DSFree(ds);
+ return FALSE;
+ }
+
+ DSFree(ds);
+
+ ret=Parse(inc);
+
+ fclose(inc);
+
+ return ret;
+}
+
+
/* ---------------------------------------- PRVIVATE FUNCTIONS
*/
static int Getc(FILE *fp)
@@ -752,6 +809,11 @@ static int Parse(FILE *fp)
ok=FALSE;
break;
+ case TOK_Include:
+ if (!DoInclude(fp))
+ ok=FALSE;
+ break;
+
case TOK_Expression:
DSAddCP(error,"Unknown command in config file: ");
DSAddDS(error,txt);
@@ -780,7 +842,7 @@ static int Parse(FILE *fp)
/* ---------------------------------------- INTERFACES
*/
-int ConfigLoad(void)
+int ConfigLoad(const char *path)
{
DString ds;
FILE *fp;
@@ -793,14 +855,19 @@ int ConfigLoad(void)
password=CopyStr("");
log=CopyStr("");
- if (!getenv("HOME"))
- return FALSE;
-
ds=DSInit();
- DSAddCP(ds,getenv("HOME"));
- DSAddChar(ds,'/');
- DSAddCP(ds,".kbsrc");
+ if (path)
+ DSAddCP(ds,path);
+ else
+ {
+ if (!getenv("HOME"))
+ return FALSE;
+
+ DSAddCP(ds,getenv("HOME"));
+ DSAddChar(ds,'/');
+ DSAddCP(ds,".kbsrc");
+ }
if ((fp=fopen(ds->text,"r")))
ret=Parse(fp);
diff --git a/src/config.h b/src/config.h
index 7f48a70..d545531 100644
--- a/src/config.h
+++ b/src/config.h
@@ -61,9 +61,10 @@ typedef enum
/* ---------------------------------------- INTERFACES
*/
-/* Loads in config file. Returns FALSE for problems.
+/* Loads in config file (the default name if path is NULL). Returns FALSE
+ for problems.
*/
-int ConfigLoad(void);
+int ConfigLoad(const char *path);
/* Returns a reason for config load failure
diff --git a/src/dbase.c b/src/dbase.c
index c3e83b7..e1caa04 100644
--- a/src/dbase.c
+++ b/src/dbase.c
@@ -49,9 +49,11 @@ struct Domain
int no_user;
int no_block;
int no_allow;
+ int no_allow_to;
char **user;
RE_Expression *block;
RE_Expression *allow;
+ RE_Expression *allow_to;
Domain *next;
Domain *prev;
};
@@ -165,6 +167,7 @@ Domain *DBNewDomain(RE_Expression name)
dom->user=NULL;
dom->block=NULL;
dom->allow=NULL;
+ dom->allow_to=NULL;
if (tail)
tail->next=dom;
@@ -208,6 +211,15 @@ void DBBlockSubject(Domain *domain, RE_Expression re)
}
+void DBAllowTo(Domain *domain, RE_Expression re)
+{
+ domain->no_allow_to++;
+ domain->allow_to=Realloc(domain->allow_to,
+ sizeof(RE_Expression)*domain->no_allow_to);
+ domain->allow_to[domain->no_allow_to-1]=re;
+}
+
+
void DBTrustedUser(const char *username)
{
no_trusted_users++;
@@ -254,7 +266,7 @@ int DBBlockMessage(const POP3Message *msg)
(strncmp(msg->content_type,html,strlen(html))==0 ||
strcmp(msg->content_type,"UNKNOWN")==0))
{
- DSAddCP(reason,"HTML message");
+ DSAddCP(reason,"HTML message or unknown content type");
return TRUE;
}
@@ -293,6 +305,30 @@ int DBBlockMessage(const POP3Message *msg)
}
}
+ if (dom->no_allow_to)
+ {
+ int found=FALSE;
+ int f;
+
+ for(f=0;f<dom->no_allow_to && !found;f++)
+ if (RESearch(dom->allow_to[f],msg->to))
+ found=TRUE;
+
+ if (!found)
+ {
+ if (show)
+ {
+ DSAddCP(reason,"disallowed to address - ");
+ DSAddCP(reason,msg->to);
+ }
+ else
+ DSAddCP(reason,"disallowed to address");
+
+ DSFree(ds);
+ return TRUE;
+ }
+ }
+
for(f=0;f<dom->no_user;f++)
{
int res;
@@ -392,6 +428,11 @@ void DBClose(void)
free(t->allow);
+ for(f=0;f<t->no_allow;f++)
+ REFree(t->allow_to[f]);
+
+ free(t->allow_to);
+
free(t);
}
diff --git a/src/dbase.h b/src/dbase.h
index 3c771af..1b3dfa6 100644
--- a/src/dbase.h
+++ b/src/dbase.h
@@ -70,6 +70,12 @@ void DBAllowSubject(Domain *domain, RE_Expression re);
void DBBlockSubject(Domain *domain, RE_Expression re);
+/* Sets the allowed 'To:' name for a domain.
+ re will be freed by the database when closed.
+*/
+void DBAllowTo(Domain *domain, RE_Expression re);
+
+
/* Adds a trusted username
*/
void DBTrustedUser(const char *username);
diff --git a/src/kbs.c b/src/kbs.c
index c951a63..8cdf8be 100644
--- a/src/kbs.c
+++ b/src/kbs.c
@@ -60,7 +60,7 @@ int main(int argc, char *argv[])
else
name=argv[0];
- if (!ConfigLoad())
+ if (!ConfigLoad(argv[1]))
{
fprintf(stderr,"%s: %s\n",name,ConfigError());
exit(EXIT_FAILURE);
diff --git a/src/kbsrc b/src/kbsrc
index 5361dd3..27031df 100644
--- a/src/kbsrc
+++ b/src/kbsrc
@@ -69,6 +69,12 @@ domain "."
#
block_user emailname
+
+ # Only allow emails legitimately directed to us:
+ #
+ allow_to emailname@myisp.co.uk
+
+
# Obviously, things like "credit card" will not be blocked from the bank
# as that's in the trusted_domains.
#
@@ -94,9 +100,10 @@ domain "."
block_subject ".* ?naked ?.*"
block_subject ".* ?nigeria ?.*"
+
# Why would anyone sane want to use my username in a subject?
# I know who I am! Note it's capitilised so nothing bizarre gets
# done by the subject macros.
#
- block_subject "EMAILNAME@MY-ISP.CO.UK"
+ block_subject "EMAILNAME@MYISP.CO.UK"
}
diff --git a/src/pop3.c b/src/pop3.c
index c6b6fe1..bfd9e6c 100644
--- a/src/pop3.c
+++ b/src/pop3.c
@@ -57,6 +57,7 @@ static const char message_id[]=KBS_MSG_H;
#define HDR_FROM 1
#define HDR_TO 2
#define HDR_CONTENT_TYPE 3
+#define HDR_DEBUG 4
/* ---------------------------------------- TYPES
*/
@@ -71,6 +72,7 @@ static int connected=FALSE;
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
+/*
static void Chomp(char *p)
{
size_t l;
@@ -87,6 +89,7 @@ static void Chomp(char *p)
p[--l]=0;
}
}
+*/
static char *ChompQuotesWS(char *p)
@@ -182,13 +185,14 @@ static int Send(const char *cmd, ...)
}
+static char gc_buff[BLOCKSIZE];
+static ssize_t gc_len=0;
+static int gc_ptr=0;
+
+
static int GetChar(void)
{
- static char buff[BLOCKSIZE];
- static ssize_t len=0;
- static int ptr=0;
-
- if (len==0 || ptr==len)
+ if (gc_len==0 || gc_ptr==gc_len)
{
struct timeval tv;
fd_set set;
@@ -205,13 +209,20 @@ static int GetChar(void)
if (!FD_ISSET(sock,&set))
return -1;
- if ((len=read(sock,buff,sizeof buff))<1)
+ if ((gc_len=read(sock,gc_buff,sizeof gc_buff))<1)
return -1;
- ptr=0;
+ gc_ptr=0;
}
- return buff[ptr++];
+ return gc_buff[gc_ptr++];
+}
+
+
+static void UnGetChar(int c)
+{
+ if (gc_ptr)
+ gc_buff[--gc_ptr]=c;
}
@@ -225,7 +236,8 @@ static int GetLine(DString t)
while(!done && (ch=GetChar())!=-1)
{
- DSAddChar(t,ch);
+ if (ch!='\015' && ch!='\012')
+ DSAddChar(t,ch);
if (last=='\015' && ch=='\012')
done=TRUE;
@@ -236,8 +248,6 @@ static int GetLine(DString t)
if (ch==-1)
return FALSE;
- Chomp(t->text);
-
return TRUE;
}
@@ -263,14 +273,59 @@ static int GetResponse(DString ret)
}
+static int IsHeader(const DString line)
+{
+ const char *p;
+
+ p=line->text;
+
+ while(*p && !isspace(*p))
+ {
+ if (*p==':')
+ return TRUE;
+
+ p++;
+ }
+
+ return FALSE;
+}
+
+
static int GetMultiLine(DString ret)
{
- int flag;
+ int done=FALSE;
+ int flag=TRUE;
- if (GetLine(ret))
- flag=TRUE;
- else
- flag=FALSE;
+ while (!done)
+ {
+ if (GetLine(ret))
+ {
+ if (IsHeader(ret))
+ {
+ int ch;
+
+ if ((ch=GetChar())!=-1)
+ {
+ if (ch!=' ' && ch!='\t')
+ done=TRUE;
+
+ UnGetChar(ch);
+ }
+ else
+ {
+ flag=FALSE;
+ done=TRUE;
+ }
+ }
+ else
+ done=TRUE;
+ }
+ else
+ {
+ flag=FALSE;
+ done=TRUE;
+ }
+ }
return flag;
}
@@ -289,15 +344,18 @@ static void ParseHeader(POP3Message *msg, char *line)
{"To: ", HDR_TO},
{"Content-type: ", HDR_CONTENT_TYPE},
{"Content-Type: ", HDR_CONTENT_TYPE},
+ /* {"Received: ", HDR_DEBUG}, */
{NULL, HDR_UNKNOWN}
};
+ const char *name;
int f;
int type=HDR_UNKNOWN;
for(f=0;type==HDR_UNKNOWN && field[f].text;f++)
if (strncmp(field[f].text,line,strlen(field[f].text))==0)
{
+ name=field[f].text;
type=field[f].type;
line+=strlen(field[f].text);
}
@@ -341,6 +399,10 @@ static void ParseHeader(POP3Message *msg, char *line)
msg->content_type=CopyStr(line);
break;
+ case HDR_DEBUG:
+ printf("%s = '%s'\n",name,line);
+ break;
+
default:
break;
}