Source code: FTreeP/FTPCommandRelay.java
1 package FTreeP;
2
3 import java.io.*;
4 import java.net.*;
5 import java.util.*;
6
7 import java.awt.*;
8 import java.awt.event.*;
9
10 import javax.swing.*;
11 import javax.swing.tree.*;
12 import javax.swing.event.*;
13
14 import DirectoryIndexer.*;
15
16 class FTPCommandRelay
17 extends Thread
18 {
19 private Socket command_in_socket = null, command_out_socket = null;
20 private BufferedReader cmd_in_in = null, cmd_out_in = null;
21 private BufferedWriter cmd_in_out = null, cmd_out_out = null;
22
23 private String mode = "STREAM",
24 pwd = "/",
25 type = "ASCII";
26
27 private String current_host = "";
28 private int current_host_port = 0;
29
30 private boolean quit = false;
31
32 private FTPConsole console = null;
33
34 public FTPCommandRelay(Socket command_in_socket) throws Exception
35 {
36 FTPBase.system_println("Incoming connection detected, spawning FTPCommandRelay instance.");
37 this.command_in_socket = command_in_socket;
38
39 if(FTPBase.gui) createGUIElements();
40 }
41
42 public void run()
43 {
44 try {
45
46 cmd_in_in = new BufferedReader(new InputStreamReader(command_in_socket.getInputStream()));
47 cmd_in_out = new BufferedWriter(new OutputStreamWriter(command_in_socket.getOutputStream()));
48
49 respond(cmd_in_out, 220, "Connected to FTreeP");
50
51 boolean quit = false;
52
53 while(!quit && command_in_socket != null && cmd_in_in != null && cmd_in_out != null)
54 {
55 String cmd = cmd_in_in.readLine();
56
57 if(FTPBase.DEBUG) console.println("COMMAND: " + cmd);
58
59 interpret(cmd);
60 }
61
62 FTPBase.system_println("Outgoing connection terminated, killing FTPCommandRelay instance.");
63
64 } catch(Exception e) {}
65
66 if(FTPBase.gui) destroyGUIElements();
67 }
68
69 private void interpret(String cmd) throws Exception
70 {
71 String argument = "";
72
73 if(cmd.indexOf(" ") != -1)
74 argument = cmd.substring(cmd.indexOf(" ") + 1);
75
76 cmd = cmd.toLowerCase();
77
78 if(cmd.startsWith("cdup")) cdup();
79 else if(cmd.startsWith("cwd")) cwd(argument);
80 else if(cmd.startsWith("list")) list(argument);
81 else if(cmd.startsWith("mode")) mode(argument);
82 else if(cmd.startsWith("noop")) noop();
83 else if(cmd.startsWith("pasv")) pasv();
84 else if(cmd.startsWith("port")) port(argument);
85 else if(cmd.startsWith("pwd") | cmd.startsWith("xpwd")) pwd();
86 else if(cmd.startsWith("quit")) quit();
87 else if(cmd.startsWith("retr")) retr(argument);
88 else if(cmd.startsWith("size")) size(argument);
89 else if(cmd.startsWith("type")) type(argument);
90 else if(cmd.startsWith("user")) user(argument);
91 else
92 {
93 respond(cmd_in_out, 502, "Command not implemented.");
94 }
95 }
96
97 private Resource getResourceFromPath(String path) throws Exception
98 {
99 Resource r = null;
100
101 int score = -1;
102 StringTokenizer score_tokens = null;
103
104 for(int i = 0; i < FTPBase.directory_index.size(); i++)
105 {
106 String virtual_directory = FTPBase.directory_index.get(i).getVirtualDirectory();
107
108 if(path.startsWith(virtual_directory))
109 {
110 score_tokens = new StringTokenizer(virtual_directory, "/");
111 if(score_tokens.countTokens() > score)
112 {
113 r = FTPBase.directory_index.get(i);
114 score = score_tokens.countTokens();
115 }
116 }
117 }
118
119 if(FTPBase.DEBUG && r == null) FTPBase.system_println("DEBUG: Error, could not locate appropriate Resource!");
120
121 return r;
122 }
123
124 private void connectToHostWithResource(String resource) throws Exception
125 {
126 Resource r = getResourceFromPath(resource);
127
128 if(current_host.equals(r.getHost()) && current_host_port == r.getPort())
129 {
130 if(FTPBase.DEBUG) console.println("DEBUG: Already connected to the desired host.");
131 return;
132 } else if(FTPBase.DEBUG) console.print("DEBUG: Connecting to Data Host providing " + resource + "... ");
133
134 if(command_out_socket != null && cmd_out_in != null && cmd_out_out != null)
135 {
136 relay(cmd_out_out, "QUIT");
137 command_out_socket = null;
138 cmd_out_in = null;
139 cmd_out_out = null;
140 }
141
142 command_out_socket = new Socket(r.getHost(), r.getPort());
143
144 cmd_out_in = new BufferedReader(new InputStreamReader(command_out_socket.getInputStream()));
145 cmd_out_out = new BufferedWriter(new OutputStreamWriter(command_out_socket.getOutputStream()));
146
147 current_host = r.getHost();
148 current_host_port = r.getPort();
149
150 if(FTPBase.DEBUG) console.println("Done!");
151 }
152
153 // Make a path into its simplist form; the most direct representation
154 // of a directory from the root directory '/'. All paths end up in
155 // the form: '/dir1/dir2/dir3/'.
156 private String simplifyPath(String path) throws Exception
157 {
158 if(path.equals("")) return path;
159
160 StringTokenizer st = new StringTokenizer(path, "/");
161
162 ArrayList d = new ArrayList();
163
164 while(st.hasMoreTokens())
165 {
166 String s = st.nextToken();
167
168 if(s.equals(".")) continue;
169 else if(s.equals(".."))
170 {
171 if(d.size() >= 1) d.remove(d.size() - 1);
172 }
173 else d.add(s);
174 }
175
176 String p = "/";
177
178 int i = 0;
179 for(i = 0; i < d.size() - 1; i++)
180 p = p + (String) d.get(i) + "/";
181
182 if(d.size() >= 1) p = p + (String) d.get(i);
183
184 return p;
185 }
186
187 // Reply to a machine with a message comprised of the response_code and
188 // the message over the given BufferedWriter. Terminte the response with a CRLF.
189 public void respond(BufferedWriter cmd_out, int response_code, String message) throws Exception
190 {
191 cmd_out.write(response_code + " " + message + FTPBase.CRLF);
192 cmd_out.flush();
193
194 if(FTPBase.DEBUG) console.println("RESPOND: " + response_code + " " + message);
195 }
196
197 // Reply to a machine with a message comprised of the message over the given Socket.
198 // Terminte the response with a CRLF.
199 public void relay(BufferedWriter cmd_out, String message) throws Exception
200 {
201 cmd_out.write(message + FTPBase.CRLF);
202 cmd_out.flush();
203
204 if(FTPBase.DEBUG) console.println("RELAY: " + message);
205 }
206
207 private void createGUIElements()
208 {
209 console = new FTPConsole();
210
211 FTPBase.tabbed_pane.addTab("FTPCommandRelay", console);
212 }
213
214 private void destroyGUIElements()
215 {
216 //FTPBase.tabbed_pane.remove(console);
217
218 //console = null;
219 }
220
221 /*
222 --------------
223 | FTP COMMANDS |
224 --------------
225 */
226
227 // Handle the CDUP command.
228 private void cdup() throws Exception
229 {
230 cwd("..");
231 }
232
233 // Handle the CWD command.
234 private void cwd(String directory) throws Exception
235 {
236 if(!directory.startsWith("/")) directory = pwd + "/" + directory;
237
238 directory = simplifyPath(directory);
239
240 connectToHostWithResource(directory);
241
242 relay(cmd_out_out, "CWD " + directory);
243 relay(cmd_in_out, cmd_out_in.readLine());
244
245 pwd = directory;
246 }
247
248 // Handle the LIST command.
249 private void list(String directory) throws Exception
250 {
251 if(!directory.startsWith("/")) directory = pwd + "/" + directory;
252
253 directory = simplifyPath(directory);
254
255 relay(cmd_out_out, "LIST " + directory);
256 relay(cmd_in_out, cmd_out_in.readLine());
257 relay(cmd_in_out, cmd_out_in.readLine());
258 }
259
260 // Handle the MODE command.
261 private void mode(String mode) throws Exception
262 {
263 if(mode.equalsIgnoreCase("S")) this.mode = "STREAM";
264 else if(mode.equalsIgnoreCase("B")) this.mode = "BLOCK";
265 else if(mode.equalsIgnoreCase("C")) this.mode = "COMPRESSED";
266 else
267 {
268 // FIX - Find the appropriate response here...
269 return;
270 }
271
272 respond(cmd_in_out, 200, "Mode set to " + this.mode + ".");
273 }
274
275 // Handle the NOOP command.
276 private void noop() throws Exception
277 {
278 respond(cmd_in_out, 200, "Command okay.");
279 }
280
281 // Handle the PASV command.
282 private void pasv() throws Exception
283 {
284 connectToHostWithResource(pwd);
285
286 relay(cmd_out_out, "PASV");
287 relay(cmd_in_out, cmd_out_in.readLine());
288 }
289
290 // Handle the PORT command.
291 private void port(String port_text) throws Exception
292 {
293 connectToHostWithResource(pwd);
294
295 relay(cmd_out_out, "PORT " + port_text);
296 relay(cmd_in_out, cmd_out_in.readLine());
297 }
298
299 // Handle the PWD command.
300 private void pwd() throws Exception
301 {
302 respond(cmd_in_out, 257, "\"" + pwd + "\" is current directory.");
303 }
304
305 // Handle the QUIT command.
306 private void quit() throws Exception
307 {
308 relay(cmd_out_out, "QUIT");
309 respond(cmd_in_out, 221, "Goodbye!");
310
311 this.quit = true;
312
313 if(command_in_socket != null)
314 {
315 command_in_socket.close();
316 command_in_socket = null;
317
318 cmd_in_in = null;
319 cmd_in_out = null;
320 }
321
322 if(command_out_socket != null)
323 {
324 command_out_socket.close();
325 command_out_socket = null;
326
327 cmd_out_in = null;
328 cmd_out_out = null;
329 }
330 }
331
332 // Handle the RETR command.
333 private void retr(String file) throws Exception
334 {
335 if(!file.startsWith("/")) file = pwd + "/" + file;
336 file = simplifyPath(file);
337
338 relay(cmd_out_out, "RETR" + "_" + this.type + " " + file);
339
340 relay(cmd_in_out, cmd_out_in.readLine());
341 relay(cmd_in_out, cmd_out_in.readLine());
342 }
343
344 // Handle the SIZE command.
345 private void size(String file) throws Exception
346 {
347 if(!file.startsWith("/")) file = pwd + file;
348 file = simplifyPath(file);
349
350 connectToHostWithResource(file);
351
352 relay(cmd_out_out, "SIZE " + file);
353 relay(cmd_in_out, cmd_out_in.readLine());
354 }
355
356 // Handle the TYPE command.
357 private void type(String type) throws Exception
358 {
359 if(type.equalsIgnoreCase("A")) this.type = "ASCII";
360 else if(type.equalsIgnoreCase("I")) this.type = "IMAGE";
361 else
362 {
363 // FIX - Find the appropriate response here...
364 return;
365 }
366
367 respond(cmd_in_out, 200, "Type set to " + this.type + ".");
368 }
369
370 // Handle the USER command.
371 private void user(String user) throws Exception
372 {
373 respond(cmd_in_out, 230, "Login ocomplete. Anonymous user rights granted.");
374 }
375 }