#clonecontrol
#by wiebe @ QuakeNet


#raw part for me part

#make work with whox, compare clones on host and ip

######################################################################
#settings
######################################################################

#ban reason
set clonecontrol(reason) "Too many clients from your host."

#default ban duration in minutes
set clonecontrol(bantime) 30

######################################################################
#code
######################################################################

#flags
setudef flag clonecontrol
setudef int clonecontrol-max
setudef int clonecontrol-bantime


#clear the array on connect
bind evnt - connect-server clonecontrol:connect
proc clonecontrol:connect { type } {
  global clonecontroldb
  if { [info exists clonecontroldb] } { unset clonecontroldb }
}


#bot joins, add each user and check for clones
bind raw -|- "315" clonecontrol:raw

proc clonecontrol:raw { server raw rest } {
  set chan [string tolower [lindex [split $rest] 1]]

#invalid chan or bot is not on it, bail out
  if { ![validchan $chan] || ![botonchan $chan] } { return 0 }
  global clonecontroldb

#first user in the channel is known, meaning the whole channel is, bail out
  set h [string tolower [lindex [split [getchanhost [lindex [chanlist $chan] 0]] @] 1]]
  if { [info exists clonecontroldb($chan,$h)] } { return 0 }

#count clones from host and user@host
  foreach nick [chanlist $chan] {
    if { ![onchansplit $nick $chan] } {
      set uhost [string tolower [getchanhost $nick]]
      set host [string tolower [lindex [split $uhost @] 1]]
      if { ![info exists clonecontroldb($chan,$host)] } { set clonecontroldb($chan,$host) 1 } else { incr clonecontroldb($chan,$host) 1 }
      if { ![info exists clonecontroldb($chan,$uhost)] } { set clonecontroldb($chan,$uhost) 1 } else { incr clonecontroldb($chan,$uhost) 1 }
    }
  }

#check each user for clones
  foreach nick [chanlist $chan] { clonecontrol:checkuser $chan $nick }
}


#add user on join
bind join -|- * clonecontrol:join
bind rejn -|- * clonecontrol:join

proc clonecontrol:join { nick uhost handle chan } {
  if { ![validchan $chan] } { return 0 }
  if { [isbotnick $nick] } {
    clonecontrol:unset $chan
    return 0
  }
  global clonecontroldb
  set chan [string tolower $chan]
  set uhost [string tolower $uhost]
  set host [string tolower [lindex [split $uhost @] 1]]
  if { ![info exists clonecontroldb($chan,$host)] } { set clonecontroldb($chan,$host) 1 } else { incr clonecontroldb($chan,$host) 1 }
  if { ![info exists clonecontroldb($chan,$uhost)] } { set clonecontroldb($chan,$uhost) 1 } else { incr clonecontroldb($chan,$uhost) 1 }
  clonecontrol:checkuser $chan $nick
}


#user leaves
bind part -|- * clonecontrol:part
bind sign -|- * clonecontrol:part
bind splt -|- * clonecontrol:part

proc clonecontrol:part { nick uhost handle chan {msg ""}} {
  if { [isbotnick $nick] } {
    clonecontrol:unset $chan
    return 0
  }
  global clonecontroldb
  set chan [string tolower $chan]
  set uhost [string tolower $uhost]
  set host [string tolower [lindex [split $uhost @] 1]]
  if { [info exists clonecontroldb($chan,$host)] } {
    incr clonecontroldb($chan,$host) -1
    if { [string equal $clonecontroldb($chan,$host) 0] } { unset clonecontroldb($chan,$host) }
  }

  if { [info exists clonecontroldb($chan,$uhost)] } {
    incr clonecontroldb($chan,$uhost) -1
    if { [string equal $clonecontroldb($chan,$uhost) 0] } { unset clonecontroldb($chan,$uhost) }
  }
}


#bot/user gets removed
bind kick -|- * clonecontrol:kick

