# Copyright (c) 2020 Marek Küthe # This program is free software. It comes without any warranty, to # the extent permitted by applicable law. You can redistribute it # and/or modify it under the terms of the Do What The Fuck You Want # To Public License, Version 2, as published by Sam Hocevar. See # http://www.wtfpl.net/ for more details. #source "samapi.tcl" namespace eval samapi { # default sam settings variable defaultHost 127.0.0.1 variable defaultPort 7656 # example: samapi::create_samcmd HELLO VERSION # example: samapi::create_samcmd NAMING LOOKUP name bandura.i2p proc create_samcmd {fircmd {seccmd ""} args} { set cmd [string toupper $fircmd] if {$seccmd != ""} { append cmd " " [string toupper $seccmd] } foreach {key value} $args { append cmd " [string toupper $key]=\"$value\"" } return $cmd } # return a list: [main command, second command, key1, val1, key2, val2, keyn, valn] proc parse_samcmd {cmd} { set cmdlen [string length $cmd] set firpos [string first " " $cmd] set fircmd [string range $cmd 0 [expr $firpos - 1]] set secpos [string first " " $cmd [expr $firpos + 1]] if {$secpos == -1} { # case that no arguments are given - # as a result only one space exists set secpos $cmdlen } set seccmd [string range $cmd [expr $firpos + 1] [expr $secpos - 1]] set argus [list $fircmd $seccmd] for {set pos [expr $secpos + 2]} {$pos != [expr $cmdlen + 2]} {set pos [expr $nextpos + 2]} { set equelsign [string first "=" $cmd $pos] set signAfterEs [string index $cmd [expr $equelsign + 1]] if {$signAfterEs == "\""} { # case: argument is in " set nextpos [expr [string first "\"" $cmd [expr $equelsign + 2]] + 1] } else { # case: argument is not in " set nextpos [string first " " $cmd $pos] } if {$nextpos == -1} { set nextpos $cmdlen } lappend argus [string range $cmd [expr $pos - 1] [expr $equelsign - 1]] [string trim [string range $cmd [expr $equelsign + 1] [expr $nextpos - 1]] "\""] } return $argus } } namespace eval sam { # argument 1: sam socket # return: { result status e. g. OK, sam version } proc hello_version { socket } { puts $socket [samapi::create_samcmd HELLO VERSION] flush $socket set status [gets $socket res] if {$status == -1} { error "Cannot read data from the socket." } set res [samapi::parse_samcmd $res] array set resary [lrange $res 2 [llength $res]] return [list $resary(RESULT) $resary(VERSION)] } # argument 1: sam socket # argument 2: Name of the address to be resolved # return: { result status e. g. OK, # Name of the address that was resolved, # The base64 of the resolved address} proc lookup { socket name } { puts $socket [samapi::create_samcmd naming lookup name $name] flush $socket set status [gets $socket res] if {$status == -1} { error "Cannot read data from the socket." } set res [samapi::parse_samcmd $res] array set resary [lrange $res 2 [llength $res]] if [info exists resary(VALUE)] { return [list $resary(RESULT) $resary(NAME) $resary(VALUE)] } else { return [list $resary(RESULT) $resary(NAME)] } } # argument 1: sam socket # result: Sends a QUIT message to the SAM socket, # but does not evaluate it, and then closes the socket. proc quit { socket } { puts $socket "QUIT" flush $socket close $socket } } if { $argc == 0 } { error "Usage: tclsh $argv0 \[NAME\]" } set name [lindex $argv 0] puts "Creating sam session ..." flush stdout set ssock [socket $::samapi::defaultHost $::samapi::defaultPort] puts "Sending hello version message ..." flush stdout set hello_version [sam::hello_version $ssock] if {[lindex $hello_version 0] != "OK"} { error "Can not open sam session - [lindex $hello_version 0]" } puts "SAM Version: [lindex $hello_version 1]" puts "Looking up $name ..." flush stdout set res [sam::lookup $ssock $name] if {[lindex $res 0] != "OK"} { puts "Lookup failed : [lindex $res 0]" } else { puts "Lookup successful!" puts "[lindex $res 1]: [lindex $res 2]" } puts "Close sam session ..." flush stdout sam::quit $ssock