001/*
002 * Version 0.70 01/04/2002
003 *
004 * Visit my url for update: http://www.geocities.com/beapetrovicova/
005 * 
006 * jFtp was developed by Bea Petrovicova <beapetrovicova@yahoo.com>.
007 * The design and implementation of jFtp are available for royalty-free 
008 * adoption and use. This software is provided 'as is' without any 
009 * guarantees. Copyright is retained by Bea Petrovicova. Redistribution 
010 * of any part of jFtp or any derivative works must include this notice.
011 * 
012 */  
013package cz.dhl.ftp;
014
015import java.io.FileNotFoundException;
016import java.io.IOException;
017import java.io.InputStream;
018import java.io.OutputStream;
019import java.net.InetAddress;
020import java.net.ServerSocket;
021import java.net.Socket;
022import java.net.SocketException;
023import java.net.UnknownHostException;
024import java.util.StringTokenizer;
025import java.util.NoSuchElementException;
026
027final class FtpDataSocket
028{  private ServerSocket dataserver = null;
029   private Socket data = null;
030   
031   FtpContext context = null;
032   private FtpControlSocket control = null;
033   
034   FtpDataSocket(Ftp client) throws IOException
035   {  if(client.isConnected()) 
036      {  this.control = client.control; 
037         this.context = client.getContext(); 
038      } else { throw new IOException("Data: CreateSocket, No Connection!"); }
039   }   
040
041   /** @return Connect string.
042    * Using format: 'h1,h2,h3,h4,p1,p2'; */
043   String getConnect(String reply) throws NumberFormatException
044   {  if(reply==null)
045         throw new NumberFormatException
046         ("Null Reply!\n");
047
048      int begin = reply.indexOf('(');
049      int end = reply.indexOf(')');
050      if(begin!=-1 && end !=-1 && begin < end)
051         return reply.substring(begin+1,end);
052      else throw new NumberFormatException
053         ("Invalid Reply!\n"+reply); }
054
055   /** @param connect Remote hostport number string.<BR> 
056    * Expected format: 'h1,h2,h3,h4,p1,p2'<BR>
057    * h1..h4 - IP address; p1,p2 - port number; 
058    * @return IP address<BR>
059      Using format: 'h1.h2.h3.h4' */
060   String getConnectAddress(String connect) throws NumberFormatException
061   {  int s4=-1;
062      for(int i=0;i<4;i++)
063         if((s4=connect.indexOf(',',s4+1))==-1)
064            throw new NumberFormatException
065               ("Misformated Reply! " +i+":"+s4 +" " +connect); 
066      return connect.substring(0,s4).replace(',','.'); }
067
068   /** @param connect Remote hostport number string.<BR> 
069    * Expected format: 'h1,h2,h3,h4,p1,p2'<BR>
070    * h1..h4 - IP address; p1,p2 - port number;
071    * @return Port number. */
072   int getConnectPort(String connect) 
073      throws NumberFormatException, NoSuchElementException 
074   {  int s4=-1;
075      for(int i=0;i<4;i++)
076         if((s4=connect.indexOf(',',s4+1))==-1)
077            throw new NumberFormatException
078               ("Misformated Reply! " +i+":"+s4 +" " +connect); 
079      StringTokenizer tokenizer = new StringTokenizer(connect.substring(s4+1),",");
080      return Integer.parseInt(tokenizer.nextToken())*256+
081             Integer.parseInt(tokenizer.nextToken()); }
082   
083
084   void openPassiveDataSocket(String commandline, char type) throws IOException
085   {  if(control.isConnected())
086      {  try
087         {   
088            control.executeCommand("TYPE " + type);
089            control.executeCommand("PASV");
090            String connect = getConnect(control.replyOfCommand());
091            String address = getConnectAddress(connect); 
092            int port = getConnectPort(connect);
093            data = new Socket(address, port);
094            data.setSoTimeout(60000); 
095            if(!control.executeCommand(commandline))
096               throw new IOException(control.replyOfCommand());
097         } 
098         catch(NoSuchElementException e)
099            { throw new IOException("Data: OpenSocket, Invalid Format!\n"+e); }
100         catch(NumberFormatException e)
101            { throw new IOException("Data: OpenSocket, Invalid Format!\n"+e); }
102         catch(SocketException e)
103            { throw new IOException("Data: OpenSocket, Socket Error!\n"+e); }
104         catch(IOException e)
105            { throw new IOException("Data: OpenSocket, IO Error!\n"+e); } 
106         catch(Exception e) 
107            { throw new IOException("Data: OpenSocket, Permission Denied?\n"+e); }  
108      } else { throw new IOException("Data: OpenSocket, No Connection!"); }
109   }
110
111   /** 
112    * @return Local hostport number string,
113    * Using format: 'h1,h2,h3,h4,p1,p2'<BR>
114    * h1..h4 - IP address; p1,p2 - port number; */
115   String getConnect() throws UnknownHostException
116   {  short port = (short) dataserver.getLocalPort();
117      return ((InetAddress.getLocalHost()).getHostAddress()).replace('.',',') 
118             + "," + port/256 + "," + port%256; }
119
120   void openActiveDataSocket(String commandline, char type) throws IOException
121   {  if(control.isConnected())
122      {  try
123         { 
124            control.executeCommand("TYPE " + type);
125            dataserver = new ServerSocket(0);
126            dataserver.setSoTimeout(20000);
127            control.executeCommand("PORT " + getConnect());
128            synchronized(control)
129            {  control.writeCommand(commandline);
130               data=dataserver.accept(); 
131               data.setSoTimeout(60000); 
132               if(!control.completeCommand(FtpInterpret.getReplies(commandline)))
133                  throw new IOException(control.replyOfCommand()); }
134         } 
135         catch(SocketException e)
136            { throw new IOException("Data: OpenSocket, Socket Error!\n"+e); }
137         catch(IOException e)
138            { throw new IOException("Data: OpenSocket, IO Error!\n"+e); } 
139         catch(Exception e) 
140            { throw new IOException("Data: OpenSocket, Permission Denied!\n"+e); }  
141      } else { throw new IOException("Data: OpenSocket, No Connection!"); }
142   }
143
144   void openDataSocket(String commandline, char type) throws IOException
145   {  if(context.getActiveSocketMode())
146         openActiveDataSocket(commandline,type);
147      else openPassiveDataSocket(commandline,type); }
148
149   InputStream getInputStream(String commandline, char type) throws IOException      
150   {  if(data==null)
151         openDataSocket(commandline,type);
152      return data.getInputStream(); }
153   
154   OutputStream getOutputStream(String commandline, char type) throws IOException 
155   {  if(data==null)
156         openDataSocket(commandline,type);
157      return data.getOutputStream(); }
158   
159   void close() throws IOException
160   {  try { if(data!=null) data.close(); } finally 
161      {  data=null;
162         if(control.isConnected())
163         {  if(!control.completeCommand(FtpInterpret.getReplies("data-done")))
164            {  control.executeCommand("ABOR");
165               throw new IOException("Data: CloseSocket, Transfer Aborted!"); }
166         } else { throw new IOException("Data: CloseSocket, No Connection!"); }
167         try { if(dataserver!=null) dataserver.close(); } finally 
168         {  dataserver=null; }
169      }
170   }
171}