proc clonecontrol:kick { nick uh handle chan target reason } {
  if { ![isbotnick $target] } {
    clonecontrol:part $target [getchanhost $target] [nick2hand $target] $chan
  } else {
    clonecontrol:unset $chan
  }
}


#remove all from chan
proc clonecontrol:unset { chan } {
  global clonecontroldb
  foreach name [array names clonecontroldb] {
    regsub -all {\[} $chan {\\[} chan
    regsub -all {\]} $chan {\\]} chan
    regsub -all {\\} $chan {\\\\} chan
    if { [string match -nocase $chan,* $name] } { unset clonecontroldb($name) }
  }
}


#check user, set ban
proc clonecontrol:checkuser { chan nick } {
  global clonecontrol clonecontroldb
  set chan [string tolower $chan]

  if { ![validchan $chan] } { return 0 }
  if { [isbotnick $nick] } { return 0 }
  if { ![channel get $chan clonecontrol] } { return 0 }
  if { ![onchan $nick $chan] } { return 0 }
  if { [onchansplit $nick $chan] } { return 0 }

  set handle [nick2hand $nick]
  if { [matchattr $handle Cbfvlomn|Cfvlomn $chan] } { return 0 }

  set max [channel get $chan clonecontrol-max]
  if { [string equal 0 $max] } { return 0 }

  set bantime [channel get $chan clonecontrol-bantime]
  if { [string equal $bantime 0] } { set bantime $clonecontrol(bantime) }
  if { [string equal $bantime 0] } { return 0 }

  set reason $clonecontrol(reason)
  set uhost [string tolower [getchanhost $nick]]
  set host [string tolower [lindex [split $uhost @] 1]]
  if { [info exists clonecontroldb($chan,$host)] && ![string equal [finduser -cc!$host] -cc] } {
    set mask *!*@$host
    if { $clonecontroldb($chan,$host) > $max && ![isban $mask $chan] && ![isban $mask] } {
      putlog "CC: $clonecontroldb($chan,$host) clones detected from $mask -> banning $bantime minutes"
      pushmode $chan +b $mask
      utimer 5 [list newchanban $chan $mask $::botnick $reason $bantime]
    }
  }
  if { [info exists clonecontroldb($chan,$uhost)] && [string equal [finduser -cc!$host] -cc] } {
    set mask *!$uhost
    if { $clonecontroldb($chan,$uhost) > $max && ![isban $mask $chan] && ![isban $mask] } {
      pushmode $chan +b $mask
      putlog "CC: $clonecontroldb($chan,$host) clones detected from $mask -> banning $bantime minutes"
      utimer 5 [list newchanban $chan $mask $::botnick $reason $bantime]
    }
  }
}


#when loading file
foreach chan [channels] { clonecontrol:raw server 315 "rest $chan" }

if { ![validuser -cc] } {
  utimer 1 [list adduser -cc]
}


#dcc command
bind dcc -|- clonecontrol clonecontrol:dcc

