3333#include "numeric.h"
3434#include "send.h"
3535#include "s_newconf.h"
36+ #include "s_conf.h"
3637#include "s_serv.h"
3738#include "s_user.h"
3839#include "msg.h"
4142#include "operhash.h"
4243#include "inline/stringops.h"
4344#include "msgbuf.h"
45+ #include "newconf.h"
4446
4547#include <hs_common.h>
4648#include <hs_runtime.h>
4749
48- #define FILTER_NICK 0
49- #define FILTER_USER 0
50- #define FILTER_HOST 0
51-
52- #define FILTER_EXIT_MSG "Connection closed"
50+ #define FILTER_DEFAULT_EXIT_MSG "Connection closed"
5351
5452static const char filter_desc [] = "Filter messages using a precompiled Hyperscan database" ;
5553
54+ static int modinit (void );
55+ static void moddeinit (void );
5656static void filter_msg_user (void * data );
5757static void filter_msg_channel (void * data );
5858static void filter_client_quit (void * data );
5959static void on_client_exit (void * data );
60+ static void filter_init_conf (void * data );
61+ static void filter_conf_info (void * data );
62+ static void filter_version_info (void * data );
63+ static void filter_conf_set_sees_user_info (void * data );
64+ static void filter_conf_set_bypass_all (void * data );
65+ static void filter_conf_set_exit_message (void * data );
6066
6167static void mo_setfilter (struct MsgBuf * , struct Client * , struct Client * , int , const char * * );
6268static void me_setfilter (struct MsgBuf * , struct Client * , struct Client * , int , const char * * );
@@ -67,6 +73,9 @@ static hs_database_t *filter_db;
6773static hs_scratch_t * filter_scratch ;
6874
6975static int filter_enable = 1 ;
76+ static int filter_sees_user_info = 0 ;
77+ static int filter_bypass_all = 0 ;
78+ static char * filter_exit_message = NULL ;
7079
7180static const char * cmdname [MESSAGE_TYPE_COUNT ] = {
7281 [MESSAGE_TYPE_PRIVMSG ] = "PRIVMSG" ,
@@ -81,9 +90,15 @@ enum filter_state {
8190 FILTER_LOADED
8291};
8392
93+ struct match_context {
94+ unsigned int actions ;
95+ bool require_bypass ;
96+ };
97+
8498#define ACT_DROP (1 << 0)
8599#define ACT_KILL (1 << 1)
86100#define ACT_ALARM (1 << 2)
101+ #define ACT_BYPASS (1 << 3)
87102
88103static enum filter_state state = FILTER_EMPTY ;
89104static char check_str [21 ] = "" ;
@@ -95,21 +110,30 @@ mapi_hfn_list_av1 filter_hfnlist[] = {
95110 { "privmsg_channel" , filter_msg_channel },
96111 { "client_quit" , filter_client_quit },
97112 { "client_exit" , on_client_exit },
113+ { "conf_read_start" , filter_init_conf },
114+ { "doing_info_conf" , filter_conf_info },
115+ { "doing_version_confopts" , filter_version_info },
98116 { NULL , NULL }
99117};
100118
101-
102119struct Message setfilter_msgtab = {
103120 "SETFILTER" , 0 , 0 , 0 , 0 ,
104121 {mg_unreg , mg_not_oper , mg_ignore , mg_ignore , {me_setfilter , 2 }, {mo_setfilter , 2 }}
105122};
106123
124+ mapi_clist_av1 filter_clist [] = { & setfilter_msgtab , NULL };
125+
126+ DECLARE_MODULE_AV2 (filter , modinit , moddeinit , filter_clist , NULL , filter_hfnlist , NULL , "0.5" , filter_desc );
127+
107128static int
108129modinit (void )
109130{
110131 filter_umode = user_modes ['u' ] = find_umode_slot ();
111132 construct_umodebuf ();
112133 filter_chmode = cflag_add ('u' , chm_simple );
134+ add_conf_item ("general" , "filter_sees_user_info" , CF_YESNO , filter_conf_set_sees_user_info );
135+ add_conf_item ("general" , "filter_bypass_all" , CF_YESNO , filter_conf_set_bypass_all );
136+ add_conf_item ("general" , "filter_exit_message" , CF_QSTRING , filter_conf_set_exit_message );
113137 return 0 ;
114138}
115139
@@ -128,12 +152,73 @@ moddeinit(void)
128152 hs_free_database (filter_db );
129153 if (filter_data )
130154 rb_free (filter_data );
155+ if (filter_exit_message )
156+ rb_free (filter_exit_message );
157+ remove_conf_item ("general" , "filter_sees_user_info" );
158+ remove_conf_item ("general" , "filter_bypass_all" );
159+ remove_conf_item ("general" , "filter_exit_message" );
131160}
132161
162+ static void
163+ filter_init_conf (void * data )
164+ {
165+ if (filter_exit_message )
166+ {
167+ rb_free (filter_exit_message );
168+ filter_exit_message = NULL ;
169+ }
170+ }
133171
134- mapi_clist_av1 filter_clist [] = { & setfilter_msgtab , NULL };
172+ static void
173+ filter_conf_info (void * data_ )
174+ {
175+ hook_data * data = data_ ;
176+ sendto_one (data -> client , ":%s %d %s :%-30s %-16s [%s]" ,
177+ get_id (& me , data -> client ), RPL_INFO ,
178+ get_id (data -> client , data -> client ),
179+ "filter_sees_user_info" ,
180+ filter_sees_user_info ? "YES" : "NO" ,
181+ "Let the spamfilter engine see the hostmasks of senders" );
182+
183+ sendto_one (data -> client , ":%s %d %s :%-30s %-16s [%s]" ,
184+ get_id (& me , data -> client ), RPL_INFO ,
185+ get_id (data -> client , data -> client ),
186+ "filter_bypass_all" ,
187+ filter_bypass_all ? "YES" : "NO" ,
188+ "Let the spamfilter BYPASS action work on KILL/ALARM in addition to DROP" );
189+
190+ sendto_one (data -> client , ":%s %d %s :%-30s %-16s [%s]" ,
191+ get_id (& me , data -> client ), RPL_INFO ,
192+ get_id (data -> client , data -> client ),
193+ "filter_exit_message" ,
194+ filter_exit_message != NULL ? filter_exit_message : FILTER_DEFAULT_EXIT_MSG ,
195+ "Message to quit users with if they hit a filter KILL action" );
196+ }
197+
198+ static void
199+ filter_version_info (void * data )
200+ {
201+ if (filter_sees_user_info || filter_bypass_all )
202+ ((char * )data )['F' ] = 1 ;
203+ }
204+
205+ static void
206+ filter_conf_set_sees_user_info (void * data )
207+ {
208+ filter_sees_user_info = * (int * )data ;
209+ }
135210
136- DECLARE_MODULE_AV2 (filter , modinit , moddeinit , filter_clist , NULL , filter_hfnlist , NULL , "0.4" , filter_desc );
211+ static void
212+ filter_conf_set_bypass_all (void * data )
213+ {
214+ filter_bypass_all = * (int * )data ;
215+ }
216+
217+ static void
218+ filter_conf_set_exit_message (void * data )
219+ {
220+ filter_exit_message = (char * )data ;
221+ }
137222
138223static int
139224setfilter (const char * check , const char * data , const char * * error )
@@ -313,8 +398,6 @@ me_setfilter(struct MsgBuf *msgbuf, struct Client *client_p, struct Client *sour
313398 if (r ) {
314399 sendto_one_notice (source_p , ":SETFILTER failed: %s" , error );
315400 }
316-
317- return ;
318401}
319402
320403/* will be called for every match
@@ -328,8 +411,17 @@ int match_callback(unsigned id,
328411 unsigned flags ,
329412 void * context_ )
330413{
331- unsigned * context = context_ ;
332- * context |= id ;
414+ struct match_context * context = context_ ;
415+
416+ if (context -> require_bypass )
417+ {
418+ unsigned int bypass_mask = filter_bypass_all ? UINT_MAX : ACT_DROP ;
419+ if (id & ACT_BYPASS )
420+ context -> actions |= id & bypass_mask ;
421+ }
422+ else
423+ context -> actions |= id ;
424+
333425 return 0 ;
334426}
335427
@@ -338,50 +430,42 @@ static char clean_buffer[BUFSIZE];
338430
339431unsigned match_message (const char * prefix ,
340432 struct Client * source ,
433+ bool require_bypass ,
341434 const char * command ,
342435 const char * target ,
343436 const char * msg )
344437{
345- unsigned state = 0 ;
438+ struct match_context ctx = { 0 , require_bypass };
439+
346440 if (!filter_enable )
347441 return 0 ;
348442 if (!filter_db )
349443 return 0 ;
350444 if (!command )
351445 return 0 ;
446+
352447 snprintf (check_buffer , sizeof check_buffer , "%s:%s!%s@%s#%c %s%s%s :%s" ,
353448 prefix ,
354- #if FILTER_NICK
355- source -> name ,
356- #else
357- "*" ,
358- #endif
359- #if FILTER_USER
360- source -> username ,
361- #else
362- "*" ,
363- #endif
364- #if FILTER_HOST
365- source -> host ,
366- #else
367- "*" ,
368- #endif
449+ filter_sees_user_info ? source -> name : "*" ,
450+ filter_sees_user_info ? source -> username : "*" ,
451+ filter_sees_user_info ? source -> host : "*" ,
369452 source -> user && source -> user -> suser [0 ] != '\0' ? '1' : '0' ,
370453 command ,
371454 target ? " " : "" ,
372455 target ? target : "" ,
373456 msg );
374- hs_error_t r = hs_scan (filter_db , check_buffer , strlen (check_buffer ), 0 , filter_scratch , match_callback , & state );
457+ hs_error_t r = hs_scan (filter_db , check_buffer , strlen (check_buffer ), 0 , filter_scratch , match_callback , & ctx );
375458 if (r != HS_SUCCESS && r != HS_SCAN_TERMINATED )
376459 return 0 ;
377- return state ;
460+ return ctx . actions ;
378461}
379462
380463void
381464filter_msg_user (void * data_ )
382465{
383466 hook_data_privmsg_user * data = data_ ;
384467 struct Client * s = data -> source_p ;
468+
385469 /* we only need to filter once */
386470 if (!MyClient (s )) {
387471 return ;
@@ -392,14 +476,14 @@ filter_msg_user(void *data_)
392476 if (IsOper (s ) || IsOper (data -> target_p )) {
393477 return ;
394478 }
395- if (data -> target_p -> umodes & filter_umode ) {
396- return ;
397- }
479+
480+ bool require_bypass = (data -> target_p -> umodes & filter_umode ) == filter_umode ;
398481 char * text = strcpy (clean_buffer , data -> text );
399482 strip_colour (text );
400483 strip_unprintable (text );
401- unsigned r = match_message ("0" , s , cmdname [data -> msgtype ], "0" , data -> text ) |
402- match_message ("1" , s , cmdname [data -> msgtype ], "0" , text );
484+ unsigned r = match_message ("0" , s , require_bypass , cmdname [data -> msgtype ], "0" , data -> text ) |
485+ match_message ("1" , s , require_bypass , cmdname [data -> msgtype ], "0" , text );
486+
403487 if (r & ACT_DROP ) {
404488 if (data -> msgtype == MESSAGE_TYPE_PRIVMSG ) {
405489 sendto_one_numeric (s , ERR_CANNOTSENDTOCHAN ,
@@ -415,7 +499,7 @@ filter_msg_user(void *data_)
415499 }
416500 if (r & ACT_KILL ) {
417501 data -> approved = 1 ;
418- exit_client (NULL , s , s , FILTER_EXIT_MSG );
502+ exit_client (NULL , s , s , filter_exit_message != NULL ? filter_exit_message : FILTER_DEFAULT_EXIT_MSG );
419503 }
420504}
421505
@@ -424,6 +508,7 @@ filter_msg_channel(void *data_)
424508{
425509 hook_data_privmsg_channel * data = data_ ;
426510 struct Client * s = data -> source_p ;
511+
427512 /* we only need to filter once */
428513 if (!MyClient (s )) {
429514 return ;
@@ -433,14 +518,14 @@ filter_msg_channel(void *data_)
433518 if (IsOper (s )) {
434519 return ;
435520 }
436- if (data -> chptr -> mode .mode & filter_chmode ) {
437- return ;
438- }
521+
522+ bool require_bypass = (data -> chptr -> mode .mode & filter_chmode ) == filter_chmode ;
439523 char * text = strcpy (clean_buffer , data -> text );
440524 strip_colour (text );
441525 strip_unprintable (text );
442- unsigned r = match_message ("0" , s , cmdname [data -> msgtype ], data -> chptr -> chname , data -> text ) |
443- match_message ("1" , s , cmdname [data -> msgtype ], data -> chptr -> chname , text );
526+ unsigned r = match_message ("0" , s , require_bypass , cmdname [data -> msgtype ], data -> chptr -> chname , data -> text ) |
527+ match_message ("1" , s , require_bypass , cmdname [data -> msgtype ], data -> chptr -> chname , text );
528+
444529 if (r & ACT_DROP ) {
445530 if (data -> msgtype == MESSAGE_TYPE_PRIVMSG ) {
446531 sendto_one_numeric (s , ERR_CANNOTSENDTOCHAN ,
@@ -456,7 +541,7 @@ filter_msg_channel(void *data_)
456541 }
457542 if (r & ACT_KILL ) {
458543 data -> approved = 1 ;
459- exit_client (NULL , s , s , FILTER_EXIT_MSG );
544+ exit_client (NULL , s , s , filter_exit_message != NULL ? filter_exit_message : FILTER_DEFAULT_EXIT_MSG );
460545 }
461546}
462547
@@ -471,8 +556,8 @@ filter_client_quit(void *data_)
471556 char * text = strcpy (clean_buffer , data -> orig_reason );
472557 strip_colour (text );
473558 strip_unprintable (text );
474- unsigned r = match_message ("0" , s , "QUIT" , NULL , data -> orig_reason ) |
475- match_message ("1" , s , "QUIT" , NULL , text );
559+ unsigned r = match_message ("0" , s , false, "QUIT" , NULL , data -> orig_reason ) |
560+ match_message ("1" , s , false, "QUIT" , NULL , text );
476561 if (r & ACT_DROP ) {
477562 data -> reason = NULL ;
478563 }
0 commit comments