####################################################
# by wiebe @ QuakeNet
#
# provides procs:
#   sethand nick hand, sets nick's handle to hand
#   unsethand nick, unsets nick's handle (only works when set with sethand)
#
# used by whox (whox.tcl) to recognise users based on Q account
# used by login command (login.tcl) and logout command (logout.tcl)
#
# scripts works by temporary adding NICK!USER@HOST mask to a handle on the bot
# console mode 3 is used for reporting +host and -host actions by this script
#
# script can use: bannick.tcl (for mask conversion for banning +k users)
# script provides info for script.tcl
####################################################


####################################################
# sethand
####################################################
proc sethand { nick hand } {
  set n [string tolower $nick]; set h [string tolower $hand]; global sethanddb
  if {![sethand:onchan $n]} { return 0 }
  if {![validuser $h]} { return 0 }
  if {[string equal -nocase [nick2hand $n] $h]} { return 0 }
  set m $n![getchanhost $n]
  if {[info exists sethanddb($n)]} { sethand:delmask $h $m }
  sethand:addmask $h $m; set sethanddb($n) $h; set s [clock seconds]; setuser $h LASTON $s
  foreach c [channels] {
    if {[onchan $n $c] && ![onchansplit $n $c]} { setuser $h LASTON $s $c }
    if {![botisop $c] && ![botishalfop $c]} { continue }
    if {![onchan $n $c] || [onchansplit $n $c]} { sethand:invite $n $h $c
    } else { sethand:op $n $h $c; sethand:hop $n $h $c; sethand:voice $n $h $c; sethand:ban $n $h $c }
  }
  return 1
}


####################################################
# sethand:op
####################################################
proc sethand:op { n h c } {
  if {![botisop $c]} { return 0 }
  if {[matchattr $h d|d $c]} { if {[isop $n $c]} { pushmode $c -o $n } ; return 0 }
  if {[isop $n $c]} { return 0 }
  if {[matchattr $h a|a $c]} { pushmode $c +o $n; return 0 }
  if {[matchattr $h omn|omn $c] && [channel get $c autoop]} { pushmode $c +o $n; return 0 }
}


####################################################
# sethand:hop
####################################################
proc sethand:hop { n h c } {
  if {![botisop $c]} { return 0 }
  if {[matchattr $h r|r $c]} { if {[ishalfop $n $c]} { pushmode $c -h $n } ; return 0 }
  if {[isop $n $c]} { return 0 }
  if {[matchattr $h y|y $c]} { pushmode $c +h $n; return 0 }
  if {[matchattr $h lomn|lomn $c] && [channel get $c autohalfop]} { pushmode $c +h $n; return 0 }
}


####################################################
# sethand:voice
####################################################
proc sethand:voice { n h c } {
  if {[matchattr $h q|q $c]} { if {[isvoice $n $c]} { pushmode $c -v $n } ; return 0 }
  if {[matchattr $h g|g $c]} { pushmode $c +v $n; return 0 }
  if {[matchattr $h vlomn|vlomn $c] && [channel get $c autovoice]} { pushmode $c +v $n; return 0 }
}


####################################################
# sethand:invite
####################################################
proc sethand:invite { n h c } { if {[matchattr $h omn|fvlomn $c] && [matchattr $h I|I $c]} { puthelp "INVITE $n $c" } }


####################################################
# sethand:ban
####################################################
proc sethand:ban { n h c } {
  if {![matchattr $h k|k $c]} { return 0 }
  if {![botisop $c] && ([isop $n $c] || [ishalfop $n $c])} { return 0 }
  if {[isop $n $c]} { pushmode $c -o $n }
  if {[ishalfop $n $c]} { pushmode $c -h $n }
  if {[isvoice $n $c]} { pushmode $c -v $n }
  set l 60; set r [getuser $h comment]; set u [getchanhost $n]; set m *!$u; set d sethand.tcl
  if {$r == ""} { set r "@ $n is handle $h and has +k flag" }
  if {[string match @* $r]} { append r " $n is handle $h and has +k flag" }
  if {[string match ~* $u] || [string match *.users.quakenet.org $u]} { set m *!*@[lindex [split $u @] 1] }
  if {[info procs bannick:mask] != ""} { set m [bannick:mask $u] }
  if {[matchattr $h k]} { if {[info procs newban:ban] != ""} { newban:ban $m $d $r $l } else { newban $m $d $r $l }
  } else { if {[info procs newchanban:ban] != ""} { newchanban:ban $c $m $d $r $l } else { newchanban $c $m $d $r $l } }
}


