@@ -69,6 +69,8 @@ def initialize()
6969 [ "STATS" , 0 , 0 , SFLG_NOMAXPARAM , ADMIN_FLAG , lm ( 'GS_HLP_STS_SHORT' ) , lm ( 'GS_HLP_STS_LONG' ) ] ,
7070 [ "ENFORCE" , 0 , 0 , SFLG_NOMAXPARAM , ADMIN_FLAG , lm ( 'GS_HLP_ENF_SHORT' ) , lm ( 'GS_HLP_ENF_LONG' ) ] ,
7171 [ "BADSERV" , 0 , 1 , SFLG_NOMAXPARAM , ADMIN_FLAG , lm ( 'GS_HLP_SRV_SHORT' ) , lm ( 'GS_HLP_SRV_LONG' ) ] ,
72+ [ "PROTECT" , 1 , 2 , SFLG_NOMAXPARAM , ADMIN_FLAG , lm ( 'GS_HLP_PRT_SHORT' ) , lm ( 'GS_HLP_PRT_LONG' ) ] ,
73+ [ "UNPROTECT" , 1 , 2 , SFLG_NOMAXPARAM , ADMIN_FLAG , lm ( 'GS_HLP_UPR_SHORT' ) , lm ( 'GS_HLP_UPR_LONG' ) ] ,
7274 ] ) # register
7375
7476 # Which hooks do we want?
@@ -100,6 +102,15 @@ def initialize()
100102 irc_lower($1)' )
101103 @dbq [ 'INCREASE_KILLS' ] = DB . prepare ( 'UPDATE ganneffserv SET kills = kills+1
102104 WHERE irc_lower(channel) = irc_lower($1)' )
105+ @dbq [ 'INSERT_PROTECT' ] = DB . prepare ( 'INSERT INTO ganneffprotect(setter, time,
106+ pattern, reason) VALUES($1, $2, $3, $4)' )
107+ @dbq [ 'DELETE_PROTECT' ] = DB . prepare ( 'DELETE FROM ganneffprotect WHERE
108+ irc_lower(pattern) = irc_lower($1)' )
109+ @dbq [ 'GET_PROTECTED_PATTERNS' ] = DB . prepare ( 'SELECT pattern, reason FROM ganneffprotect' )
110+ @dbq [ 'GET_PROTECTED_PATTERNS_DETAILED' ] = DB . prepare ( 'SELECT pattern, setter, time,
111+ reason FROM ganneffprotect' )
112+
113+ load_protected_patterns
103114 end # def initialize
104115
105116########################################################################
@@ -221,6 +232,48 @@ def DEL(client, parv = [])
221232 true
222233 end # def DEL
223234
235+ # ------------------------------------------------------------------------
236+
237+ # Protect users from collatoral damage
238+ def PROTECT ( client , parv = [ ] )
239+ parv [ 1 ] . downcase!
240+ debug ( LOG_DEBUG , "#{ client . name } called PROTECT and the params are #{ parv . join ( "," ) } " )
241+
242+ requested_pattern = parv [ 1 ]
243+ pattern = irc_pattern_to_regex ( requested_pattern )
244+ reason = parv [ 2 ]
245+
246+ ret = DB . execute_nonquery ( @dbq [ 'INSERT_PROTECT' ] , 'iiss' , client . nick . account_id ,
247+ Time . now . to_i , pattern , reason )
248+ if ret then
249+ debug ( LOG_NOTICE , "#{ client . name } added protection #{ pattern } , reason #{ reason } " )
250+ @protection [ pattern ] = reason
251+ load_protected_patterns
252+ reply ( client , "Protection #{ requested_pattern } successfully added" )
253+ else
254+ reply ( client , "Failed to add #{ requested_pattern } " )
255+ end
256+
257+ # ------------------------------------------------------------------------
258+
259+ # Unprotect users from collatoral damage
260+ def UNPROTECT ( client , parv = [ ] )
261+ parv [ 1 ] . downcase!
262+ debug ( LOG_DEBUG , "#{ client . name } called UNPROTECT and the params are #{ parv . join ( "," ) } " )
263+
264+ pattern = irc_pattern_to_regex ( parv [ 1 ] )
265+ return unless @protection . has_key? ( pattern )
266+
267+ ret = DB . execute_nonquery ( @dbq [ 'DELETE_PROTECT' ] , 's' , pattern )
268+ if ret then
269+ debug ( LOG_NOTICE , "#{ client . name } removed protection #{ pattern } " )
270+ @protection . delete ( pattern )
271+ load_protected_patterns
272+ reply ( client , "Protection #{ pattern } successfully deleted." )
273+ else
274+ reply ( client , "Failed to delete protection #{ pattern } ." )
275+ end
276+
224277# ------------------------------------------------------------------------
225278
226279 # List all channels we monitor
@@ -245,6 +298,18 @@ def LIST(client, parv = [])
245298 }
246299 result . free
247300
301+ reply ( client , "Protected host patterns\n \n " )
302+ reply ( client , "%-50s %-10s %-19s %s" % [ "Pattern" , "By" , "When" , "Reason" ] )
303+ result = DB . execute ( @dbq [ 'GET_PROTECTED_PATTERNS_DETAILED' ] )
304+ result . row_each { |row |
305+ pattern = row [ 0 ]
306+ by = row [ 1 ]
307+ time = Time . at ( row [ 2 ] . to_i ) . strftime ( '%Y-%m-%d %H:%M:%S' )
308+ reason = row [ 3 ]
309+ reply ( client , "%-50s %-10s %-19s %s" % [ pattern , by , time , reason ] )
310+ }
311+ result . free
312+
248313 reply ( client , "\n CRFJ - checks Connect, Register nick, Join channel within 15 seconds (i.e. Fast)" )
249314 reply ( client , "J - triggers on every Join" )
250315
@@ -574,8 +639,13 @@ def akill(client, reason, operreason, channel="")
574639 ret = kill_user ( client , reason )
575640 else # if host
576641 reason = "#{ reason } |#{ operreason } "
577- debug ( LOG_DEBUG , "Issuing AKILL: *@#{ host } , #{ reason } lasting for #{ @akill_duration } seconds" )
578- ret = akill_add ( "*@#{ host } " , reason , @akill_duration )
642+ if client . host =~ /#{ @protected_patterns } /i # if protected hosts
643+ debug ( LOG_DEBUG , "Using /kill instead of AKILL for protected user #{ client . name } " )
644+ ret = kill_user ( client , reason )
645+ else
646+ debug ( LOG_DEBUG , "Issuing AKILL: *@#{ host } , #{ reason } lasting for #{ @akill_duration } seconds" )
647+ ret = akill_add ( "*@#{ host } " , reason , @akill_duration )
648+ end # if protected hosts
579649 end # if host
580650
581651 channel . downcase!
@@ -596,6 +666,31 @@ def akill(client, reason, operreason, channel="")
596666 end # if kill_user
597667 end # def akill
598668
669+ # ------------------------------------------------------------------------
670+
671+ # convert irc pattern to regular expression
672+ def irc_pattern_to_regex ( pattern
673+ # "." -> "\.", "*" -> ".*", "?" -> "."
674+ # wrap with "^...$"
675+ return pattern if pattern . start_with? '^'
676+ pattern = pattern . gsub ( /\. / , '\\.' )
677+ . gsub ( /\* / , '.*' )
678+ . gsub ( /\? / , '.' )
679+ return "^#{ pattern } $"
680+ end
681+
682+ # ------------------------------------------------------------------------
683+
684+ # get protected patterns as pattern
685+ def load_protected_patterns ( )
686+ patterns = @protection . keys
687+ if patterns . empty?
688+ @protected_patterns = '^$'
689+ else
690+ @protected_patterns = patterns . join ( '|' )
691+ end
692+ end # def get_protected_patterns
693+
599694# ------------------------------------------------------------------------
600695
601696 # enforce a channel - kill all of its users
@@ -648,6 +743,20 @@ def load_data()
648743 count += 1
649744 }
650745 result . free
746+
747+ @protection = Hash . new
748+ result = DB . execute ( @dbq [ 'GET_PROTECTED_PATTERNS' ] , '' )
749+ count = 0
750+ result . row_each { |row |
751+ pattern = row [ 0 ]
752+ reason = row [ 1 ]
753+ pattern = irc_pattern_to_regex ( pattern )
754+ @protection [ pattern ] = reason
755+ count += 1
756+ }
757+ result . free
758+ load_protected_patterns
759+
651760 debug ( LOG_DEBUG , "All channel data successfully loaded" )
652761 end # def load_data
653762
0 commit comments