http://i2p2.de%|%I2P%|% [Zarutian]: An start of an http://www.i2p2.de/samv2.html%|%SAMv2%|% library for Tcl ====== # I, Zarutian, hereby put this code into the International public domain. package require Tcl 8.5 package provide i2p/sam2 0.1 namespace eval i2p {} namespace eval i2p::sam2 { variable defaults { bridge {host localhost port none} style STREAM stream {limit none}} variable session_counter 0 variable sessions proc new_session {params} { variable session_counter set i session[incr session_counter] variable defaults if {![dict exists $params style]} { dict set params style [dict get $defaults style] } if {![dict exists $params bridge]} { dict set params bridge [dict get $defaults bridge] } if {![dict exists $params bridge host]} { dict set params bridge host [dict get $defaults bridge host] } if {![dict exists $params bridge port]} { dict set params bridge port [dict get $defaults bridge port] } if {![dict exists $params stream]} { dict set params stream [dict get $defaults stream] } if {![dict exists $params condition_callback} { error "an condition_callback must be provided" } if {![dict exists $params my_name]} { error "my_name must be provided" } variable sessions dict set sessions $i condition_callback [dict get $params condition_callback] dict set sessions $i my_name [dict get $params my_name] dict set sessions $i stream_counter 0 dict set sessions $i socket [set sock [socket -async [dict get $params bridge host] [dict get $params bridge port]]] fconfigure $sock -buffering none -translation binary -encoding binary -blocking no fileevent $sock readable [list i2p::sam2::raw_recive_stage1 $i] raw_send $sock "HELLO VERSION MIN=2.0 MAX=2.0\n" return $i } proc raw_recive_stage1 {session_id} { variable sessions set sock [dict get $sessions $session_id socket] if {[eof $sock]} { close $sock after 0 [list {*}[dict get $sessions $session_id condition_callback] bridge connection closed $session_id ] return } dict append sessions $session_id raw_buffer [set buffer [read $sock]] if {[string first $buffer "\n"] != -1} { if {![string equal [string range $buffer 0 12] "HELLO REPLY "]} { after 0 [list {*}[dict get $sessions $session_id conditional_callback] bridge connection noHelloReply $session_id] return } set line [split $buffer "\n"] set done 0 foreach pair [split [string range $line 13 end] " "] { lassign [split $pair "="] key value if {[string equal $key "RESULT"]} { incr done; set RESULT $value } if {[string equal $key "VERSION"]} { incr done; set VERSION $value } } if {$done != 2} { after 0 [list {*}[dict get $sessions $session_id conditional_callback] bridge connection missingHelloReplyParams $session_id] return } if {[string equal $RESULT "NOVERSION]} { after 0 [list {*}[dict get $sessions $session_id conditional_callback] bridge connection noversion $session_id] return } if {![string equal $RESULT "OK"]} { after 0 [list {*}[dict get $sessions $session_id conditional_callback] bridge connection resultNotOk $session_id] return } if {![string equal $VERSION "2.0"]} { after 0 [list {*}[dict get $sessions $session_id conditional_callback] bridge connection versionNot2.0 $session_id] return } #--- dict set sessions $session_id raw_buffer [string range [dict get $sessions $session_id raw_buffer] \ [expr {[string first $buffer "\n"] + 1} end] set p "SESSION CREATE STYLE=" append p [string toupper [dict get $sessions $session_id style]] append p " DESTINATION=" append p [dict get $sessions $session_id my_name] append p "\n" raw_send $sock $p fileevent $sock readable [list i2p::sam2::raw_recive_stage2 $session_id] } } proc raw_recive_stage2 {session_id} { variable sessions set sock [dict get $sessions $session_id socket] if {[eof $sock]} { close $sock after 0 [list {*}[dict get $sessions $session_id condition_callback] bridge connection closed $session_id ] return } dict append sessions $session_id raw_buffer [set buffer [read $sock]] if {[string first $buffer "\n"] != -1} { if {![string equal "SESSION STATUS " [string range $buffer 0 14]]} { after 0 [list {*}[dict get $sessions $session_id conditional_callback] bridge connection noSessionStatus $session_id] return } set line [split $buffer "\n"] set continue? no foreach pair [split [string range $buffer 15 end] " "] { lassign [split $pair "="] key value if {[string equal $key "RESULT"]} { switch -exact -- $value { "OK" {} "DUPLICATED_DEST" {} "I2P_ERROR" {} "INVALID_KEY" {} } } elseif {[string equal $key "DESTINATION"]} { } elseif {[string equal $key "MESSAGE"]} {} } if {$continue?} { #--- dict set sessions $session_id raw_buffer [string range [dict get $sessions $session_id raw_buffer] \ [expr {[string first $buffer "\n"] + 1} end] fileevent $sock readable [list i2p::sam2::raw_recive_stage3 $session_id] } } } proc raw_recive_stage3 {session_id} {} proc raw_send {socket data} { catch { puts -nonewline $socket $data; flush $socket } } proc new_stream {session_id destination callback} { variable sessions set sock [dict get $sessions $session_id socket] set i [expr {[dict get $sessions $session_id stream_counter] + 1}] dict set sessions $session_id stream_counter $i set i [set session_id]::stream[set i] return $i } } ======