# $Id: dtcp.tcl 1209 2007-08-31 11:52:26Z sergei $

namespace eval ::dtcp {}
namespace eval ::dtcp::active {}
namespace eval ::dtcp::passive {}

proc ::dtcp::active::connect {connid jid key} {
    variable connection

    set_status [::msgcat::mc "Opening DTCP active connection"]

    jlib::send_iq set \
	[jlib::wrapper:createtag query \
	     -vars {xmlns jabber:iq:dtcp} \
	     -subtags [list \
			   [jlib::wrapper:createtag key \
				-chdata $key] \
			   [jlib::wrapper:createtag comment \
				-subtags [list [jlib::wrapper:createtag key \
						    -chdata $key]]]]] \
	-to $jid \
	-command [list ::dtcp::active::recv_connect_response $connid $jid $key] \
	-connection $connid
	 
    vwait [namespace current]::connection(status,$key)
}

proc ::dtcp::active::recv_connect_response {connid jid key res child} {
    variable connection

    if {$res != "OK"} {
	# TODO
	set connection(status,$key) 0
	return
    }

    jlib::wrapper:splitxml $child tag vars isempty chdata children

    if {$tag == "query"} {
	set hosts {}
	foreach item $children {
	    jlib::wrapper:splitxml $item tag1 vars1 isempty1 chdata1 children1
	    switch -- $tag1 {
		verify {
		    set verify $chdata1
		    debugmsg jidlink "VERIFY: $verify"
		}
		host {
		    lappend hosts [list $chdata1 \
				       [jlib::wrapper:getattr $vars1 port]]
		}
	    }
	}

	if {[info exists verify]} {
	    variable verify_key
	    variable key_verify

	    set verify_key($verify) $key
	    set key_verify($key) $verify

	    set connection(connid,$key) $connid
	    set connection(jid,$key) $jid

	    foreach host $hosts {
		lassign $host addr port
		debugmsg jidlink "CONNECTING TO $addr:$port..."
		if {[catch {set sock [socket $addr $port]}]} continue
		debugmsg jidlink "CONNECTED"
		fconfigure $sock -encoding binary
		set connection(sock,$key) $sock
		puts $sock "key:$key"
		flush $sock

		fileevent $sock readable \
		    [list ::dtcp::active::wait_for_verify $key $sock]
		return
	    }
	    
	    debugmsg jidlink "FAILED"

	    set connection(status,$key) 0
	    return
	} else {
	    # TODO
	}
    } else {
	# TODO
    }
    set connection(status,$key) 0
}

proc ::dtcp::active::sock_connect {key hosts} {
    variable connection
    variable verify_key
    variable key_verify


    foreach host $hosts {
	lassign $host addr port
	debugmsg jidlink "CONNECTING TO $addr:$port..."
	if {[catch {set sock [socket $addr $port]}]} continue
	debugmsg jidlink "CONNECTED"
	fconfigure $sock -encoding binary
	set connection(sock,$key) $sock
	puts $sock "key:$key"
	flush $sock
	
	fileevent $sock readable \
	    [list ::dtcp::active::wait_for_verify $key $sock]
	return
    }

    debugmsg jidlink "FAILED"
    
    set connection(status,$key) 0

}

proc ::dtcp::active::send_data {key data} {
    variable connection
    variable key_stream

    puts -nonewline $connection(sock,$key) $data
    flush $connection(sock,$key)

    return 1
}

proc ::dtcp::active::close {key} {
    variable connection
    variable key_stream

    ::close $connection(sock,$key)
}


proc ::dtcp::active::wait_for_verify {key chan} {
    variable connection
    variable key_verify

    set s [gets $chan]
    debugmsg jidlink "WFV: [list $s]"

    if {[crange $s 0 6] == "verify:"} {
	set verify [crange $s 7 end]
	if {$verify == $key_verify($key)} {
	    fconfigure $chan -translation binary -blocking no
	    fileevent $chan readable \
		[list ::dtcp::readable $key $chan]
	    set connection(status,$key) 1
	    return
	}
    }
    ::close $chan
    jidlink::closed $key
}

jidlink::register_transport dtcp-active dtcp-passive 25 \
    ::dtcp::active::connect ::dtcp::active::send_data ::dtcp::active::close

proc ::dtcp::passive::connect {connid jid key} {
    variable connection

    set_status [::msgcat::mc "Opening DTCP passive connection"]

    set servsock [socket -server [list ::dtcp::passive::accept $key] 0]
    lassign [fconfigure $servsock -sockname] addr hostname port
    set ip [lindex [fconfigure $jlib::lib($connid,sck) -sockname] 0]

    jlib::send_iq set \
	[jlib::wrapper:createtag query \
	     -vars {xmlns jabber:iq:dtcp} \
	     -subtags [list \
			   [jlib::wrapper:createtag key \
				-chdata $key] \
			   [jlib::wrapper:createtag host \
				-vars [list port $port] \
				-chdata $ip] \
			   [jlib::wrapper:createtag comment \
				-subtags [list [jlib::wrapper:createtag key \
						    -chdata $key]]]]] \
	-to $jid \
	-command [list ::dtcp::passive::recv_connect_response $connid $jid $key] \
	-connection $connid
	 
    vwait [namespace current]::connection(status,$key)
}

