# Usage line
set usage "Usage: $argv0 \[-dhSV\] \[-m|M\] \[-autoenable\] \[-noenable\] \
\[-c command\] \[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \
\[-p user-password\] \[-r passphrase\] \[-s script-file\] \[-t timeout\] \
\[-u username\] \[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
\[-y ssh_cypher_type\] router \[router...\]\n"

# Password file
set password_file $env(HOME)/.cloginrc
# Default is to login to the router
set do_command 0
set do_script 0
# The default is to automatically enable
set avenable 1
# The default is that you login non-enabled (tacacs can have you login already
# enabled)
set avautoenable 0
# The default is to look in the password file to find the passwords.  This
# tracks if we receive them on the command line.
set do_passwd 1
set do_enapasswd 1
# Save config, if prompted
set do_saveconfig 0
# cloginrc debugging knob
set do_cloginrcdbg 0
# Sometimes routers take awhile to answer (the default is 10 sec)
if {! [info exists timeoutdflt]} {
    set timeoutdflt 10
}
# intialize cloginrc parsing stacks
set int_file {}
set int_lineno {}

# env(CLOGIN) may contain:
#	x == do not set xterm banner or name

# Find the user in the ENV, or use the unix userid.
if {![info exists default_user] && [info exists env(CISCO_USER)]} {
    if {[string length $env(CISCO_USER)]} {
	set default_user $env(CISCO_USER)
    }
}
if {![info exists default_user] && [info exists env(USER)]} {
    if {[string length $env(USER)]} {
	set default_user $env(USER)
    }
}
if {![info exists default_user] && [info exists env(LOGNAME)]} {
    if {[string length $env(LOGNAME)]} {
	set default_user $env(LOGNAME)
    }
}
if (![info exists default_user]) {
    # This uses "id" which I think is portable.  At least it has existed
    # (without options) on all machines/OSes I've been on recently -
    # unlike whoami or id -nu.
    if [catch {exec id} reason] {
	send_error "\nError: could not exec id: $reason\n"
	exit 1
    }
    regexp {\(([^)]*)} "$reason" junk default_user
}
if {[info exists env(CLOGINRC)]} {
    set password_file $env(CLOGINRC)
}

# Process the command line
for {set i 0} {$i < $argc} {incr i} {
    set arg [lindex $argv $i]

    switch  -glob -- $arg {
	# Expect debug mode
	-d* {
	    exp_internal 1
	# Help
	} -h* {
	    send_user "$usage"
	    exit 0
	# Command to run.
	} -c* {
	    if {! [regexp .\[cC\](.+) $arg ignore command]} {
		incr i
		set command [lindex $argv $i]
	    }
	    set do_command 1
	# Environment variable to pass to -s scripts
	} -E* {
	    if {[regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
		set E$varname $varvalue
	    } else {
		send_user "\nError: invalid format for -E in $arg\n"
		exit 1
	    }
	# alternate cloginrc file
	} -f* {
	    if {! [regexp .\[fF\](.+) $arg ignore password_file]} {
		incr i
		set password_file [lindex $argv $i]
	    }
	# VTY Password
	} -p* {
	    if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} {
		incr i
		set userpasswd [lindex $argv $i]
	    }
	    set do_passwd 0
	# cloginrc debugging knobs
	} -m* {
	    set do_cloginrcdbg 1
	} -M* {
	    set do_cloginrcdbg 2
	# ssh passphrase
	} -r* {
	    if {! [regexp .\[rR\](.+) $arg ignore passphrase]} {
		incr i
		set avpassphrase [lindex $argv $i]
	    }
	# Expect script to run.
	} -s* {
	    if {! [regexp .\[sS\](.+) $arg ignore sfile]} {
		incr i
		set sfile [lindex $argv $i]
	    }
	    if { ! [file readable $sfile] } {
		send_user "\nError: Can't read $sfile\n"
		exit 1
	    }
	    set do_script 1
	# save config on exit
	} -S* {
	    # may not be supported by the script and may not be applicable to
	    # the platform
	    set do_saveconfig 1
	# Timeout
	} -t* {
	    if {! [regexp .\[tT\](.+) $arg ignore timeout]} {
		incr i
	        set timeoutdflt [lindex $argv $i]
	    }
	# Username
	} -u* {
	    if {! [regexp .\[uU\](.+) $arg ignore user]} {
		incr i
		set username [lindex $argv $i]
	    }
	# VTY Password
	} -v* {
	    # some scripts ignore -v, like jlogin
	    if {! [regexp .\[vV\](.+) $arg ignore passwd]} {
		incr i
		set passwd [lindex $argv $i]
	    }
	    set do_passwd 0
	# Version string
	} -V* {
	    send_user "@PACKAGE@ @VERSION@\n"
	    exit 0
	# Enable Username
	} -w* {
	    # may not be supported by the script and may not be applicable to
	    # the platform
	    if {! [regexp .\[wW\](.+) $arg ignore enauser]} {
		incr i
		set enausername [lindex $argv $i]
	    }
	# Enable Password
	} -e* {
	    if {! [regexp .\[e\](.+) $arg ignore enapasswd]} {
		incr i
		set enapasswd [lindex $argv $i]
	    }
	    set do_enapasswd 0
	# 'ssh -c' cypher type
	} -y* {
	    if {! [regexp .\[eE\](.+) $arg ignore cypher]} {
		incr i
		set cypher [lindex $argv $i]
	    }
	# Command file
	} -x* {
	    if {! [regexp .\[xX\](.+) $arg ignore cmd_file]} {
		incr i
		set cmd_file [lindex $argv $i]
	    }
	    if [catch {set cmd_fd [open $cmd_file r]} reason] {
		send_user "\nError: $reason\n"
		exit 1
	    }
	    set cmd_text [read $cmd_fd]
	    close $cmd_fd
	    set command [join [split $cmd_text \n] \;]
	    set do_command 1
	# Do we enable?
	} -noenable {
	    set avenable 0
	# Does tacacs automatically enable us?
	} -autoenable {
	    set avautoenable 1
	    set avenable 0
	} -* {
	    send_user "\nError: Unknown argument! $arg\n"
	    send_user $usage
	    exit 1
	} default {
	    break
	}
    }
}
# Process routers...no routers listed is an error.
if { $i == $argc } {
    send_user "\nError: $usage"
}