proc clonecontrol:dcc { handle idx text } {
  set cmd [lindex [split $text] 0]
  set host [lindex [split $text] 1]
  global clonecontrol
  if { [string equal $cmd add] } {
    if { ![matchattr $handle mn] } {
      putidx $idx "no access"
    } elseif { ![string equal $host ""] } {
      setuser -cc hosts -cc!$host
      putidx $idx "clonecontrol added $host \017to exclude list"
    } else {
      putidx $idx "clonecontrol add <host>"
    }
  } elseif { [string equal $cmd del] } {
    if { ![matchattr $handle mn] } {
      putidx $idx "no access"
    } elseif { ![string equal $host ""] } {
      if { [string equal [finduser -cc!$host] -cc] } {
        delhost -cc -cc!$host
        putidx $idx "clonecontrol removed $host \017from exclude list"
        foreach chan [channels] { foreach nick [chanlist $chan] { clonecontrol:checkuser $chan $nick } }
      } else {
        putidx $idx "clonecontrol no record for $host"
      }
    } else {
      putidx $idx "clonecontrol del <host>"
    }
  } elseif { [string equal $cmd list] } {
    if { ![matchattr $handle mn] } {
      putidx $idx "no access"
    } else {
      if { [string equal $host ""] } { set host "*" }
      set host2 $host
      regsub -all {\[} $host {\\[} host
      regsub -all {\]} $host {\\]} host
      regsub -all {\\} $host {\\\\} host
      foreach h [getuser -cc hosts] {
        if { [string match -nocase $host $h] } {
          set h [join [lrange [split $h !] 1 end] !]
          putidx $idx "clonecontrol list: $h"
        }
      }
      putidx $idx "clonecontrol list end for $host2"
    }
  } elseif { [string equal $cmd set] } {
    set chan [lindex [split $text] 1]
    set status [lindex [split $text] 2]
    set max [lindex [split $text] 3]
    set bantime [lindex [split $text] 4]
    if { ![validchan $chan] } {
      putidx $idx "clonecontrol set <channel> <on|off> <max> <bantime>"
    } elseif { ![matchattr $handle mn|mn $chan] } {
      putidx $idx "no access"
    } elseif { ![string equal -nocase $status on] && ![string equal -nocase $status off] && ![string equal $status ""] } {
      putidx $idx "clonecontrol set <channel> <on|off> <max> <bantime>"
    } else {
      if { [string equal -nocase $status on] } { channel set $chan +clonecontrol }
      if { [string equal -nocase $status off] } { channel set $chan -clonecontrol }
      if { ![string equal $max ""] } { channel set $chan clonecontrol-max $max }
      if { ![string equal $bantime ""] } { channel set $chan clonecontrol-bantime $bantime }
      if { [channel get $chan clonecontrol] } { set status on } else { set status off }
      set max [channel get $chan clonecontrol-max]
      set bantime [channel get $chan clonecontrol-bantime]
      putidx $idx "clonecontrol setttings for $chan status=$status max=$max bantime=$bantime"
      foreach nick [chanlist $chan] { clonecontrol:checkuser $chan $nick }
    }
  } elseif { [string equal -nocase $cmd clones] } {
    set chan [lindex [split $text] 1]
    if { ![validchan $chan] } {
      putidx $idx "clonecontrol clones <channel>"
    } elseif { ![matchattr $handle omn|omn $chan] } {
      return 0
    } elseif { ![botonchan $chan] } {
      putidx $idx "not on channel"
    } else {
      global clonecontroldb
      if { ![info exists clonecontroldb] } { return 0 }
      foreach name [lsort [array names clonecontroldb]] {
        regsub -all {\[} $chan {\\[} chan
        regsub -all {\]} $chan {\\]} chan
        regsub -all {\\} $chan {\\\\} chan
        if { [string match -nocase $chan,* $name] && $clonecontroldb($name) > 1 && ![string match $chan,*@* $name] } {
          putidx $idx "$clonecontroldb($name) clones from [lindex [split $name ,] 1]"
        }
      }
      putidx $idx "end of clones list"
    }
  } else {
    putidx $idx "clonecontrol <add|del|list|set>"
  }
  return 1
}



#msg command
#bind msg omn|omn clonecontrol clonecontrol:msg

