# shroudBNC - an object-oriented framework for IRC
# Copyright (C) 2005 Gunnar Beutner
#
# Modified by Khobbits - 2011
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

internalbind client partyline:partyline
internalbind attach partyline:partyattach
internalbind detach partyline:partydetach
internalbind usrdelete partyline:partysync
internalbind command partyline:partycom
internalbind server partyline:partychantypes 005

proc partyline:init {} {
  putmainlog "~ Innitialising Partyline ~"
	# This is the default channel created if none exist
	if {[bncgetglobaltag partyline] == ""} {
		bncsetglobaltag partyline "~partyline"
	}
	if {[bncgetglobaltag partyline-nopart] == ""} {
		bncsetglobaltag partyline-nopart ""
	}
	if {[bncgetglobaltag partyline-host] == ""} {
		bncsetglobaltag partyline-host "sBNC.net"
	}
	set ::partyline_settings [list partyline partyline-host partyline-nopart]
	set ::partyban(-sbnc,-sbnc) 1
	set ::partyline [bncgetglobaltag partyline]
	foreach chan $::partyline {
		partyline:partyline_add $chan
	}
  if {![info exists ::partylinit]} {
    foreach user [bncuserlist] {
      partyline:partyattach $user
    }
  } 	
	set ::partylinit 1
	partyline:syncall
}

# Set the default channel settings
proc partyline:partyline_add {channel} {
	if {[lsearch -exact $::partyline $channel] == -1} {
		lappend ::partyline $channel
		bncsetglobaltag partyline $::partyline
	}
	if {![info exists ::partytopic($channel)] || ![info exists ::partyts($channel)] || ![info exists ::partywho($channel)]} {
		set ::partytopic($channel) "shroudBNC Partyline"
		set ::partyts($channel) [unixtime]
		set ::partywho($channel) "-sBNC"	
	}
}

# work around some weird "feature" in mirc, which sends a /part for channels when the channel's prefix isn't in CHANTYPES
proc partyline:partychantypes {client params} {
	if {[lindex $params 1] != 5} { return }
	set toks [lrange $params 3 end-1]
	set i 0
	while {$i < [llength $toks]} {
		set tok [split [lindex $toks $i] "="]
		if {[string equal -nocase [lindex $tok 0] "CHANTYPES"]} {
			if {[string first "~" [lindex $tok 1]] == -1} {
				set chantypes "[lindex $tok 1]~"
				setisupport CHANTYPES $chantypes
				putclient ":[join [lrange $params 0 2]] [join [lreplace $toks $i $i "CHANTYPES=$chantypes"]] :[lindex $params end]"
				haltoutput
			}
		}
		incr i
	}
}

