#!../expect --
# allow another user to share a shell (or other program) with you
# See kibitz(1) man page for complete info.
# Author: Don Libes, NIST
# Date written: December 5, 1991
# Date last editted: July 3, 1993 (switched to using "-open")
# Version: 2.4

# if environment variable "EXPECT_PROMPT" exists, it is taken as a regular
# expression which matches the end of your login prompt (but does not other-
# wise occur while logging in).

set prompt "(%|#|\$) "		;# default prompt
set noproc 0
set tty ""			;# default if no -tty flag
set allow_escape 1		;# allow escapes if true
set escape_char \035		;# control-right-bracket
set escape_printable "^\]"
set verbose 1			;# if true, describe what kibitz is doing

# if you are on a system such as HPUX 8.0 or IBM AIX 3.2 that requires
# "cat -u" for kibitz to work,
# comment out the following line, and uncomment the next one.
set catflags ""
#set catflags "-u"

trap exit SIGINT

set argv [lrange $argv 1 end]
while [llength $argv]>0 {
	set flag [lindex $argv 0]
	case $flag in \
	"-noproc" {
		set noproc 1
		set argv [lrange $argv 1 end]
	} "-catu" {
		set catflags "-u"
		set argv [lrange $argv 1 end]
	} "-tty" {
		set tty [lindex $argv 1]
		set argv [lrange $argv 2 end]
	} "-noescape" {
		set allow_escape 0
		set argv [lrange $argv 1 end]
	} "-escape" {
		set escape_char [lindex $argv 1]
		set escape_printable $escape_char
		set argv [lrange $argv 2 end]
	} "-silent" {
		set verbose 0
		set argv [lrange $argv 1 end]
	} default {
		break
	}
}

if {([llength $argv]<1) && ($noproc==0)} {
	send_user "usage: kibitz user \[program ...\]\n"
	send_user "   or: kibitz user@host \[program ...\]\n"
	exit
}

# 2nd user invokes it as "kibitz -####" but there is no
# reason to document this, as user is told this on the fly.
# known internally as user_number==2

# 1st user on remote machine invokes it as "kibitz -r user"
# and is known internally as user_number==3 while
# locally known internally as user_number==1

log_user 0
set timeout -1

set user [lindex $argv 0]
if [string match -r $user] {
	send_user "KRUN"	;# this tells user_number 1 that we're running
				;# and to prepare for possible error messages
	set user_number 3
	# need to check that it exists first!
	set user [lindex $argv 1]
} else {
	set user_number [expr 1+(0==[string first - $user])]
}

# at this point, user_number and user are correctly determined

#for debugging, uncommenting, leaves each user's session in a file: 1, 2 or 3
#exec rm -f $user_number
#debug -f $user_number 0

set user2_islocal 1	;# assume local at first

# later move inside following if $user_number == 1
# return true if x is a prefix of xjunk, given that prefixes are only
# valid at . delimiters
# if !do_if0, skip the whole thing - this is here just to make caller simpler
proc is_prefix {do_if0 x xjunk} {
	if 0!=$do_if0 {return 0}
	set split [split $xjunk .]
	for {set i [expr [llength $split]-1]} {$i>=0} {incr i -1} {
		if [string match $x [join [lrange $split 0 $i] .]] {return 1}
	}
	return 0
}

# get domainname.  Unfortunately, on some systems, domainname(1)
# returns NIS domainname which is not the internet domainname.
proc domainname {} {
	# open pops stack upon failure
	set rc [catch {open /etc/resolve.conf r} file]
	if {$rc==0} {
		while {-1!=[gets $file buf]} {
			if 1==[scan $buf "domain %s" name] {
				close $file
				return $name
			}
		}
		close $file
	}

	# fall back to using domainname
	if {0==[catch {exec domainname} name]} {return $name}

	error "could not figure out domainname"
}

if $user_number==1 {
	if $noproc==0 {
		if [llength $argv]>1 {
			set pid [eval spawn [lrange $argv 1 end]]
		} else {
			set pid [spawn $env(SHELL)]
		}
		set shell $spawn_id
	}

	# is user2 remote?
	regexp (\[^@\]*)@*(.*) $user ignore tmp host
	set user $tmp
	if ![string match $host ""] {
		set h_rc [catch {exec hostname}	hostname]
		set d_rc [catch domainname 	domainname]

		if {![is_prefix $h_rc $host $hostname]
		 && ![is_prefix $d_rc $host $hostname.$domainname]} {
			set user2_islocal 0
		}
	}

	if !$user2_islocal {
		if $verbose {send_user "connecting to $host\n"}
		spawn rlogin $host
		set userin $spawn_id
		set userout $spawn_id

		if [info exists env(EXPECT_PROMPT)] {
			set prompt $env(EXPECT_PROMPT)
		}

		set timeout 120
		expect {
			assword: {
				system stty -echo
				send_user "password (for [exec whoami]) on $host: "
				set old_timeout $timeout; set timeout -1
				expect_user -re "(.*)\n"
				send_user "\n"
				set timeout $old_timeout
				send "$expect_out(1,string)\r"
				# bother resetting echo?
				continue -expect
			} "TERM = *) " {
				send "\r"
				continue -expect
			} incorrect* {
				send_user "invalid password or account\n"
				exit
			} timeout {
				send_user "connection to $host timed out\n"
				exit
			} eof {
				send_user "connection to host failed: $expect_out(buffer)"
				exit
			} -re $prompt
		}
		if $verbose {send_user "starting kibitz on $host\n"}
		# the kill protects user1 from receiving user3's
		# prompt if user2 exits via expect's exit.
		send "kibitz -r $user;kill -9 $$\r"

		expect {
			-re "kibitz -r $user.*KRUN" {}
			-re "kibitz -r $user.*(kibitz\[^\r\]*)\r" {
				send_user "unable to start kibitz on $host: \"$expect_out(1,string)\"\n"
				send_user "try rlogin by hand followed by \"kibitz $user\"\n"
				exit
			}
			timeout {
				send_user "unable to start kibitz on $host: "
				set expect_out(buffer) "timed out"
				set timeout 0; expect -re .+
				send_user $expect_out(buffer)
				exit
			}
		}
		expect {
			-re ".*\n" {
				# pass back diagnostics
				# should really strip out extra cr
				send_user $expect_out(buffer)
				continue -expect
			}
			KABORT exit
			default exit
			KDATA
		}
	}
}

