diff --git a/doc/technical/protoctl.txt b/doc/technical/protoctl.txt
index 3209a2f27..ae6e30ffa 100644
--- a/doc/technical/protoctl.txt
+++ b/doc/technical/protoctl.txt
@@ -98,6 +98,10 @@ SJ3           Notifies the server that the SJOIN command with SJ3 syntax is
 TKLEXT        This allows 10 instead of 8 parameters in TKL's for spamfilter, see s_kline.c
               function m_tkl for more info on this (added in 3.2RC2).
 
+TKLEXT2       This allows 11 instead of 8 or 10 parameters in TKL's for spamfilter,
+              see s_kline.c function m_tkl for more info on this (added in 3.4-alpha3).
+              Note that TKLEXT2 implies TKLEXT support as well.
+
 NICKIP        This token indicates that a (standard) base64 encoded IP address is included
               in the NICK command. The IP is in binary network byte order formated and
               encoded using the standard base64 algorithm. '*' is used if no IP is available.
diff --git a/help.conf b/help.conf
index 4534e165d..c6e150f3b 100644
--- a/help.conf
+++ b/help.conf
@@ -1349,7 +1349,11 @@ help Spamfilter {
 	" This command adds/removes global spam filters.";
 	" Spamfilters can be used to get rid of spam, advertising, bots, etc.";
 	" -";
-	" Use: /spamfilter [add|del|remove|+|-] [type] [action] [tkltime] [reason] [regex]";
+	" Use: /spamfilter [add|del|remove|+|-] [method] [type] [action] [tkltime] [reason] [string]";
+	" [method]  Matching method, must be one of:";
+	"           -regex (PCRE fast regex),";
+	"           -posix (old 3.2.x POSIX regex), or";
+	"           -simple (fastest but only supports ? and * wildcards)";
 	" [type]    specifies the target type, you can specify multiple targets:";
 	"           'c' channel msg, 'p' private msg, 'n' private notice,";
 	"           'N' channel notice, 'P' part msg, 'q' quit msg, 'd' dcc,";
@@ -1359,22 +1363,22 @@ help Spamfilter {
 	"           'kline', 'gline', 'zline', 'gzline', 'block' (blocks the msg),";
 	"           'dccblock' (unable to send any dccs), 'viruschan' (part all channels";
 	"           and join the virus help chan), 'warn' (warn for IRC Operators).";
-	" [regex]   this is the actual regex where we should block on";
+	" [string]  this is the actual string that should be blocked (regex or simple pattern)";
 	" [tkltime] the duration of the *LINEs placed by action (use '-' to use the default";
 	"           set::spamfilter::ban-time, this value is ignored for block/tempshun');";
 	" [reason]  the reason for the *LINE or blockmsg, CANNOT CONTAIN SPACES,";
 	"           '_' will be translated to spaces. Again, if you use '-' for this";
 	"           the default (set::spamfilter::ban-reason) is used.";
 	" - ";
-	" A few examples (note they will probably linewrap!):";
-	" /spamfilter add p block - - Come watch me on my webcam";
-	" /spamfilter add p block - Possible_virus_detected,_join_#help Come watch me on my webcam";
-	" /spamfilter add p tempshun - - You_are_infected me\.mpg";
-	" /spamfilter add p gline - - Come watch me on my webcam";
-	" /spamfilter add p gline 3h Please_go_to_www.viruscan.xx/nicepage/virus=blah Come watch me on my webcam";
-	" /spamfilter add p kill - Please_go_to_www.viruscan.xx/nicepage/virus=blah Come watch me on my webcam";
-	" /spamfilter del p block - - Come watch me on my webcam*";
-	" /spamfilter add cN gzline 1d No_advertising_please come to irc\..+\..+";
+	" A few examples (note they will probably line-wrap!):";
+	" /spamfilter add -simple p block - - Come watch me on my webcam";
+	" /spamfilter add -simple p block - Possible_virus_detected,_join_#help Come watch me on my webcam";
+	" /spamfilter add -simple p tempshun - - You_are_infected me.mpg";
+	" /spamfilter add -simple p gline - - Come watch me on my webcam";
+	" /spamfilter add -simple p gline 3h Please_go_to_www.viruscan.xx/nicepage/virus=blah Come watch me on my webcam";
+	" /spamfilter add -simple p kill - Please_go_to_www.viruscan.xx/nicepage/virus=blah Come watch me on my webcam";
+	" /spamfilter del -simple p block - - Come watch me on my webcam*";
+	" /spamfilter add -regex cN gzline 1d No_advertising_please /come to irc\..+\..+/";
 };
 
 help Tempshun {
diff --git a/include/common.h b/include/common.h
index 6d4dc8cab..bfbc17db7 100644
--- a/include/common.h
+++ b/include/common.h
@@ -239,6 +239,8 @@ static char *StsMalloc(size_t size, char *file, long line)
 
 #define ircstrdup(x,y) do { if (x) MyFree(x); if (!y) x = NULL; else x = strdup(y); } while(0)
 #define ircfree(x) do { if (x) MyFree(x); x = NULL; } while(0)
+#define safefree ircfree
+#define safestrdup ircstrdup
 
 extern struct SLink *find_user_link( /* struct SLink *, struct Client * */ );
 
@@ -279,6 +281,7 @@ extern struct SLink *find_user_link( /* struct SLink *, struct Client * */ );
                         " VL" \
                         " SJ3" \
                         " TKLEXT" \
+                        " TKLEXT2" \
                         " NICKIP" \
                         " ESVID"
 
diff --git a/include/config.h b/include/config.h
index 374a6295f..a6006054b 100644
--- a/include/config.h
+++ b/include/config.h
@@ -467,6 +467,9 @@
 #define SPAMFILTER_DETECTSLOW
 #endif
 
+/* Use TRE Regex Library (as well) ? */
+#define USE_TRE
+
 /* The 3.4-alpha* series, especially the first few, are highly experimental.
  * If EXPERIMENTAL is #define'd then all users will receive a notice about
  * this when they connect, along with a pointer to bugs.unrealircd.org where
diff --git a/include/h.h b/include/h.h
index 23218452c..f6950ee83 100644
--- a/include/h.h
+++ b/include/h.h
@@ -517,7 +517,7 @@ extern void count_memory(aClient *cptr, char *nick);
 extern void list_scache(aClient *sptr);
 extern char *oflagstr(long oflag);
 extern int rehash(aClient *cptr, aClient *sptr, int sig);
-extern int _match(char *mask, char *name);
+extern int _match(const char *mask, const char *name);
 extern void outofmemory(void);
 extern int add_listener2(ConfigItem_listen *conf);
 extern void link_cleanup(ConfigItem_link *link_ptr);
@@ -659,7 +659,7 @@ extern MODVAR int (*register_user)(aClient *cptr, aClient *sptr, char *nick, cha
 extern MODVAR int (*tkl_hash)(unsigned int c);
 extern MODVAR char (*tkl_typetochar)(int type);
 extern MODVAR aTKline *(*tkl_add_line)(int type, char *usermask, char *hostmask, char *reason, char *setby,
-                  TS expire_at, TS set_at, TS spamf_tkl_duration, char *spamf_tkl_reason);
+                  TS expire_at, TS set_at, TS spamf_tkl_duration, char *spamf_tkl_reason, MatchType match_type);
 extern MODVAR aTKline *(*tkl_del_line)(aTKline *tkl);
 extern MODVAR void (*tkl_check_local_remove_shun)(aTKline *tmp);
 extern MODVAR aTKline *(*tkl_expire)(aTKline * tmp);
@@ -751,3 +751,8 @@ extern void send_moddata_members(aClient *srv);
 extern int ssl_used_in_config_but_unavail(void);
 extern void config_report_ssl_error(void);
 extern int dead_link(aClient *to, char *notice);
+extern aMatch *unreal_create_match(MatchType type, char *str, char **error);
+extern void unreal_delete_match(aMatch *m);
+extern int unreal_match(aMatch *m, char *str);
+extern int unreal_match_method_strtoval(char *str);
+extern char *unreal_match_method_valtostr(int val);
diff --git a/include/struct.h b/include/struct.h
index 9d9b1ba65..34e945f9a 100644
--- a/include/struct.h
+++ b/include/struct.h
@@ -316,6 +316,7 @@ typedef unsigned int u_int32_t;	/* XXX Hope this works! */
 #define PROTO_NICKv2	0x0008	/* Negotiated NICKv2 protocol */
 #define PROTO_SJOIN2	0x0010	/* Negotiated SJOIN2 protocol */
 #define PROTO_UMODE2	0x0020	/* Negotiated UMODE2 protocol */
+#define PROTO_TKLEXT2	0x0040	/* TKL extension 2: 11 parameters instead of 8 or 10 */
 #define PROTO_INVITENOTIFY	0x0080	/* client supports invite-notify */
 #define PROTO_VL		0x0100	/* Negotiated VL protocol */
 #define PROTO_SJ3		0x0200	/* Negotiated SJ3 protocol */
@@ -465,6 +466,7 @@ typedef unsigned int u_int32_t;	/* XXX Hope this works! */
 #define SupportSJ3(x)		(CHECKPROTO(x, PROTO_SJ3))
 #define SupportVHP(x)		(CHECKPROTO(x, PROTO_VHP))
 #define SupportTKLEXT(x)	(CHECKPROTO(x, PROTO_TKLEXT))
+#define SupportTKLEXT2(x)	(CHECKPROTO(x, PROTO_TKLEXT2))
 #define SupportNAMESX(x)	(CHECKPROTO(x, PROTO_NAMESX))
 #define SupportCLK(x)		(CHECKPROTO(x, PROTO_CLK))
 #define SupportUHNAMES(x)	(CHECKPROTO(x, PROTO_UHNAMES))
@@ -479,6 +481,7 @@ typedef unsigned int u_int32_t;	/* XXX Hope this works! */
 #define SetSJ3(x)		((x)->proto |= PROTO_SJ3)
 #define SetVHP(x)		((x)->proto |= PROTO_VHP)
 #define SetTKLEXT(x)	((x)->proto |= PROTO_TKLEXT)
+#define SetTKLEXT2(x)	((x)->proto |= PROTO_TKLEXT2)
 #define SetNAMESX(x)	((x)->proto |= PROTO_NAMESX)
 #define SetCLK(x)		((x)->proto |= PROTO_CLK)
 #define SetUHNAMES(x)	((x)->proto |= PROTO_UHNAMES)
@@ -491,6 +494,8 @@ typedef unsigned int u_int32_t;	/* XXX Hope this works! */
 #define ClearVL(x)		((x)->proto &= ~PROTO_VL)
 #define ClearVHP(x)		((x)->proto &= ~PROTO_VHP)
 #define ClearSJ3(x)		((x)->proto &= ~PROTO_SJ3)
+#define ClearTKLEXT(x)		((x)->proto &= ~PROTO_TKLEXT)
+#define ClearTKLEXT2(x)		((x)->proto &= ~PROTO_TKLEXT2)
 
 /*
  * defined operator access levels
@@ -724,6 +729,27 @@ struct aloopStruct {
 	int rehash_save_sig;
 };
 
+/** Matching types for aMatch.type */
+typedef enum {
+	MATCH_SIMPLE=1, /**< Simple pattern with * and ? */
+	MATCH_PCRE_REGEX=2, /**< PCRE2 Perl-like regex (new) */
+#ifdef USE_TRE
+	MATCH_TRE_REGEX=3, /**< TRE POSIX regex (old, unreal3.2.x) */
+#endif
+} MatchType;
+
+/** Match struct, which allows various matching styles, see MATCH_* */
+typedef struct _match {
+	char *str; /**< Text of the glob/regex/whatever. Always set. */
+	MatchType type;
+	union {
+//		pcre2_code *pcre_expr; /**< PCRE2 Perl-like Regex (New) */
+#ifdef USE_TRE
+		regex_t *tre_expr; /**< TRE POSIX Regex (Old) */
+#endif
+	} ext;
+} aMatch;
+
 typedef struct Whowas {
 	int  hashv;
 	char *name;
@@ -845,7 +871,7 @@ struct Server {
 
 struct _spamfilter {
 	unsigned short action; /* see BAN_ACT* */
-	regex_t expr;
+	aMatch *expr;
 	char *tkl_reason; /* spamfilter reason field [escaped by unreal_encodespace()!] */
 	TS tkl_duration;
 };
@@ -1359,7 +1385,7 @@ struct _configitem_alias_format {
 	char *nick;
 	AliasType type;
 	char *format, *parameters;
-	regex_t expr;
+	aMatch *expr;
 };
 
 /**
diff --git a/src/match.c b/src/match.c
index 680d4bd3a..14224561e 100644
--- a/src/match.c
+++ b/src/match.c
@@ -21,6 +21,7 @@
 #include "struct.h"
 #include "common.h"
 #include "sys.h"
+#include "h.h"
 
 ID_Copyright("(C) 1990 Jarkko Oikarinen");
 
@@ -427,3 +428,119 @@ int match(const char *mask, const char *name) {
 	}
 	return match2(mask,name);
 }
+
+/** Free up all resources of an aMatch entry (including the struct itself).
+ * NOTE: this function may (also) be called for aMatch structs that have only been
+ *       setup half-way, so use special care when accessing members (NULL checks!)
+ */
+void unreal_delete_match(aMatch *m)
+{
+	safefree(m->str);
+#ifdef USE_TRE
+	if (m->type == MATCH_TRE_REGEX)
+	{
+		if (m->ext.tre_expr)
+			regfree(m->ext.tre_expr);
+	}
+#endif
+	// TODO: PCRE2 !!
+	MyFree(m);
+}
+
+aMatch *unreal_create_match(MatchType type, char *str, char **error)
+{
+	aMatch *m = MyMallocEx(sizeof(aMatch));
+	static char errorbuf[512];
+
+	m->str = strdup(str);
+	m->type = type;
+	
+	if (m->type == MATCH_SIMPLE)
+	{
+		/* Nothing to do */
+	}
+	else if (m->type == MATCH_PCRE_REGEX)
+	{
+		/* TODO */
+	}
+#ifdef USE_TRE
+	else if (m->type == MATCH_TRE_REGEX)
+	{
+		int errorcode;
+		
+		m->ext.tre_expr = MyMallocEx(sizeof(regex_t));
+		errorcode = regcomp(m->ext.tre_expr, str, REG_ICASE|REG_EXTENDED|REG_NOSUB);
+		if (errorcode > 0)
+		{
+			int errorbufsize = 512;
+			char *errtmp = MyMallocEx(errorbufsize);
+			regerror(errorcode, m->ext.tre_expr, errtmp, errorbufsize);
+			strlcpy(errorbuf, errtmp, sizeof(errorbuf));
+			MyFree(errtmp);
+			if (error)
+				*error = errorbuf;
+			goto fail;
+		}
+	}
+#endif
+	else {
+		abort(); /* unknown type, how did that happen ? */
+	}
+	return m;
+	
+fail:
+	unreal_delete_match(m);
+	return NULL;
+}
+
+/** Try to match an aMatch entry ('m') against a string ('str').
+ * @returns 1 if matched, 0 if not.
+ * @notes These (more logical) return values are opposite to the match() function.
+ */
+int unreal_match(aMatch *m, char *str)
+{
+	if (m->type == MATCH_SIMPLE)
+	{
+		if (_match(m->str, str) == 0)
+			return 1;
+		return 0;
+	}
+	
+	if (m->type == MATCH_PCRE_REGEX)
+	{
+		// todo
+		return 0;
+	}
+
+#ifdef USE_TRE
+	if (m->type == MATCH_TRE_REGEX)
+	{
+		if (regexec(m->ext.tre_expr, str, 0, NULL, 0) == 0)
+			return 1;
+		return 0;
+	}
+#endif
+}
+
+int unreal_match_method_strtoval(char *str)
+{
+	if (!strcmp(str, "regex") || !strcmp(str, "pcre"))
+		return MATCH_PCRE_REGEX;
+	if (!strcmp(str, "posix") || !strcmp(str, "tre"))
+		return MATCH_TRE_REGEX;
+	if (!strcmp(str, "simple") || !strcmp(str, "glob"))
+		return MATCH_SIMPLE;
+	return 0;
+}
+
+char *unreal_match_method_valtostr(int val)
+{
+	if (val == MATCH_PCRE_REGEX)
+		return "regex";
+	if (val == MATCH_TRE_REGEX)
+		return "posix";
+	if (val == MATCH_SIMPLE)
+		return "simple";
+	
+	return "unknown";
+}
diff --git a/src/modules.c b/src/modules.c
index 4ad04131c..71e7b5581 100644
--- a/src/modules.c
+++ b/src/modules.c
@@ -91,7 +91,7 @@ int (*register_user)(aClient *cptr, aClient *sptr, char *nick, char *username, c
 int (*tkl_hash)(unsigned int c);
 char (*tkl_typetochar)(int type);
 aTKline *(*tkl_add_line)(int type, char *usermask, char *hostmask, char *reason, char *setby,
-    TS expire_at, TS set_at, TS spamf_tkl_duration, char *spamf_tkl_reason);
+    TS expire_at, TS set_at, TS spamf_tkl_duration, char *spamf_tkl_reason, MatchType match_type);
 aTKline *(*tkl_del_line)(aTKline *tkl);
 void (*tkl_check_local_remove_shun)(aTKline *tmp);
 aTKline *(*tkl_expire)(aTKline * tmp);
diff --git a/src/modules/m_protoctl.c b/src/modules/m_protoctl.c
index c7c9d6186..7439c390e 100644
--- a/src/modules/m_protoctl.c
+++ b/src/modules/m_protoctl.c
@@ -267,6 +267,12 @@ CMD_FUNC(m_protoctl)
 			Debug((DEBUG_ERROR, "Chose protocol %s for link %s", proto, cptr->name));
 			SetTKLEXT(cptr);
 		}
+		else if (strcmp(s, "TKLEXT2") == 0)
+		{
+			Debug((DEBUG_ERROR, "Chose protocol %s for link %s", proto, cptr->name));
+			SetTKLEXT2(cptr);
+			SetTKLEXT(cptr); /* TKLEXT is implied as well. always. */
+		}
 		else if (strcmp(s, "NICKIP") == 0)
 		{
 			Debug((DEBUG_ERROR, "Chose protocol %s for link %s", proto, cptr->name));
diff --git a/src/modules/m_tkl.c b/src/modules/m_tkl.c
index 8868866dd..ca5b5685f 100644
--- a/src/modules/m_tkl.c
+++ b/src/modules/m_tkl.c
@@ -76,7 +76,7 @@ DLLFUNC int m_spamfilter(aClient *cptr, aClient *sptr, int parc, char *parv[]);
 int _tkl_hash(unsigned int c);
 char _tkl_typetochar(int type);
 aTKline *_tkl_add_line(int type, char *usermask, char *hostmask, char *reason, char *setby,
-    TS expire_at, TS set_at, TS spamf_tkl_duration, char *spamf_tkl_reason);
+    TS expire_at, TS set_at, TS spamf_tkl_duration, char *spamf_tkl_reason, MatchType match_type);
 aTKline *_tkl_del_line(aTKline *tkl);
 static void _tkl_check_local_remove_shun(aTKline *tmp);
 aTKline *_tkl_expire(aTKline * tmp);
@@ -160,7 +160,7 @@ DLLFUNC int MOD_INIT(m_tkl)(ModuleInfo *modinfo)
 	CommandAdd(modinfo->handle, MSG_ZLINE, m_tzline, 3, M_OPER);
 	CommandAdd(modinfo->handle, MSG_KLINE, m_tkline, 3, M_OPER);
 	CommandAdd(modinfo->handle, MSG_GZLINE, m_gzline, 3, M_OPER);
-	CommandAdd(modinfo->handle, MSG_SPAMFILTER, m_spamfilter, 6, M_OPER);
+	CommandAdd(modinfo->handle, MSG_SPAMFILTER, m_spamfilter, 7, M_OPER);
 	CommandAdd(modinfo->handle, MSG_TKL, _m_tkl, MAXPARA, M_OPER|M_SERVER);
 	MARK_AS_OFFICIAL_MODULE(modinfo);
 	return MOD_SUCCESS;
@@ -480,16 +480,17 @@ DLLFUNC int  m_tkl_line(aClient *cptr, aClient *sptr, int parc, char *parv[], ch
 	char *mask = NULL;
 	char mo[1024], mo2[1024];
 	char *p, *usermask, *hostmask;
-	char *tkllayer[9] = {
-		me.name,	/*0  server.name */
-		NULL,		/*1  +|- */
-		NULL,		/*2  G   */
-		NULL,		/*3  user */
-		NULL,		/*4  host */
-		NULL,		/*5  setby */
-		"0",		/*6  expire_at */
-		NULL,		/*7  set_at */
-		"no reason"	/*8  reason */
+	char *tkllayer[10] = {
+		me.name,		/*0  server.name */
+		NULL,			/*1  +|- */
+		NULL,			/*2  G   */
+		NULL,			/*3  user */
+		NULL,			/*4  host */
+		NULL,			/*5  setby */
+		"0",			/*6  expire_at */
+		NULL,			/*7  set_at */
+		"no reason",	/*8  reason */
+		NULL
 	};
 	struct tm *t;
 
@@ -713,17 +714,19 @@ DLLFUNC int  m_tkl_line(aClient *cptr, aClient *sptr, int parc, char *parv[], ch
 
 int spamfilter_usage(aClient *sptr)
 {
-	sendnotice(sptr, "Use: /spamfilter [add|del|remove|+|-] [type] [action] [tkltime] [tklreason] [regex]");
+	sendnotice(sptr, "Use: /spamfilter [add|del|remove|+|-] [-simple|-regex|-posix] [type] [action] [tkltime] [tklreason] [regex]");
 	sendnotice(sptr, "See '/helpop ?spamfilter' for more information.");
 	return 0;
 }
 
+/** /spamfilter [add|del|remove|+|-] [match-type] [type] [action] [tkltime] [reason] [regex]
+ *                   1                    2         3        4        5        6        7
+ */
 DLLFUNC int m_spamfilter(aClient *cptr, aClient *sptr, int parc, char *parv[])
 {
 int  whattodo = 0;	/* 0 = add  1 = del */
 char mo[32], mo2[32];
-char *p;
-char *tkllayer[11] = {
+char *tkllayer[13] = {
 	me.name,	/*  0 server.name */
 	NULL,		/*  1 +|- */
 	"F",		/*  2 F   */
@@ -734,12 +737,17 @@ char *tkllayer[11] = {
 	"0",		/*  7 set_at */
 	"",			/*  8 tkl time */
 	"",			/*  9 tkl reason */
-	""			/* 10 regex */
+	"",			/* 10 match method */
+	"",			/* 11 regex */
+	NULL
 };
 int targets = 0, action = 0;
 char targetbuf[64], actionbuf[2];
 char reason[512];
 int n;
+aMatch *m;
+int match_type = 0;
+char *err = NULL;
 
 	if (IsServer(sptr))
 		return 0;
@@ -759,15 +767,20 @@ int n;
 			sptr->name, sptr->user->username, GetHost(sptr));
 		return 0;
 	}
-	if ((parc < 7) || BadPtr(parv[4]))
+
+	if ((parc == 7) && (*parv[2] != '-'))
+		goto new_spamfilter_syntax;
+		
+	if ((parc < 8) || BadPtr(parv[7]))
 		return spamfilter_usage(sptr);
 
 	/* parv[1]: [add|del|+|-]
-	 * parv[2]: type
-	 * parv[3]: action
-	 * parv[4]: tkl time
-	 * parv[5]: tkl reason (or block reason..)
-	 * parv[6]: regex
+	 * parv[2]: match-type
+	 * parv[3]: type
+	 * parv[4]: action
+	 * parv[5]: tkl time
+	 * parv[6]: tkl reason (or block reason..)
+	 * parv[7]: regex
 	 */
 	if (!strcasecmp(parv[1], "add") || !strcmp(parv[1], "+"))
 		whattodo = 0;
@@ -779,50 +792,66 @@ int n;
 		return spamfilter_usage(sptr);
 	}
 
-	targets = spamfilter_gettargets(parv[2], sptr);
+	match_type = unreal_match_method_strtoval(parv[2]+1);
+	if (!match_type)
+	{
+new_spamfilter_syntax:
+		sendnotice(sptr, "Unknown match-type '%s'. Must be one of: -regex (new fast PCRE regexes), "
+		                 "-posix (old unreal 3.2.x posix regexes) or "
+		                 "-simple (simple text with ? and * wildcards)",
+		                 parv[2]);
+		if (*parv[2] != '-')
+			sendnotice(sptr, "Using the old 3.2.x /SPAMFILTER syntax? Note the new -regex/-posix/-simple field!!");
+		
+		return spamfilter_usage(cptr);
+	}
+
+	targets = spamfilter_gettargets(parv[3], sptr);
 	if (!targets)
 		return spamfilter_usage(sptr);
 
 	strlcpy(targetbuf, spamfilter_target_inttostring(targets), sizeof(targetbuf));
 
-	action = banact_stringtoval(parv[3]);
+	action = banact_stringtoval(parv[4]);
 	if (!action)
 	{
-		sendto_one(sptr, ":%s NOTICE %s :Invalid 'action' field (%s)", me.name, sptr->name, parv[3]);
+		sendto_one(sptr, ":%s NOTICE %s :Invalid 'action' field (%s)", me.name, sptr->name, parv[4]);
 		return spamfilter_usage(sptr);
 	}
 	actionbuf[0] = banact_valtochar(action);
 	actionbuf[1] = '\0';
 
-	/* now check the regex... */
-	p = unreal_checkregex(parv[6],0,1);
-	if (p)
+	/* now check the regex / match field... */
+	m = unreal_create_match(match_type, reason, &err);
+	if (!m)
 	{
 		sendto_one(sptr, ":%s NOTICE %s :Error in regex '%s': %s",
-			me.name, sptr->name, parv[6], p);
+			me.name, sptr->name, parv[7], err);
 		return 0;
 	}
+	unreal_delete_match(m);
 
 	tkllayer[1] = whattodo ? "-" : "+";
 	tkllayer[3] = targetbuf;
 	tkllayer[4] = actionbuf;
 	tkllayer[5] = make_nick_user_host(sptr->name, sptr->user->username, GetHost(sptr));
 
-	if (parv[4][0] == '-')
+	if (parv[5][0] == '-')
 	{
 		ircsnprintf(mo, sizeof(mo), "%li", SPAMFILTER_BAN_TIME);
 		tkllayer[8] = mo;
 	}
 	else
-		tkllayer[8] = parv[4];
+		tkllayer[8] = parv[5];
 
-	if (parv[5][0] == '-')
+	if (parv[6][0] == '-')
 		strlcpy(reason, unreal_encodespace(SPAMFILTER_BAN_REASON), sizeof(reason));
 	else
-		strlcpy(reason, parv[5], sizeof(reason));
+		strlcpy(reason, parv[6], sizeof(reason));
 
 	tkllayer[9] = reason;
-	tkllayer[10] = parv[6];
+	tkllayer[10] = parv[2]+1; /* +1 to skip the '-' */
+	tkllayer[11] = parv[7];
 
 	/* SPAMFILTER LENGTH CHECK.
 	 * We try to limit it here so '/stats f' output shows ok, output of that is:
@@ -832,7 +861,7 @@ int n;
 	 * We also do >500 instead of >510, since that looks cleaner ;).. so actually we count
 	 * on 50 characters for the rest... -- Syzop
 	 */
-	n = strlen(reason) + strlen(parv[6]) + strlen(tkllayer[5]) + (NICKLEN * 2) + 40;
+	n = strlen(reason) + strlen(parv[7]) + strlen(tkllayer[6]) + (NICKLEN * 2) + 40;
 	if ((n > 500) && (whattodo == 0))
 	{
 		sendnotice(sptr, "Sorry, spamfilter too long. You'll either have to trim down the "
@@ -846,7 +875,7 @@ int n;
 		tkllayer[7] = mo2;
 	}
 	
-	m_tkl(&me, &me, 11, tkllayer);
+	m_tkl(&me, &me, 12, tkllayer);
 
 	return 0;
 }
@@ -921,10 +950,11 @@ char _tkl_typetochar(int type)
 */
 
 aTKline *_tkl_add_line(int type, char *usermask, char *hostmask, char *reason, char *setby,
-                  TS expire_at, TS set_at, TS spamf_tkl_duration, char *spamf_tkl_reason)
+                  TS expire_at, TS set_at, TS spamf_tkl_duration, char *spamf_tkl_reason, MatchType match_type)
 {
 	aTKline *nl;
 	int index;
+	aMatch *m;
 
 	/* Pre-allocate etc check for spamfilters that fail to compile.
 	 * This could happen if for example TRE supports a feature on server X, but not
@@ -932,11 +962,12 @@ aTKline *_tkl_add_line(int type, char *usermask, char *hostmask, char *reason, c
 	 */
 	if (type & TKL_SPAMF)
 	{
-		char *myerr = unreal_checkregex(reason, 0, 0);
-		if (myerr)
+		char *err = NULL;
+		m = unreal_create_match(match_type, reason, &err);
+		if (!m)
 		{
 			sendto_realops("[TKL ERROR] ERROR: Spamfilter was added but did not compile. ERROR='%s', Spamfilter='%s'",
-				myerr, reason);
+				err, reason);
 			return NULL;
 		}
 	}
@@ -957,7 +988,8 @@ aTKline *_tkl_add_line(int type, char *usermask, char *hostmask, char *reason, c
 	{
 		/* Need to set some additional flags like 'targets' and 'action'.. */
 		nl->subtype = spamfilter_gettargets(usermask, NULL);
-		nl->ptr.spamf = unreal_buildspamfilter(reason);
+		nl->ptr.spamf = MyMallocEx(sizeof(Spamfilter));
+		nl->ptr.spamf->expr = m;
 		nl->ptr.spamf->action = banact_chartoval(*hostmask);
 		nl->expire_at = 0; /* temporary spamfilters are NOT supported! (makes no sense) */
 		if (!spamf_tkl_reason)
@@ -1005,7 +1037,7 @@ aTKline *_tkl_del_line(aTKline *tkl)
 			MyFree(p->setby);
 			if (p->type & TKL_SPAMF && p->ptr.spamf)
 			{
-				regfree(&p->ptr.spamf->expr);
+				unreal_delete_match(p->ptr.spamf->expr);
 				if (p->ptr.spamf->tkl_reason)
 					MyFree(p->ptr.spamf->tkl_reason);
 				MyFree(p->ptr.spamf);
@@ -1435,7 +1467,7 @@ aClient *acptr;
 		if (MyClient(acptr))
 		{
 			spamfilter_build_user_string(spamfilter_user, acptr->name, acptr);
-			if (regexec(&tk->ptr.spamf->expr, spamfilter_user, 0, NULL, 0))
+			if (!unreal_match(tk->ptr.spamf->expr, spamfilter_user))
 				continue; /* No match */
 
 			/* matched! */
@@ -1465,7 +1497,7 @@ aClient *acptr;
 		if (IsPerson(acptr))
 		{
 			spamfilter_build_user_string(spamfilter_user, acptr->name, acptr);
-			if (regexec(&tk->ptr.spamf->expr, spamfilter_user, 0, NULL, 0))
+			if (!unreal_match(tk->ptr.spamf->expr, spamfilter_user))
 				continue; /* No match */
 
 			/* matched! */
@@ -1783,6 +1815,7 @@ void _tkl_stats(aClient *cptr, int type, char *para)
 			sendto_one(cptr, rpl_str(RPL_STATSSPAMF), me.name,
 				cptr->name,
 				(tk->type & TKL_GLOBAL) ? 'F' : 'f',
+				unreal_match_method_valtostr(tk->ptr.spamf->expr->type),
 				spamfilter_target_inttostring(tk->subtype),
 				banact_valtostring(tk->ptr.spamf->action),
 				(tk->expire_at != 0) ? (tk->expire_at - curtime) : 0,
@@ -1850,21 +1883,23 @@ void _tkl_synch(aClient *sptr)
  * This routine is used both internally by the ircd (to
  * for example add local klines, zlines, etc) and over the
  * network (glines, gzlines, spamfilter, etc).
- *           add:      remove:    spamfilter:    spamfilter+TKLEXT  sqline:
- * parv[ 1]: +         -          +/-            +                  +/-
- * parv[ 2]: type      type       type           type               type
- * parv[ 3]: user      user       target         target             hold
- * parv[ 4]: host      host       action         action             host
- * parv[ 5]: setby     removedby  (un)setby      setby              setby
- * parv[ 6]: expire_at            expire_at (0)  expire_at (0)      expire_at
- * parv[ 7]: set_at               set_at         set_at             set_at
- * parv[ 8]: reason               regex          tkl duration       reason
- * parv[ 9]:                                     tkl reason [A]        
- * parv[10]:                                     regex              
+ *           add:      remove:    spamfilter:    spamfilter+TKLEXT  spamfilter+TKLEXT2  sqline:
+ * parv[ 1]: +         -          +/-            +                  +                   +/-
+ * parv[ 2]: type      type       type           type               type                type
+ * parv[ 3]: user      user       target         target             target              hold
+ * parv[ 4]: host      host       action         action             action              host
+ * parv[ 5]: setby     removedby  (un)setby      setby              setby               setby
+ * parv[ 6]: expire_at            expire_at (0)  expire_at (0)      expire_at (0)       expire_at
+ * parv[ 7]: set_at               set_at         set_at             set_at              set_at
+ * parv[ 8]: reason               regex          tkl duration       tkl duration        reason
+ * parv[ 9]:                                     tkl reason [A]     tkl reason [A]
+ * parv[10]:                                     regex              match-type [B]
+ * parv[11]:                                                        match-string [C]
  *
  * [A] tkl reason field must be escaped by caller [eg: use unreal_encodespace()
  *     if m_tkl is called internally].
- *
+ * [B] match-type must be one of: regex, simple, posix.
+ * [C] Could be a regex or a regular string with wildcards, depending on [B]
  */
 int _m_tkl(aClient *cptr, aClient *sptr, int parc, char *parv[])
 {
@@ -1874,6 +1909,7 @@ int _m_tkl(aClient *cptr, aClient *sptr, int parc, char *parv[])
 	char gmt[256], gmt2[256];
 	char txt[256];
 	TS   expiry_1, setat_1, spamf_tklduration = 0;
+	MatchType spamf_match_method = MATCH_TRE_REGEX; /* (if unspecified, default to this) */
 	char *reason = NULL, *timeret;
 
 	if (!IsServer(sptr) && !IsOper(sptr) && !IsMe(sptr))
@@ -1935,7 +1971,27 @@ int _m_tkl(aClient *cptr, aClient *sptr, int parc, char *parv[])
 		  found = 0;
 		  if ((type & TKL_SPAMF) && (parc >= 11))
 		  {
-		  	reason = parv[10];
+		  	if (parc >= 12)
+		  	{
+		  	    reason = parv[11];
+		  	    spamf_match_method = unreal_match_method_strtoval(parv[10]);
+		  	    if (spamf_match_method == 0)
+		  	    {
+		  	    	sendto_realops("Ignoring spamfilter from %s with unknown match type '%s'",
+		  	    		sptr->name, parv[10]);
+		  	    	return 0;
+				}
+		  	} else {
+		  		reason = parv[10];
+#ifdef USE_TRE
+		  		spamf_match_method = MATCH_TRE_REGEX;
+#else
+				sendto_realops("Ignoring spamfilter from %s. Spamfilter is of type 'posix' (TRE) and this "
+				               "build was compiled without TRE support. Suggestion: upgrade the other server",
+				               sptr->name);
+				return 0;
+#endif
+		  	}
 		  	spamf_tklduration = config_checkval(parv[8], CFG_TIME); /* was: atol(parv[8]); */
 		  }
 		  for (tk = tklines[tkl_hash(parv[2][0])]; tk; tk = tk->next)
@@ -2030,10 +2086,10 @@ int _m_tkl(aClient *cptr, aClient *sptr, int parc, char *parv[])
 		  /* there is something fucked here? */
 		  if ((type & TKL_SPAMF) && (parc >= 11))
 			tk = tkl_add_line(type, parv[3], parv[4], reason, parv[5],
-				expiry_1, setat_1, spamf_tklduration, parv[9]);
+				expiry_1, setat_1, spamf_tklduration, parv[9], spamf_match_method);
 		  else
 			tk = tkl_add_line(type, parv[3], parv[4], reason, parv[5],
-				expiry_1, setat_1, 0, NULL);
+				expiry_1, setat_1, 0, NULL, 0);
 
 		  if (!tk)
 		     return 0; /* ERROR on allocate or something else... */
@@ -2137,6 +2193,22 @@ int _m_tkl(aClient *cptr, aClient *sptr, int parc, char *parv[])
 
 		  if (type & TKL_GLOBAL)
 		  {
+		  	if ((parc == 12) && (type & TKL_SPAMF))
+		  	{
+		  		/* Oooooh.. so many flavours ! */
+				sendto_server(cptr, PROTO_TKLEXT2, 0,
+					":%s TKL %s %s %s %s %s %s %s %s %s %s :%s", sptr->name,
+					parv[1], parv[2], parv[3], parv[4], parv[5],
+					parv[6], parv[7], parv[8], parv[9], parv[10], parv[11]);
+				sendto_server(cptr, PROTO_TKLEXT, PROTO_TKLEXT2,
+					":%s TKL %s %s %s %s %s %s %s %s %s :%s", sptr->name,
+					parv[1], parv[2], parv[3], parv[4], parv[5],
+					parv[6], parv[7], parv[8], parv[9], parv[11]); /* parv[11] = regex */
+				sendto_server(cptr, 0, PROTO_TKLEXT,
+					":%s TKL %s %s %s %s %s %s %s :%s", sptr->name,
+					parv[1], parv[2], parv[3], parv[4], parv[5],
+					parv[6], parv[7], parv[10]);
+		  	} else
 		  	if ((parc == 11) && (type & TKL_SPAMF))
 		  	{
 				sendto_server(cptr, PROTO_TKLEXT, 0,
@@ -2542,7 +2614,7 @@ long ms_past;
 		getrusage(RUSAGE_SELF, &rprev);
 #endif
 
-		ret = regexec(&tk->ptr.spamf->expr, str, 0, NULL, 0);
+		ret = unreal_match(tk->ptr.spamf->expr, str);
 
 #ifdef SPAMFILTER_DETECTSLOW
 		getrusage(RUSAGE_SELF, &rnow);
@@ -2564,7 +2636,7 @@ long ms_past;
 		}
 #endif
 
-		if (ret == 0)
+		if (ret)
 		{
 		        /* We have a match! */
 			char buf[1024];
diff --git a/src/s_conf.c b/src/s_conf.c
index 60cf75b36..92b8a6af9 100644
--- a/src/s_conf.c
+++ b/src/s_conf.c
@@ -2140,7 +2140,7 @@ void	config_rehash()
 				ircfree(fmt->format);
 				ircfree(fmt->nick);
 				ircfree(fmt->parameters);
-				regfree(&fmt->expr);
+				unreal_delete_match(fmt->expr);
 				DelListItem(fmt, alias_ptr->format);
 				MyFree(fmt);
 			}
@@ -5634,10 +5634,11 @@ int _conf_spamfilter(ConfigFile *conf, ConfigEntry *ce)
 	char *word = NULL, *reason = NULL, *bantime = NULL;
 	int action = 0, target = 0;
 	char has_reason = 0, has_bantime = 0;
+	int match_type = 0;
 	
 	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
 	{
-		if (!strcmp(cep->ce_varname, "regex"))
+		if (!strcmp(cep->ce_varname, "match"))
 		{
 			nl->reason = strdup(cep->ce_vardata);
 
@@ -5668,6 +5669,10 @@ int _conf_spamfilter(ConfigFile *conf, ConfigEntry *ce)
 			has_bantime = 1;
 			bantime = cep->ce_vardata;
 		}
+		else if (!strcmp(cep->ce_varname, "match-type"))
+		{
+			match_type = unreal_match_method_strtoval(cep->ce_vardata);
+		}
 	}
 	nl->type = TKL_SPAMF;
 	nl->expire_at = 0;
@@ -5677,7 +5682,8 @@ int _conf_spamfilter(ConfigFile *conf, ConfigEntry *ce)
 	nl->subtype = target;
 
 	nl->setby = BadPtr(me.name) ? NULL : strdup(me.name); /* Hmm! */
-	nl->ptr.spamf = unreal_buildspamfilter(word);
+	nl->ptr.spamf = MyMallocEx(sizeof(Spamfilter));
+	nl->ptr.spamf->expr = unreal_create_match(match_type, word, NULL);
 	nl->ptr.spamf->action = action;
 
 	if (has_reason && reason)
@@ -5698,8 +5704,9 @@ int _test_spamfilter(ConfigFile *conf, ConfigEntry *ce)
 {
 	ConfigEntry *cep, *cepp;
 	int errors = 0;
-	char *regex = NULL, *reason = NULL;
-	char has_target = 0, has_regex = 0, has_action = 0, has_reason = 0, has_bantime = 0;
+	char *match = NULL, *reason = NULL;
+	char has_target = 0, has_match = 0, has_action = 0, has_reason = 0, has_bantime = 0, has_match_type = 0;
+	int match_type = 0;
 	
 	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
 	{
@@ -5775,26 +5782,16 @@ int _test_spamfilter(ConfigFile *conf, ConfigEntry *ce)
 			has_reason = 1;
 			reason = cep->ce_vardata;
 		}
-		else if (!strcmp(cep->ce_varname, "regex"))
+		else if (!strcmp(cep->ce_varname, "match"))
 		{
-			char *errbuf;
-			if (has_regex)
+			if (has_match)
 			{
 				config_warn_duplicate(cep->ce_fileptr->cf_filename,
-					cep->ce_varlinenum, "spamfilter::regex");
+					cep->ce_varlinenum, "spamfilter::match");
 				continue;
 			}
-			has_regex = 1;
-			if ((errbuf = unreal_checkregex(cep->ce_vardata,0,0)))
-			{
-				config_error("%s:%i: spamfilter::regex contains an invalid regex: %s",
-					cep->ce_fileptr->cf_filename,
-					cep->ce_varlinenum,
-					errbuf);
-				errors++;
-				continue;
-			}
-			regex = cep->ce_vardata;
+			has_match = 1;
+			match = cep->ce_vardata;
 		}
 		else if (!strcmp(cep->ce_varname, "action"))
 		{
@@ -5822,6 +5819,26 @@ int _test_spamfilter(ConfigFile *conf, ConfigEntry *ce)
 			}
 			has_bantime = 1;
 		}
+		else if (!strcmp(cep->ce_varname, "match-type"))
+		{
+			if (has_match_type)
+			{
+				config_warn_duplicate(cep->ce_fileptr->cf_filename,
+					cep->ce_varlinenum, "spamfilter::match-type");
+				continue;
+			}
+			match_type = unreal_match_method_strtoval(cep->ce_vardata);
+			if (match_type == 0)
+			{
+				config_error("%s:%i: spamfilter::match-type: unknown match type '%s', "
+				             "should be one of: 'simple', 'regex' or 'posix'",
+				             cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
+				             cep->ce_vardata);
+				errors++;
+				continue;
+			}
+			has_match_type = 1;
+		}
 		else
 		{
 			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
@@ -5831,10 +5848,26 @@ int _test_spamfilter(ConfigFile *conf, ConfigEntry *ce)
 		}
 	}
 
-	if (!has_regex)
+	if (match && match_type)
+	{
+		aMatch *m;
+		char *err;
+		
+		m = unreal_create_match(match_type, match, &err);
+		if (!m)
+		{
+			config_error("%s:%i: spamfilter::match contains an invalid regex: %s",
+				cep->ce_fileptr->cf_filename,
+				cep->ce_varlinenum,
+				err);
+			errors++;
+		}
+	}
+	
+	if (!has_match)
 	{
 		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
-			"spamfilter::regex");
+			"spamfilter::match");
 		errors++;
 	} 
 	if (!has_target)
@@ -5849,13 +5882,25 @@ int _test_spamfilter(ConfigFile *conf, ConfigEntry *ce)
 			"spamfilter::action");
 		errors++;
 	}
-	if (regex && reason && (strlen(regex) + strlen(reason) > 505))
+	if (match && reason && (strlen(match) + strlen(reason) > 505))
 	{
-		config_error("%s:%i: spamfilter block problem: regex + reason field are together over 505 bytes, "
+		config_error("%s:%i: spamfilter block problem: match + reason field are together over 505 bytes, "
 		             "please choose a shorter regex or reason",
 		             ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
 		errors++;
 	}
+	if (!has_match_type)
+	{
+		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
+			"spamfilter::match-type");
+		errors++;
+	}
+
+	if (!has_match_type && !has_match && has_action && has_target)
+	{
+		config_error("Upgrading from 3.2.x to 3.4.x? Your spamfilter { } blocks need to be converted. "
+					 "See https://www.unrealircd.org/docs/Upgrading_from_3.2.x#Spamfilter");
+	}
 
 	return errors;
 }
@@ -8392,7 +8437,9 @@ int	_conf_alias(ConfigFile *conf, ConfigEntry *ce)
 		if (!strcmp(cep->ce_varname, "format")) {
 			format = MyMallocEx(sizeof(ConfigItem_alias_format));
 			ircstrdup(format->format, cep->ce_vardata);
-			regcomp(&format->expr, cep->ce_vardata, REG_ICASE|REG_EXTENDED);
+			format->expr = unreal_create_match(MATCH_PCRE_REGEX, cep->ce_vardata, NULL);
+			if (!format->expr)
+				abort(); /* Impossible due to _test_alias earlier */
 			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
 				if (!strcmp(cepp->ce_varname, "nick") ||
 				    !strcmp(cepp->ce_varname, "target") ||
@@ -8477,26 +8524,22 @@ int _test_alias(ConfigFile *conf, ConfigEntry *ce) {
 			continue;
 		}
 		if (!strcmp(cep->ce_varname, "format")) {
-			int errorcode, errorbufsize;
-			char *errorbuf;
-			regex_t expr;
+			char *err = NULL;
+			aMatch *expr;
 			char has_type = 0, has_target = 0, has_parameters = 0;
 
 			has_format = 1;
-			errorcode = regcomp(&expr, cep->ce_vardata, REG_ICASE|REG_EXTENDED);
-                        if (errorcode > 0)
-                        {
-                                errorbufsize = regerror(errorcode, &expr, NULL, 0)+1;
-                                errorbuf = MyMalloc(errorbufsize);
-                                regerror(errorcode, &expr, errorbuf, errorbufsize);
-                                config_error("%s:%i: alias::format contains an invalid regex: %s",
- 					cep->ce_fileptr->cf_filename,
- 					cep->ce_varlinenum,
- 					errorbuf);
-                                errors++;
-                                free(errorbuf);
-                        }
-			regfree(&expr);	
+			expr = unreal_create_match(MATCH_PCRE_REGEX, cep->ce_vardata, &err);
+			if (!expr)
+			{
+				config_error("%s:%i: alias::format contains an invalid regex: %s",
+					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err);
+				config_error("Upgrading from 3.2.x to 3.4.x? Note that regex changed from POSIX Regex "
+				             "to PCRE Regex!"); /* TODO: refer to some url ? */
+			} else {
+				unreal_delete_match(expr);
+			}
+
 			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
 				if (config_is_blankorempty(cepp, "alias::format"))
 				{
diff --git a/src/s_err.c b/src/s_err.c
index 087ec4562..ff167dde1 100644
--- a/src/s_err.c
+++ b/src/s_err.c
@@ -262,7 +262,7 @@ static char *replies[] = {
 /* 226    RPL_STATSNLINE */ ":%s 226 %s n %s %s",
 /* 227    RPL_STATSVLINE */ ":%s 227 %s v %s %s %s",
 /* 228    RPL_STATSBANVER */ ":%s 228 %s %s %s",
-/* 229    RPL_STATSSPAMF */  ":%s 229 %s %c %s %s %li %li %li %s %s :%s",
+/* 229    RPL_STATSSPAMF */  ":%s 229 %s %c %s %s %s %li %li %li %s %s :%s",
 /* 230    RPL_STATSEXCEPTTKL */ ":%s 230 %s %c %s",
 /* 231 */ NULL, /* rfc1459 */
 /* 232    RPL_RULES */ ":%s 232 %s :- %s",
diff --git a/src/s_misc.c b/src/s_misc.c
index 25ce38a9e..878abfbdb 100644
--- a/src/s_misc.c
+++ b/src/s_misc.c
@@ -847,26 +847,8 @@ Ilovegotos:
 	return NULL;
 }
 
-
-
-#define SPF_REGEX_FLAGS (REG_ICASE|REG_EXTENDED|REG_NOSUB)
-
-/** Allocates a new Spamfilter entry and compiles/fills in the info.
- * NOTE: originally I wanted to integrate both badwords and spamfilter
- * into one function, but that was quickly getting ugly :(.
- */
-Spamfilter *unreal_buildspamfilter(char *s)
-{
-Spamfilter *e = MyMallocEx(sizeof(Spamfilter));
-
-	regcomp(&e->expr, s, SPF_REGEX_FLAGS);
-	return e;
-}
-
-
 /*|| BAN ACTION ROUTINES FOLLOW ||*/
 
-
 /** Converts a banaction string (eg: "kill") to an integer value (eg: BAN_ACT_KILL) */
 int banact_stringtoval(char *s)
 {