# Only be quiet if we are running a script (it can log its output
# on its own)
if { $do_script } {
    log_user 0
} else {
    log_user 1
}

#
# Done configuration/variable setting.  Now run with it...
#

# Sets Xterm title if interactive...if its an xterm and the user cares
proc label { host } {
    global env
    # if CLOGIN has an 'x' in it, don't set the xterm name/banner
    if [info exists env(CLOGIN)] {
	if {[string first "x" $env(CLOGIN)] != -1} { return }
    }
    # take host from ENV(TERM)
    if [info exists env(TERM)] {
	if [regexp \^(xterm|vs) $env(TERM) ignore] {
	    send_user "\033]1;[lindex [split $host "."] 0]\a"
	    send_user "\033]2;$host\a"
	}
    }
}

# This is a helper function to make the password file easier to
# maintain.  Using this the password file has the form:
# add password sl*	pete cow
# add password at*	steve
# add password *	hanky-pie
proc add {var args} {
    global int_file int_lineno int_$var
    set file [lindex $int_file 0]
    set lineno [lindex $int_lineno 0]
    lappend int_$var "$var:$file:$lineno: $args"
}
proc include {args} {
    global env
    regsub -all "(^{|}$)" $args {} args
    if { [regexp "^/" $args ignore] == 0 } {
	set args $env(HOME)/$args
    }
    source_password_file $args
}

proc find {var router} {
    global do_cloginrcdbg
    upvar int_$var list
    if { [info exists list] } {
	foreach line $list {
	    if { [string match -nocase [lindex $line 1] $router] } {
		if { $do_cloginrcdbg > 0 } {
		    send_error -- [join [list [lindex $line 0] [lrange $line 1 end] "\r\n"]]
		}
		if { $do_cloginrcdbg == 2 } {
		    # save return value
		    if {! [info exists result]} {
			set result [lrange $line 2 end]
		    }
		} else {
		    return [lrange $line 2 end]
		}
	    }
	}
    }

    if { $do_cloginrcdbg == 2 } {
	if {[info exists result]} {
	    return $result
	}
    }
    return {}
}

# Loads the password file.  Note that as this file is tcl, and that
# it is sourced, the user better know what to put in there, as it
# could install more than just password info...  I will assume however,
# that a "bad guy" could just as easy put such code in the clogin
# script, so I will leave .cloginrc as just an extention of that script
proc source_password_file { file } {
    global env int_file int_lineno
    if { ! [file exists $file] } {
	send_user "\nError: password file ($file) does not exist\n"
	exit 1
    }
    file stat $file fileinfo
    if { [expr ($fileinfo(mode) & 007)] != 0000 } {
	send_user "\nError: $file must not be world readable/writable\n"
	exit 1
    }
    if [catch {set fd [open $file "r"]} reason] {
	send_user "\nError: $reason\n"
	exit 1
    }
    set int_file [linsert $int_file 0 $file]
    set int_lineno [linsert $int_lineno 0 0]
    while { [gets $fd line] >= 0 } {
	set tmp [lindex $int_lineno 0]; incr tmp
	lset int_lineno 0 $tmp
	eval $line
    }
    set int_file [lrange $int_file 1 end]
    set int_lineno [lrange $int_lineno 1 end]
    close $fd
}
