Source code: com/yaftp/ftp/FtpClientSession.java
1 /**
2 *
3 * CopyRights Jean-Yves MENGANT 1999,2000,2001,2002
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 package com.yaftp.ftp ;
21
22 /**
23
24 Build a basic FTP prococol class using Multiple java threads
25
26 @Author Jean-Yves MENGANT
27 @version : 0.0.1
28
29 */
30
31 import java.net.* ;
32 import java.io.* ;
33 import java.util.* ;
34 import java.lang.Thread ;
35
36 // Local project utils imports
37 import com.yaftp.utils.* ;
38
39
40 /**
41
42 FtpClientSession is the main Ftp access class
43
44 */
45
46 public class FtpClientSession
47 implements FtpConnection
48 {
49 private static final String _EMPTY_ = "" ;
50 private static final int FTP_DEFAULT_COMMAND_PORT = 21 ;
51 private static final int FTP_DEFAULT_DATA_PORT = 1150 ;
52 private static final short MAX_UNSIGNED_BYTE = 256 ;
53
54 // Some stable FTP standard return code categories defined as constant
55
56 private static final int FTP_POSITIVE_PRELIMINARY_REPLY = 1 ;
57 private static final int FTP_POSITIVE_COMPLETION_REPLY = 2 ;
58 private static final int FTP_POSITIVE_INTERMEDIATE_REPLY = 3 ;
59 private static final int FTP_TRANSIENT_NEGATIVE_COMPLETION = 4 ;
60 private static final int FTP_PERMANENT_NEGATIVE_COMPLETION = 5 ;
61
62 private static final int FTP_COMMAND_OK = 200 ;
63 private static final int FTP_SYSTEM_TYPE = 215 ;
64 private static final int FTP_SUCCESSFULL_CONNECTION = 220 ;
65 private static final int FTP_FILE_NOT_AVAILABLE = 550 ;
66 private static final int FTP_PASV_OPEN = 227 ;
67 private static final int FTP_PASV_CLOSE = 226 ;
68 /** this code is used by MVS FTP server */
69 private static final int FTP_PASV_CLOSE_ALTERNATE = 250 ;
70
71 private static final String _OPENPAR_ = "(" ;
72 private static final String _CLOSEPAR_ = ")" ;
73 private static final String _COMMA_ = "," ;
74 private static final String _DOT_ = "." ;
75
76
77 private int _commandPort = FTP_DEFAULT_COMMAND_PORT ;
78 private String _ftpSrvName ;
79 transient private CommandSocket _cmdSock ; // Use this socket for command
80 private String _currentUser ;
81 private String _currentPsw ;
82
83 transient private FtpTraces _traces = new FtpTraces(this) ; // optional ftp traces
84
85 transient private FtpDataNotifier _dataEvt ;// DATA is ready
86 transient private Ftp _parent ; // the Launcher
87
88 transient private FtpSessionOs _serverOS ; // What is FTP Server Os
89 transient private String _lastCommandMessage ;
90 transient private String _directoryMessage ;
91
92 public String get_lastCommandMessage()
93 { return _lastCommandMessage ; }
94
95 public String getDirectoryMessage()
96 { return _directoryMessage ; }
97
98
99 /**
100
101 CommandSocket INNER CLASS is used to deal with FTP COMMAND port
102
103 */
104
105 // Use socket below to send commands to FTP server
106 class CommandSocket extends Socket {
107
108 private BufferedWriter _cmdStream ; // Use it to build FTP commands
109 private BufferedReader _answStream ; // Use it to collect command status
110 private int _lastReturn ; // value of last command return code
111 private int _lastReturnType ; // Last return error category
112 private String _lastMessage ; // Last returned Message
113 private DataSocket _dataSock ; // Use this for Ftp Data
114 private FtpClientListener _rListener ; // the one listening to data
115 private Socket _pasvSocket ; // client socket used to RETR files in PASV mode
116
117 // following data is stored in order to be able to reconnect when
118 // FTP session times out
119 private String _loggedUser ;
120 private String _ftpPaswd ;
121
122 // Collect Ftp server last command Status
123 private void getStatus()
124 throws ClientFtpError
125 {
126 String header = _EMPTY_ ;
127 String headerFirst = _EMPTY_ ;
128 try {
129 _lastMessage = _answStream.readLine() ;
130
131 // this may happen if ftp connection has broken
132 if ( _lastMessage == null )
133 return ;
134
135 if ( _lastMessage.length() >= 3 )
136 {
137 header = _lastMessage.substring(0,3) ;
138 headerFirst = _lastMessage.substring(0,1) ;
139
140 StringBuffer collector = new StringBuffer( _lastMessage ) ;
141 while ( _lastMessage.charAt(3) == '-' )
142 {
143 _lastMessage = _answStream.readLine() ;
144 _traces.header(_lastMessage) ;
145 collector.append( ";" + _lastMessage ) ;
146 }
147 _lastMessage = collector.toString() ;
148 }
149
150 } catch ( IOException e )
151 { throw new ClientFtpError(" Ftp socket Read Status error " +
152 e.toString() ) ;
153 }
154
155 try {
156 _lastReturn = new Integer(header).intValue() ;
157 _lastReturnType = new Integer(headerFirst).intValue() ;
158 } catch ( NumberFormatException e )
159 {
160 // sometimes FTP servers may not comply the RFC about status
161 // starting with 3 digits codes and assume that message is informative
162 // and assume a command OK return code then
163 _lastReturnType = FTP_POSITIVE_COMPLETION_REPLY ;
164 _lastReturn = FTP_COMMAND_OK ;
165 }
166
167 if (( _lastReturnType == FTP_TRANSIENT_NEGATIVE_COMPLETION ) ||
168 ( _lastReturnType == FTP_PERMANENT_NEGATIVE_COMPLETION )
169 )
170 {
171 _traces.error(_lastMessage) ;
172 throw new ClientFtpError( _lastMessage ) ;
173 }
174 else
175 _traces.header(_lastMessage) ;
176
177 _traces.trace("last error type is : " + _lastReturnType ) ;
178 _lastCommandMessage = _lastMessage ;
179 }
180
181 // Sending a command to FTP server
182 private void sendCommand( String cmd )
183 throws ClientFtpError
184 {
185 _dataEvt.setDataNotAvailable() ; // Reset read data availability to false
186 if ( _rListener != null )
187 _rListener.set_dataMessage(null) ; // Reset any data process error
188 _traces.trace(" before executing : "+ cmd) ;
189 try {
190 _cmdStream.write(cmd + "\n") ;
191 _cmdStream.flush() ;
192 } catch ( IOException e )
193 { throw new ClientFtpError(" Ftp socket Write Command error " +
194 e.toString() ) ;
195 }
196 getStatus() ;
197 }
198
199 // Launch DATAListener thread and give back listening port number
200 private void launchDATAListenerThread()
201 throws ClientFtpError
202 {
203 _dataSock = new DataSocket() ;
204 // Start Data Thread
205 _traces.trace("Starting Thread for :"+_dataSock.toString()) ;
206 _dataSock.start() ;
207
208 }
209
210 // build a full valid PORT command string ready to execute
211 private String buildPORTCommand( int dataPort )
212 throws ClientFtpError
213 {
214 // Since client connection may have multiple Internet adresses
215 // (ie : on PPP on ETH on Loopback ... the only way to kow
216 // which IP adress is used by FTP is to query the opened socket
217 StringBuffer hostAdr = new StringBuffer(getLocalAddress().getHostAddress()) ;
218
219 while ( hostAdr.toString().indexOf('.') != -1 )
220 hostAdr.setCharAt( hostAdr.toString().indexOf('.') , ',' ) ;
221
222 int hiPort = BYTEFX.HIBYTE((short)dataPort) ;
223 int loPort = BYTEFX.LOWBYTE((short)dataPort) ;
224
225 if ( hiPort < 0 )
226 hiPort = MAX_UNSIGNED_BYTE + hiPort ;
227
228 if ( loPort < 0 )
229 loPort = MAX_UNSIGNED_BYTE + loPort ;
230
231 _traces.trace(" Port : " + dataPort + "h :" + hiPort +" l: " + loPort) ;
232
233 return( "PORT " + hostAdr + "," + String.valueOf(hiPort) + ","
234 + String.valueOf(loPort)
235 ) ;
236 }
237
238 // Sync with Data
239 private void cmdWaitForDATA()
240 throws ClientFtpError
241 {
242 _dataEvt.waitForData() ;
243 // check that nothing bad has happened in data socket thread
244 if ( _rListener.get_dataMessage() != null )
245 throw new ClientFtpError( _rListener.get_dataMessage() ) ;
246 }
247
248 private void pasvReceiveData()
249 throws ClientFtpError
250 {
251 // Activate reader
252 try {
253 _rListener.readData( _pasvSocket.getInputStream() , _dataEvt ) ;
254 _pasvSocket.close() ;
255 _pasvSocket = null ;
256 } catch( IOException e ) {
257 throw new ClientFtpError( " PASV reader failed IOERR : " +
258 e.toString()
259 ) ;
260 }
261 // check that nothing bad has happened in data socket thread
262 if ( _rListener.get_dataMessage() != null )
263 throw new ClientFtpError( _rListener.get_dataMessage() ) ;
264 }
265
266 private void pasvSendData()
267 throws ClientFtpError
268 {
269 // Activate reader
270 try {
271 _rListener.writeData( _pasvSocket.getOutputStream() , _dataEvt ) ;
272 _pasvSocket.close() ;
273 _pasvSocket = null ;
274 } catch( IOException e ) {
275 throw new ClientFtpError( " PASV writer failed IOERR : " +
276 e.toString()
277 ) ;
278 }
279 // check that nothing bad has happened in data socket thread
280 if ( _rListener.get_dataMessage() != null )
281 throw new ClientFtpError( _rListener.get_dataMessage() ) ;
282 }
283
284 // FTP implemented COMMAND list in UPPERCASE follow
285
286 // set data Callback object
287 public void set_rListener( FtpClientListener callback )
288 {
289 _rListener = callback ;
290 _dataSock.set_callback(callback) ;
291 }
292
293 // Basic NOOP for testing purposes
294 public void NOOP()
295 throws ClientFtpError
296 { sendCommand("NOOP") ; }
297
298 // Basic SITE
299 public String SITE( String value )
300 throws ClientFtpError
301 {
302 sendCommand("SITE "+value) ;
303 return(_lastMessage) ;
304 }
305
306 // Basic LOGIN to ftp server
307 public void LOGIN( String userid ,
308 String Passwd ,
309 FtpSessionOs sessionOs
310 )
311 throws ClientFtpError
312 {
313 _currentUser = userid ;
314 _currentPsw = Passwd ;
315
316 if ( _currentPsw == null )
317 _currentPsw = new String("") ;
318
319 sendCommand( "USER "+ _currentUser ) ;
320 sendCommand( "PASS " + _currentPsw ) ;
321 SYST() ; // Query system after login some FTP do not support this
322 // command before
323 if ( sessionOs == null ) // not choosed by user try to guess it
324 _serverOS = new FtpSessionOs(_lastMessage) ;
325 else
326 _serverOS = sessionOs ;
327 }
328
329 // do a basic LIST on server current directory using PORT protocol
330 public void LISTPORT()
331 throws ClientFtpError
332 {
333 _dataSock.setReader() ;
334 sendCommand( buildPORTCommand( _dataSock.getPort() ) ) ;
335 sendCommand("LIST") ;
336 cmdWaitForDATA() ;
337 getStatus() ;
338 }
339
340 // do a basic LIST on server current directory using PORT protocol
341 public void LISTPASV()
342 throws ClientFtpError
343 {
344 _dataSock.setReader() ;
345 PASV() ;
346 sendCommand("LIST") ;
347 pasvReceiveData() ;
348 PASV() ;
349 getStatus() ;
350 }
351
352 // send QUIT
353 public void QUIT()
354 throws ClientFtpError
355 { sendCommand("QUIT") ; }
356
357 // use SYST to determine host type
358 public void SYST()
359 throws ClientFtpError
360 {
361 sendCommand("SYST") ;
362 // Some FTP server may provide UNEXPECTED VERBOSITY
363 // on this command and are not emitting the expected 215
364 // return status immediately => we should track it
365
366 while ( _lastReturn != FTP_SYSTEM_TYPE )
367 {
368 if ( _lastReturnType > FTP_POSITIVE_INTERMEDIATE_REPLY ) // give up on severe
369 throw new ClientFtpError( _lastMessage ) ;
370 getStatus() ;
371 }
372 }
373
374 public void CWDIR( String dirName )
375 throws ClientFtpError
376 {
377 _serverOS.convertDirectory(dirName) ;
378 sendCommand("CWD " + _serverOS.getCurDirectory() ) ;
379 _directoryMessage = _serverOS.checkCurDirectory( _lastMessage ) ;
380 }
381
382 public void MKDIR( String dirName )
383 throws ClientFtpError
384 {
385 _serverOS.convertDirectory(dirName) ;
386 sendCommand("MKD " + _serverOS.getCurDirectory() ) ;
387 }
388
389 public void RMDIR( String dirName )
390 throws ClientFtpError
391 {
392 _serverOS.convertDirectory(dirName) ;
393 sendCommand("RMD " + _serverOS.getCurDirectory() ) ;
394 }
395
396 public void RENAME( String from , String to )
397 throws ClientFtpError
398 {
399 sendCommand("RNFR " + from ) ;
400 sendCommand("RNTO " + to ) ;
401 }
402
403 // send a DELE command on given file
404 public boolean DELETE( String candidate )
405 throws ClientFtpError
406 {
407 try {
408 sendCommand("DELE " + candidate ) ;
409 } catch ( ClientFtpError e )
410 {
411 // just capture the file not found case
412 if ( _lastReturn == FTP_FILE_NOT_AVAILABLE )
413 return false ;
414 else
415 throw e ;
416 }
417 return true ;
418 }
419
420 // GET requested file using PORT protocol
421 public void GETPORT ( String fName )
422 throws ClientFtpError
423 {
424 _serverOS.parseFileName(fName) ;
425 if (
426 ( _serverOS.getCurDirectory() != null )&&
427 ( _serverOS.hasDirChanged() )
428 )
429 sendCommand("CWD " + _serverOS.getCurDirectory() ) ;
430 _dataSock.setReader() ;
431 sendCommand( buildPORTCommand( _dataSock.getPort() ) ) ;
432 sendCommand("RETR " + _serverOS.getCurFile() ) ;
433 cmdWaitForDATA() ;
434 getStatus() ;
435 }
436
437 // GET requested file using PASV protocol
438 public void GETPASV ( String fName )
439 throws ClientFtpError
440 {
441 _serverOS.parseFileName(fName) ;
442 if (
443 ( _serverOS.getCurDirectory() != null )&&
444 ( _serverOS.hasDirChanged() )
445 )
446 sendCommand("CWD " + _serverOS.getCurDirectory() ) ;
447 PASV() ;
448 sendCommand("RETR " + _serverOS.getCurFile() ) ;
449 pasvReceiveData() ;
450 PASV() ;
451 getStatus() ;
452 }
453
454 // PUT requested file in PORT mode
455 public void PUTPORT ( String fName )
456 throws ClientFtpError
457 {
458 _serverOS.parseFileName(fName) ;
459 if (
460 ( _serverOS.getCurDirectory() != null )&&
461 ( _serverOS.hasDirChanged() )
462 )
463 sendCommand("CWD " + _serverOS.getCurDirectory() ) ;
464 _dataSock.setWriter() ;
465 sendCommand( buildPORTCommand( _dataSock.getPort() ) ) ;
466 sendCommand("STOR " + _serverOS.getCurFile() ) ;
467 cmdWaitForDATA() ;
468 getStatus() ;
469 }
470
471 // GET requested file using PASV protocol
472 public void PUTPASV ( String fName )
473 throws ClientFtpError
474 {
475 _serverOS.parseFileName(fName) ;
476 if (
477 ( _serverOS.getCurDirectory() != null )&&
478 ( _serverOS.hasDirChanged() )
479 )
480 sendCommand("CWD " + _serverOS.getCurDirectory() ) ;
481 PASV() ;
482 sendCommand("STOR " + _serverOS.getCurFile() ) ;
483 pasvSendData() ;
484 PASV() ;
485 getStatus() ;
486 }
487
488
489 // Just Stop DATA thread
490 // And Quit FTP
491 public void STOP()
492 throws ClientFtpError
493 {
494 QUIT() ;
495 if ( _dataSock != null )
496 {
497 _traces.trace("before STOP :"+_dataSock.toString()) ;
498 _dataSock.shutDown() ;
499 }
500 try {
501 _cmdSock.close() ;
502 } catch ( IOException e ) {
503 throw new ClientFtpError(" close CmdSock failed " +
504 e.toString() ) ;
505 }
506 }
507
508 // Give back current directory info
509 public String PRINTWD()
510 throws ClientFtpError
511 {
512 sendCommand("PWD") ;
513 // return bypassing FTP error code number
514 return(_lastMessage.substring(3));
515 }
516
517 // set transfer mode to ASCII
518 public void ASCII()
519 throws ClientFtpError
520 {
521 sendCommand("TYPE A N") ;
522 }
523
524 // set transfer mode to BINARY
525 public void BINARY()
526 throws ClientFtpError
527 {
528 sendCommand("TYPE I") ;
529 }
530
531 private void buildPasvSocket( String msg227 )
532 throws ClientFtpError
533 {
534 int openPar = msg227.indexOf(_OPENPAR_) ;
535 int closePar = msg227.indexOf(_CLOSEPAR_) ;
536 String socketPort = msg227.substring(openPar+1,closePar) ;
537 StringTokenizer parser = new StringTokenizer(socketPort , _COMMA_ ) ;
538 try {
539 StringBuffer ipAddress = new StringBuffer( parser.nextToken()) ;
540 ipAddress.append(_DOT_) ;
541 ipAddress.append(parser.nextToken()) ;
542 ipAddress.append(_DOT_) ;
543 ipAddress.append(parser.nextToken()) ;
544 ipAddress.append(_DOT_) ;
545 ipAddress.append(parser.nextToken()) ;
546 try {
547 int p1 = Integer.parseInt(parser.nextToken()) ;
548 int p2 = Integer.parseInt(parser.nextToken()) ;
549 int port = (p1*256)+p2 ;
550 try {
551 _pasvSocket = new Socket(ipAddress.toString(),port) ;
552 } catch ( IOException g )
553 { throw new ClientFtpError("unexpected IOERR opening PASV socket : "+g.getMessage());}
554 } catch ( NumberFormatException e )
555 { throw new ClientFtpError("bad port on 227 msg : "+msg227 ) ; }
556 } catch ( NoSuchElementException f )
557 { throw new ClientFtpError("bad msg 227 format : "+ msg227 ) ; }
558 }
559
560 // send a PASV request building a client connection if succeeded
561 // the connection will be used at RET time
562 public void PASV()
563 throws ClientFtpError
564 {
565 sendCommand("PASV") ;
566 if ( _lastReturn == FTP_PASV_OPEN ) // a 227 message is expected for PASV
567 buildPasvSocket(_lastMessage) ;
568 else if ( ( _lastReturn == FTP_PASV_CLOSE ) || // a 226 message may request closing the socket
569 ( _lastReturn == FTP_PASV_CLOSE_ALTERNATE ) // a 250 is thrown by MVS
570 ) ;
571 else
572 throw new ClientFtpError("expecting 227,226 or 250 msg instead of : " + _lastMessage ) ;
573 }
574
575 public String FTPCOMMAND( String command )
576 throws ClientFtpError
577 {
578 sendCommand(command) ;
579 return(_lastMessage) ;
580 }
581
582 // CommandSocket Constructor
583 public CommandSocket( String ftpSrvName ,
584 int commandPort ,
585 FtpClientListener listener
586 )
587 throws IOException, ClientFtpError
588 // Connect to FtpServer and initialize streams
589 {
590 super( ftpSrvName , commandPort ) ;
591
592 _rListener = listener ;
593 _cmdStream = new BufferedWriter(
594 new OutputStreamWriter( getOutputStream() )
595 ) ;
596 _answStream = new BufferedReader (
597 new InputStreamReader( getInputStream() )
598 ) ;
599
600 getStatus() ;
601 if ( _lastReturn != FTP_SUCCESSFULL_CONNECTION )
602 throw new ClientFtpError("Connection error :" + _lastMessage );
603
604 // Launch the DATA listener Thread
605 launchDATAListenerThread() ;
606
607 // try a NOOP and SYST after successfull connection
608 // Use SYST result to get FTP SERVER OS
609 // not a good idea to do a NOOP here since server may refuse
610 // the operation until the user is logged in
611 // NOOP() ;
612 }
613 }
614
615 /**
616
617 DataSocket INNER CLASS is a threaded data transfert class used to
618 listen on FTP data port
619
620 */
621
622 class DataSocket extends Thread {
623
624 private ServerSocket _dataSocket ;
625 private boolean _reader ; // should we read or write data ????
626 private boolean _stopping = false ;
627 private FtpClientListener _callBack ;
628
629 /**
630 LighWeight data Thread launched by main data thread to work on sockets
631 */
632 class LaunchDataThread extends Thread {
633
634 private Socket _lSock ;
635 private boolean _reader ;
636
637 public LaunchDataThread( Socket lsock , boolean reader )
638 {
639 _lSock = lsock ;
640 _reader = reader ;
641 }
642
643 // Thread execution
644 // Listen waiting for incoming ftp connections
645 public void run ()
646 {
647 if ( _reader )
648 {
649 // Callback giving called back data stream to empty
650 try {
651 _callBack.readData( _lSock.getInputStream() , _dataEvt );
652
653 } catch( IOException e ) {
654 _callBack.dataError( " Data thread reader build failed " +
655 e.toString() ,
656 _dataEvt
657 ) ;
658 }
659 }
660 else
661 {
662 try {
663 _callBack.writeData( _lSock.getOutputStream() , _dataEvt ) ;
664
665 } catch( IOException e ) {
666 _callBack.dataError( " Data thread writer build failed " +
667 e.toString() ,
668 _dataEvt
669 ) ;
670 }
671 }
672 try {
673 _lSock.close() ;
674 _traces.trace( "local work Socket closed ") ;
675 } catch( IOException e ) {
676 _callBack.dataError( "Data thread close Socket failed " + e.toString(),
677 _dataEvt
678 ) ;
679 }
680 }
681 }
682
683 // Actuate the data listener
684 public void set_callback( FtpClientListener ftpLstn )
685 { _callBack = ftpLstn ; }
686
687 // public constructor
688 public DataSocket()
689 {
690 super() ;
691 }
692
693 // Actuators
694 public final void setReader() { _reader = true ; }
695 public final void setWriter() { _reader = false; }
696
697 // tell caller on which port we are listening
698 public final int getPort()
699 { return _dataSocket.getLocalPort() ; }
700
701 // Set the stopping Flag on and Connect to port to force
702 // connection to terminate(NB : this method is called by a different thread)
703 public void shutDown()
704 throws ClientFtpError
705 {
706 _stopping = true ; // Force STOP listening
707 try {
708 // cmdSock should be queried since given computer may have
709 // multiple IP interfaces availables
710 Socket stopSock = new Socket( _cmdSock.getLocalAddress() , getPort() );
711 } catch ( IOException e )
712 {
713 throw new ClientFtpError("unable to connect dataSocket "+e.toString());
714 }
715 }
716
717 /**
718 Thread execution
719 Listening waiting for incoming SERVER side ftp connections
720 */
721 public void run ()
722 {
723 Socket lSock = null ;
724 _traces.trace( "data Socket thread started" ) ;
725
726 try {
727 while ( ! _stopping )
728 {
729 _dataSocket = new ServerSocket(0) ;
730 _traces.trace( "data Socket enters listening on :"
731 + _dataSocket.getLocalPort()
732 ) ;
733 lSock = _dataSocket.accept() ;
734 if ( _stopping )
735 _traces.trace( "Data Socket thread asked to terminate") ;
736 else
737 {
738 _traces.trace( "Data Socket accepting incoming connection") ;
739
740 LaunchDataThread th = new LaunchDataThread(lSock,_reader) ;
741 th.start() ; // Start DATA reading or Writin process
742 }
743 _dataSocket.close() ;
744 }
745 } catch ( IOException e ){
746 // Activate the callback routine with a Null data and error Msg
747 _callBack.dataError(
748 " main Data thread startup failed " + e.toString() ,
749 _dataEvt
750 ) ;
751 }
752
753 }
754 }
755
756
757 /**
758 Bean compliant class constructor
759 */
760 public FtpClientSession()
761 {}
762
763 /**
764 Init debugging on (Should be set before ftpInit call
765 */
766 public void set_parent( Ftp parent )
767 {
768 _parent = parent ;
769
770 if ( _parent.is_debug() )
771 {
772 _traces.set_graphics( _parent.is_graphicalDebug() );
773 _traces.set_trace(true);
774 }
775 }
776
777 public FtpTraces get_traces()
778 { return _traces ; }
779
780 public final void trace( String message )
781 {
782 _traces.trace(message);
783 }
784
785 // Initialize FTP server connection
786 public synchronized void ftpInit( String ftpSrvName )
787 throws ClientFtpError
788 {
789 _ftpSrvName = ftpSrvName ;
790 _dataEvt = new FtpDataNotifier() ;
791
792 try {
793 _cmdSock = new CommandSocket(_ftpSrvName , _commandPort , null ) ;
794 } catch ( IOException e )
795 { throw new ClientFtpError(" Ftp socket Connection error " +
796 e.toString() ) ;
797 }
798 }
799
800 public String get_ftpSrvName()
801 { return _ftpSrvName ; }
802
803 /**
804
805 Log in into FTP server
806
807 @param userID user id connecting to FTP server
808 @param passwd associated password
809
810 */
811 public synchronized void login ( String userID ,
812 String passwd
813 )
814 throws ClientFtpError
815 {
816 _cmdSock.LOGIN( userID , passwd , null ) ;
817 _currentUser = userID ;
818 _currentPsw = passwd ;
819 }
820
821 /**
822
823 Log in into FTP server
824
825 @param userID user id connecting to FTP server
826 @param passwd associated password
827
828 */
829 public synchronized void login ( String userID ,
830 String passwd ,
831 FtpSessionOs ftpOs
832 )
833 throws ClientFtpError
834 {
835 _cmdSock.LOGIN( userID , passwd , ftpOs ) ;
836 _currentUser = userID ;
837 _currentPsw = passwd ;
838 }
839
840 /**
841
842 use this method to reconnect a timed out session ; the
843 FTP session will be negociated using login provided userid and
844 passwords
845
846 */
847 public synchronized void reconnectSession ()
848 throws ClientFtpError
849 {
850 if ( ( _currentUser == null ) || ( _currentPsw == null ) )
851 throw new ClientFtpError("undefined user/passwd : unable to reconnect " ) ;
852 ftpInit(_ftpSrvName) ;
853 _cmdSock.LOGIN( _currentUser , _currentPsw , _parent.get_ftpOs() ) ;
854 }
855
856 /**
857
858 Loging out Ftp Client Session
859
860 */
861 public synchronized final void stop()
862 throws ClientFtpError
863 { _cmdSock.STOP() ; }
864
865 /**
866
867 return current FTP server working directory
868
869 @return current server working directory
870
871 */
872 public final String getWorkingDirectory()
873 throws ClientFtpError
874 { return (_cmdSock.PRINTWD()) ; }
875
876 /**
877 send a Site Command
878 */
879 public final String site( String siteValue )
880 throws ClientFtpError
881 {
882 return _cmdSock.SITE(siteValue);
883 }
884
885 /**
886 send a DELE Command
887 */
888 public final boolean delete( String candidate )
889 throws ClientFtpError
890 {
891 return _cmdSock.DELETE(candidate);
892 }
893
894 /**
895 send a RENAME Command
896 */
897 public final void rename( String from , String to )
898 throws ClientFtpError
899 {
900 _cmdSock.RENAME( from , to );
901 }
902
903 public final void removeDirectory( String candidate )
904 throws ClientFtpError
905 {
906 _cmdSock.RMDIR(candidate);
907 }
908
909 /**
910
911 list command with callback
912
913 @param callBack listener to be notified when list is ready
914
915 */
916 public synchronized void list( FtpClientListener callback )
917 throws ClientFtpError
918 {
919 _cmdSock.set_rListener(callback) ;
920 if ( _parent.is_pasv() )
921 _cmdSock.LISTPASV() ;
922 else
923 _cmdSock.LISTPORT() ;
924 }
925
926 /**
927
928 Filtered file list operation
929
930 @return the server side file list
931 */
932 public synchronized Vector filteredList()
933 throws ClientFtpError
934 {
935 return ( _serverOS.ftpList(this) ) ;
936 }
937
938 /**
939
940 proceed with a change directory command on server side
941
942 @param dirName directory location to point to
943 */
944 public synchronized void changeFtpSrvDir( String dirName )
945 throws ClientFtpError
946 {
947 _cmdSock.CWDIR(dirName) ;
948 }
949
950
951 /**
952
953 proceed with a make directory command on server side
954
955 @param dirName directory location to point to
956 */
957 public synchronized void newDirectory( String dirName )
958 throws ClientFtpError
959 {
960 _cmdSock.MKDIR(dirName) ;
961 }
962
963 /**
964
965 proceed with a remove directory command on server side
966
967 @param dirName directory location to point to
968 */
969 public synchronized void deleFtpSrvDir( String dirName )
970 throws ClientFtpError
971 {
972 _cmdSock.RMDIR(dirName) ;
973 }
974
975 /**
976
977 change transfert mode to binary
978
979 @param command ftp command to procees
980 */
981 public synchronized void ftpBinary()
982 throws ClientFtpError
983 {
984 _cmdSock.BINARY() ;
985 }
986
987 /**
988
989 execute a user entered FTP command
990
991 @param command ftp command to procees
992 */
993 public synchronized String ftpCommand( String command )
994 throws ClientFtpError
995 {
996 return _cmdSock.FTPCOMMAND( command ) ;
997 }
998
999 /**
1000
1001 change transfert mode to ASCII
1002
1003 @param command ftp command to procees
1004 */
1005 public synchronized void ftpAscii()
1006 throws ClientFtpError
1007 {
1008 _cmdSock.ASCII() ;
1009 }
1010
1011 /* IMPLEMENTING FtpConnection Interface */
1012
1013 /**
1014
1015 request info about the server OS hosting the FTP service
1016
1017 @return server side operating system name
1018 */
1019 public final String getOSFtp()
1020 { return _serverOS.toString() ; }
1021
1022 public final String getOsDetails()
1023 { return _serverOS.getOsDetails() ; }
1024
1025 public String[] get_column_names()
1026 { return _serverOS.get_Column_Names() ; }
1027
1028 public boolean isLocal()
1029 { return false ; }
1030
1031 public void changeDirectory( String dirName )
1032 throws ClientFtpError
1033 { changeFtpSrvDir(dirName) ; }
1034
1035 public boolean isMVS()
1036 { return _serverOS.isMVS() ; }
1037
1038 public void set_jesMode( boolean jesMode )
1039 { _serverOS.set_jesMode(jesMode) ; }
1040
1041 /* IMPLEMENTING FtpConnection Interface ENDS */
1042
1043 /**
1044
1045 FTP receive File Operation
1046
1047 @param fName remote file to be transfered locally
1048 @param callBack listener which will be notified when transfer completed
1049
1050 */
1051 public synchronized void getFile( String fName ,
1052 FtpClientListener callback
1053 )
1054 throws ClientFtpError
1055 {
1056 _traces.trace("GET : " + fName );
1057 _cmdSock.set_rListener(callback) ;
1058 if ( _parent.is_pasv() )
1059 _cmdSock.GETPASV(fName) ;
1060 else
1061 _cmdSock.GETPORT(fName) ;
1062 }
1063
1064 /**
1065
1066 FTP send File Operation
1067
1068 @param fName local file name to be put to server
1069 @param callBack listener which will be notified when transfer completed
1070
1071 */
1072 public synchronized void putFile ( String fName ,
1073 FtpClientListener callback
1074 )
1075 throws ClientFtpError
1076 {
1077 _cmdSock.set_rListener(callback) ;
1078 if ( _parent.is_pasv() )
1079 _cmdSock.PUTPASV(fName) ;
1080 else
1081 _cmdSock.PUTPORT(fName) ;
1082 }
1083
1084 /**
1085 <PRE>
1086 this main method is used to test and demonstrate FtpClientSession
1087 class usage to tranfert file in and out.
1088
1089 usage of FtpClientSession class is straightforward :
1090
1091 here is the main test static class content :
1092
1093 *************************** CODE STARTS *************************
1094
1095 String host = null ;
1096 String user = null ;
1097 String passw = null ;
1098 String fName = null ;
1099
1100 System.out.println( "Starting ftp test" ) ;
1101 if ( argv.length >= 1 )
1102 host = argv[0] ;
1103 if ( argv.length >= 2 )
1104 user = argv[1] ;
1105 if ( argv.length >= 3 )
1106 passw = argv[2] ;
1107 if ( argv.length >= 4 )
1108 fName = argv[3] ;
1109
1110 try {
1111 Ftp ftp = new Ftp() ;
1112 ftp.set_ftpServerName( host );
1113 FtpClientSession myFtp = new FtpClientSession() ;
1114 myFtp.set_parent( ftp ) ;
1115
1116 System.out.println( "Before init " ) ;
1117 myFtp.ftpInit(host) ;
1118 System.out.println( "Init done " ) ;
1119
1120 myFtp.login( user , passw ) ;
1121 System.out.println( "Login done " ) ;
1122
1123 FtpVectorListener fileList = new FtpVectorListener() ;
1124
1125 myFtp.list( fileList ) ;
1126
1127 System.out.println( "Ending ftp test part 1 " ) ;
1128
1129 if ( fName != null )
1130 try {
1131 DataOutputStream out = new DataOutputStream (
1132 new BufferedOutputStream (
1133 new FileOutputStream( fName )
1134 ) ) ;
1135
1136
1137 for ( int ii = 0 ; ii < fileList.get_dataList().size() ; ii++ )
1138 {
1139 System.out.println( (String)( fileList.get_dataList().elementAt(ii))) ;
1140 out.writeBytes((String)( fileList.get_dataList().elementAt(ii))+"\n") ;
1141 }
1142 out.close() ;
1143 } catch ( IOException e ){
1144 System.out.println( "Severe file access Error : " ) ;
1145 System.out.println( e.getMessage() ) ;
1146
1147 }
1148
1149 Vector fileFilteredList = myFtp.filteredList() ;
1150 System.out.println( "Ending ftp test part 2 " ) ;
1151
1152 for ( int ii = 0 ; ii < fileFilteredList.size() ; ii++ )
1153 System.out.println( (FtpOsFile)( fileFilteredList.elementAt(ii) ) ) ;
1154
1155 // resend FileList local sored file back to caller
1156
1157 if ( fName != null )
1158 myFtp.putFile(fName , fileList ) ;
1159
1160 System.out.println( "Ending ftp test part 4" ) ;
1161
1162
1163 // reRead FileList once again
1164 if ( fName != null )
1165 myFtp.getFile(fName , fileList ) ;
1166
1167 System.out.println( "Ending ftp test part 3 " ) ;
1168
1169 for ( int ii = 0 ; ii < fileList.get_dataList().size() ; ii++ )
1170 System.out.println( (String)( fileList.get_dataList().elementAt(ii) ) ) ;
1171
1172 myFtp.stop() ;
1173
1174 } catch( ClientFtpError e )
1175 {
1176 System.out.println( "Severe ftp error occured : " ) ;
1177 System.out.println( e.getMessage() ) ;
1178 }
1179 System.out.println( "Ending ftp test :" ) ;
1180
1181 *************************** CODE ENDS *************************
1182
1183 now let's explain it in details :
1184
1185 String host = null ; ==> IP name or address of the host to connect with
1186 String user = null ; ==> ftp user name
1187 String passw = null ; ==> associated password
1188 String fName = null ; ==> file name to transfer in or out
1189
1190 we will need 4 parameter to establish a FTP client connection, which
1191 are :
1192
1193 Ftp ftp = new Ftp() ;
1194 ftp.set_ftpServerName( host );
1195 ftp.set_userId( user ) ;
1196 ftp.set_passWord( passw );
1197 FtpClientSession myFtp = new FtpClientSession() ;
1198 myFtp.set_parent( ftp ) ;
1199
1200 the two statement above proceed with FtpClientSession class initialization
1201 and should occur in the above specified order.
1202
1203 We then establish connection with the FTP host server
1204
1205 myFtp.ftpInit(host) ;
1206
1207 Before any operation we should log into the system providing username
1208 and password :
1209
1210 myFtp.login( user , passw ) ;
1211
1212 we will then list the available files on server using a provided
1213 callback container class : FtpVectorListener which will store the
1214 FileList into a String vector :
1215
1216 FtpVectorListener fileList = new FtpVectorListener() ;
1217 myFtp.list( fileList ) ;
1218
1219 We will then write the File List into a local file and send it
1220 ovr the ftp connection and display it on standard locl output :
1221
1222 if ( fName != null )
1223 try {
1224 DataOutputStream out = new DataOutputStream (
1225 new BufferedOutputStream (
1226 new FileOutputStream( fName )
1227 ) ) ;
1228
1229
1230 for ( int ii = 0 ; ii < fileList.get_dataList().size() ; ii++ )
1231 {
1232 System.out.println( (String)( fileList.get_dataList().elementAt(ii))) ;
1233 out.writeBytes((String)( fileList.get_dataList().elementAt(ii))+"\n") ;
1234 }
1235 out.close() ;
1236 } catch ( IOException e ){
1237 System.out.println( "Severe file access Error : " ) ;
1238 System.out.println( e.getMessage() ) ;
1239 }
1240 Vector fileFilteredList = myFtp.filteredList() ;
1241 System.out.println( "Ending ftp test part 2 " ) ;
1242
1243 for ( int ii = 0 ; ii < fileFilteredList.size() ; ii++ )
1244 System.out.println( (FtpOsFile)( fileFilteredList.elementAt(ii) ) ) ;
1245
1246 We now send the file over the FTP connection , the transfer messages
1247 beeing strored into the fileList string vector :
1248
1249 if ( fName != null )
1250 myFtp.putFile(fName , fileList ) ;
1251
1252 We after do the reverse operation transfering the file back :
1253
1254 if ( fName != null )
1255 myFtp.getFile(fName , fileList ) ;
1256
1257 </PRE>
1258 */
1259 public static void main ( String argv[] )
1260 {
1261 String host = null ;
1262 String user = null ;
1263 String passw = null ;
1264 String fName = null ;
1265 String binFname = null ;
1266
1267 System.out.println( "Starting ftp test" ) ;
1268 if ( argv.length >= 1 )
1269 host = argv[0] ;
1270 if ( argv.length >= 2 )
1271 user = argv[1] ;
1272 if ( argv.length >= 3 )
1273 passw = argv[2] ;
1274 if ( argv.length >= 4 )
1275 fName = argv[3] ;
1276 if ( argv.length >= 5 )
1277 binFname = argv[4] ;
1278
1279 try {
1280 Ftp ftp = new Ftp() ;
1281 ftp.set_ftpServerName( host );
1282 FtpClientSession myFtp = new FtpClientSession() ;
1283 myFtp.set_parent( ftp ) ;
1284
1285 System.out.println( "Before init " ) ;
1286 myFtp.ftpInit(host) ;
1287 System.out.println( "Init done " ) ;
1288
1289 myFtp.login( user , passw ) ;
1290 System.out.println( "Login done " ) ;
1291
1292 FtpVectorListener fileList = new FtpVectorListener() ;
1293
1294 myFtp.list( fileList ) ;
1295
1296 System.out.println( "Ending ftp test part 1 " ) ;
1297
1298 if ( fName != null )
1299 {
1300 try {
1301 File file = new File(fName) ;
1302 if ( ! file.exists() )
1303 {
1304 DataOutputStream out = new DataOutputStream (
1305 new BufferedOutputStream (
1306 new FileOutputStream( fName )
1307 )
1308 ) ;
1309
1310
1311 for ( int ii = 0 ; ii < fileList.get_dataList().size() ; ii++ )
1312 {
1313 System.out.println( (String)( fileList.get_dataList().elementAt(ii))) ;
1314 out.writeBytes((String)( fileList.get_dataList().elementAt(ii))+"\n") ;
1315 }
1316 out.close() ;
1317 }
1318 } catch ( IOException e ){
1319 System.out.println( "Severe file access Error : " ) ;
1320 System.out.println( e.getMessage() ) ;
1321 }
1322 }
1323
1324 Vector fileFilteredList = myFtp.filteredList() ;
1325 System.out.println( "Ending ftp test part 2 " ) ;
1326
1327 for ( int ii = 0 ; ii < fileFilteredList.size() ; ii++ )
1328 System.out.println( (FtpOsFile)( fileFilteredList.elementAt(ii) ) ) ;
1329
1330 // resend FileList local sored file back to caller
1331
1332 if ( fName != null )
1333 myFtp.putFile(fName , fileList ) ;
1334
1335 System.out.println( "Ending ftp test part 4" ) ;
1336
1337
1338 // reRead FileList once again
1339 if ( fName != null )
1340 myFtp.getFile(fName , fileList ) ;
1341
1342 System.out.println( "Ending ftp test part 3 " ) ;
1343
1344 for ( int ii = 0 ; ii < fileList.get_dataList().size() ; ii++ )
1345 System.out.println( (String)( fileList.get_dataList().elementAt(ii) ) ) ;
1346
1347 // checking for binary data tranferts
1348 if ( binFname != null )
1349 {
1350 try {
1351 File myFile = new File(binFname) ;
1352 int fileSize = -1 ;
1353 if ( myFile.isFile() )
1354 fileSize = (int) myFile.length() ;
1355 if ( fileSize != -1 )
1356 {
1357 byte array[] = new byte[fileSize] ;
1358 BufferedInputStream toFtpPut = new BufferedInputStream(
1359 new FileInputStream( binFname )
1360 ) ;
1361 toFtpPut.read( array , 0 , array.length ) ;
1362 FtpBytesListener myTransmitter = new FtpBytesListener() ;
1363 myTransmitter.set_dataList( array ) ;
1364 myFtp.ftpBinary();
1365 myFtp.putFile( binFname , myTransmitter ) ;
1366
1367 myFile.delete() ; // clear file locally
1368 myTransmitter = new FtpBytesListener() ;
1369 myFtp.ftpBinary();
1370 myFtp.getFile( binFname , myTransmitter ) ;
1371
1372 }
1373 else
1374 System.out.println("unable to proceed file : " + binFname ) ;
1375 } catch ( IOException e )
1376 { System.out.println("IOERROR transferring binary file over FTP : " + e.getMessage() ) ; }
1377 }
1378
1379 myFtp.stop() ;
1380
1381 } catch( ClientFtpError e )
1382 {
1383 System.out.println( "Severe ftp error occured : " ) ;
1384 System.out.println( e.getMessage() ) ;
1385 }
1386
1387 Thread.currentThread().getThreadGroup().list() ;
1388 System.out.println( "Ending ftp test :" ) ;
1389 }
1390}