if $user_number==2 {
	set pid [string trimleft $user -]
}

set local_io [expr ($user_number==3)||$user2_islocal]
if $local_io||($user_number==2) {
	if 0==[info exists pid] {set pid [getpid]}

	set userinfile /tmp/exp0.$pid
	set useroutfile /tmp/exp1.$pid
}

proc prompt1 {} {
	return "kibitz[info level].[history nextid]> "
}

if {$allow_escape} {
    expect_before -i $user_spawn_id $escape_char {
	send_user "\nto exit kibitz, enter: exit\n"
	send_user "to suspend kibitz, press appropriate job control sequence\n"
	send_user "to return to kibitzing, enter: return\n"
	interpreter
	send_user "returning to kibitz\n"
	continue -expect
    }

    proc prompt1 {} {
	return "kibitz[info level].[history nextid]> "
    }
}

set timeout -1

# kibitzer executes following code
if $user_number==2 {
	# for readability, swap variables
	set tmp $userinfile
	set userinfile $useroutfile
	set useroutfile $tmp

	if ![file readable $userinfile] {
		send_user "Eh?  No one is asking you to kibitz.\n"
		exit -1
	}
	spawn -open [open "|cat $catflags < $userinfile" "r"]
	exec rm -f $userinfile
	set userin $spawn_id

	spawn -open [open $useroutfile w]
	set userout $spawn_id
	# open will hang until other user's cat starts

	system stty -echo raw
	if $allow_escape {send_user "Escape sequence is $escape_printable\n"}

	# While user is reading message, try to delete other fifo
	exec rm -f $userinfile

	interact {
		-output $userout
		-input $userin
	}
	exit
}

# only user_numbers 1 and 3 execute remaining code

proc abort {} {
	global user_number

	# KABORT tells user_number 1 that user_number 3 has run into problems
	# and is exiting, and diagnostics have been returned already
	if $user_number==3 {send_user KABORT}
	exit
}

if $local_io {
    proc mkfifo {f} {
	if 0==[catch {exec mknod $f p}] return
	# some systems put mknod in wierd places
	if 0==[catch {exec /usr/etc/mknod $f p}] return	;# Sun
	if 0==[catch {exec /etc/mknod $f p}] return	;# AIX, Cray
	send_user "Couldn't figure out how to make a fifo - where is mknod?\n"
	abort
    }

    proc rmfifos {} {
	global userinfile useroutfile
	exec rm -f $userinfile $useroutfile
    }

    trap {rmfifos; exit} SIGINT

    # create 2 fifos to communicate with other user
    mkfifo $userinfile
    mkfifo $useroutfile
    # make sure other user can access despite umask
    exec chmod 666 $userinfile $useroutfile

    if $verbose {send_user "asking $user to type:  kibitz -$pid\n"}

    # can't use exec since write insists on being run from a tty!
    set rc [catch {
		   system echo "Can we talk?  Run: \"kibitz -$pid\"" | \
			/bin/write $user $tty
		}
	]
    if $rc {rmfifos;abort}

    spawn -open [open $useroutfile w]
    set userout $spawn_id
    # open will hang until other user's cat starts

    spawn -open [open $userinfile "r"]
    exec rm $userinfile
    set userin $spawn_id
}

system stty -echo raw

if $user_number==3 {
	send_user "KDATA"	;# this tells user_number 1 to send data

	interact {
		-output $userout
		-input $userin -output $user_spawn_id -eof {
			wait -i $userin
			return -tcl
		}
	}
} else {
	if $allow_escape {send_user "Escape sequence is $escape_printable\n"}

	if $noproc {
		expect {
			-i $user_spawn_id -re .+ {
					send -i $userout $expect_out(buffer)
				continue -expect
			}

			-i $userin -re .+ {
				send_user -raw $expect_out(buffer)
				continue -expect
			} eof {
				wait -i $userin
			}
		}
	} else {
		expect {
			-i $user_spawn_id -re .+ {
				send -i $shell $expect_out(buffer)
				continue -expect
			}

			-i $userin -re .+ {
				send -i $shell $expect_out(buffer)
				continue -expect
			} eof {
				wait -i $userin
				close -i $shell
			}

			-i $shell -re .+ {
				send_user -raw $expect_out(buffer)
				   send -i $userout $expect_out(buffer)
				continue -expect
			}
		}

		wait -i $shell
	}
}

if $local_io rmfifos
