summaryrefslogtreecommitdiff
path: root/example/ftp-inband
blob: 5a28302b80c94391218328eaeda9aa1d2ccbd77d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#!/bin/sh
# -*- tcl -*-
# The next line is executed by /bin/sh, but not tcl \
exec tclsh "$0" ${1+"$@"}

package require Expect


# ftp-inband - copy files over a telnet/rlogin/etc link
# Author: Don Libes, NIST
# Date: Jan 11, 1993

# Program follows usual conventions and is otherwise self-documenting.
# Assumes standard UNIX conventions on both sides.  It uses "compress"
# which can be replaced with gzip or removed totally - it's just there
# for efficiency.
# Assumes error-free transmission (i.e., MNP modems), telnet links, etc.
# Assumes remote shell does not reset tty modes after each command.

# Note, there is very little error checking.  This script was written
# primarily as an exercise - just to demonstrate Expect.

set prompt "(%|#|\\\$) $"		;# default prompt
catch {set prompt $env(EXPECT_PROMPT)}

set timeout -1
set verbose_flag 0

proc send_verbose {msg} {
	global verbose_flag

	if {$verbose_flag} {
		send_user $msg
	}
}

proc get {infile outfile} {
	global prompt verbose_flag

	if {!$verbose_flag} {
		log_user 0
	}

	send_verbose "disabling echo: "
	send "stty -echo\r"
	expect -re $prompt

	send_verbose "remote pid is "
	send "echo $$\r"
	expect -re "(.*)\r\n.*$prompt" {set rpid $expect_out(1,string)}

	set pid [pid]
	# pid is local pid, rpid is remote pid

	set infile_plain	"/tmp/$rpid"
	set infile_compressed	"$infile_plain.Z"
	set infile_encoded	"$infile_compressed.uu"

	set outfile_plain	"/tmp/$pid"
	set outfile_compressed	"$outfile_plain.Z"
	set outfile_encoded	"$outfile_compressed.uu"

	set out [open $outfile_encoded w]

	send_verbose "compressing\n"
	send "compress -fc $infile > $infile_compressed\r"
	expect -re $prompt

	# use label corresponding to temporary name on local system
	send_verbose "uuencoding\n"
	send "uuencode $infile_compressed $outfile_compressed > $infile_encoded\r"
	expect -re $prompt

	send_verbose "copying\n"
	send "cat $infile_encoded\r"

	log_user 0

	expect {
		-re "^end\r\n" {
			puts $out "end"
			close $out
		} -re "^(\[^\r]*)\r\n" {
			puts $out $expect_out(1,string)
			send_verbose "."
			exp_continue
		}
	}

	if {$verbose_flag} {
		send_user "\n"		;# after last "."
		log_user 1
	}

	expect -re $prompt			;# wait for prompt from cat

	send_verbose "deleting temporary files\n"
	send "rm -f $infile_compressed $infile_encoded\r"
	expect -re $prompt

	send_verbose "switching attention to local system\nuudecoding\n"
	exec uudecode $outfile_encoded

	send_verbose "uncompressing\n"
	exec uncompress -f $outfile_compressed

	send_verbose "renaming\n"
	if {[catch "exec cp $outfile_plain $outfile" msg]} {
		send_user "could not move file in place, reason: $msg\n"
		send_user "left as $outfile_plain\n"
		exec rm -f $outfile_encoded
	} else {
		exec rm -f $outfile_plain $outfile_encoded
	}

	# restore echo and serendipitously reprompt
	send "stty echo\r"

	log_user 1
}	