####################################################
# unsethand
####################################################
proc unsethand { nick } {
  set n [string tolower $nick]; global sethanddb
  if {![sethand:onchan $n]} { return 0 }
  if {![info exists sethanddb($n)]} { return 0 }
  set u [getchanhost $n]; set h [lindex [split $sethanddb($n)] 0]; unset sethanddb($n)
  if {![validuser $h]} { return 0 }
  sethand:delmask $h $n!$u
  return 1
}


####################################################
# sethand:join
####################################################
bind join -|- * sethand:join
proc sethand:join { n u h c } {
  global sethanddb; set n [string tolower $n]
  if {![info exists sethanddb($n)]} {
    if {![validuser $h]} { return 0 }
    set m [string toupper $n!$u]; set m [sethand:strip $m]
    if {[lsearch -exact [getuser $h HOSTS] $m] > -1} { set sethanddb($n) $h }
  } else {
    if {[lindex [split $sethanddb($n)] 1] != 1} { return 0 }
    set h [lindex [split $sethanddb($n)] 0]; unset sethanddb($n); sethand $n $h
  }
}


####################################################
# sethand:part
####################################################
bind raw -|- "PART" sethand:part
proc sethand:part { s r t } {
  if {![string equal -nocase $r "PART"]} { return 0 }
  set s [split $s !@]; set t [split $t]; set n [lindex $s 0]; set c [lindex $t 0]
  sethand:unset $n $c
  return 0
}


####################################################
# sethand:kick
####################################################
bind kick -|- * sethand:kick
proc sethand:kick { n u h c t m } { sethand:unset $t $c }


####################################################
# sethand:sign
####################################################
bind sign -|- * sethand:sign
proc sethand:sign { n u h c m } {
  if {[string equal -nocase $m "registered"] || [string equal -nocase $m "host change"]} {
    global sethanddb; set n [string tolower $n]
    if {![info exists sethanddb($n)]} { return 0 }
    set h [lindex [split $sethanddb($n)] 0]
    set sethanddb($n) "$h 1"
    sethand:delmask $h $n!$u
  } else { sethand:unset $n $c 1 }
}


####################################################
# sethand:splt
####################################################
bind splt -|- * sethand:splt
proc sethand:splt { n u h c } { sethand:unset $n $c }


####################################################
# sethand:nick
# bind raw to avoid nick change -> deop on +bitch channels
####################################################
bind raw -|- "NICK" sethand:nick
proc sethand:nick { s n t } {
  if {![string equal -nocase $n "NICK"]} { return 0 }
  set s [split $s !]; set n [string tolower [lindex $s 0]]; set u [lindex $s 1]
  set m [string tolower [string trimleft [lindex [split $t] 0] :]]
  if {$n == "" || $m == "" || $u == ""} { return 0 }
  if {![string match "*@*" $u]} { return 0 }
  if {$n == $m} { return 0 }
  if {![sethand:onchan $n]} { return 0 }
  global sethanddb
  if {![info exists sethanddb($n)]} { return 0 }
  set h [lindex [split $sethanddb($n)] 0]; unset sethanddb($n)
  if {![validuser $h]} { unset sethanddb($n); return 0 }
  set sethanddb($m) $h; sethand:addmask $h $m!$u; sethand:delmask $h $n!$u
  return 0
}