#This handles all the normal channel ircd emulation
proc partyline:partyline {client parameters} {
	global botnick botname server partyline partytopic partyts partywho partyban
	set thisctx [getctx 1]
	set cmd [lindex $parameters 0]
	set chan [string tolower [lindex $parameters 1]]
	set serv [lindex [split $server ":"] 0]
	set chans [split [string tolower [getbncuser $client tag partyline]] ","]

	if {[lsearch -exact [string tolower $partyline] $chan] == -1} {
		if {[string match {-*} $chan]} {
			flood $client $parameters
			partyline:privateparty $client $parameters
		}
		return
	}
	haltoutput
	flood $client $parameters
	if {[string equal -nocase "join" $cmd]} {
		if {[info exists partyban($chan,$client)]} {
			putclient ":$serv 484 $botnick $chan :Cannot join channel (You have been banned)"
		} else {
    	if {[string first "~" [getisupport chantypes]] == -1} {
    			set chantypes "[getisupport chantypes]~"
    			putclient ":[lindex [split $server ":"] 0] 005 $botnick CHANTYPES=[getisupport chantypes]~ :are supported by this server."
    			setisupport CHANTYPES $chantypes
      }	
			setctx [getctx]
			putclient ":$botname JOIN :$chan"
			partyline:partyline $client "NAMES $chan"
			partyline:partyline $client "TOPIC $chan"
			setctx $thisctx
			if {[lsearch $chans $chan] == -1} {
				lappend chans $chan
				setbncuser $client tag partyline [join $chans ","]
      	partyline:bcpartybutone $thisctx ":-$client!user@[bncgetglobaltag partyline-host] JOIN $chan"
  			if {[getbncuser $client admin] == 1} {
  				putclient ":-sBNC!core@shroudbnc.org MODE $chan +o $botnick"
  				partyline:bcpartybutone $thisctx ":-sBNC!core@shroudbnc.org MODE $chan +o -$client"
  			} elseif {[getbncuser $client tag reseller] == 1} {
  				putclient ":-sBNC!core@shroudbnc.org MODE $chan +v $botnick"
  				partyline:bcpartybutone $thisctx ":-sBNC!core@shroudbnc.org MODE $chan +v -$client"
  			}
  		}
		}
	}

	if {[string equal -nocase "part" $cmd]} {
		set idx [lsearch $chans $chan]
		if {$idx != -1 && [lsearch [bncgetglobaltag partyline-nopart] $chan] == -1} {
			setbncuser $client tag partyline [join [lreplace $chans $idx $idx] ","]
			setctx [getctx 0]
			putclient ":$botname PART $chan"
			setctx $thisctx
			partyline:bcpartybutone $thisctx ":-$client!user@[bncgetglobaltag partyline-host] PART $chan"
		}
	}

	if {[string equal -nocase "names" $cmd]} {
		set idents "@-sBNC @Any-Abuse/Spam @Risks-Your-BNC"
		foreach user [bncuserlist] {
			if {[lsearch [split [string tolower [getbncuser $user tag partyline]] ","] $chan] != -1 && [getbncuser $user hasclient]} {
				if {[getbncuser $user admin] == 1} {
					set acc "@"
				} elseif {[getbncuser $user tag reseller] == 1} {
					set acc "+"
				} else {
					set acc ""
				}
				if {$client == $user} {
					lappend idents "$acc$botnick"
				} else {
					lappend idents "$acc\-$user"
				}
			}
		}
		putclient ":$serv 353 $botnick @ $chan :[join $idents]"
		putclient ":$serv 366 $botnick $chan :End of /NAMES list."
	}

	if {[string equal -nocase "mode" $cmd]} {
		if {[llength $parameters] < 3} {
			putclient ":$serv 324 $botnick $chan +n"
			putclient ":$serv 329 $botnick $chan 0"
		} elseif {[lindex $parameters 2] == "+b"} {
			putclient ":$serv 368 $botnick $chan :End of Channel Ban List"
		} else {
			putclient ":$serv 482 $botnick $chan :You can't change modes on $chan"
		}
	}

	if {[string equal -nocase "topic" $cmd]} {
		if {[llength $parameters] < 3} {
			putclient ":$serv 332 $botnick $chan :$partytopic($chan)"
			putclient ":$serv 333 $botnick $chan $partywho($chan) $partyts($chan)"
		} else {
			set partytopic($chan) [lindex $parameters 2]
			set partyts($chan) [unixtime]
			set partywho($chan) "-$client"
			partyline:bcparty ":-$client!user@[bncgetglobaltag partyline-host] TOPIC $chan :$partytopic($chan)"
		}
	}

	if {[string equal -nocase "kick" $cmd]} {
		if {[llength $parameters] < 3} {
			putclient ":$serv 461 $botnick KICK :Not enough parameters"
		} else {
			putclient ":$serv 482 $botnick $chan :You can't kick users from $chan"
		}
	}

	if {[string equal -nocase "privmsg" $cmd]} {
		if {[llength $parameters] < 3} {
			putclient ":$serv 461 $botnick PRIVMSG :Not enough parameters"
		} else {
			if {[lsearch [split [string tolower [getbncuser $client tag partyline]] ","] $chan] != -1} {
				partyline:bcpartybutone $thisctx ":-$client!user@[bncgetglobaltag partyline-host] PRIVMSG $chan :[lindex $parameters 2]"
			} else {
				putclient ":$serv 404 $botnick $chan :Cannot send to channel"
			}
		}
	}

	if {[string equal -nocase "notice" $cmd]} {
		if {[llength $parameters] < 3} {
			putclient ":$serv 461 $botnick NOTICE :Not enough parameters"
		} else {
			if {[lsearch [split [string tolower [getbncuser $client tag partyline]] ","] $chan] != -1} {
				partyline:bcpartybutone $thisctx ":-$client!user@[bncgetglobaltag partyline-host] NOTICE $chan :[lindex $parameters 2]"
			} else {
				putclient ":$serv 404 $botnick $chan :Cannot send to channel"
			}
		}
	}
}

