# lib/client.tcl
#
# Client code.
#
# Copyright (C) 2003, 2004
# Andy Goth <unununium@openverse.com>
#
# This code is available under the GNU General Public License; see COPYING.

# Client mode.  This process was started by the user on the source host.  It's
# responsible for starting the --remote untunnel, which in turn starts the
# --server untunnel.  Next the client connects to the --server untunnel.  Then
# it starts server sockets for each requested port forward.
proc main {} {
    global tunnel_host tunnels rsh_cmd untunnel_cmd argv clients

    # Scan command line.
    set tunnel_host [lindex $argv 0]
    set tunnels {}
    foreach tunnel [lrange $argv 1 end] {
        set result [regexp -inline {^([^:]+):([^:]+):([^:]+)$} $tunnel]
        if {[llength $result] == 0} {
            puts stderr "Invalid tunnel specification"
            exit 1
        } else {
            lappend tunnels [lrange $result 1 3]
        }
    }

    # Start untunnel on the tunnel host.
    set cmd "$untunnel_cmd --remote"
    set cmd [string map [list %HOST% $tunnel_host %CMD% $cmd] $rsh_cmd]
    set chan [open "|$cmd" r]
    set failure 0
    while {1} {
        gets $chan line
        if {[eof $chan] || $line eq "end"} {
            # Link termination.
            if {[catch {close $chan} result]} {
                puts stderr "Error in remote untunnel: [unescape $result]"
            } elseif {$line ne "end"} {
                puts stderr "Unexpected EOF on remote untunnel"
            }
            break
        } else {
            # Data.
            set delim [string first " " $line]
            if {$delim == -1} {set delim [string length $line]}
            set cmd   [string range $line 0 [expr {$delim - 1}]]
            set parms [string range $line [expr {$delim + 1}] end]

            switch -- $cmd {
            warning - error - fatal {
                puts stderr "$cmd: [unescape $parms]"
                set failure 1
            } port  {set tunnel_port $parms        }
            secret  {set secret      $parms        }
            pid     {puts "PID: $parms"            }
            version {puts "Version: $parms"        }
            default {puts "Unknown command: $line"}}
        }
    }
    if {$failure} {exit 1}

    # Make sure we have all the requisite information.
    if {![info exists tunnel_port]} {
        puts stderr "fatal: remote untunnel failed to provide port"
        exit 1
    } elseif {![info exists secret]} {
        puts stderr "fatal: remote untunnel failed to provide secret"
        exit 1
    }

    # Connect to the --server untunnel.
    if {[catch {set chan [socket $tunnel_host $tunnel_port]} result]} {
        puts stderr "fatal: socket $tunnel_host:$tunnel_port: $result"
        exit 1
    }
    fconfigure $chan -blocking 0
    # fileevent $chan readable [list server_recv $chan]

    puts $chan "secret $secret"
    puts $chan "quiet 1"
    puts $chan "auth"
    puts $chan "end"
    close $chan

    # Event loop.
    vwait forever
}

# Background error handler.
proc bgerror {msg} {
    global errorInfo
    log 0 "bgerror [escape $errorInfo]"
}

# Humble logger procedure.
proc log {level msg} {
    global log_level clock_fmt
    if {$level <= $log_level} {
        if {$clock_fmt ne ""} {
            set msg "[clock format [clock seconds] -format $clock_fmt]$msg"
        }
        puts stdout $msg
    }
}

# vim: set ts=4 sts=4 sw=4 tw=80 et:

