tclserv

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

2003-ts6.tcl (45367B)


      1 #source nda.tcl
      2 #source 9999-protocol-common.tcl
      3 
      4 
      5 namespace eval ts6 {
      6 #proc putcmdlog {args} {}
      7 proc ::ts6::b64e {numb} {
      8         set b64 [split "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" {}]
      9 
     10         set res ""
     11 	while {$numb != 0} {
     12 		append res [lindex $b64 [expr {$numb % 36}]]
     13 		set numb [expr {$numb / 36}]
     14 	}
     15 	if {[string length $res] == 0} {
     16 		set res "A"
     17 	}
     18         return [string reverse $res]
     19 }
     20 
     21 proc ::ts6::b64d {numb} {
     22         set b64 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
     23 	set numb [string trimleft $numb "A"]
     24 	set res 0
     25 	for {set i 0} {$i<[string length $numb]} {incr i} {
     26 		set new [string first [string index $numb $i] $b64]
     27 		incr res [expr {$new * (36 * $i)+1}]
     28 	}
     29         return $res
     30 }
     31 }
     32 
     33 proc putl {args} {
     34 	puts stdout [join $args " "]
     35 	puts {*}$args
     36 }
     37 
     38 namespace eval ts6 {
     39 
     40 proc ::ts6::sendUid {sck nick ident host dhost uid {realname "* Unknown *"} {modes "+oiS"} {server ""}} {
     41 	if {""==$server} {set server $::sid($sck)}
     42 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $server]]}]]
     43 	append sid [::ts6::b64e $server]
     44 	set sendid [::ts6::b64e $uid]
     45 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
     46 	append sendnn $sendid
     47 	if {![tnda get "netinfo/$::netname($sck)/euid"]} {
     48 		set sl [format ":%s UID %s 1 %s %s %s %s 0 %s%s :%s" $sid $nick [clock format [clock seconds] -format %s] $modes $ident $host $sid $sendnn $realname]
     49 	} {
     50 		set sl [format ":%s EUID %s 1 %s %s %s %s 0 %s%s %s * :%s" $sid $nick [clock format [clock seconds] -format %s] $modes $ident $dhost $sid $sendnn $host $realname]
     51 	}
     52 	tnda set "login/$::netname($sck)/${sid}${sendnn}" ""
     53 	tnda set "oper/$::netname($sck)/${sid}${sendnn}" [expr {[string first "o" $modes] != -1 ? 1 : 0}]
     54 	tnda set "metadata/$::netname($sck)/${sid}${sendnn}" [list]
     55 	tnda set "certfps/$::netname($sck)/${sid}${sendnn}" ""
     56 	tnda set "intclient/$::netname($sck)/${sid}${sendnn}" $uid
     57 	tnda set "nick/$::netname($sck)/${sid}${sendnn}" $nick
     58 	tnda set "ident/$::netname($sck)/${sid}${sendnn}" $ident
     59 	tnda set "rhost/$::netname($sck)/${sid}${sendnn}" $host
     60 	tnda set "vhost/$::netname($sck)/${sid}${sendnn}" $dhost
     61 	tnda set "rname/$::netname($sck)/${sid}${sendnn}" $realname
     62 	tnda set "ipaddr/$::netname($sck)/${sid}${sendnn}" 0
     63 	putl $sck $sl
     64 }
     65 
     66 proc ::ts6::topic {sck uid targ topic} {
     67 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
     68 	append sid [::ts6::b64e $::sid($sck)]
     69 	set sendid [::ts6::b64e $uid]
     70 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
     71 	append sendnn $sendid
     72 	putl $sck [format ":%s%s TOPIC %s :%s" $sid $sendnn $targ $topic]
     73 }
     74 
     75 proc ::ts6::setnick {sck uid newnick} {
     76 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
     77 	append sid [::ts6::b64e $::sid($sck)]
     78 	set sendid [::ts6::b64e $uid]
     79 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
     80 	append sendnn $sendid
     81 	putl $sck [format ":%s%s NICK %s :%s" $sid $sendnn $newnick [clock format [clock seconds] -format %s]]
     82 }
     83 
     84 proc ::ts6::sethost {sck targ topic} {
     85 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
     86 	append sid [::ts6::b64e $::sid($sck)]
     87 	if {![tnda get "netinfo/$::netname($sck)/euid"]} {
     88 		putl $sck [format ":%s ENCAP * CHGHOST %s %s" $sid $targ $topic]
     89 	} {
     90 		putl $sck [format ":%s CHGHOST %s %s" $sid $targ $topic]
     91 	}
     92 }
     93 
     94 proc ::ts6::sendSid {sck sname sid {realname "In use by Services"}} {
     95 set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
     96 append sid [::ts6::b64e $::sid($sck)]
     97 	set sl [format ":%s SID %s 1 %s :%s" [::ts6::b64e $sid] $sname [::ts6::b64e $sid] $realname]
     98 	putl $sck $sl
     99 }
    100 
    101 proc ::ts6::privmsg {sck uid targ msg} {
    102 set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
    103 append sid [::ts6::b64e $::sid($sck)]
    104 	set sendid [::ts6::b64e $uid]
    105 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    106 	append sendnn $sendid
    107 	putl $sck [format ":%s%s PRIVMSG %s :%s" $sid $sendnn $targ $msg]
    108 }
    109 
    110 proc ::ts6::snote {sck targ msg} {
    111 set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
    112 append sid [::ts6::b64e $::sid($sck)]
    113 	set sendid [::ts6::b64e $uid]
    114 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    115 	append sendnn $sendid
    116 	putl $sck [format ":%s ENCAP * SNOTE %s :%s" $sid $sendnn $targ $msg]
    117 }
    118 
    119 proc ::ts6::metadata {sck targ direction type {msg ""}} {
    120 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
    121 	append sid [::ts6::b64e $::sid($sck)]
    122 	if {[string toupper $direction] != "ADD" && [string toupper $direction] != "DELETE"} {putloglev d * "failed METADATA attempt (invalid arguments)";return} ;#no that didn't work
    123 	if {[string toupper $direction] == "ADD"} {
    124 		tnda set "metadata/$::netname($sck)/$targ/[ndaenc $type]" $msg
    125 		putl $sck [format ":%s ENCAP * METADATA %s %s %s :%s" $sid [string toupper $direction] $targ [string toupper $type] $msg]
    126 	}
    127 	if {[string toupper $direction] == "DELETE"} {
    128 		tnda unset "metadata/$::netname($sck)/$targ/[ndaenc $type]"
    129 		putl $sck [format ":%s ENCAP * METADATA %s %s :%s" $sid [string toupper $direction] $targ [string toupper $type]]
    130 	}
    131 }
    132 
    133 proc ::ts6::kick {sck uid targ tn msg} {
    134 set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
    135 append sid [::ts6::b64e $::sid($sck)]
    136 	set sendid [::ts6::b64e $uid]
    137 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    138 	append sendnn $sendid
    139 	putl $sck [format ":%s%s KICK %s %s :%s" $sid $sendnn $targ $tn $msg]
    140 }
    141 
    142 proc ::ts6::notice {sck uid targ msg} {
    143 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    144 	set sendid [::ts6::b64e $uid]
    145 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    146 	append sendnn $sendid
    147 	putl $sck [format ":%s%s NOTICE %s :%s" $sid $sendnn $targ $msg]
    148 }
    149 
    150 proc ::ts6::part {sck uid targ msg} {
    151 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    152 	set sendid [::ts6::b64e $uid]
    153 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    154 	append sendnn $sendid
    155 	putl $sck [format ":%s%s PART %s :%s" $sid $sendnn $targ $msg]
    156 	set chan [ndaenc $targ]
    157 	tnda unset "userchan/$::netname($sck)/$sid$sendnn/$chan"
    158 	tnda unset "channels/$::netname($sck)/$chan/status/$sid$sendnn"
    159 	firellbind $sck part "-" "-" [ndadec $chan] ${sid}${sendnn} $msg
    160 }
    161 
    162 proc ::ts6::quit {sck uid msg} {
    163 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    164 	set sendid [::ts6::b64e $uid]
    165 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    166 	append sendnn $sendid
    167 	putl $sck [format ":%s%s QUIT :%s" $sid $sendnn $msg]
    168 	foreach {chan _} [tnda get "userchan/$::netname($sck)/${sid}${sendnn}"] {
    169 		#firellbind $sck part "-" "-" $sid$sendnn [ndadec $chan] $msg
    170 		firellbind $sck quit "-" "-" $sid$sendnn [ndadec $chan] $msg
    171 		tnda unset "userchan/$::netname($sck)/${sid}${sendnn}/$chan"
    172 		tnda unset "channels/$::netname($sck)/$chan/status/${sid}${sendnn}"
    173 	}
    174 	firellbind $sck nquit "-" "-" $sid$sendnn $msg
    175 	tnda unset "login/$::netname($sck)/${sid}${sendnn}"
    176 	tnda unset "nick/$::netname($sck)/${sid}${sendnn}"
    177 	tnda unset "intclient/$::netname($sck)/${sid}${sendnn}"
    178 	tnda set "oper/$::netname($sck)/${sid}${sendnn}" 0
    179 	tnda unset "ident/$::netname($sck)/${sid}${sendnn}"
    180 	tnda unset "rhost/$::netname($sck)/${sid}${sendnn}"
    181 	tnda unset "vhost/$::netname($sck)/${sid}${sendnn}"
    182 	tnda unset "rname/$::netname($sck)/${sid}${sendnn}"
    183 	tnda unset "ipaddr/$::netname($sck)/${sid}${sendnn}"
    184 	tnda set "metadata/$::netname($sck)/${sid}${sendnn}" [list]
    185 	tnda unset "certfps/$::netname($sck)/${sid}${sendnn}"
    186 }
    187 
    188 proc ::ts6::setacct {sck targ msg} {
    189 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    190 	if {[ts6 uid2nick $sck $targ] == ""} {return}
    191 	putl $sck [format ":%s ENCAP * SU %s %s" $sid $targ $msg]
    192 	tnda set "login/$::netname($sck)/$targ" $msg
    193 }
    194 
    195 proc ::ts6::grant {sck targ msg {fmult 65}} {
    196 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    197 	if {[ts6 uid2nick $sck $targ] == ""} {return}
    198 	putl $sck [format ":%s ENCAP * GRANT %s %s %s" $sid $targ $fmult $msg]
    199 	tnda set "oper/$::netname($sck)/$targ" 1
    200 }
    201 
    202 proc ::ts6::putmotd {sck targ msg} {
    203 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    204 	if {[ts6 uid2nick $sck $targ] == ""} {return}
    205 	putl $sck [format ":%s 372 %s :- %s" $sid $targ $msg]
    206 }
    207 
    208 proc ::ts6::putmotdend {sck targ} {
    209 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    210 	if {[ts6 uid2nick $sck $targ] == ""} {return}
    211 	putl $sck [format ":%s 376 %s :End of global MOTD." $sid $targ]
    212 }
    213 
    214 proc ::ts6::putmode {sck uid targ mode {parm ""} {ts ""}} {
    215 	if {$ts == ""} {
    216 		if {[set ts [tnda get "channels/$::netname($sck)/[ndaenc [string tolower $targ]]/ts"]] == ""} {return} ;#cant do it, doesnt exist
    217 	}
    218 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    219 	set sendid [::ts6::b64e $uid]
    220 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    221 	append sendnn $sendid
    222 	putl $sck [format ":%s" [set com [format "%s%s TMODE %s %s %s %s" $sid $sendnn $ts $targ $mode $parm]]]
    223 	set comd [split $com " "]
    224 	set ctr 4
    225 	set state 1
    226 	foreach {c} [split $mode {}] {
    227 		if {$c == "+"} {
    228 			set state 1
    229 		} elseif {$c == "-"} {
    230 			set state 0
    231 		} elseif {[string match [format "*%s*" $c] [tnda get "netinfo/$::netname($sck)/chmparm"]] || ($state&&[string match [format "*%s*" $c] [tnda get "netinfo/$::netname($sck)/chmpartparm"]])} {
    232 			[expr {$state?"::ts6::checkop":"::ts6::checkdeop"}] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] [lindex $comd [incr ctr]]
    233 			firellmbind $sck mode - [format "%s %s%s" [string tolower [lindex $comd 3]] [expr {$state ? "+" : "-"}] $c] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] [lindex $comd [incr ctr]]
    234 		} else {
    235 			[expr {$state?"::ts6::checkop":"::ts6::checkdeop"}] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] [lindex $comd [incr ctr]]
    236 			firellmbind $sck mode - [format "%s %s%s" [string tolower [lindex $comd 3]] [expr {$state ? "+" : "-"}] $c] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] ""
    237 #			firellmbind $sck mode "-" [expr {$state ? "+" : "-"}] $c [lindex $comd 0] [lindex $comd 3] ""
    238 		}
    239 	}
    240 }
    241 
    242 proc ::ts6::sendencap {sck uid targ args} {
    243 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    244 	if {[ts6 uid2nick $sck $targ] == ""} {return}
    245 	if {$uid == "-1"} {	set sendnn ""} {
    246 			set sendid [::ts6::b64e $uid]
    247 			set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    248 			append sendnn $sendid
    249 	}
    250 	if {[string first " " [lindex $args end]] != -1} {
    251 		putl $sck [format ":%s%s ENCAP %s %s :%s" $sid $sendnn $targ [join [lrange $args 0 end-1] " "] [lindex $args end]]
    252 	} {
    253 		putl $sck [format ":%s%s ENCAP %s %s" $sid $sendnn $targ [join $args " "]]
    254 	}
    255 }
    256 
    257 proc ::ts6::putjoin {sck uid targ {ts ""}} {
    258 	if {$ts == ""} {
    259 		if {[set ts [tnda get "channels/$::netname($sck)/[ndaenc [string tolower $targ]]/ts"]] == ""} {set ts [clock format [clock seconds] -format %s]}
    260 	}
    261 	set sid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append sid [::ts6::b64e $::sid($sck)]
    262 	set sendid [::ts6::b64e $uid]
    263 	set sendnn [string repeat "A" [expr {6-[string length $sendid]}]]
    264 	append sendnn $sendid
    265 	putl $sck [format ":%s SJOIN %s %s + :%s%s" $sid $ts $targ $sid $sendnn]
    266 	set chan [ndaenc $targ]
    267 	tnda set "userchan/$::netname($sck)/$sid$sendnn/$chan" 1
    268 #	tnda set "channels/$::netname($sck)/$chan/status/[lindex $comd 0]" ""
    269 }
    270 
    271 proc ::ts6::validchan {sck channelname} {
    272 	if {[string is digit [string index $channelname 0]] && [string length $channelname] == 9} {return 0} ;# valid handle, not valid channel
    273 	if {[string first [string index $channelname 0] [tnda get "netinfo/$::netname($sck)/[ndaenc CHANTYPES]"]] != -1} {return 1} ;# could be valid channel, so let's just say yes
    274 }
    275 
    276 proc ::ts6::quitstorm {sck sid comment {doinit 1}} {
    277 	if {$doinit} {set splits [list $sid]} {set splits [list]}
    278 	foreach {sid64 sdesc} [tnda get "servers/$::netname($sck)"] {
    279 		# if the server doesn't have $sid as the uplink, continue
    280 		if {[dict get $sdesc uplink] != $sid} {
    281 			continue
    282 		}
    283 		# but if it does... they split and we should see who they're taking down
    284 		lappend splits [string toupper [ndadec $sid64]]
    285 		foreach {splitid} [::ts6::quitstorm $sck [ndadec $sid64] $comment 0] {
    286 			lappend splits $splitid
    287 		}
    288 	}
    289 	return $splits
    290 }
    291 
    292 proc ::ts6::irc-main {sck} {
    293 	global sid sock socksid
    294 	if {[eof $sck]} {
    295 		puts stdout "We're dead, folks. [clock format [clock seconds] -format {%Y%m%d %H:%M.%S}]"
    296 #		firellbind $sck evnt "-" "ts6.dead" $::netname($sck) $sck
    297 		firellbind $sck evnt "-" "dead" $::netname($sck) $sck
    298 		firellbind - evnt "-" "dead" $sck $::netname($sck)
    299 		close $sck
    300 	}
    301 	set ::errorInfo ""
    302 	gets $sck line
    303 	setctx $::netname($sck)
    304 	putloglev r * $line
    305 	set line [string trim $line "\r\n"]
    306 	set one [string match ":*" $line]
    307 	set line [string trimleft $line ":"]
    308 	set gotsplitwhere [string first " :" $line]
    309 	if {$gotsplitwhere==-1} {set comd [split $line " "]} {set comd [split [string range $line 0 [expr {$gotsplitwhere - 1}]] " "]}
    310 	if {$gotsplitwhere==-1} {set payload [lindex $comd end]} {set payload [string range $line [expr {$gotsplitwhere + 2}] end]}
    311 	if {$gotsplitwhere != -1} {lappend comd $payload}
    312 	if {[lindex $comd 0] == "PING"} {putl $sck "PONG $::snames($sck) :$payload"}
    313 	if {[lindex $comd 0] == "SERVER"} {putl $sck "VERSION"}
    314 	if {$one == 1} {
    315 		set sourceof [lindex $comd 0]
    316 		set two 2
    317 	} {	set sourceof ""
    318 		set two 1}
    319 	firellbind $sck raw - [lindex $comd $one] $sourceof [lindex $comd $one] [join [lrange $comd $two end] " "]
    320 	set erreno [catch {
    321 	switch -nocase -- [lindex $comd $one] {
    322 		"479" {putloglev d * $payload}
    323 		"PASS" {
    324 		#	putquick "PRIVMSG #services :$line"
    325 			#puts stdout "we have a winner! $one"
    326 			set ssid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append ssid [::ts6::b64e $::sid($sck)]
    327 			tnda set "servers/$::netname($sck)/[ndaenc [lindex $comd 4]]/uplink" $ssid
    328 			tnda set "servers/$::netname($sck)/[ndaenc [lindex $comd 4]]/sid" $payload
    329 			tnda set "socksid/$::netname($sck)" $payload
    330 		}
    331 
    332 		"SERVER" {
    333 			#puts stdout "we have a winner! $one"
    334 #			if {[lindex $comd [expr {$one + 2}]] != 1} {return};#we don't support jupes
    335 			tnda set "servers/$::netname($sck)/[ndaenc [tnda get "socksid/$::netname($sck)"]]/name" [lindex $comd [expr {$one + 1}]]
    336 			tnda set "servers/$::netname($sck)/[ndaenc [tnda get "socksid/$::netname($sck)"]]/description" [lindex $comd [expr {$one + 3}]]
    337 #			firellbind $sck evnt "-" "alive" $::netname($sck)
    338 			if {$one == 0} {firellbind - evnt "-" "alive" $::netname($sck)}
    339 #			firellbind $sck evnt "-" "ts6.alive" $::netname($sck)
    340 		}
    341 
    342 		"SID" {
    343 			#puts stdout "we have a winner! $one"
    344 			tnda set "servers/$::netname($sck)/[ndaenc [lindex $comd 4]]/name" [lindex $comd 2]
    345 			tnda set "servers/$::netname($sck)/[ndaenc [lindex $comd 4]]/description" [lindex $comd 5]
    346 			tnda set "servers/$::netname($sck)/[ndaenc [lindex $comd 4]]/uplink" [lindex $comd 0]
    347 			tnda set "servers/$::netname($sck)/[ndaenc [lindex $comd 4]]/sid" [lindex $comd 4]
    348 #			putloglev o * [tnda get "servers"]
    349 		}
    350 
    351 		"SQUIT" {
    352 			set ssid [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]];append ssid [::ts6::b64e $::sid($sck)]
    353 			set failedserver [lindex $comd [expr {$one + 1}]]
    354 			# is it us?
    355 			if {$failedserver == $ssid} {
    356 				#yes, it's us.
    357 				putloglev d * "We're dead, folks. [clock format [clock seconds] -format {%Y%m%d %H:%M:%S}]"
    358 				firellbind $sck evnt "-" "ts6.dead" $::netname($sck)
    359 				firellbind $sck evnt "-" "dead" $::netname($sck)
    360 				firellbind - evnt "-" "dead" $sck $::netname($sck)
    361 				return
    362 			}
    363 			# Mark all servers with an uplink in failedservers as split
    364 			set slist [::ts6::quitstorm $sck [lindex $comd [expr {$one + 1}]] [lindex $comd [expr {$one + 2}]]]
    365 			foreach {srv} $slist {
    366 				::ts6::snote $sck x [format "!! NETSPLIT: %s (%s) has left the network (Server Quit: %s)" [tnda get "servers/$::netname($sck)/[ndaenc $srv]/name"] $srv [lindex $comd [expr {$one + 2}]]
    367 				tnda unset "servers/$::netname($sck)/[ndaenc $srv]"
    368 				foreach {uidd _} [tnda get "nick/$::netname($sck)"] {
    369 					if {[string range $uidd 0 2] != $srv} {continue};# not a dead user
    370 					foreach {chan _} [tnda get "userchan/$::netname($sck)/$uidd"] {
    371 						firellbind $sck part "-" "-" [ndadec $chan] $uidd $::netname($sck)
    372 #						firellbind $sck cquit "-" "-" [ndadec $chan] $uidd $::netname($sck)
    373 						tnda set "userchan/$::netname($sck)/$uidd/$chan" 0
    374 					}
    375 
    376 					::ts6::snote $sck x [format "!! NETSPLIT: %s (%s) has quit due to netsplit (%s: %s)" [tnda get "nick/$::netname($sck)/$uidd"] $uidd [tnda get "servers/$::netname($sck)/[ndaenc $srv]/name"] [lindex $comd [expr {$one + 2}]]
    377 					tnda unset "login/$::netname($sck)/$uidd"
    378 					tnda unset "nick/$::netname($sck)/$uidd"
    379 					tnda set "oper/$::netname($sck)/$uidd" 0
    380 					tnda unset "ident/$::netname($sck)/$uidd"
    381 					tnda unset "rhost/$::netname($sck)/$uidd"
    382 					tnda unset "vhost/$::netname($sck)/$uidd"
    383 					tnda unset "rname/$::netname($sck)/$uidd"
    384 					tnda unset "ipaddr/$::netname($sck)/$uidd"
    385 					tnda set "metadata/$::netname($sck)/$uidd" [list]
    386 					tnda unset "certfps/$::netname($sck)/$uidd"
    387 					firellbind $sck quit "-" "-" $uidd $::netname($sck)
    388 				}
    389 			}
    390 		}
    391 
    392 		"005" - "105" {
    393 			foreach {tok} [lrange $comd 3 end] {
    394 				foreach {key val} [split $tok "="] {
    395 					if {$key == "PREFIX"} {
    396 						# We're in luck! Server advertises its PREFIX in VERSION reply to servers.
    397 						if {[tnda get "netinfo/$::netname($sck)/pfxissjoin"] == 1} {continue}
    398 						set v [string range $val 1 end]
    399 						set mod [split $v ")"]
    400 						set modechar [split [lindex $mod 1] {}]
    401 						set modepref [split [lindex $mod 0] {}]
    402 						foreach {c} $modechar {x} $modepref {
    403 							tnda set "netinfo/$::netname($sck)/prefix/$c" $x
    404 						}
    405 						foreach {x} $modechar {c} $modepref {
    406 							tnda set "netinfo/$::netname($sck)/pfxchar/$c" $x
    407 						}
    408 					} elseif {$key == "SJOIN"} {
    409 						# We're in luck! Server advertises its PREFIX in VERSION reply to servers.
    410 						tnda set "netinfo/$::netname($sck)/pfxissjoin" 1
    411 						set v [string range $val 1 end]
    412 						set mod [split $v ")"]
    413 						set modechar [split [lindex $mod 1] {}]
    414 						set modepref [split [lindex $mod 0] {}]
    415 						foreach {c} $modechar {x} $modepref {
    416 							tnda set "netinfo/$::netname($sck)/prefix/$c" $x
    417 						}
    418 						foreach {x} $modechar {c} $modepref {
    419 							tnda set "netinfo/$::netname($sck)/pfxchar/$c" $x
    420 						}
    421 					} elseif {$key == "CHANMODES"} {
    422 						set spt [split $val ","]
    423 						tnda set "netinfo/$::netname($sck)/chmban" [lindex $spt 0]
    424 						tnda set "netinfo/$::netname($sck)/chmparm" [format "%s%s" [lindex $spt 0] [lindex $spt 1]]
    425 						tnda set "netinfo/$::netname($sck)/chmpartparm" [lindex $spt 2]
    426 						tnda set "netinfo/$::netname($sck)/chmnoparm" [lindex $spt 3]
    427 					} else {
    428 						tnda set "netinfo/$::netname($sck)/[ndaenc $key]" $val
    429 					}
    430 				}
    431 			}
    432 		}
    433 
    434 		"PRIVMSG" {
    435 			if {[::ts6::validchan $sck [lindex $comd 2]]} {
    436 				set client chan
    437 				set words [split $payload " "]
    438 				set kword [lindex $words 0]
    439 				if {[string index $payload 0] == "\001"} {
    440 					set payload [string range $payload 1 end-1]
    441 					set words [split $payload " "]
    442 					set kword [lindex $words 0]
    443 					set payload [join [lrange $words 1 end] " "]
    444 					firellbind $sck ctcp - [string tolower $payload] [lindex $comd 0] [lindex $comd 2] $kword $payload
    445 				} {
    446 					set mpayload [join [lrange $words 1 end] " "]
    447 					firellbind $sck pub - [string tolower $payload] [lindex $comd 0] [lindex $comd 2] $mpayload
    448 					firellmbind $sck pubm - [string tolower $payload] [lindex $comd 0] [lindex $comd 2] $payload
    449 				}
    450 				#firellbind $sck notc "-" [string tolower [lindex [split $payload " "] 0]] [lindex $comd 2] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    451 				#firellmbind $sck pnotcm $client [string tolower [lindex [split $payload " "] 0]] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    452 #				firellbind $sck pubnotc-m "-" [string tolower [lindex [split $payload " "] 0]] [lindex $comd 2] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    453 				firellbind $sck "evnt" "-" "chanmsg" [lindex $comd 0] [lindex $comd 2] $payload
    454 			} {
    455 				set client [tnda get "intclient/$::netname($sck)/[lindex $comd 2]"]
    456 				set words [split $payload " "]
    457 				set kword [lindex $words 0]
    458 				if {[string index $payload 0] == "\001"} {
    459 					set payload [string range $payload 1 end-1]
    460 					set words [split $payload " "]
    461 					set kword [lindex $words 0]
    462 					set payload [join [lrange $words 1 end] " "]
    463 					firellbind $sck ctcp - [string tolower $kword] [lindex $comd 0] [% uid2nick [lindex $comd 2]] $kword $payload
    464 				} {
    465 					set mpayload [join [lrange $words 1 end] " "]
    466 					firellbind $sck msg $client [string tolower $kword] [lindex $comd 0] $mpayload
    467 					firellmbind $sck msgm $client [string tolower $payload] [lindex $comd 0] $payload
    468 				}
    469 				#firellbind $sck notc $client [string tolower [lindex [split $payload " "] 0]] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    470 				#firellmbind $sck notcm $client [string tolower [lindex [split $payload " "] 0]] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    471 				firellbind $sck "evnt" "-" "privmsg" [lindex $comd 0] [lindex $comd 2] $payload
    472 			}
    473 		}
    474 
    475 		"NOTICE" {
    476 			if {0==[tnda get "netinfo/$::netname($sck)/connected"]} {} {
    477 			if {[::ts6::validchan $sck [lindex $comd 2]]} {
    478 				set client chan
    479 				if {[string index $payload 0] == "\001"} {
    480 					set payload [string range $payload 1 end-1]
    481 					set words [split $payload " "]
    482 					set kword [lindex $words 0]
    483 					set payload [join [lrange $words 1 end] " "]
    484 					firellbind $sck ctcr - [string tolower $payload] [lindex $comd 0] [lindex $comd 2] $kword $payload
    485 				} {
    486 					firellmbind $sck notc - [string tolower $payload] [lindex $comd 0] [lindex $comd 2] $payload
    487 				}
    488 				#firellbind $sck notc "-" [string tolower [lindex [split $payload " "] 0]] [lindex $comd 2] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    489 				#firellmbind $sck pnotcm $client [string tolower [lindex [split $payload " "] 0]] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    490 #				firellbind $sck pubnotc-m "-" [string tolower [lindex [split $payload " "] 0]] [lindex $comd 2] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    491 				#firellbind $sck "evnt" "-" "channotc" [lindex $comd 0] [lindex $comd 2] $payload
    492 			} {
    493 				set client [tnda get "intclient/$::netname($sck)/[lindex $comd 2]"]
    494 				if {[string index $payload 0] == "\001"} {
    495 					set payload [string range $payload 1 end-1]
    496 					set words [split $payload " "]
    497 					set kword [lindex $words 0]
    498 					set payload [join [lrange $words 1 end] " "]
    499 					firellbind $sck ctcr - [string tolower $payload] [lindex $comd 0] [% uid2nick [lindex $comd 2]] $kword $payload
    500 				} {
    501 					firellmbind $sck notc - [string tolower $payload] [lindex $comd 0] [% uid2nick [lindex $comd 2]] $payload
    502 				}
    503 				#firellbind $sck notc $client [string tolower [lindex [split $payload " "] 0]] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    504 				#firellmbind $sck notcm $client [string tolower [lindex [split $payload " "] 0]] [lindex $comd 0] [join [lrange [split $payload " "] 1 end] " "]
    505 				#firellbind $sck "evnt" "-" "privnotc" [lindex $comd 0] [lindex $comd 2] $payload
    506 			}
    507 			}
    508 		}
    509 
    510 		"MODE" {
    511 			if {[lindex $comd 2] == [tnda get "nick/$::netname($sck)/[lindex $comd 0]"] || [lindex $comd 2] == [lindex $comd 0]} {
    512 				foreach {c} [split [lindex $comd 3] {}] {
    513 					switch -- $c {
    514 						"+" {set state 1}
    515 						"-" {set state 0}
    516 						"o" {tnda set "oper/$::netname($sck)/[lindex $comd 0]" $state}
    517 					}
    518 				}
    519 			}
    520 		}
    521 
    522 		"JOIN" {
    523 			set chan [string map {/ [} [::base64::encode [string tolower [lindex $comd 3]]]]
    524 			if {""==[tnda get "channels/$::netname($sck)/$chan/ts"]} {firellbind $sck create "-" "-" [lindex $comd 3] [lindex $comd 0] $::netname($sck)}
    525 #			firellbind $sck join "-" "-" [lindex $comd 3] [lindex $comd 0] $::netname($sck)
    526 			firellmbind $sck join - [format "%s %s!%s@%s" [lindex $comd 3] [% uid2nick [lindex $comd 0]] [% uid2ident [lindex $comd 0]] [% uid2host [lindex $comd 0]]] [lindex $comd 0] [lindex $comd 3]
    527 			tnda set "channels/$::netname($sck)/$chan/ts" [lindex $comd 2]
    528 			tnda set "userchan/$::netname($sck)/[lindex $comd 0]/$chan" 1
    529 		}
    530 
    531 		"BMASK" {
    532 			# always +, no ctr and no state
    533 			set adding [split $payload " "]
    534 			set chan [ndaenc [lindex $comd 3]]
    535 			if {[lindex $comd 2] > [tnda get "channels/$::netname($sck)/$chan/ts"]} {return} ;# send it packing.
    536 			set type [lindex $comd 4]
    537 			foreach {mask} $adding {
    538 				::ts6::checkop [lindex $comd 0] [lindex $comd 3] [format "%s%s" + $type] $mask
    539 				firellmbind $sck mode - [format "%s +%s" [lindex $comd 3] $type] [lindex $comd 0] [lindex $comd 3] "+$type" $mask
    540 				#+ $type [lindex $comd 0] [lindex $comd 3] $mask $::netname($sck)
    541 			}
    542 		}
    543 
    544 		"TMODE" {
    545 			set ctr 4
    546 			set state 1
    547 			foreach {c} [split [lindex $comd 4] {}] {
    548 				if {$c == "+"} {
    549 					set state 1
    550 				} elseif {$c == "-"} {
    551 					set state 0
    552 				} elseif {[string match [format "*%s*" $c] [tnda get "netinfo/$::netname($sck)/chmparm"]] || ($state&&[string match [format "*%s*" $c] [tnda get "netinfo/$::netname($sck)/chmpartparm"]])} {
    553 					[expr {$state?"::ts6::checkop":"::ts6::checkdeop"}] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] [lindex $comd [incr ctr]]
    554 					firellmbind $sck mode - [format "%s %s%s" [string tolower [lindex $comd 3]] [expr {$state ? "+" : "-"}] $c] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] [lindex $comd [incr ctr]]
    555 				} else {
    556 					[expr {$state?"::ts6::checkop":"::ts6::checkdeop"}] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] [lindex $comd [incr ctr]]
    557 					firellmbind $sck mode - [format "%s %s%s" [string tolower [lindex $comd 3]] [expr {$state ? "+" : "-"}] $c] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] ""
    558 #					firellmbind $sck mode "-" [expr {$state ? "+" : "-"}] $c [lindex $comd 0] [lindex $comd 3] ""
    559 				}
    560 			}
    561 		}
    562 
    563 		"SJOIN" {
    564 			set chan [ndaenc [lindex $comd 3]]
    565 			if {[string index [lindex $comd 4] 0] == "+"} {
    566 				set four 5
    567 				set ctr 4
    568 				if {[string match "*l*" [lindex $comd 4]]} {incr four}
    569 				if {[string match "*f*" [lindex $comd 4]]} {incr four}
    570 				if {[string match "*j*" [lindex $comd 4]]} {incr four}
    571 				if {[string match "*k*" [lindex $comd 4]]} {incr four}
    572 				foreach {c} [split [lindex $comd 4] {}] {
    573 					if {$c == "+"} {
    574 						set state 1
    575 					} elseif {$c == "-"} {
    576 						# _NOTREACHED
    577 						set state 0
    578 					} elseif {[string match [format "*%s*" $c] [tnda get "netinfo/$::netname($sck)/chmparm"]] || ($state&&[string match [format "*%s*" $c] [tnda get "netinfo/$::netname($sck)/chmpartparm"]])} {
    579 						putloglev d * "check(de)op with parameter"
    580 						[expr {$state?"::ts6::checkop":"::ts6::checkdeop"}] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] [lindex $comd [incr ctr]]
    581 						firellmbind $sck mode - [format "%s %s%s" [string tolower [lindex $comd 3]] [expr {$state ? "+" : "-"}] $c] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] [lindex $comd [incr ctr]]
    582 					} else {
    583 						putloglev d * "check(de)op without parameter"
    584 						[expr {$state?"::ts6::checkop":"::ts6::checkdeop"}] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] ""
    585 						firellmbind $sck mode - [format "%s %s%s" [string tolower [lindex $comd 3]] [expr {$state ? "+" : "-"}] $c] [lindex $comd 0] [lindex $comd 3] [format "%s%s" [expr {$state?"+":"-"}] $c] ""
    586 #						firellmbind $sck mode "-" [expr {$state ? "+" : "-"}] $c [lindex $comd 0] [lindex $comd 3] ""
    587 					}
    588 				}
    589 			} {
    590 				set four 4
    591 			}
    592 			tnda set "channels/$::netname($sck)/$chan/ts" [lindex $comd 2]
    593 			# XXX: some servers don't give their SJOIN prefixes in PREFIX.
    594 			# Solution? irca will, from the next release, support 005 portion "SJOIN=" formatted same as
    595 			# PREFIX.
    596 			# Also allow hardcoding.
    597 			foreach {nick} [split $payload " "] {
    598 				set un ""
    599 				set uo ""
    600 				set state uo
    601 				set un [string range $nick end-8 end]
    602 				set uo [string map [tnda get "netinfo/$::netname($sck)/prefix"] [string range $nick 0 end-9]]
    603 #				foreach {c} [split $nick {}] {
    604 #					if {[string is digit $c]} {set state un}
    605 #					if {$state == "uo"} {set c [tnda get "netinfo/$::netname($sck)/prefix/$c"] ; }
    606 #					if {"un"==$state} {append un $c}
    607 #					if {"uo"==$state} {append uo $c}
    608 #				}
    609 				putloglev j [ndadec $chan] [format "JOIN %s by nicknumber %s (nick %s, modes %s)" [ndadec $chan] $nick [tnda get "nick/$::netname($sck)/$un"] $uo]
    610 #				firellbind $sck join "-" "-" [lindex $comd 3] $un $::netname($sck)
    611 				firellmbind $sck join - [format "%s %s!%s@%s" [lindex $comd 3] [% uid2nick $un] [% uid2ident $un] [% uid2host $un]] $un [lindex $comd 3]
    612 				tnda set "userchan/$::netname($sck)/$un/$chan" 1
    613 				if {""!=$uo} {tnda set "channels/$::netname($sck)/$chan/status/$un" $uo
    614 					foreach {c} [split $uo {}] {
    615 						::ts6::checkop [lindex $comd 0] [lindex $comd 3] [format "%s%s" + $c] $un
    616 						firellmbind $sck mode - [format "%s +%s" [lindex $comd 3] $c] [lindex $comd 0] [lindex $comd 3] "+$c" $un
    617 		#				firellbind $sck mode "-" + $c $un [lindex $comd 3] $un $::netname($sck)
    618 					}
    619 				}
    620 			}
    621 
    622 		}
    623 
    624 		"PART" {
    625 			set un [lindex $comd 0]
    626 			firellmbind $sck part - [format "%s %s!%s@%s" [lindex $comd 2] [% uid2nick $un] [% uid2ident $un] [% uid2host $un]] $un [lindex $comd 2] [lindex $comd 3]
    627 			firellbind $sck part "-" "-" [lindex $comd 2] [lindex $comd 0] [lindex $comd 3]
    628 			set chan [string map {/ [} [::base64::encode [string tolower [lindex $comd 2]]]]
    629 			foreach {c} [split [tnda get "channels/$::netname($sck)/$chan/status/[lindex $comd 0]"] {}] {
    630 				::ts6::checkdeop [lindex $comd 0] [ndadec $chan] -$c [lindex $comd 0]
    631 			}
    632 			tnda unset "userchan/$::netname($sck)/[lindex $comd 0]/$chan"
    633 			tnda unset "channels/$::netname($sck)/$chan/status/[lindex $comd 0]"
    634 		}
    635 
    636 		"KICK" {
    637 			firellbind $sck kick "-" "-" [lindex $comd 2] [lindex $comd 3] [lindex $comd 4]
    638 			set chan [string map {/ [} [::base64::encode [string tolower [lindex $comd 2]]]]
    639 			foreach {c} [split [tnda get "channels/$::netname($sck)/$chan/status/[lindex $comd 3]"] {}] {
    640 				::ts6::checkdeop [lindex $comd 0] [ndadec $chan] -$c [lindex $comd 3]
    641 			}
    642 			tnda unset "userchan/$::netname($sck)/[lindex $comd 3]/$chan"
    643 			tnda unset "channels/$::netname($sck)/$chan/status/[lindex $comd 3]"
    644 		}
    645 
    646 		"NICK" {
    647 			firellmbind $sck nick "-" [format "%s %s" "*" [lindex $comd 2]] [lindex $comd 0] "*" [lindex $comd 2]
    648 			tnda set "nick/$::netname($sck)/[lindex $comd 0]" [lindex $comd 2]
    649 			tnda set "ts/$::netname($sck)/[lindex $comd 0]" [lindex $comd 3]
    650 		}
    651 
    652 		"EUID" {
    653 			set num 9
    654 			set ctr 1
    655 			set oper 0
    656 			set loggedin [lindex $comd 11]
    657 			set realhost [lindex $comd 10]
    658 			set modes [lindex $comd 5]
    659 		#	puts stdout $comd
    660 		#	puts stdout $modes
    661 			if {[string first "o" $modes] != -1} {set oper 1}
    662 			tnda set "login/$::netname($sck)/[lindex $comd $num]" ""
    663 			if {"*"!=$loggedin} {
    664 				tnda set "login/$::netname($sck)/[lindex $comd $num]" $loggedin
    665 			}
    666 			if {"*"!=$realhost} {
    667 				tnda set "rhost/$::netname($sck)/[lindex $comd $num]" $realhost
    668 			} {
    669 				tnda set "rhost/$::netname($sck)/[lindex $comd $num]" [lindex $comd 7]
    670 			}
    671 			tnda set "nick/$::netname($sck)/[lindex $comd $num]" [lindex $comd 2]
    672 			tnda set "oper/$::netname($sck)/[lindex $comd $num]" $oper
    673 			tnda set "ident/$::netname($sck)/[lindex $comd $num]" [lindex $comd 6]
    674 			tnda set "vhost/$::netname($sck)/[lindex $comd $num]" [lindex $comd 7]
    675 			tnda set "ipaddr/$::netname($sck)/[lindex $comd $num]" [lindex $comd 8]
    676 			tnda set "ts/$::netname($sck)/[lindex $comd $num]" [lindex $comd 4]
    677 			tnda set "rname/$::netname($sck)/[lindex $comd $num]" $payload
    678 			#putloglev j * [format "New user at %s %s %s!%s@%s (IP address %s, vhost %s) :%s" $::netname($sck) [lindex $comd $num] [lindex $comd 2] [lindex $comd 6] [tnda get "rhost/$::netname($sck)/[lindex $comd $num]"] [lindex $comd 8] [tnda get "vhost/$::netname($sck)/[lindex $comd $num]"] $payload]
    679 			firellbind $sck conn "-" "-" [lindex $comd $num]
    680 		}
    681 
    682 		"KLINE" {putloglev k * [format "KLINE: %s" $line]}
    683 		"BAN" {putloglev k * [format "BAN: %s" $line]}
    684 
    685 		"ENCAP" {
    686 			switch -nocase -- [lindex $comd 3] {
    687 				"SASL" {
    688 					# we have to support sasl messages, so...
    689 					firellbind $sck encap - "sasl" [lrange $comd 4 end]
    690 					#don't bother
    691 				}
    692 				"KLINE" {
    693 					putloglev k * [format "KLINE: %s" $line]
    694 				}
    695 				"SU" {
    696 					if {$payload == ""} {set payload [lindex $comd 5]}
    697 					tnda set "login/$::netname($sck)/[lindex $comd 4]" $payload
    698 					if {$payload == ""} {firellbind $sck logout "-" "-" [lindex $comd 4]} {firellbind $sck login "-" "-" [lindex $comd 4] $payload}
    699 				}
    700 				"CERTFP" {
    701 					tnda set "certfps/$::netname($sck)/[lindex $comd 0]" $payload
    702 					firellbind $sck encap "-" "certfp" [lindex $comd 0] $payload
    703 				}
    704 				"METADATA" {
    705 					switch -nocase -- [lindex $comd 4] {
    706 						"ADD" {
    707 							tnda set "metadata/$::netname($sck)/[lindex $comd 5]/[ndcenc [lindex $comd 6]]" $payload
    708 							firellbind $sck encap "-" "metadata.[string tolower [lindex $comd 6]]" [lindex $comd 5] $payload
    709 							firellbind $sck mark "-" [lindex $comd 6] [lindex $comd 5] $payload
    710 						}
    711 						"DELETE" {
    712 							tnda unset "metadata/$::netname($sck)/[lindex $comd 5]/[ndcenc $payload]"
    713 							firellbind $sck encap "-" "metadata.[string tolower $payload]" [lindex $comd 5] ""
    714 							firellbind $sck mark "-" $payload [lindex $comd 5] ""
    715 							# WARNING!!!! Pick ONE. The official scripts use MARK; you should too.
    716 						}
    717 					}
    718 				}
    719 			}
    720 		}
    721 
    722 		"TOPIC" {
    723 			firellbind $sck topic "-" "-" [lindex $comd 2] [join $payload " "]
    724 		}
    725 		"QUIT" {
    726 			if {![string is digit [string index [lindex $comd 0] 0]]} {
    727 				set ocomd [lrange $comd 1 end]
    728 				set on [lindex $comd 0]
    729 				set comd [list [::ts6::nick2uid $::netname($sck) $on] {*}$ocomd]
    730 				putloglev k * [format "Uh-oh, netsplit! %s -> %s has split" $on [::ts6::nick2uid $::netname($sck) $on]]
    731 			}
    732 			foreach {chan _} [tnda get "userchan/$::netname($sck)/[lindex $comd 0]"] {
    733 				putloglev d * "$chan -> [ndadec $chan]"
    734 				putloglev d * "[cdbase get channels $::netname($sck) $chan]"
    735 				foreach {c} [split [cdbase get channels $::netname($sck) $chan status [lindex $comd 0]] {}] {
    736 					::ts6::checkdeop [lindex $comd 0] [ndadec $chan] -$c [lindex $comd 0]
    737 					#puts stdout $::errorInfo
    738 				}
    739 				firellbind $sck quit "-" "-" [lindex $comd 0] [ndadec $chan] $::netname($sck)
    740 				tnda unset "userchan/$::netname($sck)/[lindex $comd 0]/$chan"
    741 				#tnda unset "channels/$::netname($sck)/$chan/status/[lindex $comd 0]"
    742 			}
    743 
    744 			tnda unset "login/$::netname($sck)/[lindex $comd 0]"
    745 			tnda unset "nick/$::netname($sck)/[lindex $comd 0]"
    746 			tnda unset "oper/$::netname($sck)/[lindex $comd 0]"
    747 			tnda unset "ident/$::netname($sck)/[lindex $comd 0]"
    748 			tnda unset "rhost/$::netname($sck)/[lindex $comd 0]"
    749 			tnda unset "vhost/$::netname($sck)/[lindex $comd 0]"
    750 			tnda unset "rname/$::netname($sck)/[lindex $comd 0]"
    751 			tnda unset "ipaddr/$::netname($sck)/[lindex $comd 0]"
    752 			tnda unset "metadata/$::netname($sck)/[lindex $comd 0]"
    753 			tnda unset "certfps/$::netname($sck)/[lindex $comd 0]"
    754 			firellbind $sck nquit - - [lindex $comd 0] $payload
    755 		}
    756 
    757 		"KILL" {
    758 			foreach {chan _} [tnda get "userchan/$::netname($sck)/[lindex $comd 2]"] {
    759 				firellbind $sck quit "-" "-" [lindex $comd 2] [ndadec $chan] $payload
    760 				tnda set "userchan/$::netname($sck)/[lindex $comd 2]/$chan" 0
    761 			}
    762 			tnda unset "login/$::netname($sck)/[lindex $comd 2]"
    763 			tnda unset "nick/$::netname($sck)/[lindex $comd 2]"
    764 			tnda unset "oper/$::netname($sck)/[lindex $comd 2]"
    765 			tnda unset "ident/$::netname($sck)/[lindex $comd 2]"
    766 			tnda unset "ipaddr/$::netname($sck)/[lindex $comd 2]"
    767 			tnda unset "rhost/$::netname($sck)/[lindex $comd 2]"
    768 			tnda unset "vhost/$::netname($sck)/[lindex $comd 2]"
    769 			tnda unset "rname/$::netname($sck)/[lindex $comd 2]"
    770 			tnda unset "metadata/$::netname($sck)/[lindex $comd 2]"
    771 			tnda unset "certfps/$::netname($sck)/[lindex $comd 2]"
    772 			#firellbind $sck nquit - - [lindex $comd 2] $payload
    773 			firellbind $sck nkill - - [lindex $comd 0] [lindex $comd 2] $payload
    774 		}
    775 
    776 		"ERROR" {
    777 			putloglev s * "Recv'd an ERROR $payload from $::netname($sck)"
    778 		}
    779 
    780 		"WHOIS" {
    781 			# Usually but not always for a local client.
    782 			set num [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
    783 			append num [::ts6::b64e $::sid($sck)]
    784 			set targ [::ts6::nick2uid $::netname($sck) $payload]
    785 			if {[tnda get "nick/$::netname($sck)/$targ"] == ""} {
    786 				putl $sck [format ":%s 401 %s %s :No such user." $num [lindex $comd 0] $payload]
    787 			} else {
    788 				putl $sck [format ":%s 311 %s %s %s %s * :%s" $num [lindex $comd 0] [tnda get "nick/$::netname($sck)/$targ"] [tnda get "ident/$::netname($sck)/$targ"] [tnda get "vhost/$::netname($sck)/$targ"] [tnda get "rname/$::netname($sck)/$targ"]]
    789 			}
    790 			putl $sck [format ":%s 318 %s %s :End of /WHOIS list." $num [lindex $comd 0] $payload]
    791 		}
    792 
    793 		"CAPAB" {
    794 			tnda set "netinfo/$::netname($sck)/euid" 0
    795 			foreach {cw} [split $payload " "] {
    796 				if {$cw == "EUID"} {tnda set "netinfo/$::netname($sck)/euid" 1}
    797 			}
    798 			tnda set "netinfo/$::netname($sck)/connected" 1
    799 		}
    800 
    801 		"PING" {
    802 			set num [string repeat "0" [expr {3-[string length [::ts6::b64e $::sid($sck)]]}]]
    803 			append num [::ts6::b64e $::sid($sck)]
    804 			if {[lindex $comd 3]==""} {set pong [lindex $comd 0]} {set pong [lindex $comd 3]}
    805 			putl $sck [format ":%s PONG %s %s" $num $pong [lindex $comd 2]]
    806 		}
    807 	}
    808 	} erreur erropts]
    809 	if {$erreno != 0} {putloglev d * [format "error code %s, output %s while processing %s" $erreno $erreur [join $comd " "]]
    810 		foreach {li} [split [dict get $erropts -errorinfo] "\r\n"] {
    811 			putloglev d * [format "error info: %s" $li]
    812 		}
    813 	}
    814 }
    815 
    816 # irrelevant parameters should simply be ignored.
    817 proc ::ts6::login {sck {osid "42"} {password "link"} {servname "net"} {servername services.invalid} {cfg {}}} {
    818 	set num [string repeat "0" [expr {3-[string length [::ts6::b64e $osid]]}]]
    819 	append num [::ts6::b64e $osid]
    820 	global netname sid sock nettype socksid snames
    821     lassign [list 1 "Unconfigured"] useeuid gecos
    822 	dictassign $cfg euid useeuid gecos gecos
    823 	set snames($sck) $servername
    824 	set netname($sck) $servname
    825 	set nettype($servname) ts6
    826 	set sock($servname) $sck
    827 	set sid($sck) $osid
    828 	set sid($servname) $osid
    829 	tnda set "netinfo/$::netname($sck)/connected" 0
    830 	tnda set "netinfo/$::netname($sck)/euid" 0
    831 	#if {$halfops == ""} {tnda set "pfx/halfop" v} {tnda set "pfx/halfop" $halfops}
    832 	#if {![info exists ::ts6(ownermode)]} {tnda set "pfx/owner" o} {tnda set "pfx/owner" $ownermode)}
    833 	#if {![info exists ::ts6(protectmode)]} {tnda set "pfx/protect" o} {tnda set "pfx/protect" $protectmode}
    834 	if {$useeuid == ""} {tnda set "netinfo/$::netname($sck)/euid" 1} {tnda set "netinfo/$::netname($sck)/euid" $useeuid}
    835 
    836 	putl $sck [format "PASS %s TS 6 :%s" $password $num]
    837 	putl $sck "CAPAB :UNKLN BAN KLN RSFNC EUID ENCAP IE EX CLUSTER EOPMOD SVS SERVICES QS"
    838 	putl $sck [format "SERVER %s 1 :%s" $servername $gecos]
    839 	putl $sck [format "SVINFO 6 6 0 :%s" [clock format [clock seconds] -format %s]]
    840 	putl $sck [format ":%s VERSION" $num]
    841 #	llbind $sck mode - "* +*" ::ts6::checkop
    842 #	llbind $sck mode - "* -*" ::ts6::checkdeop
    843 
    844 	chan event $sck readable [list ::ts6::irc-main $sck]
    845 }
    846 
    847 #source services.conf
    848 
    849 proc ::ts6::nick2uid {sck nick} {
    850 	foreach {u n} [tnda get "nick/$::netname($sck)"] {
    851 		if {[string tolower $n] == [string tolower $nick]} {return $u}
    852 	}
    853 	return ""
    854 }
    855 proc ::ts6::intclient2uid {sck nick} {
    856 	foreach {u n} [tnda get "intclient/$::netname($sck)"] {
    857 		if {[string tolower $n] == [string tolower $nick]} {return $u}
    858 	}
    859 	return ""
    860 }
    861 proc ::ts6::uid2nick {sck u} {
    862 	return [tnda get "nick/$::netname($sck)/$u"]
    863 }
    864 proc ::ts6::uid2rname {sck u} {
    865 	return [tnda get "rname/$::netname($sck)/$u"]
    866 }
    867 proc ::ts6::uid2rhost {sck u} {
    868 	return [tnda get "rhost/$::netname($sck)/$u"]
    869 }
    870 proc ::ts6::uid2host {sck u} {
    871 	return [tnda get "vhost/$::netname($sck)/$u"]
    872 }
    873 proc ::ts6::uid2ident {sck u} {
    874 	return [tnda get "ident/$::netname($sck)/$u"]
    875 }
    876 proc ::ts6::nick2host {sck nick} {
    877 	return [tnda get "vhost/$::netname($sck)/[nick2uid $netname $nick]"]
    878 }
    879 proc ::ts6::nick2ident {sck nick} {
    880 	return [tnda get "ident/$::netname($sck)/[nick2uid $netname $nick]"]
    881 }
    882 proc ::ts6::nick2rhost {sck nick} {
    883 	return [tnda get "rhost/$::netname($sck)/[nick2uid $netname $nick]"]
    884 }
    885 proc ::ts6::nick2ipaddr {sck nick} {
    886 	return [tnda get "ipaddr/$::netname($sck)/[nick2uid $netname $nick]"]
    887 }
    888 proc ::ts6::getts {sck chan} {
    889 	return [tnda get "channels/$::netname($sck)/[ndaenc $chan]/ts"]
    890 }
    891 proc ::ts6::getpfx {sck chan nick} {
    892 	return [tnda get "channels/$::netname($sck)/[ndaenc $chan]/status/[::ts6::nick2uid $netname $nick]"]
    893 }
    894 proc ::ts6::getupfx {sck chan u} {
    895 	return [tnda get "channels/$::netname($sck)/[ndaenc $chan]/status/$u"]
    896 }
    897 proc ::ts6::getpfxchars {sck modes} {
    898 	set o ""
    899 	foreach {c} [split $modes {}] {
    900 		append o [nda get "netinfo/$::netname($sck)/prefix/$c"]
    901 	}
    902 	return $o
    903 }
    904 proc ::ts6::getmetadata {sck nick metadatum} {
    905 	return [tnda get "metadata/$::netname($sck)/[::ts6::nick2uid $netname $nick]/[ndcenc $metadatum]"]
    906 }
    907 proc ::ts6::getcertfp {sck nick} {
    908 	return [tnda get "certfps/$::netname($sck)/[::ts6::nick2uid $netname $nick]"]
    909 }
    910 
    911 proc ::ts6::checkop {f t m p} {
    912 	set n [curctx net]
    913 	set mc [string index $m end]
    914 	#puts stdout [format ":%s MODE %s %s %s" $f $t $m $p]
    915 	putloglev d * "up $mc $f $t $p $n"
    916 	if {[tnda get "netinfo/$n/pfxchar/$mc"]==""} {::ts6::handlemode $f $t $m $p;return}
    917 	set chan [string map {/ [} [::base64::encode [string tolower $t]]]
    918 	tnda set "channels/$n/$chan/status/$p" [format {%s%s} [string map [list $mc ""] [tnda get "channels/$n/$chan/status/$p"]] $mc]
    919 	#puts stdout [format "Now, the state machine for $t looks like:"]
    920 	#puts stdout [tnda get "channels/$n/$chan"]
    921 }
    922 
    923 proc ::ts6::checkdeop {f t m p} {
    924 	set n [curctx net]
    925 	set mc [string index $m end]
    926 #	puts stdout [format ":%s MODE %s %s %s" $f $t $m $p]
    927 	putloglev d * "down $m ($mc) $f $t $p $n"
    928 	if {[tnda get "netinfo/$n/pfxchar/$mc"]==""} {::ts6::handlemode $f $t $m $p;return}
    929 	set chan [string map {/ [} [::base64::encode [string tolower $t]]]
    930 	if {[set thestatus [string map [list $mc ""] [tnda get "channels/$n/$chan/status/$p"]]] != ""} {
    931 		tnda set "channels/$n/$chan/status/$p" $thestatus
    932 	} else {
    933 		tnda unset "channels/$n/$chan/status/$p"
    934 	}
    935 	#puts stdout [format "Now, the state machine for $t looks like:"]
    936 	#puts stdout [tnda get "channels/$n/$chan"]
    937 }
    938 
    939 proc ::ts6::handlemode {from t mode parm} {
    940 	set n [curctx net]
    941 	set chan [string map {/ [} [::base64::encode [string tolower $t]]]
    942 #	puts stdout [format ":%s MODE %s %s %s" $from $t $mode $parm]
    943 	if {[string index $mode 0] == "+"} {set state 1} {set state 0}
    944 	set mc [string index $mode 1]
    945 	if {$state} {
    946 		if {[lsearch -exact [split [tnda get "netinfo/$n/chmban"] {}] $mc] == -1} {tnda set "channels/$n/$chan/mode" [format {%s%s} [string map [list $mc ""] [tnda get "channels/$n/$chan/mode"]] $mc]}
    947 		if {$parm != ""} {
    948 			if {[lsearch -exact [split [tnda get "netinfo/$n/chmban"] {}] $mc] != -1} {
    949 				set ban [tnda get "channels/$n/$chan/modes/[ndcenc $mc]"]
    950 				lappend ban $parm
    951 				tnda set "channels/$n/$chan/modes/[ndcenc $mc]" $ban
    952 			} {
    953 				tnda set "channels/$n/$chan/modes/[ndcenc $mc]" $parm
    954 			}
    955 		}
    956 	} {
    957 		if {[lsearch -exact [split [tnda get "netinfo/$n/chmban"] {}] $mc] == -1} {tnda set "channels/$n/$chan/mode" [string map [list $mc ""] [tnda get "channels/$n/$chan/mode"]]}
    958 		if {$parm != "" || [lsearch -exact [split [tnda get "netinfo/$n/chmpartparm"] {}] $mc] != -1} {
    959 			if {[lsearch -exact [split [tnda get "netinfo/$n/chmban"] {}] $mc] != -1} {
    960 				set ban [tnda get "channels/$n/$chan/modes/[ndcenc $mc]"]
    961 				lappend ban $parm
    962 				tnda set "channels/$n/$chan/modes/[ndcenc $mc]" $ban
    963 			} {
    964 				if {$parm == ""} {
    965 					tnda unset "channels/$n/$chan/modes/[ndcenc $mc]"
    966 				} {
    967 					tnda set "channels/$n/$chan/modes/[ndcenc $mc]" $parm
    968 				}
    969 			}
    970 		}
    971 	}
    972 }
    973 
    974 proc ::ts6::putnow {sck intclient msg} {
    975 	if {$intclient != ""} {
    976 		set nick [% intclient2uid $intclient]
    977 	} {
    978 		set nick $sid($sck)
    979 	}
    980 	putl $sck [format ":%s %s" $nick $msg]
    981 }
    982 proc ::ts6::uid2intclient {sck u} {
    983 	return [tnda get "intclient/$::netname($sck)/$u"]
    984 }
    985 
    986 proc ::ts6::getfreeuid {sck} {
    987 set work 1
    988 set cns [list]
    989 foreach {_ cnum} [tnda get "intclient/$::netname($sck)"] {lappend cns $cnum}
    990 while {0!=$work} {set num [expr {[rand 30000]+10000}];if {[lsearch -exact $cns $num]==-1} {set work 0}}
    991 return $num
    992 }
    993 
    994 namespace export *
    995 namespace ensemble create
    996 }
    997 
    998 #ts6 login $::sock