proc partyline:privateparty {client parameters} {
	global botnick botname server partyline partytopic partyts partywho
	set thisctx [getctx 1]
	if {![string match {-*} [lindex $parameters 1]]} {
		return
	}
	set cmd [lindex $parameters 0]
	if {[string tolower [lindex $parameters 1]] != {--}} {
		set nick [string tolower [string trimleft [lindex $parameters 1] {-}]]
		if {[string tolower $nick] == "sbnc"} { return }
	} else {
		set nick {--}
	}
	set serv [lindex [split $server ":"] 0]
	if {[string equal -nocase "privmsg" $cmd]} {
		haltoutput
		if {[llength $parameters] < 3} {
			putclient ":$serv 461 $botnick PRIVMSG :Not enough parameters"
		} else {
			if {$nick == {--}} {
				foreach user [bncuserlist] {
					partyline:privateparty $client "$cmd -$user {[lindex $parameters 2]}"
				}
				setctx $thisctx
			} elseif {[lsearch [string tolower [bncuserlist]] $nick] != -1} {
				if {![getbncuser $nick hasclient]} {
					putclient ":$serv 301 $botnick -$nick :Disconnected from BNC"
					setctx $nick
					putlog "-$client (Partyline): [lindex $parameters 2]"
					setctx $thisctx
				} else {
					partyline:bcpartyone $thisctx $nick ":-$client!user@[bncgetglobaltag partyline-host] PRIVMSG $nick :[lindex $parameters 2]"
				}
			} elseif {[string match #* $nick ] != 0} {
				set chan [string tolower [string trimleft [lindex $parameters 1] {-}]]
				set network [getisupport NETWORK]
				foreach x [bncuserlist] {
					if { [getbncuser $x hasclient] } {
						set user_channel_list [split [string tolower [getbncuser $x channels]] {,}]
						setctx $x
						if {[lsearch $user_channel_list $chan] != -1 && [string match $network [getisupport NETWORK]] == 1} {
							putclient ":-$client!user@[bncgetglobaltag partyline-host] PRIVMSG $chan :[lindex $parameters 2]"
						}
						setctx $client
					}
				}
			} else {
				putclient ":$serv 401 $botnick $nick :No such user on the BNC"
			}
		}
	}
	if {[string equal -nocase "notice" $cmd]} {
		haltoutput
		if {[llength $parameters] < 3} {
			putclient ":$serv 411 $botnick :Not enough parameters"
		} else {
			if {$nick == {--}} {
				foreach user [bncuserlist] {
					partyline:privateparty $client "$cmd -$user {[lindex $parameters 2]}"
				}
				setctx $thisctx
			} elseif {[lsearch [string tolower [bncuserlist]] $nick] != -1} {
				if {![getbncuser $nick hasclient]} {
					putclient ":$serv 301 $botnick -$nick :Disconnected from BNC"
					setctx $nick
					putlog "-$client (Partyline:Notice): [lindex $parameters 2]"
					setctx $thisctx
				} else {
					partyline:bcpartyone $thisctx $nick ":-$client!user@[bncgetglobaltag partyline-host] NOTICE $nick :[lindex $parameters 2]"
				}
			} elseif {[string match #* $nick ] != 0} {
				set chan [string tolower [string trimleft [lindex $parameters 1] {-}]]
				set network [getisupport NETWORK]
				foreach x [bncuserlist] {
					if { [getbncuser $x hasclient] } {
						set user_channel_list [split [string tolower [getbncuser $x channels]] {,}]
						setctx $x
						if {[lsearch $user_channel_list $chan] != -1 && [string match $network [getisupport NETWORK]] == 1} {
							putclient ":-$client!user@[bncgetglobaltag partyline-host] NOTICE $chan :[lindex $parameters 2]"
						}
						setctx $thisctx
					}
				}
			} else {
				putclient ":$serv 401 $botnick $nick :No such user on the BNC"
			}
		}
	}
	if {[string equal -nocase "whois" $cmd]} {
		haltoutput
		if {[llength $parameters] < 2} {
			putclient ":$serv 431 $botnick :No nickname given"
		} else {
			if {$nick == {--}} {
				foreach user [bncuserlist] {
					partyline:privateparty $client "$cmd -$user {[lindex $parameters 2]}"
				}
				setctx $thisctx
			} elseif {[lsearch [string tolower [bncuserlist]] $nick] != -1} {
				putclient ":$serv 311 $botnick -$nick user [bncgetglobaltag partyline-host] * :[getbncuser $nick realname]"
				if {[info procs vhost:ip2host] == "vhost:ip2host"} {
					putclient ":$serv 378 $botnick -$nick :is connecting from [vhost:ip2host [getbncuser $nick vhost]] [getbncuser $nick vhost]"
				} else {
					putclient ":$serv 378 $botnick -$nick :is connecting from [getbncuser $nick vhost]"
				}
				putclient ":$serv 319 $botnick -$nick :[split [getbncuser $nick tag partyline] ,]"
				putclient ":$serv 312 $botnick -$nick [bncgetglobaltag partyline-host] :Shroudbnc based goodness"
				if {![getbncuser $nick hasclient]} {
					putclient ":$serv 301 $botnick -$nick :Disconnected from BNC"
				}
				if {[getbncuser $nick tag reseller] == "1"} {
					putclient ":$serv 310 $botnick -$nick :is a BNC Virtual Admin"
				}
				if {[getbncuser $nick admin] == 1} {
					putclient ":$serv 313 $botnick -$nick :is an IRC Operator - BNC Admin"
				}
				putclient ":$serv 307 $botnick -$nick :IRC nickname is [getbncuser $nick nick]"
				putclient ":$serv 317 $botnick -$nick :[getbncuser $nick uptime] [getbncuser $nick seen] :seconds connected, last bnc login"
				putclient ":$serv 318 $botnick -$nick :End of /WHOIS list."
			} else {
				putclient ":$serv 401 $botnick -$nick :No such user on the BNC"
			}
		}
	}
}

proc partyline:partyattach {client} {
	global botnick botname partyline server
	set chans [split [string tolower [getbncuser $client tag partyline]] ","]
	set thisctx [getctx 1]
	if {[getisupport chantypes] == ""} {
	    putclient ":irc.shroudbnc.info 005 $botnick CHANTYPES=~ :are supported by this server."
	}	elseif {[string first "~" [getisupport chantypes]] == -1} {
			set chantypes "[getisupport chantypes]~"
			setisupport CHANTYPES $chantypes
			putclient ":[lindex [split $server ":"] 0] 005 $botnick CHANTYPES=[getisupport chantypes] :are supported by this server."			
  }
	
	foreach chan $partyline {
		if {[lsearch $chans [string tolower $chan]] != -1} {
			setctx $thisctx
			putclient ":$botname JOIN $chan"
			partyline:partyline $client "NAMES $chan"
			partyline:partyline $client "TOPIC $chan"
			if {[getbncuser $client clientcount] == 1} {
				partyline:bcpartybutone $thisctx ":-$client!user@[bncgetglobaltag partyline-host] JOIN $chan"
				if {[getbncuser $client admin] == 1} {
					putclient ":-sBNC!core@shroudbnc.org MODE $chan +o $botnick"
					partyline:bcpartybutone $thisctx ":-sBNC!core@shroudbnc.org MODE $chan +o -$client"
				} elseif {[getbncuser $client tag reseller] == 1} {
					putclient ":-sBNC!core@shroudbnc.org MODE $chan +v $botnick"
					partyline:bcpartybutone $thisctx ":-sBNC!core@shroudbnc.org MODE $chan +v -$client"
				}
			}
		}
	}
}

proc partyline:partydetach {client {message {Disconnected from BNC}}} {
	global partyline
	set chans [split [string tolower [getbncuser $client tag partyline]] ","]
	set thisctx [getctx 1]
	foreach chan $partyline {
		if {[lsearch $chans [string tolower $chan]] != -1 && [getbncuser $client clientcount] == 0} {
			partyline:bcpartybutone $thisctx ":-$client!user@[bncgetglobaltag partyline-host] PART $chan :$message"
		}
	}
}

proc partyline:partysync {client} {
	global partyline
	set chans [split [string tolower [getbncuser $client tag partyline]] ","]
	foreach chan $partyline {
		if {[lsearch $chans [string tolower $chan]] == -1} {
			partyline:bcpartybutone $client ":-$client!user@[bncgetglobaltag partyline-host] PART $chan :Resyncing BNC: User isnt on channel."
		}
		if {[lsearch $chans [string tolower $chan]] != -1 && ![getbncuser $client hasclient]} {
			partyline:bcpartybutone $client ":-$client!user@[bncgetglobaltag partyline-host] PART $chan :Resyncing BNC: User isnt connected to BNC."
		}
		if {[lsearch $chans [string tolower $chan]] != -1 && [getbncuser $client hasclient]} {
			setctx $client
			partyline:partyline $client "NAMES $chan"
			partyline:partyline $client "TOPIC $chan"
		}
	}
}

proc partyline:syncall {} {
	foreach user [bncuserlist] {
		partyline:partysync $user
	}
}

# command catch
proc partyline:partycom {client parameters} {
	set command [lindex $parameters 0]
	if {[string equal -nocase $command "help"]} {
		if {[getbncuser $client admin]} {
			set help "Syntax: partyline <command> \[values/params\]\n
			Commands:
			join <user> <chan> - same as the user typing /join <chan>
			part <user> <chan> - same as the user typing /part <chan>
			set <user> <chan1,chan2,chan3> - sets the partyline channels the user is on
			show <user> - lists the partyline channels the user is on
			ban <user> <chan> - prevents a user from rejoining the channel
			unban <user> <chan> - undoes the above action"
			bncaddcommand "partyline" "Admin" "Allows the admin of the partyline channels" $help
		}
	}
	if {![getbncuser $client admin]} { return }
	if {[string equal -nocase $command "partyline"]} { partyline:admincmd $client $parameters }
	if {[string equal -nocase $command "globalset"]} {
		if {[llength $parameters] < 3} {
			foreach setting $::partyline_settings {
				if {[bncgetglobaltag $setting] != ""} {
					set reply [bncgetglobaltag $setting]
				} else {
					set reply "Not set"
				}
				internaltimer 0 0 bncreply "$setting - $reply"
			}
			return
		}

		set setting [string tolower [lindex $parameters 1]]
		set value [join [lrange $parameters 2 end] " "]
		if {[lsearch -exact $::partyline_settings $setting] > -1} {
			partyline:setglobal $setting $value
			partyline:init
			haltoutput
		}
	}
}

proc partyline:setglobal {setting value} {
	# add some error handling here.
	switch $setting {
		default {
			bncsetglobaltag $setting $value
			bncreply "Done."
		}
	}
}

# admin command handler
proc partyline:admincmd {client parameters} {
	if { [string equal -nocase "-a" [lindex $parameters 2]] } {
		foreach user [bncuserlist] {
			partyline:admincmd $client "partyline [lindex $parameters 1] $user [join [lrange $parameters 3 end]]"
		}
	} else {
  	catch {partyline [join [lrange $parameters 1 end]]} result
  	setctx $client
  	if {$result == ""} { set result "<null>" }
  	foreach sline [split $result \n] {
  		bncreply "$sline"
  	}
	}
	haltoutput
	return
}

# all the per user admin commands.
proc partyline {parameters} {
	global partyban botnick partyline botname partyhost
	variable ctx [getctx]
	set command [lindex $parameters 0]
	set user [lindex $parameters 1]
	set params [join [lrange $parameters 2 end]]
	set paramsnouser [join [lrange $parameters 1 end]]

	set result ""
	switch -- $command {
		join {
			setctx $user
			partyline:partyline $user "join [lindex $params 0]"
			set result "$user joining [lindex $params 0]"
		}
		part {
			setctx $user
			partyline:partyline $user "part [lindex $params 0] :Parted by admin."
			set result "$user parting [lindex $params 0]"
		}
		set {
			setbncuser $user tag partyline "[join [string tolower [lindex $params 0]] ","]"
			set result "$user is now on: [split [string tolower [getbncuser $user tag partyline]] ","]"
		}
		show {
			set result "$user is on: [split [string tolower [getbncuser $user tag partyline]] ","]"
		}
		ban {
			set partyban([lindex $params 0],$user) 1
			set result "$user banned on [lindex $params 0]"
		}
		unban {
			unset partyban([lindex $params 0],$user)
			set result "$user unbanned on [lindex $params 0]"
		}
		default {
			set command "Help"
			set result "Syntax: partyline <command> <user> \[values/params\]
			/sbnc help partline for more information and a list of valid commands."
		}
	}
	setctx $ctx
	return "partyline $command - $result"
}

#3 little utlity procs to save code
proc partyline:bcparty {text} {
	set chan [lindex [split $text] 2]
	foreach user [bncuserlist] {
		set chans [split [string tolower [getbncuser $user tag partyline]] ","]
		if {[lsearch $chans $chan] != -1} {
			setctx $user
			putclient "$text"
		}
	}
}

proc partyline:bcpartybutone {clientctx text} {
	set chan [string tolower [lindex [split $text] 2]]
	setctx $clientctx
	set clientname [getctx]
	global botnick
	foreach user [bncuserlist] {
		set chans [split [string tolower [getbncuser $user tag partyline]] ","]
		if {[lsearch $chans $chan] == -1} {
			continue
		}
		if {[string equal -nocase $clientname $user]} {
			foreach session [getbncuser $clientname sessions] {
				if {![string equal -nocase $clientctx $session]} {
					setctx $session
					putclient [string map "{-$clientname} {$botnick}" $text]
				}
			}
		} else {
			setctx $user
			putclient "$text"
		}
	}
	setctx $clientctx
}

proc partyline:bcpartyone {client nick text} {
	setctx $nick
	putclient "$text"
	setctx $client
}

variable floodcheck
#Number of lines to break before warning
set floodcheck(settings,lowcap) 20
#How many lines between warnings
set floodcheck(settings,period) 10
#Time period to log between
set floodcheck(settings,intime) 30
proc flood {client msg} {
	global floodcheck
	if { [info exists floodcheck($client,start)] == 0} {
		set floodcheck($client,start) [unixtime]
		set floodcheck($client,lines) 0
	}
	if { [expr {$floodcheck($client,start) + $floodcheck(settings,intime)}] < [unixtime]} {
		set floodcheck($client,start) [unixtime]
		set floodcheck($client,lines) 0
	}
	incr floodcheck($client,lines)
	if {$floodcheck($client,lines) >= $floodcheck(settings,lowcap)} {
		set i [expr {$floodcheck($client,lines) - $floodcheck(settings,lowcap)}]
		if {[expr {$i % $floodcheck(settings,period)}] == 0 } {
			putmainlog "User $client reached $floodcheck($client,lines) lines in $floodcheck(settings,intime) seconds.  Triggered by: [join $msg]"
		}
	}
}

utimer 2 partyline:init