proc ::dtcp::passive::recv_connect_response {connid jid key res child} {
    variable connection

    if {$res != "OK"} {
	# TODO
	set connection(status,$key) 0
	return
    }

    jlib::wrapper:splitxml $child tag vars isempty chdata children

    if {$tag == "query"} {
	set hosts {}
	foreach item $children {
	    jlib::wrapper:splitxml $item tag1 vars1 isempty1 chdata1 children1
	    switch -- $tag1 {
		verify {
		    set verify $chdata1
		    debugmsg jidlink "VERIFY: $verify"
		}
		host {
		    lappend hosts [list $chdata1 \
				       [jlib::wrapper:getattr $vars port]]
		}
	    }
	}

	if {[info exists verify]} {
	    variable verify_key
	    variable key_verify

	    set verify_key($verify) $key
	    set key_verify($key) $verify

	    set connection(connid,$key) $connid
	    set connection(jid,$key) $jid

	    #set connection(status,$key) 1
	    return
	} else {
	    # TODO
	}
    } else {
	# TODO
    }
    set connection(status,$key) 0
}

proc ::dtcp::passive::send_data {key data} {
    variable connection
    variable key_stream

    puts -nonewline $connection(sock,$key) $data
    flush $connection(sock,$key)

    return 1
}

proc ::dtcp::passive::close {key} {
    variable connection
    variable key_stream

    ::close $connection(sock,$key)
}

proc ::dtcp::passive::accept {key chan addr port} {
    variable connection
    variable key_verify

    debugmsg jidlink "CONNECT FROM $addr:$port"

    set connection(sock,$key) $chan

    fileevent $chan readable \
	[list ::dtcp::passive::wait_for_key $key $chan]
}

proc ::dtcp::passive::wait_for_key {key chan} {
    variable connection
    variable key_verify

    set s [gets $chan]

    if {[crange $s 0 3] == "key:"} {
	set key2 [crange $s 4 end]
	if {$key == $key2} {
	    debugmsg jidlink [array get key_verify]
	    puts $chan "verify:$key_verify($key)"
	    flush $chan
	    fconfigure $chan -translation binary -blocking no
	    fileevent $chan readable \
		[list ::dtcp::readable $key $chan]
	    set connection(status,$key) 1
	    return
	}
    }
    puts $chan error
    flush $chan
}

proc ::dtcp::readable {key chan} {
    if {![eof $chan]} {
	set buf [read $chan 4096]
	jidlink::recv_data $key $buf
    } else {
	fileevent $chan readable {}
	jidlink::closed $key
    }
}

proc ::dtcp::iq_set_handler {connid from lang child} {
    jlib::wrapper:splitxml $child tag vars isempty chdata children

    if {$tag == "query"} {
	set hosts {}
	foreach item $children {
	    jlib::wrapper:splitxml $item tag1 vars1 isempty1 chdata1 children1
	    switch -- $tag1 {
		comment {
		    foreach item1 $children1 {
			jlib::wrapper:splitxml $item1 tag2 vars2 isempty2 \
			    chdata2 children2
			if {$tag2 == "key"} {
			    set key $chdata2
			    debugmsg jidlink "KEY: $key"
			}
		    }
		}
		key {
		    set key $chdata1
		    debugmsg jidlink "KEY: $key"
		}
		host {
		    lappend hosts [list $chdata1 \
				       [jlib::wrapper:getattr $vars1 port]]
		}
	    }
	}

	if {[info exists key]} {
	    set verify [rand 1000000000]

	    if {$hosts == {}} {
		variable passive::verify_key
		variable passive::key_verify
		set passive::verify_key($verify) $key
		set passive::key_verify($key) $verify

		set servsock \
		    [socket -server \
			 [list ::dtcp::passive::accept $key] 0]
		lassign [fconfigure $servsock -sockname] addr hostname port
		set ip [lindex [fconfigure $jlib::lib($connid,sck) -sockname] 0]

		
		set res [jlib::wrapper:createtag query \
			     -vars {xmlns jabber:iq:dtcp} \
			     -subtags [list \
					   [jlib::wrapper:createtag verify \
						-chdata $verify] \
					   [jlib::wrapper:createtag host \
						-vars [list port $port] \
						-chdata $ip]]]
	    } else {
		variable active::verify_key
		variable active::key_verify
		set active::verify_key($verify) $key
		set active::key_verify($key) $verify

		debugmsg jidlink [list $hosts]
		after idle [list ::dtcp::active::sock_connect $key $hosts]
		set res [jlib::wrapper:createtag query \
			     -vars {xmlns jabber:iq:dtcp} \
			     -subtags [list [jlib::wrapper:createtag verify \
						 -chdata $verify]]]
	    }
	    return [list result $res]
	} else {
	    # TODO
	}
    } else {
	# TODO
    }
}

iq::register_handler set query jabber:iq:dtcp ::dtcp::iq_set_handler

jidlink::register_transport dtcp-passive dtcp-active 50 \
    ::dtcp::passive::connect ::dtcp::passive::send_data ::dtcp::passive::close