####################################################
# sethand:unset
####################################################
proc sethand:unset { n c {q 0} } {
  global sethanddb
  if {[isbotnick $n]} {
    if {[validchan $c] && [llength [chanlist $c]] > 0} {
      foreach n [chanlist $c] {
        set n [string tolower $n]
        if {![info exists sethanddb($n)]} { continue }
        if {[sethand:comchan $n $c]} { continue }
        set h [lindex [split $sethanddb($n)] 0]; set u [getchanhost $n]
        sethand:delmask $h $n!$u; unset sethanddb($n)
      }
      # botnet - recheck all users on $c
      putallbots "SH C $c"
    } else {
      foreach n [array names sethanddb] {
        if {[sethand:onchan $n]} { continue }
        set h [lindex [split $sethanddb($n)] 0]; set u [getchanhost $n]
        sethand:delmask $h $n!$u; unset sethanddb($n)
      }
      # botnet - recheck all users
      putallbots "SH A"
    }
  } else {
    set n [string tolower $n]
    if {![info exists sethanddb($n)]} { return 0 }
    if {$q == 0 && [sethand:comchan $n $c]} { return 0 }
    set h [lindex [split $sethanddb($n)] 0]; set u [getchanhost $n]
    sethand:delmask $h $n!$u; unset sethanddb($n)
    # botnet - recheck user $n, only when $q 0
    if {$q == 0} { putallbots "SH U $n $u" }
  }
}


####################################################
# sethand:comchan
####################################################
proc sethand:comchan { n c } {
  foreach d [channels] {
    if {[botonchan $d] && [onchan $n $d] && ![onchansplit $n $d] && ![string equal -nocase $d $c]} { return 1 }
  }
  return 0
}


####################################################
# sethand:onchan
####################################################
proc sethand:onchan { n } {
  if {![onchan $n]} { return 0 }
  foreach c [channels] { if {[onchan $n $c] && ![onchansplit $n $c]} { return 1 } }
  return 0
}


####################################################
# sethand:addmask
####################################################
proc sethand:addmask { h m } {
  set m [string toupper $m]; set m [sethand:strip $m]
  putloglev 3 * "SETHAND: +host $h $m"
  setuser $h HOSTS $m
  return 1
}


####################################################
# sethand:delmask
####################################################
proc sethand:delmask { h m } {
  set m [string toupper $m]; set m [sethand:strip $m]
  if {[lsearch -exact [getuser $h HOSTS] $m] == -1} { return 0 }
  putloglev 3 * "SETHAND: -host $h $m"
  delhost $h $m
  return 1
}


####################################################
# sethand:strip
####################################################
proc sethand:strip { m } {
  # when m is NICK!~@HOST, leave the ~
  if {${::strict-host} == 0 && ![string match ?*!~@?* $m]} { set m [string map [list !~ !] $m] }
  return $m
}


####################################################
# sethand:evnt
####################################################
bind evnt - init-server sethand:evnt; bind evnt - disconnect-server sethand:evnt
bind evnt - prerestart sethand:evnt; bind evnt - userfile-loaded sethand:evnt
proc sethand:evnt { t } {
  set t [string tolower $t]
  if {$t == "userfile-loaded"} {
    global sethanddb
    foreach n [array names sethanddb] {
      set h [lindex [split $sethanddb($n)] 0]
      if {![validuser $h] || ![sethand:onchan $n]} { unset sethanddb($n); continue }
      if {[string equal -nocase [nick2hand $n] $h]} { continue }
      sethand:addmask $h $n![getchanhost $n]
    }
  }
  if {$t == "disconnect-server" || $t == "prerestart"} {
    global sethanddb
    foreach n [array names sethanddb] {
      set u [lindex [split sethanddb($n)] 0]; unset sethanddb($n)
      if {![validuser $u]} { continue }
      foreach h [getuser $u HOSTS] {
        if {![string match ?*!?*@?* $h]} { continue }
        if {![string equal $h [string toupper $h]]} { continue }
        if {[regexp {\*|\?} $h]} { continue }
        sethand:delmask $u $h
      }
    }
    # botnet - let recheck all users
    putallbots "SH A"
  }
  if {$t == "init-server"} {
    foreach u [userlist] {
      foreach h [getuser $u HOSTS] {
        if {![string match ?*!?*@?* $h]} { continue }
        if {![string equal $h [string toupper $h]]} { continue }
        if {[regexp {\*|\?} $h]} { continue }
        sethand:delmask $u $h
      }
    }
    # botnet - let recheck all users
    putallbots "SH A"
  }
}


####################################################
# sethand:nkch
####################################################
bind nkch - * sethand:nkch
proc sethand:nkch { o n } {
  global sethanddb
  foreach e [array names sethanddb] {
    if {[string equal -nocase $o [lindex [split $sethanddb($e)] 0]]} { set sethanddb($e) $n }
  }
}