proc clonecontrol:msg { nick uhost handle text } {
  set cmd [lindex [split $text] 0]
  set host [lindex [split $text] 1]
  global clonecontrol
  if { [string equal $cmd add] } {
    if { ![matchattr $handle mn] } {
      lappend output "Need to be global master or owner to add entries."
    } elseif { ![string equal $host ""] } {
      setuser -cc hosts -cc!$host
      lappend output "Clonecontrol added $host\017 to exclude list"
    } else {
      lappend output "usage: clonecontrol add <host>"
    }
  } elseif { [string equal $cmd del] } {
    if { ![matchattr $handle mn] } {
      lappend output "You have no access to this sub-command."
    } elseif { ![string equal $host ""] } {
      if { [string equal [finduser -cc!$host] -cc] } {
        delhost -cc -cc!$host
        lappend output "Clonecontrol removed $host\017 from exclude list"
        foreach chan [channels] { foreach nick [chanlist $chan] { clonecontrol:checkuser $chan $nick } }
      } else {
        lappend output "Clonecontrol no record for $host"
      }
    } else {
      lappend output "usage: clonecontrol del <host>"
    }
  } elseif { [string equal $cmd list] } {
    if { ![matchattr $handle mn] } {
      lappend output "Need to be global master or owner to list entries."
    } else {
      if { [string equal $host ""] } { set host "*" }
      set host2 $host
      regsub -all {\[} $host {\\[} host
      regsub -all {\]} $host {\\]} host
      regsub -all {\\} $host {\\\\} host
      foreach h [getuser -cc hosts] {
        if { [string match -nocase $host $h] } {
          set h [join [lrange [split $h !] 1 end] !]
          lappend output "Clonecontrol list: $h"
        }
      }
      lappend output "Clonecontrol list end for $host2"
    }
  } elseif { [string equal $cmd set] } {
    set chan [lindex [split $text] 1]
    set status [lindex [split $text] 2]
    set max [lindex [split $text] 3]
    set bantime [lindex [split $text] 4]
    if { ![validchan $chan] } {
      puthelp "NOTICE $nick :clonecontrol set <channel> \[<on|off> <max> <bantime>\]"
    } elseif { ![matchattr $handle mn|mn $chan] } {
      lappend output "Need to be master or owner on $chan to change these settings."
    } elseif { ![string equal -nocase $status on] && ![string equal -nocase $status off] && ![string equal $status ""] } {
      lappend output "Clonecontrol set <channel> <on|off> <max> <bantime>"
    } else {
      if { [string equal -nocase $status on] } { channel set $chan +clonecontrol }
      if { [string equal -nocase $status off] } { channel set $chan -clonecontrol }
      if { ![string equal $max ""] } { channel set $chan clonecontrol-max $max }
      if { ![string equal $bantime ""] } { channel set $chan clonecontrol-bantime $bantime }
      if { [channel get $chan clonecontrol] } { set status on } else { set status off }
      set max [channel get $chan clonecontrol-max]
      set bantime [channel get $chan clonecontrol-bantime]
      lappend output "Clonecontrol setttings for $chan status=$status max=$max bantime=$bantime"
      foreach nick [chanlist $chan] { clonecontrol:checkuser $chan $nick }
    }
  } elseif { [string equal -nocase $cmd clones] } {
    set chan [lindex [split $text] 1]
    if { ![validchan $chan] || ![matchattr $handle omn|omn $chan] } {      lappend output "$chan\017 unknown channel or you have no access on it."
    } elseif { ![botonchan $chan] } {
      lappend output "$chan\017 I am not there"
    } else {
      global clonecontroldb
      if { ![info exists clonecontroldb] } { return 0 }
      foreach name [lsort [array names clonecontroldb]] {
        regsub -all {\[} $chan {\\[} chan
        regsub -all {\]} $chan {\\]} chan
        regsub -all {\\} $chan {\\\\} chan
        if { [string match -nocase $chan,* $name] && $clonecontroldb($name) > 1 && ![string match $chan,*@* $name] } {
          lappend output "$clonecontroldb($name) clones from [lindex [split $name ,] 1]"
        }
      }
      lappend output "End of clones list"
    }
  } else {
    lappend output "usage: clonecontrol <add|del|list|clones|set>"
  }
  if { [catch {set x [cnotice $nick $output]} error] || !$x } {
    foreach t $output { puthelp "NOTICE $nick :$t" }
  }
  return 1
}

