#
# $Source: /home/nlfm/Working/Zircon/Released/lib/RCS/IRC.tcl,v $
# $Date: 2002/05/09 21:19:59 $
# $Revision: 1.18.1.65 $
#
package provide zircon 1.18
#
#   Handle IRC cmds
#
proc ctcpAnswer {net usr nk cp} {
    handleOn $net CTCPREPLY [list $nk $cp]
    if {[string match ZIRCON* $cp]} {
	if {[zirconReply $net $usr $nk $cp]} return
    }
    set chn {}
    foreach x [$usr channels] {
	if {[$x active] && [[$x wid] visible]} {lappend chn $x}
    }
    regsub -all "\[\x01-\x1f\]" $nk {} nk
    if {$chn == {}} {
	tellInfo $net {CTCP Reply} "CTCP Reply from $nk:\n$cp" CTCP
    } {
	foreach x $chn {
	    $x addText @CTCP "*** CTCP Reply from $nk: $cp"
	}
    }
}
#
proc ircERROR {net prefix param pargs} { $net error $prefix $param $pargs }
#
proc ircPONG {net unusedprefix unusedparam unusedpargs} { $net handlePong }
#
proc mungNotice {msg} {
    if {[regexp \
      {Received KILL message for ([^ ]+). From ([^ ]+) Path: ([^ ]+) (.*)} \
      $msg match user from path rest]} {
	return "*** KILL from $from for $user $rest"
    }
    return $msg
}
#
proc pingVal {val} {
    if {[regexp "^PING\[ \t\]+(\[0-9\]+).*" $val m t]} {
	return "[zping $t] secs"
    }
    return {Corrupt PING value}
}
#
proc ircNOTICE {net prefix param pargs} {
    set nkinfo [mungPrefix $net $prefix]
    if {[ignoreSet [lindex $nkinfo 3] notices]} {ditchPrefix $nkinfo ; return}
    set id [find [set chan [lindex $pargs 0]] $net]
    if {{nil} == [set usr [lindex $nkinfo 0]]} {
	if {[$id active]} {
	    $id addText {} "-[string range $prefix 1 end]- $param"
	} {
	    switch nil $id {
	        $net inform [mungNotice $param]
	        set chan {}
	    }
	}
    } {
	set nk [$usr name]
	while {[regexp "(\[^\001\]*)\001(\[^\001\]*)\001(.*)" $param sub a cp b]} {
	    switch -glob -- $cp {
	    {ZIRCON Sorry*} { }
	    {PING *} {ctcpAnswer $net $usr $nk "PING - [pingVal $cp]"}
	    default { ctcpAnswer $net $usr $nk $cp }
	    }
	    switch {} [set param $a$b] return
	}
	if {[$id isa Channel]} {
	    $id addText $usr "-$nk- $param"
	} \
	elseif {![string compare nil [set id [Notice :: find $nk $net]]] &&
	  ![string compare nil [set id [Message :: find $nk $net]]]} {
	    if {[$net busy] || [listmember [$net toInfo] NOTICE]} {
		$net inform "Notice from $nk at [getDate] : $param"
	    } {
		set id [Notice :: make $net $nk]
		if {[$id timestamp]} {set ts {}} {set ts "[getDate]\n"}
		$id addText {} "$ts$param"
	    }
	} {
	    if {![$id active]} {$id show}
	    $id addText $usr $param
	}
    }
    handleOn $net NOTICE [list $prefix $param $chan]
    ditchPrefix $nkinfo
}
#
proc ircMODE {net prefix param pargs} {
    global userFlags
    set chan [lindex $pargs 0]
    if {{nil} == [set id [Channel :: find $chan $net]]} {
	if {[me $chan $net]} {
	    if {{} == [set md [lindex $pargs 1]]} {set md $param}
	    foreach m [split $md {}] {
		switch -- $m {
		- { set val 0 }
		+ { set val 1 }
		default { catch {$net configure -$userFlags($m) $val} }
		}
	    }
	}
	return
    }
    $id mode [set pd [lrange $pargs 1 end]] $prefix
    $id optText MODE "*** Mode change \"[string trim \
      [join $pd]]\" on channel $chan by [lindex [mung1Prefix $net $prefix] 0]"
}
#
proc doPatterns {where prefix param} {
    regsub {^:} $prefix {} prefix
    foreach p [$where patterns] {
	set pt [lindex $p 0]
	if {[regexp -nocase -- [lindex $pt 0] $prefix] && \
	  [regexp -- [lindex $pt 1] $param]} {
	    uplevel #0 set 0 [list $prefix]
	    uplevel #0 set 1 [list [$where name]]
	    uplevel #0 set 2 [list $param]
	    uplevel #0 set net [$where net]
	    if {[catch {uplevel #0 [lindex $p 1]} msg]} {
		tellError {} {Pattern Command Error} \
		  "Error when executing [$where name] pattern command \"[lindex $p 1]\" : $msg"
	    }
	    uplevel #0 safeUnset 0 1 2 net
	}
    }
}
#
proc ircPRIVMSG {net prefix param pargs} {
    set nkinfo [mungPrefix $net $prefix]
    set usr [lindex $nkinfo 0]
    set ign [lindex $nkinfo 3]
    set chan [lindex $pargs 0]
    set where [find $chan $net]
    set ctcnt [$net ctcpmax]
    while {$ctcnt > 0 && [regexp "(\[^\001\]*)\001\[ \t\]*(\[^ \t\001\]+)\[ \t\]*(\[^\001\]*)\001(.*)" $param sub a op cp b]} {
	switch {} \
	  [set param $a[handleCTCP $net $op $where $chan $usr $prefix $ign $cp]$b] {
	    ditchPrefix $nkinfo
	    return
	}
	incr ctcnt -1
    }
    regsub -all "\[\x01-\x1f\]" "<[set nk [$usr name]]>" {} pfx
    if {[me $chan $net]} {
	if {[ignoreSet $ign msgs] || [ignoreSet $ign notes]} return
	switch nil [set where [Message :: find $nk $net]] {
	    global zircon
	    set mmx [$net msgmax]
	    if {($mmx > 0 && $mmx < [llength [$net messages]]) || [$net busy]} {
		$net queue "NOTICE $nk :[$net busymsg]"
		$net inform "Message from $nk at [getDate] : $param"
	    } {
		handleOn $net POPUP [list $nk]
		set where [Message :: make $net $nk]
		if {[$where timestamp]} {set ts {}} {set ts "[getDate]\n"}
		$where addText $usr "$ts$pfx $param"
		doPatterns $where $prefix $param
	    }
	} default {
	    $where show
	    if {![$where isJoined $usr]} {$where addUser $usr 0 0}
	    $where addText $usr "$pfx $param"
	    doPatterns $where $prefix $param
	}
	handleOn $net MSG [list $prefix $param]
    } {
	if {[ignoreSet $ign public]} return
	set where [Channel :: find $chan $net]
	switch nil $where {
	    set where [$net info]
	    regsub -all "\[\x01-\x1f\]" "<$nk/$chan>" {} pfx
	}
	$where addText $usr "$pfx $param"
	doPatterns $where $prefix $param
	set onOp PUBLIC
	if {![$where isJoined $usr]} { set onOp PUBLIC_MSG }
	handleOn $net $onOp [list $prefix $chan $param]
    }
    ditchPrefix $nkinfo
}
#
proc ircJOIN {net prefix param pargs} {
    switch {} $param {
	set chan $pargs
	set ov {}
    } default {
	if {![regexp "^(.*)\a(\[ov\]+)\$" $param m chan ov]} {
	    set chan $param
	    set ov {}
        }
    }
    if {[notAnon $chan $prefix]} {
	set nkinfo [mungPrefix $net $prefix]
	if {[lindex $nkinfo 1]} {
	    [set chn [Channel :: make $net $chan]] show
	    $chn handleOV $ov [lindex $nkinfo 0]
	} {
	    set usr [lindex $nkinfo 0]
	    set nm [lindex $nkinfo 2]
	    switch nil [set frd [Friend :: find [$usr name] $net]] {} default {
		$frd appear $nm
	    }
	    [Channel :: find $chan $net ] doJoin $usr $nm $prefix $ov
	}
	ditchPrefix $nkinfo
    }
    handleOn $net JOIN [list $chan $prefix $ov]
}
#
proc ircNICK {net prefix param pargs} {
    set usr [lindex [set nkinfo [mungPrefix $net $prefix]] 0]
    switch {} $param {
#
# Some systems seem to send NICKS that omit the :
#
	set param [lindex $pargs end]
	set pargs [lreplace $pargs end end]
    }
    if {[lindex $nkinfo 1]} { $net configure -nickname $param } \
    elseif {[string compare nil [set orig [User :: find $param $net]]] &&
	[string compare $usr $orig]} {
	$usr substitute $orig
    } {
	foreach id [$usr channels] {
	    $id nickChange $usr $param
	    handleOn $net CHANNEL_NICK [list [$id name] $prefix $param]
	}
	foreach x {Message Notice Chat} {
	    switch nil [set old [$x :: find [$usr lname] $net]] {} default {
		$old nickChange $usr $param
	    }
	}
	$usr rename $param
    }
    handleOn $net CHANNEL_NICK [list $prefix $param]
    ditchPrefix $nkinfo
}
#
proc notAnon {c p} {
    switch :anonymous!anonymous@anonymous $p {
	switch -glob $c {[#+]*} { return 1 }
	return 0
    }
    return 1
}
#
proc ircPART {net prefix param pargs} {
    switch nil [set chan [Channel :: find [set chn [lindex $pargs 0]] $net]] return
    if {[notAnon $chn $prefix]} {
	set nkinfo [mungPrefix $net $prefix]
	if {[lindex $nkinfo 1]} {$chan delete} {
	    set usr [lindex $nkinfo 0]
	    set msg {}
	    switch {} $param {} default { set msg " : ($param)" }
	    $chan optText LEAVE "*** [$usr name] has left channel $chn$msg"
	    $chan killUser $usr
	}
	ditchPrefix $nkinfo
    }
    handleOn $net LEAVE [list $chn $prefix $param]
}
#
proc netsplit {str} {
    return [regexp -nocase \
      {^([a-z0-9*_-]+\.)+([a-z0-9_-]+) ([a-z0-9*_-]+\.)+([a-z0-9_-]+)$} $str]
}
#
proc ircQUIT {net prefix param unusedpargs} {
    if {![lindex [set nkinfo [mungPrefix $net $prefix]] 1]} {
	set nk [[set usr [lindex $nkinfo 0]] name]
	if {![$net nosplit] && [netsplit $param]} {
	    $usr split $param
	} {
	    switch nil [set fobj [Friend :: find $nk $net]] {} default {
		$fobj configure -ison 0 -usr nil
		[$net finfo] remove $fobj
	    }
	    $usr off

	    if {[set ti [listmember [$net toInfo] SIGNOFF]]} {
		$net display @QUIT "*** Signoff: $nk ($param)"
	    }
	    set lnk [$usr lname]
	    foreach x {channels messages notices chats} {
		foreach id [$net $x] {
		    if {[$id active]} {
			if {[$id isJoined $usr]} {
			    if {!$ti} {$id optText QUIT "*** Signoff: $nk ($param)"}
			    $id killUser $usr
		        } {
		            switch -- [$id lname] $lnk {
				$id addText @QUIT "*** $nk has signed off : $param"
			    }
			}
		    }
		}
	    }
	    handleOn $net QUIT [list $prefix $param]
	}
    }
    ditchPrefix $nkinfo
}
#
proc ircKICK {net prefix param pargs} {
    set kicker [lindex [mung1Prefix $net $prefix] 0]
    set chan [lindex $pargs 0]
    set who [[User :: make $net [set nk [lindex $pargs 1]]] ref]
    set id [Channel :: find $chan $net]
    if {[$net me $who]} {
	$id optText KICKME "*** You have been kicked off channel $chan by $kicker ($param)"
	if {[askUser KICKED "Kicked from $chan" \
	  "You have been kicked off channel $chan by $kicker\
($param). Do you want to rejoin?"]} {
	# the next code is in case the channel window was closed before
	# answering so we need to find the id again.
	    [Channel :: make $net $chan] rejoin
	} {
	    catch {$id delete}
	}
    } {
	$id optText KICK \
	  "*** $nk has been kicked off channel $chan by $kicker ($param)"
	$id kickUser $who $prefix 0
   }
   handleOn $net KICK [list $chan $prefix $nk $param]
   $who deref
}
#
proc ircINVITE {net prefix param unusedpargs} {
    if {![ignoreSet [lindex [set nkinfo [mungPrefix $net $prefix]] 3] invites]} {
	set name [[lindex $nkinfo 0] name]
	if {[askUser {} Invitation  "$name invites you to channel $param."]} {
	    set chid [Channel :: make $net $param]
	    catch {destroy .@kick$chid}
	    $chid sendJoin {}
	}
    }
    handleOn $net INVITE [list $prefix $param]
    ditchPrefix $nkinfo
}
#
proc ircKILL {net prefix param pargs} {
    set killer [lindex [mung1Prefix $net $prefix] 0]
    set who [[User :: make $net [lindex $pargs 0]] ref]
    $who off
    set nk [$who name]
    if {[$net me $who]} {
	tellInfo $net Killed "You have been killed by $killer ($param)" KILL
    } {
	foreach x {channels notices messages} {
	    foreach id [$net $x] {
		if {[$id isJoined $who]} {
		    $id optText KILL "*** $nk has been killed by $killer ($param)"
		    $id kickUser $who $prefix 1
		}
	    }
	}
    }
    handleOn $net KILL [list $prefix $nk]
    $who deref
}
#
proc ircTOPIC {net prefix param pargs} {
    set id [Channel :: find [set chan [lindex $pargs 0]] $net]
    $id setTopic $param
    set who [lindex [mung1Prefix $net $prefix] 0]
    $id optText TOPIC "*** $who has set the topic: \"$param\""
    $id log "*** $who has set the topic: $param"
    handleOn $net TOPIC [list $chan $prefix $param]
}
#
proc ircWALLOPS {net prefix param unusedpargs} {
   $net display WALLOP "[getDate] $prefix (WALLOPS) - $param"
   handleOn $net WALLOP [list $prefix $param]
}
#
proc ircNUM {net number pargs} {
    set number [string range $number 3 end]
    foreach arg [lrange [lindex $pargs 4] 1 end] {
	switch {} $arg {} default {append txt " $arg"}
    }
    append txt " [lindex $pargs 3]"
    switch -glob $number {
    [45]* {
	    set txt "Error $number from [string range [lindex $pargs 2] 1 end] : $txt"
	    tellError $net "[trans error] $number" $txt
	}
    default { $net inform $txt } 
    }
}
#
#	proc mungPrefix : breaks up the prefix to an IRC message
#	returns : {user object, me?, user@host, ignores}
#
proc mungPrefix {net prefix} {
    if {![regexp {^:([^!]+)!(.*)} $prefix m1 nk nm]} {
	return [list nil 0 {} {}]
    }
    set usr [[User :: make $net $nk $nm] ref]
    return [list $usr [$net me $usr] $nm [z_ignore $usr $nm]]
}
#
proc mung1Prefix {unusednet prefix} {
    if {![regexp {^:([^!]+)!(.*)} $prefix m1 nk nm]} {
	set nk [string range $prefix 1 end]
	set nm {}
    }
    return [list $nk $nm]
}
#
proc ditchPrefix {mng} { catch {[lindex $mng 0] deref} }
