-
Notifications
You must be signed in to change notification settings - Fork 0
/
Access.gem
331 lines (288 loc) · 9.39 KB
/
Access.gem
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#!/usr/bin/ruby
require 'tk'
require 'net/ftp'
# Close the connection and terminate pgm.
def term(conn)
if conn
begin
conn.quit
ensure
conn.close
end
end
exit
end
# Display an error dialog.
def thud(title, message)
Tk.messageBox('icon' => 'error', 'type' => 'ok',
'title' => title, 'message' => message)
end
# This is the login window. It pops up and asks for the remote host and the
# user credentials, and a button to initiate the login when the fields are
# ready.
class LoginWindow
# Generate s label/entry pair for the login window. These will be
# appropriately gridded on row row inside par. Text box has width
# width and places its contents into the reference $ref. If $ispwd,
# treat it as a password entry box. Returns the text variable which
# gives access to the entry.
def genpair(row, text, width, ispwd=false)
tbut = TkLabel.new(@main, 'text' => text) {
grid('row' => row, 'column' => 0, 'sticky' => 'nse')
}
tvar = TkVariable.new('')
lab = TkEntry.new(@main) {
background 'white'
foreground 'black'
textvariable tvar
width width
grid('row' => row, 'column' => 1, 'sticky' => 'nsw')
}
lab.configure('show' => '*') if ispwd
return tvar
end
# Log into the remote host. If successful, start the directory loader.
# Modes are: 1: Anonymous, 2: User, 3: Return, which does anon if the
# user infor was not filled in, and user otw.
def do_login(mode)
host = @host.value
acct = @acct.value
password = @password.value
# Adjust user data by mode.
if mode == 1 || (mode == 3 && acct == "" && password == "")
acct ='anonymous'
if password == ""
password = 'anonymous'
end
end
# Make sure we're all filled in.
if host == "" || acct == "" || password == ""
thud('No Login Info',
"You must provide a hostname and login credentials.")
return
end
# Attempt to connect to the remote host and log in
begin
@conn = Net::FTP.new(host, acct, password)
@conn.passive = true
rescue
thud("Login Failed", $!)
@conn = nil
return
end
# Display the listing in the window.
@listwin.setconn(@conn)
@main.destroy()
end
def initialize(main, listwin, titfont, titcolor)
@main = TkToplevel.new(main)
@main.title('FTP Login')
# Listing window.
@listwin = listwin
@conn = nil
# This counts through the rows, which makes it easier to modify
# the program.
row = -1
# Label at the top of window.
toplab = TkLabel.new(@main) {
text "FTP Server Login"
justify 'center'
font titfont
foreground titcolor
grid('row' => (row += 1), 'column' => 0, 'columnspan' => 2,
'sticky' => 'news')
}
# Hostname entry
@host = genpair(row += 1, 'Host:', 25)
# Login buttons, in a frame for layout.
bframe = TkFrame.new(@main) {
grid('row' => (row += 1), 'column' => 0, 'columnspan' => 2,
'sticky' => 'news')
}
TkButton.new(bframe, 'command' => proc { self.do_login(1) }) {
text 'Anon. Login'
pack('side' => 'left', 'expand' => 'yes', 'fill' => 'both')
}
TkButton.new(bframe, 'command' => proc { self.do_login(2) }) {
text 'User Login'
pack('side' => 'left', 'expand' => 'yes', 'fill' => 'both')
}
# Login and password entries.
@acct = genpair(row += 1, 'Login:', 15)
@password = genpair(row += 1, 'Password:', 15, true)
stop = TkButton.new(@main, 'command' => proc { term(@conn) } ) {
text 'Exit'
grid('row' => (row += 1), 'column' => 0, 'columnspan' => 2,
'sticky' => 'news')
}
# CR same as pushing login.
@main.bind('Return', proc { self.do_login(3) })
end
end
# This is a window containing the file listing.
class FileWindow < TkFrame
def initialize(main)
super
# Set up the title appearance.
titfont = 'arial 16 bold'
titcolor = '#228800'
@conn = nil
# Label at top.
TkLabel.new(self) {
text 'FTP Download Agent'
justify 'center'
font titfont
foreground titcolor
pack('side' => 'top', 'fill' => 'x')
}
# Status label.
@statuslab = TkLabel.new(self) {
text 'Not Logged In'
justify 'center'
pack('side' => 'top', 'fill' => 'x')
}
# Exit button
TkButton.new(self) {
text 'Exit'
command { term(@conn) }
pack('side'=> 'bottom', 'fill' => 'x')
}
# List area with scroll bar. The list area is disabled since we
# don't want the user to type into it.
@listarea = TkText.new(self) {
height 10
width 40
cursor 'sb_left_arrow'
state 'disabled'
pack('side' => 'left')
yscrollbar(TkScrollbar.new).pack('side' => 'right', 'fill' => 'y')
}
# Bind the system exit button to our exit.
main.protocol('WM_DELETE_WINDOW', proc { term(@conn) } )
# Create the login window.
LoginWindow.new(main, self, titfont, titcolor)
end
# Change the color of a tag for entering and leaving. Unfortunately, there
# is no active color for tags in a text box.
def recolor(tag, color)
@listarea.tag_configure(tag, 'foreground' => color)
end
# Do a CD and load the contents. If there is no directory name, skip
# the CD.
def load_dir(dir)
if dir
begin
@conn.chdir(dir)
rescue
thud('No ' + dir, $!)
end
@statuslab.configure('text' => "[Loading " + dir + "]")
else
@statuslab.configure('text' => '[Loading Home Dir]')
end
update
# Get the list of files.
files = [ ]
dirs = [ ]
sawdots = false
@conn.list() do |line|
# Real lines start with the perm bits. And we don't want specials.
if line =~ /^[\-d]([r\-][w\-][x\-]){3}/
# Extract the useful parts, toss the bones. The limit keeps us from
# dividing file names containing spaces.
parts = line.split(/\s+/, 9)
if parts.length >= 9
fn = parts.pop()
sawdots = true if fn == '..'
if parts[0][0..0] == 'd'
dirs.push(fn)
else
files.push(fn)
end
end
end
end
# Add .. if not present, then sort the list.
dirs.push('..') unless sawdots
files.sort!
dirs.sort!
# Clear the old contents from the directory listing box.
@listarea.configure('state' => 'normal')
@listarea.delete('1.0', 'end')
# Fill in the directories. Bind for directory load (us).
ct = 0
while fn = dirs.shift
tagname = "fn" + ct.to_s
@listarea.insert('end', fn+"\n", tagname)
@listarea.tag_configure(tagname, 'foreground' => '#4444FF')
@listarea.tag_bind(tagname, 'Button-1',
proc { |f| self.load_dir(f) }, fn)
@listarea.tag_bind(tagname, 'Enter',
proc { |t| self.recolor(t, '#0000aa') },
tagname)
@listarea.tag_bind(tagname, 'Leave',
proc { |t| self.recolor(t, '#4444ff') },
tagname)
ct += 1
end
# Fill in the files. Bind for download.
while fn = files.shift
tagname = "fn" + ct.to_s
@listarea.insert('end', fn+"\n", tagname)
@listarea.tag_configure(tagname, 'foreground' => 'red')
@listarea.tag_bind(tagname, 'Button-1',
proc { |f| self.dld_file(f) }, fn)
@listarea.tag_bind(tagname, 'Enter',
proc { |t| self.recolor(t, '#880000') },
tagname)
@listarea.tag_bind(tagname, 'Leave',
proc { |t| self.recolor(t, 'red') },
tagname)
ct += 1
end
# Lock it up so the user can't mess with it.
@listarea.configure('state' => 'disabled')
# Update the status label.
begin
loc = @conn.pwd()
rescue
thud('PWD Failed', $!)
loc = '???'
end
@statuslab.configure('text' => loc)
end
# Download the file.
def dld_file(fn)
# Announce.
@statuslab.configure('text' => "[Retrieving " + fn + "]")
update
# Get the file.
begin
@conn.getbinaryfile(fn)
rescue
thud('DLD Failed', fn + ': ' + $!)
@statuslab.configure('text' => '')
else
@statuslab.configure('text' => 'Got ' + fn)
end
end
# This is a hook that the login window calls after a successful login.
# The login window makes the connection and attempts to login. When this
# succeeds, it calls setconn() and destroys itself. Setconn records the
# connection (which the login box created), then does the initial
# directory load.
def setconn(conn)
@conn = conn
load_dir(nil)
end
end
# Create the main window, set the default colors, create the GUI, then
# fire the sucker up.
BG = '#E6E6FA'
root = TkRoot.new('background' => BG) { title "FTP Download" }
TkOption.add("*background", BG)
TkOption.add("*activebackground", '#FFE6FA')
TkOption.add("*foreground", '#0000FF')
TkOption.add("*activeforeground", '#0000FF')
FileWindow.new(root).pack()
Tk.mainloop