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 */
013 
014package cz.dhl.ftp;
015
016import cz.dhl.ui.CoConsole;
017import java.io.OutputStream;
018import java.io.IOException;
019
020/**
021 * Allows writing into FTP file.
022 * 
023 * @Version 0.70 01/04/2002
024 * @author Bea Petrovicova <beapetrovicova@yahoo.com>  
025 * @see Ftp
026 * @see FtpFile
027 */
028public final class FtpOutputStream extends OutputStream 
029{  Ftp client;
030   private FtpDataSocket data;
031   private OutputStream stream;
032
033   /** Opens 'store' OutputStream for given filename.
034    * <P>This constructor behaves similarly to 
035    * <code>{@link #FtpOutputStream(FtpFile,boolean)}</code>.</P>
036    * @param file the file to be opened for writing
037    * @exception IOException socket error */
038   public FtpOutputStream(FtpFile file) throws IOException { this(file,false); }
039   
040   /** Open either 'store' or 'append' OutputStream for given filename.
041    * <P><B>STOR</B> - store</P>
042    * <P>This command causes the server-DTP to accept the data 
043    * transferred via the data connection and to store the data 
044    * as a file at the server site. If the file specified in the 
045    * pathname exists at the server site, then its contents shall 
046    * be replaced by the data being transferred. A new file is 
047    * created at the server site if the file specified in the 
048    * pathname does not already exist.</P>
049    * <P><B>APPE</B> - append (with create)</P>
050    * <P>This command causes the server-DTP to accept the data 
051    * transferred via the data connection and to store the data 
052    * in a file at the server site. If the file specified in the 
053    * pathname exists at the server site, then the data shall be 
054    * appended to that file; otherwise the file specified in the 
055    * pathname shall be created at the server site.</P>
056    * @param file the file to be opened for writing
057    * @param append if true, then bytes will be written to the 
058    *        end of the file rather than the beginning 
059    * @exception IOException socket error */
060   public FtpOutputStream(FtpFile file,boolean append) throws IOException
061   {  client=null;
062      data=new FtpDataSocket(file.client); 
063      if(append) stream=data.getOutputStream("APPE " +file,file.getDataType());
064      else stream=data.getOutputStream("STOR " +file,file.getDataType()); }
065
066   /** Opens 'store' concurent OutputStream for given filename.
067    * <P>This constructor behaves similarly to 
068    * <code>{@link #FtpOutputStream(FtpFile,FtpConnect,CoConsole,boolean)}</code>.</P> 
069    * @param file the file to be opened for writing
070    * @param connect login details
071    * @param console message output
072    * @exception IOException socket error */
073   public FtpOutputStream(FtpFile file,FtpConnect connect,CoConsole console)
074      throws IOException { this(file,connect,console,false); }
075
076   /** Open either 'store' or 'append' concurent OutputStream for given filename.
077    * <P>Single ftp connection cannot handle <B>multiple concurent 
078    * data transfers</B>. This limitation can be eliminated by 
079    * creating new ftp connection for each concurent data 
080    * transfer. This constructor creates separate Ftp instance.</P>
081    * <P>Note (1) supplying same CoConsole instance for 
082    * multiple sessions will produce messed output.</P>
083    * <P>Note (2) This code may need to be run in separate 
084    * thread to process multiple files concurently.</P>
085    * <P><B>STOR</B> - store</P>
086    * <P>This command causes the server-DTP to accept the data 
087    * transferred via the data connection and to store the data 
088    * as a file at the server site. If the file specified in the 
089    * pathname exists at the server site, then its contents shall 
090    * be replaced by the data being transferred. A new file is 
091    * created at the server site if the file specified in the 
092    * pathname does not already exist.</P>
093    * <P><B>APPE</B> - append (with create)</P>
094    * <P>This command causes the server-DTP to accept the data 
095    * transferred via the data connection and to store the data 
096    * in a file at the server site. If the file specified in the 
097    * pathname exists at the server site, then the data shall be 
098    * appended to that file; otherwise the file specified in the 
099    * pathname shall be created at the server site.</P>
100    * @param file the file to be opened for writing
101    * @param connect login details
102    * @param console message output
103    * @param append if true, then bytes will be written to the 
104    *        end of the file rather than the beginning
105    * @exception IOException socket error 
106    * @see #FtpOutputStream(FtpFile,boolean) */
107   public FtpOutputStream
108      (FtpFile file,FtpConnect connect,CoConsole console,boolean append) 
109      throws IOException
110   {  client = new Ftp(); 
111      if(client.connect(connect))
112      {  if(console!=null)
113            client.getContext().setConsole(console);
114         file = new FtpFile(file.toString(),client);
115         data=new FtpDataSocket(file.client); 
116         if(append) stream=data.getOutputStream("APPE " +file,file.getDataType());
117         else stream=data.getOutputStream("STOR " +file,file.getDataType()); 
118      } else throw new IOException("Connect failed.");
119   } 
120   
121   /* <P><B>STOU</B> - store unique</P>
122    * <P>This command behaves like STOR except that the resultant 
123    * file is to be created in the current directory under a name 
124    * unique to that directory. The 250 Transfer Started response 
125    * must include the name generated.</P> */
126
127   /** Close current data transfer and close data connection.
128    * <P>If no reply <B>ABOR</B> - abort</P>
129    * <P>This command tells the server to abort the previous FTP 
130    * service command and any associated transfer of data. No 
131    * action is to be taken if the previous command has been 
132    * completed (including data transfer). The control connection 
133    * is not to be closed by the server, but the data connection 
134    * must be closed. There are two cases for the server upon 
135    * receipt of this command:</P>
136    * <P>(1) the FTP service command was already completed. The 
137    * server closes the data connection (if it is open) and 
138    * responds with a 226 reply, indicating that the abort 
139    * command was successfully processed.</P>
140    * <P>(2) the FTP service command is still in progress. The 
141    * server aborts the FTP service in progress and closes the 
142    * data connection, returning a 426 reply to indicate that 
143    * the service request terminated abnormally. The server then 
144    * sends a 226 reply, indicating that the abort command was 
145    * successfully processed.</P>
146    * @exception IOException socket error */
147   public void close() throws IOException 
148   {  IOException x = null;
149      while (stream!=null || data!=null || client!=null)
150         try 
151         {  OutputStream o; FtpDataSocket d; Ftp c;
152            if(stream!=null) { o=stream; stream = null; o.close(); }
153            if(data!=null) { d=data; data = null; d.close(); }
154            if(client!=null) { c=client; client = null; c.disconnect(); }
155         } catch(IOException e) { x=e; }
156      if(x!=null) throw x;
157   }
158   
159   /**
160    * Writes the specified byte to this output stream. The general 
161    * contract for <code>write</code> is that one byte is written 
162    * to the output stream. The byte to be written is the eight 
163    * low-order bits of the argument <code>b</code>. The 24 
164    * high-order bits of <code>b</code> are ignored.
165    *
166    * @param      b   the <code>byte</code>.
167    * @exception  IOException  if an I/O error occurs. In particular, 
168    *             an <code>IOException</code> may be thrown if the 
169    *             output stream has been closed.
170    */
171   public void write(int b) throws IOException { stream.write(b); }
172
173   /**
174    * Writes <code>b.length</code> bytes from the specified byte array 
175    * to this output stream. The general contract for <code>write(b)</code> 
176    * is that it should have exactly the same effect as the call 
177    * <code>write(b, 0, b.length)</code>.
178    *
179    * @param      b   the data.
180    * @exception  IOException  if an I/O error occurs.
181    * @see        cz.dhl.ftp.FtpOutputStream#write(byte[], int, int)
182    */
183   public void write(byte b[]) throws IOException { stream.write(b); }
184
185   /**
186    * Writes <code>len</code> bytes from the specified byte array 
187    * starting at offset <code>off</code> to this output stream. 
188    * The general contract for <code>write(b, off, len)</code> is that 
189    * some of the bytes in the array <code>b</code> are written to the 
190    * output stream in order; element <code>b[off]</code> is the first 
191    * byte written and <code>b[off+len-1]</code> is the last byte written 
192    * by this operation.
193    * <p>
194    * The <code>write</code> method of <code>OutputStream</code> calls 
195    * the write method of one argument on each of the bytes to be 
196    * written out. Subclasses are encouraged to override this method and 
197    * provide a more efficient implementation. 
198    * <p>
199    * If <code>b</code> is <code>null</code>, a 
200    * <code>NullPointerException</code> is thrown.
201    * <p>
202    * If <code>off</code> is negative, or <code>len</code> is negative, or 
203    * <code>off+len</code> is greater than the length of the array 
204    * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
205    *
206    * @param      b     the data.
207    * @param      off   the start offset in the data.
208    * @param      len   the number of bytes to write.
209    * @exception  IOException  if an I/O error occurs. In particular, 
210    *             an <code>IOException</code> is thrown if the output 
211    *             stream is closed.
212    */
213   public void write(byte b[], int off, int len) throws IOException { stream.write(b,off,len); }
214
215   /**
216    * Flushes this output stream and forces any buffered output bytes 
217    * to be written out. The general contract of <code>flush</code> is 
218    * that calling it is an indication that, if any bytes previously 
219    * written have been buffered by the implementation of the output 
220    * stream, such bytes should immediately be written to their 
221    * intended destination.
222    *
223    * @exception  IOException  if an I/O error occurs.
224    */
225   public void flush() throws IOException { stream.flush(); }
226
227}