proc put {infile outfile} {
	global prompt verbose_flag

	if {!$verbose_flag} {
		log_user 0
	}

	send_verbose "disabling echo: "
	send "stty -echo\r"
	expect -re $prompt

	send_verbose "remote pid is "
	send "echo $$\r"
	expect -re "(.*)\r\n.*$prompt" {set rpid $expect_out(1,string)}

	set pid [pid]
	# pid is local pid, rpid is remote pid

	set infile_plain	"/tmp/$pid"
	set infile_compressed	"$infile_plain.Z"
	set infile_encoded	"$infile_compressed.uu"

	set outfile_plain	"/tmp/$rpid"
	set outfile_compressed	"$outfile_plain.Z"
	set outfile_encoded	"$outfile_compressed.uu"

	set out [open $outfile_encoded w]

	send_verbose "compressing\n"
	exec compress -fc $infile > $infile_compressed

	# use label corresponding to temporary name on local system
	send_verbose "uuencoding\n"
	exec uuencode $infile_compressed $outfile_compressed > $infile_encoded

	send_verbose "copying\n"
	send "cat > $outfile_encoded\r"

	log_user 0

	set fp [open $infile_encoded r]
	while {1} {
		if {-1 == [gets $fp buf]} break
		send_verbose "."
		send -- "$buf\r"
	}

	if {$verbose_flag} {
		send_user "\n"			;# after last "."
		log_user 1
	}

	send "\004"				;# eof
	close $fp

	send_verbose "deleting temporary files\n"
	exec rm -f $infile_compressed $infile_encoded

	send_verbose "switching attention to remote system\n"

	expect -re $prompt			;# wait for prompt from cat

	send_verbose "uudecoding\n"
	send "uudecode $outfile_encoded\r"
	expect -re $prompt

	send_verbose "uncompressing\n"
	send "uncompress -f $outfile_compressed\r"
	expect -re $prompt

	send_verbose "renaming\n"
	send "cp $outfile_plain $outfile\r"
	expect -re $prompt

	send_verbose "deleting temporary files\n"
	send "rm -f $outfile_plain $outfile_encoded\r"
	expect -re $prompt

	# restore echo and serendipitously reprompt
	send "stty echo\r"

	log_user 1
}	

proc get_main {} {
	stty -raw echo
	send_user "g\nget remote file \[localfile]: "
	expect_user {
		-re "(\[^ ]+) +(\[^ ]+)\n" {
			send_user "copying (remote) $expect_out(1,string) to (local) $expect_out(2,string)\n"
			get $expect_out(1,string) $expect_out(2,string)
		} -re "(\[^ ]+)\n" {
			send_user "copying $expect_out(1,string)\n"
			get $expect_out(1,string) $expect_out(1,string)
		} -re "\n" {
			send_user "eh?\n"
		}
	}
	stty raw -echo
}

proc put_main {} {
	stty -raw echo
	send_user "p\nput localfile \[remotefile]: "
	expect_user {
		-re "(\[^ ]+) +(\[^ ]+)\n" {
			send_user "copying (local) $expect_out(1,string) to (remote) $expect_out(2,string)\n"
			put $expect_out(1,string) $expect_out(2,string)
		} -re "(\[^ ]+)\n" {
			send_user "copying $expect_out(1,string)\n"
			put $expect_out(1,string) $expect_out(1,string)
		} -re "\n" {
			send_user "eh?\n"
		}
	}
	stty raw -echo
}

proc chdir {} {
	stty -raw echo
	send_user "c\n"
	send_user "current directory is [pwd], new directory: "
	expect_user -re "(.*)\n" {
		cd $expect_out(1,string)
	}
	stty raw -echo
}

proc verbose {} {
	global verbose_flag

	set verbose_flag [expr !$verbose_flag]
	send_user "verbose [verbose_status]\r\n"
}

proc verbose_status {} {
	global verbose_flag

	if {$verbose_flag} {
		return "on"
	} else {
		return "off"
	}
}

proc cmd {} {
	set CTRLZ \032

	send_user "command (g,p,? for more): "
	expect_user {
		g get_main
		p put_main
		c chdir
		v verbose
		~ {send "~"}
		"\\?" {
			send_user "?\n"
			send_user "~~g  get file from remote system\n"
			send_user "~~p  put file to remote system\n"
			send_user "~~c  change/show directory on local system\n"
			send_user "~~~  send ~~ to remote system\n"
			send_user "~~?  this list\n"
			send_user "~~v  verbose mode toggle (currently [verbose_status])\n"
			send_user "~~^Z suspend\n"
		}
		$CTRLZ {
			stty -raw echo
			exec kill -STOP [pid]
			stty raw -echo
		}
		-re . {send_user "unknown command\n"}
	}
	send_user "resuming session...\n"
}

spawn -noecho $env(SHELL)

send_user "Once logged in, cd to directory to transfer to/from and press: ~~\n"
send_user "One moment...\n"
interact ~~ cmd