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 cz.dhl.ui.CoConsole;
016import java.io.InputStream;
017import java.io.IOException;
018
019/**
020 * Allows reading from FTP file.
021 * 
022 * @Version 0.70 01/04/2002
023 * @author Bea Petrovicova <beapetrovicova@yahoo.com>
024 * @see Ftp
025 * @see FtpFile
026 */
027public class FtpInputStream extends InputStream 
028{  Ftp client;
029   FtpDataSocket data;
030   InputStream stream;
031
032   FtpInputStream() {}
033
034   /** Open 'retreive' InputStream for given filename.
035    * <P><B>RETR</B> - retrieve</P>
036    * <P>This command causes the server-DTP to transfer a copy of 
037    * the file, specified in the pathname, to the server or 
038    * user-DTP at the other end of the data connection. 
039    * The status and contents of the file at the server site 
040    * shall be unaffected.</P>
041    * @param file the file to be opened for reading
042    * @exception IOException socket error
043    * @exception FileNotFoundException Server file not found. */
044   public FtpInputStream(FtpFile file) 
045      throws IOException
046   {  client=null;
047      data=new FtpDataSocket(file.client); 
048      stream=data.getInputStream("RETR " + file,file.getDataType()); }
049
050   /** Open 'retreive' concurent InputStream for given filename. 
051    * <P>Single ftp connection cannot handle <B>multiple concurent 
052    * data transfers</B>. This limitation can be eliminated by 
053    * creating new ftp connection for each concurent data 
054    * transfer. This constructor creates separate Ftp instance.</P>
055    * <P>Note (1) supplying same CoConsole instance for 
056    * multiple sessions will produce messed output.</P>
057    * <P>Note (2) This code may need to be run in separate 
058    * thread to process multiple files concurently.</P>
059    * <P><B>RETR</B> - retrieve</P>
060    * This command causes the server-DTP to transfer a copy of 
061    * the file, specified in the pathname, to the server or 
062    * user-DTP at the other end of the data connection. 
063    * The status and contents of the file at the server site 
064    * shall be unaffected.</P>
065    * @param file the file to be opened for reading
066    * @param connect login details
067    * @param console message output
068    * @exception IOException socket error
069    * @see #FtpInputStream(FtpFile) */
070   public FtpInputStream(FtpFile file,FtpConnect connect,CoConsole console) 
071      throws IOException
072   {  client = new Ftp(); 
073      if(client.connect(connect))
074      {  if(console!=null)
075            client.getContext().setConsole(console);
076         file = new FtpFile(file.toString(),client);
077         data=new FtpDataSocket(file.client); 
078         stream=data.getInputStream("RETR " + file,file.getDataType()); 
079      } else throw new IOException("Connect failed.");
080   } 
081
082   /** Close current data transfer and close data connection.
083    * <P>If no reply <B>ABOR</B> - abort</P>
084    * <P>This command tells the server to abort the previous FTP 
085    * service command and any associated transfer of data. No 
086    * action is to be taken if the previous command has been 
087    * completed (including data transfer). The control connection 
088    * is not to be closed by the server, but the data connection 
089    * must be closed. There are two cases for the server upon 
090    * receipt of this command:</P>
091    * <P>(1) the FTP service command was already completed. The 
092    * server closes the data connection (if it is open) and 
093    * responds with a 226 reply, indicating that the abort 
094    * command was successfully processed.</P>
095    * <P>(2) the FTP service command is still in progress. The 
096    * server aborts the FTP service in progress and closes the 
097    * data connection, returning a 426 reply to indicate that 
098    * the service request terminated abnormally. The server then 
099    * sends a 226 reply, indicating that the abort command was 
100    * successfully processed.</P>
101    * @exception IOException socket error */
102   public void close() throws IOException 
103   {  IOException x = null;
104      while (stream!=null || data!=null || client!=null)
105         try 
106         {  InputStream i; FtpDataSocket d; Ftp c;
107            if(stream!=null) { i=stream; stream = null; i.close(); }
108            if(data!=null) { d=data; data = null; d.close(); }
109            if(client!=null) { c=client; client = null; c.disconnect(); }
110         } catch(IOException e) { x=e; }
111      if(x!=null) throw x;
112   }
113   
114   /**
115    * Reads the next byte of data from the input stream. The value byte is
116    * returned as an <code>int</code> in the range <code>0</code> to
117    * <code>255</code>. If no byte is available because the end of the stream
118    * has been reached, the value <code>-1</code> is returned. This method
119    * blocks until input data is available, the end of the stream is detected,
120    * or an exception is thrown.
121    *
122    * @return     the next byte of data, or <code>-1</code> if the end of the
123    *             stream is reached.
124    * @exception  IOException  if an I/O error occurs.
125    */
126   public int read() throws IOException { return stream.read(); }
127
128   /**
129    * Reads some number of bytes from the input stream and stores them into
130    * the buffer array <code>b</code>. The number of bytes actually read is
131    * returned as an integer.  This method blocks until input data is
132    * available, end of file is detected, or an exception is thrown.
133    *
134    * <p> If <code>b</code> is <code>null</code>, a
135    * <code>NullPointerException</code> is thrown.  If the length of
136    * <code>b</code> is zero, then no bytes are read and <code>0</code> is
137    * returned; otherwise, there is an attempt to read at least one byte. If
138    * no byte is available because the stream is at end of file, the value
139    * <code>-1</code> is returned; otherwise, at least one byte is read and
140    * stored into <code>b</code>.
141    *
142    * <p> The first byte read is stored into element <code>b[0]</code>, the
143    * next one into <code>b[1]</code>, and so on. The number of bytes read is,
144    * at most, equal to the length of <code>b</code>. Let <i>k</i> be the
145    * number of bytes actually read; these bytes will be stored in elements
146    * <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>,
147    * leaving elements <code>b[</code><i>k</i><code>]</code> through
148    * <code>b[b.length-1]</code> unaffected.
149    *
150    * <p> If the first byte cannot be read for any reason other than end of
151    * file, then an <code>IOException</code> is thrown. In particular, an
152    * <code>IOException</code> is thrown if the input stream has been closed.
153    *
154    * <p> The <code>read(b)</code> method for class <code>InputStream</code>
155    * has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
156    *
157    * @param      b   the buffer into which the data is read.
158    * @return     the total number of bytes read into the buffer, or
159    *             <code>-1</code> is there is no more data because the end of
160    *             the stream has been reached.
161    * @exception  IOException  if an I/O error occurs.
162    * @see        cz.dhl.ftp.FtpInputStream#read(byte[], int, int)
163    */
164   public int read(byte b[]) throws IOException { return stream.read(b); }
165
166   /**
167    * Reads up to <code>len</code> bytes of data from the input stream into
168    * an array of bytes.  An attempt is made to read as many as
169    * <code>len</code> bytes, but a smaller number may be read, possibly
170    * zero. The number of bytes actually read is returned as an integer.
171    *
172    * <p> This method blocks until input data is available, end of file is
173    * detected, or an exception is thrown.
174    *
175    * <p> If <code>b</code> is <code>null</code>, a
176    * <code>NullPointerException</code> is thrown.
177    *
178    * <p> If <code>off</code> is negative, or <code>len</code> is negative, or
179    * <code>off+len</code> is greater than the length of the array
180    * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
181    * thrown.
182    *
183    * <p> If <code>len</code> is zero, then no bytes are read and
184    * <code>0</code> is returned; otherwise, there is an attempt to read at
185    * least one byte. If no byte is available because the stream is at end of
186    * file, the value <code>-1</code> is returned; otherwise, at least one
187    * byte is read and stored into <code>b</code>.
188    *
189    * <p> The first byte read is stored into element <code>b[off]</code>, the
190    * next one into <code>b[off+1]</code>, and so on. The number of bytes read
191    * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
192    * bytes actually read; these bytes will be stored in elements
193    * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
194    * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
195    * <code>b[off+len-1]</code> unaffected.
196    *
197    * <p> In every case, elements <code>b[0]</code> through
198    * <code>b[off]</code> and elements <code>b[off+len]</code> through
199    * <code>b[b.length-1]</code> are unaffected.
200    *
201    * <p> If the first byte cannot be read for any reason other than end of
202    * file, then an <code>IOException</code> is thrown. In particular, an
203    * <code>IOException</code> is thrown if the input stream has been closed.
204    *
205    * <p> The <code>read(b,</code> <code>off,</code> <code>len)</code> method
206    * for class <code>InputStream</code> simply calls the method
207    * <code>read()</code> repeatedly. If the first such call results in an
208    * <code>IOException</code>, that exception is returned from the call to
209    * the <code>read(b,</code> <code>off,</code> <code>len)</code> method.  If
210    * any subsequent call to <code>read()</code> results in a
211    * <code>IOException</code>, the exception is caught and treated as if it
212    * were end of file; the bytes read up to that point are stored into
213    * <code>b</code> and the number of bytes read before the exception
214    * occurred is returned.  Subclasses are encouraged to provide a more
215    * efficient implementation of this method.
216    *
217    * @param      b     the buffer into which the data is read.
218    * @param      off   the start offset in array <code>b</code>
219    *                   at which the data is written.
220    * @param      len   the maximum number of bytes to read.
221    * @return     the total number of bytes read into the buffer, or
222    *             <code>-1</code> if there is no more data because the end of
223    *             the stream has been reached.
224    * @exception  IOException  if an I/O error occurs.
225    * @see        cz.dhl.ftp.FtpInputStream#read()
226    */
227   public int read(byte b[], int off, int len) throws IOException { return stream.read(b,off,len); }
228
229   /**
230    * Skips over and discards <code>n</code> bytes of data from this input
231    * stream. The <code>skip</code> method may, for a variety of reasons, end
232    * up skipping over some smaller number of bytes, possibly <code>0</code>.
233    * This may result from any of a number of conditions; reaching end of file
234    * before <code>n</code> bytes have been skipped is only one possibility.
235    * The actual number of bytes skipped is returned.  If <code>n</code> is
236    * negative, no bytes are skipped.
237    *
238    * @param      n   the number of bytes to be skipped.
239    * @return     the actual number of bytes skipped.
240    * @exception  IOException  if an I/O error occurs.
241    */
242   public long skip(long n) throws IOException { return stream.skip(n); }
243
244   /**
245    * Returns the number of bytes that can be read (or skipped over) from
246    * this input stream without blocking by the next caller of a method for
247    * this input stream.  The next caller might be the same thread or or
248    * another thread.
249    *
250    * <p> The <code>available</code> method for class <code>InputStream</code>
251    * always returns <code>0</code>.
252    *
253    * @return     the number of bytes that can be read from this input stream
254    *             without blocking.
255    * @exception  IOException  if an I/O error occurs.
256    */
257   public int available() throws IOException { return stream.available(); }
258
259   /**
260    * Marks the current position in this input stream. A subsequent call to
261    * the <code>reset</code> method repositions this stream at the last marked
262    * position so that subsequent reads re-read the same bytes.
263    *
264    * <p> The <code>readlimit</code> arguments tells this input stream to
265    * allow that many bytes to be read before the mark position gets
266    * invalidated.
267    *
268    * <p> The general contract of <code>mark</code> is that, if the method
269    * <code>markSupported</code> returns <code>true</code>, the stream somehow
270    * remembers all the bytes read after the call to <code>mark</code> and
271    * stands ready to supply those same bytes again if and whenever the method
272    * <code>reset</code> is called.  However, the stream is not required to
273    * remember any data at all if more than <code>readlimit</code> bytes are
274    * read from the stream before <code>reset</code> is called.
275    *
276    * @param   readlimit   the maximum limit of bytes that can be read before
277    *                      the mark position becomes invalid.
278    * @see     cz.dhl.ftp.FtpInputStream#reset()
279    */
280   public synchronized void mark(int readlimit) { stream.mark(readlimit); }
281
282   /**
283    * Repositions this stream to the position at the time the
284    * <code>mark</code> method was last called on this input stream.
285    *
286    * <p> The general contract of <code>reset</code> is:
287    *
288    * <p><ul>
289    *
290    * <li> If the method <code>markSupported</code> returns
291    * <code>true</code>, then:
292    *
293    *     <ul><li> If the method <code>mark</code> has not been called since
294    *     the stream was created, or the number of bytes read from the stream
295    *     since <code>mark</code> was last called is larger than the argument
296    *     to <code>mark</code> at that last call, then an
297    *     <code>IOException</code> might be thrown.
298    *
299    *     <li> If such an <code>IOException</code> is not thrown, then the
300    *     stream is reset to a state such that all the bytes read since the
301    *     most recent call to <code>mark</code> (or since the start of the
302    *     file, if <code>mark</code> has not been called) will be resupplied
303    *     to subsequent callers of the <code>read</code> method, followed by
304    *     any bytes that otherwise would have been the next input data as of
305    *     the time of the call to <code>reset</code>. </ul>
306    *
307    * <li> If the method <code>markSupported</code> returns
308    * <code>false</code>, then:
309    *
310    *     <ul><li> The call to <code>reset</code> may throw an
311    *     <code>IOException</code>.
312    *
313    *     <li> If an <code>IOException</code> is not thrown, then the stream
314    *     is reset to a fixed state that depends on the particular type of the
315    *     input stream and how it was created. The bytes that will be supplied
316    *     to subsequent callers of the <code>read</code> method depend on the
317    *     particular type of the input stream. </ul></ul>
318    *
319    * @exception  IOException  if this stream has not been marked or if the
320    *               mark has been invalidated.
321    * @see     cz.dhl.ftp.FtpInputStream#mark(int)
322    * @see     java.io.IOException
323    */
324   public synchronized void reset() throws IOException { stream.reset(); }
325
326   /**
327    * Tests if this input stream supports the <code>mark</code> and
328    * <code>reset</code> methods. 
329    *
330    * @return  <code>true</code> if this true type supports the mark and reset
331    *          method; <code>false</code> otherwise.
332    * @see     cz.dhl.ftp.FtpInputStream#mark(int)
333    * @see     cz.dhl.ftp.FtpInputStream#reset()
334    */
335   public boolean markSupported() { return stream.markSupported(); }
336
337}