####################################################
# sethand:bot
####################################################
# A - recheck all users
# C <chan> - recheck channel
# U <nick> - recheck nick
# S <nick> <userhost> - ?
bind bot - SH sethand:bot
proc sethand:bot { b c t } {
  if {![string equal -nocase $c "SH"]} { return 0 }
  set t [split $t]; set w [string tolower [lindex $t 0]]; set v [lindex $t 1]; set x [lindex $t 2]; global sethanddb
  if {$w == "a"} {
    foreach n [array names sethanddb] {
      if {![sethand:onchan $n]} { unset sethanddb($n); continue }
      set h [lindex [split $sethanddb($n)] 0]
      if {![validuser $h]} { unset sethanddb($n); continue }
      set m $n![getchanhost $n]; sethand:addmask $h $m
    }
  } elseif {$w == "c"} {
    set c $v
    if {![validchan $c] || ![botonchan $c]} { return 0 }
    foreach n [chanlist $c] {
      set n [string tolower $n]
      if {![info exists sethanddb($n)]} { continue }
      if {[onchansplit $n $c]} { unset sethanddb($n); continue }
      set h [lindex [split $sethanddb($n)] 0]
      if {![validuser $h]} { unset sethanddb($n); continue }
      set m $n![getchanhost $n]; sethand:addmask $h $m      
    }
  } elseif {$w == "u"} {
    set n [string tolower $v]
    if {![info exists sethanddb($n)]} { return 0 }
    if {![sethand:onchan $n]} { unset sethanddb($n); return 0 }
    if {![string equal -nocase [getchanhost $n] $x]} { return 0 }
    set h [lindex [split $sethanddb($n)] 0]
    if {![validuser $h]} { unset sethanddb($n); return 0 }
    sethand:addmask $h $n!$x 
  }
}


####################################################
# sethand:disc
####################################################
# recheck ALL sethanddb() entries, recheck ALL users and their hosts
bind disc - * sethand:disc
proc sethand:disc { b } {
  global sethanddb
  foreach n [array names sethanddb] {
    if {![sethand:onchan $n]} { unset sethanddb($n); continue }
    set h [lindex [split $sethanddb($n)] 0]
    if {![validuser $h]} { unset sethanddb($n); continue }
    set m $n![getchanhost $n]; sethand:addmask $h $m
  }
  foreach u [userlist] {
    foreach h [getuser $u HOSTS] {
      if {![string match ?*!?*@?* $h]} { continue }
      if {![string equal $h [string toupper $h]]} { continue }
      if {[regexp {\*|\?} $h]} { continue }
      set n [lindex [split $h !] 0]
      if {[sethand:onchan $n] && [string equal -nocase $h [sethand:strip $n![getchanhost $n]]]} { continue }
      sethand:delmask $u $h
    }
  }
  # botnet - let recheck all users
#  [14:33] SETHAND: adding mask NICK!USER@HOST to HAND
#  [14:33] !!! writing to nonexistent socket: 12
#  [14:33] !-> 's +h HAND NICK!USER@HOST'
  utimer 10 [list putallbots "SH A"]
}


####################################################
# sethand:time
####################################################
# check all users and their hosts, synchronize our sethanddb to hosts
bind time -|- * sethand:time
proc sethand:time { n h d m y } {
  global sethanddb
  foreach u [userlist] {
    foreach h [getuser $u HOSTS] {
      if {![string match ?*!?*@?* $h]} { continue }
      if {![string equal $h [string toupper $h]]} { continue }
      if {[regexp {\*|\?} $h]} { continue }
      set n [string tolower [lindex [split $h !] 0]]
      if {![sethand:onchan $n]} { continue }
      if {[info exists sethanddb($n)] && [string equal -nocase $u $sethanddb($n)]} { continue }
      if {![string equal -nocase $h [sethand:strip $n![getchanhost $n]]]} { continue }
      set sethanddb($n) $u
    }
  }
}


set ::scriptdb(sethand) {
  "provides sethand and unsethand procs, used by whox (whox.tcl) to recognise users based on Q account, and used by login command (login.tcl) and logout command (logout.tcl). it temporary adds NICK!USER@HOST to the handle on the bot. console mode 3 is used for reporting +host and -host actions by this script"
}

