Source code: com/flexstor/common/io/xfile/ftp/FtpSession.java
1 /*
2 * FtpSession.java
3 *
4 * Copyright $Date: 2003/08/11 02:22:47 $ FLEXSTOR.net Inc.
5 *
6 * This work is licensed for use and distribution under license terms found at
7 * http://www.flexstor.org/license.html
8 *
9 */
10
11 package com.flexstor.common.io.xfile.ftp;
12
13 import java.io.BufferedReader;
14 import java.io.BufferedWriter;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.io.InputStreamReader;
18 import java.io.OutputStream;
19 import java.io.OutputStreamWriter;
20 import java.io.StringReader;
21 import java.net.InetAddress;
22 import java.net.ServerSocket;
23 import java.net.Socket;
24 import java.net.UnknownHostException;
25
26 import com.flexstor.common.settings.Settings;
27 public class FtpSession
28
29 {
30 private final boolean debug = true;
31 private static String ANONYMOUS_USER = "anonymous";
32 private static String ANONYMOUS_PASSWD = "test@flexstor.com";
33 private static String CR_LF = "\r\n";
34 private BufferedReader reader;
35 private BufferedWriter writer;
36 static final public char ASCII_TYPE = 'A';
37 static final public char IMAGE_TYPE = 'I';
38 static final public char EBCDIC_TYPE = 'E';
39 static final public char COMPRESSED_MODE = 'C';
40 static final public char STREAMING_MODE = 'S';
41 static final public char BLOCK_MODE = 'B';
42 private BufferedReader in;
43 //private PrintWriter out;
44 private BufferedWriter out;
45 private ServerSocket m_dataSocket;
46 //static final public char NON_PRINT_FORMAT = 'N';
47 static final public char NON_PRINT_FORMAT = 'N';
48 private Socket m_dataXfrSocket;
49 private FtpResponse m_response;
50 private boolean isMacintosh = (System.getProperty("mrj.version") != null);
51
52
53 /**
54 * An id that identifies a session.
55 */
56 protected FtpSessionId ftpSessionId = null;
57 protected boolean bSessionTerminated = false;
58 protected Socket socket = null;
59 protected boolean bPassive = true; // PASSIVE/ACTIVE socket switch
60 protected Socket passiveSocket = null;
61
62 /**
63 * Idea is that there should not be any other way to create a ftp session than
64 * asking ftp session pool to create one.
65 */
66 public FtpSession ( FtpSessionId sessionId )
67 {
68 this.ftpSessionId = sessionId;
69
70 // Get the user selection for data port type. Default is PASSIVE
71 bPassive = Settings.getBoolean(Settings.FTP_PASSIVE);
72 if (bPassive == true)
73 {
74 System.out.println("FTP Data Port will be PASSIVE");
75 }
76
77 else
78 {
79 System.out.println("FTP Data Port will be ACTIVE");
80 }
81
82 }
83
84 public FtpSessionId getSessionId ()
85 {
86 return ftpSessionId;
87 }
88 public void connect ()
89 throws java.net.UnknownHostException, IOException
90 {
91 socket = new Socket( ftpSessionId.getServerName(), ftpSessionId.getServerPort() );
92
93 in = new BufferedReader( new InputStreamReader( socket.getInputStream() ) );
94 out = new BufferedWriter( new OutputStreamWriter( socket.getOutputStream() ) );
95
96 setResponse();
97 }
98
99 public void transferMode(char mode)
100 throws IOException
101 {
102 sendCommand("MODE " + mode);
103 }
104
105 public void sendUserName(String userName)
106 throws IOException
107 {
108 sendCommand("USER " + userName);
109 }
110
111 public void sendPassword(String password)
112 throws IOException
113 {
114 sendCommand("PASS " + password);
115 }
116
117
118 protected void readResponse()
119 {
120 try
121 {
122 StringBuffer buffer = new StringBuffer();
123 while (true) {
124 while (!reader.ready()) {
125 try { Thread.sleep(10L); }
126 catch (InterruptedException exc) {}
127 }
128 String line = reader.readLine();
129 buffer.append(line);
130 buffer.append('\n');
131 break;
132 }
133
134 }
135 catch(Exception ex)
136 {
137 System.out.println("Exception occured while reading response " + ex);
138 }
139 }
140
141
142
143 public void changeWorkingDirectory(String path)
144 throws IOException
145 {
146 sendCommand("CWD " + path);
147 }
148
149 public void changeToParentDirectory()
150 throws IOException
151 {
152 sendCommand("CDUP");
153 }
154
155 public void logout()
156 throws IOException
157 {
158 sendCommand("QUIT");
159 bSessionTerminated = true;
160 }
161 public boolean isSessionTerminated ()
162 {
163 return bSessionTerminated;
164 }
165
166 /**
167 *
168 *
169 */
170 public void dataPort()
171 throws IOException, UnknownHostException
172 {
173 // Create a PASSIVE or ACTIVE data socket
174 if (bPassive == true)
175 {
176 // Create a PASSIVE socket
177 passiveSocket = createPassiveDataSocket();
178 }
179
180 else
181 {
182 // Create an ACTIVE socket
183 StringBuffer command = new StringBuffer("PORT ");
184 String host = InetAddress.getLocalHost().getHostAddress();
185 command.append(host.replace('.', ','));
186 if (m_dataSocket != null)
187 m_dataSocket.close();
188 m_dataSocket = new ServerSocket(0);
189 int port = m_dataSocket.getLocalPort();
190 command.append(',');
191 command.append(port/256);
192 command.append(',');
193 command.append(port%256);
194 sendCommand( command.toString() );
195 }
196 } // dataPort
197
198 public void representationType(char type)
199 throws IOException
200 {
201 representationType(type, NON_PRINT_FORMAT);
202 transferMode(COMPRESSED_MODE);
203 }
204
205 public void representationType(char type, char format)
206 throws IOException
207 {
208 //sendCommand("TYPE " + type + ' ' + format);
209 sendCommand("TYPE " + type);
210 }
211
212
213 public FtpInputStream retrieveBinary(String path)
214 throws IOException
215 {
216 transferMode(COMPRESSED_MODE);
217 sendCommand("RETR " + path);
218
219 // if (!getResponse().isPositivePreliminary())
220 // return null;
221 if ( getResponse().likelyToFail() )
222 return null;
223
224 // m_dataXfrSocket = m_dataSocket.accept();
225 m_dataXfrSocket = getSocket();
226 if (m_dataXfrSocket == null)
227 {
228 return null;
229 }
230
231 InputStream istr = m_dataXfrSocket.getInputStream();
232 //InputStreamReader in = new InputStreamReader(istr);
233 return new FtpInputStream(istr, this);
234 }
235
236
237
238 public FtpReader retrieve(String path)
239 throws IOException
240 {
241 sendCommand("RETR " + path);
242 // System.out.println("The response is " + getResponse().getMessage());
243 // if (!getResponse().isPositivePreliminary())
244 // return null;
245 if ( getResponse().likelyToFail() )
246 return null;
247
248 // m_dataXfrSocket = m_dataSocket.accept();
249 m_dataXfrSocket = getSocket();
250 if (m_dataXfrSocket == null)
251 {
252 return null;
253 }
254
255 InputStream istr = m_dataXfrSocket.getInputStream();
256 InputStreamReader in = new InputStreamReader(istr);
257 return new FtpReader(in, this);
258 }
259
260 public FtpWriter store(String path)
261 throws IOException
262 {
263 sendCommand("STOR " + path);
264 // if (!getResponse().isPositivePreliminary())
265 // return null;
266 if ( getResponse().likelyToFail() )
267 return null; // Actually it should throw exception with proper response message
268
269 // m_dataXfrSocket = m_dataSocket.accept();
270 m_dataXfrSocket = getSocket();
271 if (m_dataXfrSocket == null)
272 {
273 return null;
274 }
275
276 OutputStream ostr = m_dataXfrSocket.getOutputStream();
277 OutputStreamWriter out = new OutputStreamWriter(ostr);
278 return new FtpWriter(out, this);
279 }
280
281 public FtpOutputStream storeBinary(String path)
282 throws IOException
283 {
284 transferMode(COMPRESSED_MODE);
285 sendCommand("STOR " + path);
286 // System.out.println("STOR message is " + getResponse().getMessage());
287 // System.out.println("STOR return code is " + getResponse().getReturnCode() );
288 // if ( !getResponse().isPositivePreliminary() )
289 // return null;
290
291 if ( getResponse().likelyToFail() )
292 return null;
293
294
295 // m_dataXfrSocket = m_dataSocket.accept();
296 m_dataXfrSocket = getSocket();
297 if (m_dataXfrSocket == null)
298 {
299 return null;
300 }
301
302 OutputStream ostr = m_dataXfrSocket.getOutputStream();
303 //OutputStreamWriter out = new OutputStreamWriter(ostr);
304 //return new FtpWriter(out, this);
305 return new FtpOutputStream(ostr,this);
306 }
307
308
309 public FtpWriter append(String path) throws IOException
310 {
311 sendCommand("APPE " + path);
312 // if (!getResponse().isPositivePreliminary())
313 // return null;
314 if ( getResponse().likelyToFail() )
315 return null;
316
317 // m_dataXfrSocket = m_dataSocket.accept();
318 m_dataXfrSocket = getSocket();
319 if (m_dataXfrSocket == null)
320 {
321 return null;
322 }
323
324 OutputStream ostr = m_dataXfrSocket.getOutputStream();
325 OutputStreamWriter out = new OutputStreamWriter(ostr);
326 return new FtpWriter(out, this);
327 }
328
329 public void renameFrom(String path)
330 throws IOException
331 {
332 sendCommand("RNFR " + path);
333 }
334
335 public void renameTo(String path)
336 throws IOException
337 {
338 sendCommand("RNTO " + path);
339 }
340
341 public void delete(String path)
342 throws IOException
343 {
344 sendCommand("DELE " + path);
345 }
346
347 public void removeDirectory(String path)
348 throws IOException
349 {
350 sendCommand("RMD " + path);
351 }
352
353
354 public void makeDirectory(String path)
355 throws IOException
356 {
357 sendCommand("MKD " + path);
358 }
359
360
361 public void printWorkingDirectory()
362 throws IOException
363 {
364 sendCommand("PWD");
365
366 }
367 private FtpReader listWithSpaces(String path)
368 throws IOException
369 {
370 // System.out.println("This path has spaces, needs special processing");
371 sendCommand("CWD " + path);
372 // if (getResponse().isPositiveCompletion()){
373 //System.out.println("CWD successful..");
374 // return list(null);
375 if ( getResponse().likelyToSucceed() )
376 {
377 return list( null );
378
379 }else{
380 //System.out.println("Dir/File does not exist for CWD");
381 String failureMessage = "No such file or directory";
382 StringReader sReader = new StringReader(failureMessage);
383 return new FtpReader(sReader, this);
384 }
385
386 }
387
388 public FtpReader list(String path)
389 throws IOException
390 {
391 if (path == null)
392
393 sendCommand("LIST");
394
395 else
396 {
397 if(path.indexOf(" ")>-1) return listWithSpaces(path);
398 else sendCommand("LIST " + path);
399 //sendCommand("LIST " + path);
400 }
401
402 // if ( !getResponse().isPositivePreliminary() )
403 // return null;
404 // System.out.println ("Response "+getResponse().getMessage());
405 if ( getResponse().likelyToFail() )
406 return null;
407
408 //System.out.println("accepting the socket..");
409
410 // m_dataXfrSocket = m_dataSocket.accept();
411 m_dataXfrSocket = getSocket();
412 if (m_dataXfrSocket == null)
413 {
414 return null;
415 }
416
417 //System.out.println("getting the instream");
418 InputStream istr = m_dataXfrSocket.getInputStream();
419 //System.out.println("making a reader to return..");
420 InputStreamReader in = new InputStreamReader(istr);
421 FtpReader reader = new FtpReader(in, this);
422 return reader;
423
424 }
425
426
427 public FtpReader list()
428 throws IOException
429 {
430 return list(null);
431 }
432
433 void setResponse()
434 throws IOException
435 {
436 m_response = new FtpResponse(in);
437 }
438
439
440 public FtpReader nameList(String path) throws IOException
441 {
442 if (path == null)
443 sendCommand("NLIST");
444 else
445 sendCommand("NLST " + path);
446 // if (!getResponse().isPositivePreliminary())
447 // return null;
448
449 if ( getResponse().likelyToFail() )
450 return null;
451
452
453 // m_dataXfrSocket = m_dataSocket.accept();
454 m_dataXfrSocket = getSocket();
455 if (m_dataXfrSocket == null)
456 {
457 return null;
458 }
459
460 InputStream istr = m_dataXfrSocket.getInputStream();
461 InputStreamReader in = new InputStreamReader(istr);
462 return new FtpReader(in, this);
463 }
464
465 public FtpReader nameList() throws IOException
466 {
467 return nameList(null);
468 }
469
470
471 void closeTransferSocket()
472 throws IOException
473 {
474 if(m_dataXfrSocket==null) return;
475 m_dataXfrSocket.close();
476 setResponse();
477 }
478
479 private void sendCommand(String command)
480 throws IOException
481 {
482
483 if(!isMacintosh)
484 {
485 out.write(command);
486 out.newLine();
487 out.flush();
488 }
489 else
490 {
491 out.write(command);
492 out.write("\r\n");
493 out.flush();
494 }
495
496 setResponse();
497 }
498
499 public FtpResponse getResponse()
500 {
501 return m_response;
502 }
503
504
505 /**
506 *
507 *
508 */
509 protected Socket createPassiveDataSocket()
510 throws IOException
511 {
512 // PASSIVE command - tells the server to listen for
513 // a connection attempt rather than initiating it
514 sendCommand("PASV");
515 FtpResponse response = getResponse();
516
517 // Validate the reply
518 String sReturnCode = response.getReturnCode();
519 System.out.println("FTP PASV command return code: " + sReturnCode);
520 if (sReturnCode.equals("227") == false)
521 {
522 System.out.println("FTP - Invalid return code for 'PASV' command: " + sReturnCode);
523 return null;
524 }
525
526 // Get the actual reply message text
527 String sReply = response.getMessage();
528 System.out.println("FTP PASV command response message: " + sReply);
529
530 // The reply to PASV is in the form:
531 // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
532 // where h1..h4 are the IP address to connect and
533 // p1,p2 the port number
534 // Example:
535 // 227 Entering Passive Mode (128,3,122,1,15,87).
536
537 // extract the IP data string from between the brackets
538 int bracket1 = sReply.indexOf('(');
539 int bracket2 = sReply.indexOf(')');
540 if ((bracket1 == -1) || (bracket2 == -1))
541 {
542 System.out.println("FTP - Invalid reply for 'PASV' command, missing bracket(s): " + sReply);
543 }
544
545 String ipData = sReply.substring(bracket1+1,bracket2);
546 int parts[] = new int[6];
547
548 int len = ipData.length();
549 int partCount = 0;
550 StringBuffer buf = new StringBuffer();
551
552 // loop thru and examine each char
553 for (int i = 0; i < len && partCount <= 6; i++)
554 {
555 char ch = ipData.charAt(i);
556 if (Character.isDigit(ch))
557 {
558 buf.append(ch);
559 }
560
561 else if (ch != ',')
562 {
563 System.out.println("FTP - Malformed PASV reply (a): " + sReply);
564 return null;
565 }
566
567 // get the part
568 if (ch == ',' || i+1 == len)
569 { // at end or at separator
570 try
571 {
572 parts[partCount++] = Integer.parseInt(buf.toString());
573 buf.setLength(0);
574 }
575
576 catch (NumberFormatException ex)
577 {
578 System.out.println("FTP - Malformed PASV reply (b): " + sReply);
579 return null;
580 }
581 } // ch if
582 } // for i
583
584 // assemble the IP address
585 // we try connecting, so we don't bother checking digits etc
586 String ipAddress = parts[0] + "."+ parts[1]+ "." + parts[2] + "." + parts[3];
587
588 // assemble the port number
589 int port = (parts[4] << 8) + parts[5];
590
591 // create the socket
592 return new Socket(ipAddress, port);
593
594 } // createPassiveDataSocket
595
596 /**
597 *
598 *
599 */
600 protected Socket getSocket()
601 throws IOException
602 {
603 // Get the appropriate data socket depending on the specified mode (PASSIVE/ACTIVE)
604 if (bPassive == true)
605 {
606 System.out.println("FTP - using PASSIVE data socket: " + passiveSocket.toString());
607 return(passiveSocket);
608 }
609
610 else
611 {
612 // Return the ACTIVE socket
613 Socket activeSocket = m_dataSocket.accept();
614 System.out.println("FTP - using ACTIVE data socket: " + activeSocket.toString());
615 return(activeSocket);
616 }
617 } // get Socket
618
619 } // FtpSession
620
621
622
623
624
625