View Javadoc

1   /**
2    * This file is part of Waarp Project.
3    * 
4    * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
5    * COPYRIGHT.txt in the distribution for a full listing of individual contributors.
6    * 
7    * All Waarp Project is free software: you can redistribute it and/or modify it under the terms of
8    * the GNU General Public License as published by the Free Software Foundation, either version 3 of
9    * the License, or (at your option) any later version.
10   * 
11   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
12   * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13   * Public License for more details.
14   * 
15   * You should have received a copy of the GNU General Public License along with Waarp . If not, see
16   * <http://www.gnu.org/licenses/>.
17   */
18  package org.waarp.ftp.core.data;
19  
20  import java.net.InetAddress;
21  import java.net.InetSocketAddress;
22  
23  import io.netty.channel.Channel;
24  
25  import org.waarp.common.command.exception.Reply425Exception;
26  import org.waarp.common.crypto.ssl.WaarpSslUtility;
27  import org.waarp.common.logging.WaarpLogger;
28  import org.waarp.common.logging.WaarpLoggerFactory;
29  import org.waarp.ftp.core.command.FtpArgumentCode;
30  import org.waarp.ftp.core.command.FtpArgumentCode.TransferMode;
31  import org.waarp.ftp.core.command.FtpArgumentCode.TransferStructure;
32  import org.waarp.ftp.core.command.FtpArgumentCode.TransferType;
33  import org.waarp.ftp.core.config.FtpConfiguration;
34  import org.waarp.ftp.core.data.handler.DataNetworkHandler;
35  import org.waarp.ftp.core.exception.FtpNoConnectionException;
36  import org.waarp.ftp.core.session.FtpSession;
37  import org.waarp.ftp.core.utils.FtpChannelUtils;
38  
39  /**
40   * Main class that handles Data connection using asynchronous connection with Netty
41   * 
42   * @author Frederic Bregier
43   * 
44   */
45  public class FtpDataAsyncConn {
46      /**
47       * Internal Logger
48       */
49      private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(FtpDataAsyncConn.class);
50      /**
51       * SessionInterface
52       */
53      private final FtpSession session;
54  
55      /**
56       * Current Data Network Handler
57       */
58      private volatile DataNetworkHandler dataNetworkHandler = null;
59  
60      /**
61       * Data Channel with the client
62       */
63      private volatile Channel dataChannel = null;
64  
65      /**
66       * External address of the client (active)
67       */
68      private volatile InetSocketAddress remoteAddress = null;
69  
70      /**
71       * Local listening address for the server (passive)
72       */
73      private volatile InetSocketAddress localAddress = null;
74  
75      /**
76       * Active: the connection is done from the Server to the Client on this remotePort Passive: not
77       * used
78       */
79      private volatile int remotePort = -1;
80  
81      /**
82       * Active: the connection is done from the Server from this localPort to the Client Passive: the
83       * connection is done from the Client to the Server on this localPort
84       */
85      private volatile int localPort = -1;
86  
87      /**
88       * Is the connection passive
89       */
90      private volatile boolean passiveMode = false;
91  
92      /**
93       * Is the server binded (active or passive, but mainly passive)
94       */
95      private volatile boolean isBind = false;
96  
97      /**
98       * The FtpTransferControl
99       */
100     private final FtpTransferControl transferControl;
101 
102     /**
103      * Current TransferType. Default ASCII
104      */
105     private volatile FtpArgumentCode.TransferType transferType = FtpArgumentCode.TransferType.ASCII;
106 
107     /**
108      * Current TransferSubType. Default NONPRINT
109      */
110     private volatile FtpArgumentCode.TransferSubType transferSubType = FtpArgumentCode.TransferSubType.NONPRINT;
111 
112     /**
113      * Current TransferStructure. Default FILE
114      */
115     private volatile FtpArgumentCode.TransferStructure transferStructure = FtpArgumentCode.TransferStructure.FILE;
116 
117     /**
118      * Current TransferMode. Default Stream
119      */
120     private volatile FtpArgumentCode.TransferMode transferMode = FtpArgumentCode.TransferMode.STREAM;
121 
122     /**
123      * Constructor for Active session by default
124      * 
125      * @param session
126      */
127     public FtpDataAsyncConn(FtpSession session) {
128         this.session = session;
129         dataChannel = null;
130         remoteAddress = FtpChannelUtils.getRemoteInetSocketAddress(this.session.getControlChannel());
131         remotePort = remoteAddress.getPort();
132         setDefaultLocalPort();
133         resetLocalAddress();
134         passiveMode = false;
135         isBind = false;
136         transferControl = new FtpTransferControl(session);
137     }
138 
139     /**
140      * 
141      * @param channel
142      * @return True if the given channel is the same as the one currently registered
143      */
144     public boolean checkCorrectChannel(Channel channel) {
145         if (this.dataChannel == null || channel == null) {
146             return false;
147         }
148         return dataChannel.compareTo(channel) == 0;
149     }
150 
151     /**
152      * Clear the Data Connection
153      * 
154      */
155     public void clear() {
156         unbindPassive();
157         transferControl.clear();
158         passiveMode = false;
159         remotePort = -1;
160         localPort = -1;
161     }
162 
163     /**
164      * Set the local port to the default (20)
165      * 
166      */
167     private void setDefaultLocalPort() {
168         setLocalPort(session.getConfiguration().getServerPort() - 1);
169         // Default L-1
170     }
171 
172     /**
173      * Set the Local Port (Active or Passive)
174      * 
175      * @param localPort
176      */
177     public void setLocalPort(int localPort) {
178         this.localPort = localPort;
179     }
180 
181     /**
182      * @return the local address
183      */
184     public InetSocketAddress getLocalAddress() {
185         return localAddress;
186     }
187 
188     /**
189      * @return the remote address
190      */
191     public InetSocketAddress getRemoteAddress() {
192         return remoteAddress;
193     }
194 
195     /**
196      * @return the remotePort
197      */
198     public int getRemotePort() {
199         return remotePort;
200     }
201 
202     /**
203      * @return the localPort
204      */
205     public int getLocalPort() {
206         return localPort;
207     }
208 
209     private void resetLocalAddress() {
210         localAddress = new InetSocketAddress(FtpChannelUtils
211                 .getLocalInetAddress(session.getControlChannel()), localPort);
212     }
213 
214     /**
215      * Change to active connection (reset localPort to default)
216      * 
217      * @param address
218      *            remote address
219      */
220     public void setActive(InetSocketAddress address) {
221         unbindPassive();
222         setDefaultLocalPort();
223         resetLocalAddress();
224         remoteAddress = address;
225         passiveMode = false;
226         isBind = false;
227         remotePort = remoteAddress.getPort();
228         logger.debug("SetActive: " + this);
229     }
230 
231     /**
232      * Change to passive connection (all necessaries informations like local port should have been
233      * set)
234      */
235     public void setPassive() {
236         unbindPassive();
237         resetLocalAddress();
238         passiveMode = true;
239         isBind = false;
240         logger.debug("SetPassive: " + this);
241     }
242 
243     /**
244      * @return the passiveMode
245      */
246     public boolean isPassiveMode() {
247         return passiveMode;
248     }
249 
250     /**
251      * 
252      * @return True if the connection is bind (active = connected, passive = not necessarily
253      *         connected)
254      */
255     public boolean isBind() {
256         return isBind;
257     }
258 
259     /**
260      * Is the Data dataChannel connected
261      * 
262      * @return True if the dataChannel is connected
263      */
264     public boolean isActive() {
265         return dataChannel != null && dataChannel.isActive();
266     }
267 
268     /**
269      * @return the transferMode
270      */
271     public FtpArgumentCode.TransferMode getMode() {
272         return transferMode;
273     }
274 
275     /**
276      * @param transferMode
277      *            the transferMode to set
278      */
279     public void setMode(FtpArgumentCode.TransferMode transferMode) {
280         this.transferMode = transferMode;
281         setCorrectCodec();
282     }
283 
284     /**
285      * @return the transferStructure
286      */
287     public FtpArgumentCode.TransferStructure getStructure() {
288         return transferStructure;
289     }
290 
291     /**
292      * @param transferStructure
293      *            the transferStructure to set
294      */
295     public void setStructure(FtpArgumentCode.TransferStructure transferStructure) {
296         this.transferStructure = transferStructure;
297         setCorrectCodec();
298     }
299 
300     /**
301      * @return the transferSubType
302      */
303     public FtpArgumentCode.TransferSubType getSubType() {
304         return transferSubType;
305     }
306 
307     /**
308      * @param transferSubType
309      *            the transferSubType to set
310      */
311     public void setSubType(FtpArgumentCode.TransferSubType transferSubType) {
312         this.transferSubType = transferSubType;
313         setCorrectCodec();
314     }
315 
316     /**
317      * @return the transferType
318      */
319     public FtpArgumentCode.TransferType getType() {
320         return transferType;
321     }
322 
323     /**
324      * @param transferType
325      *            the transferType to set
326      */
327     public void setType(FtpArgumentCode.TransferType transferType) {
328         this.transferType = transferType;
329         setCorrectCodec();
330     }
331 
332     /**
333      * 
334      * @return True if the current mode for data connection is FileInterface + (Stream or Block) +
335      *         (Ascii or Image)
336      */
337     public boolean isFileStreamBlockAsciiImage() {
338         return transferStructure == TransferStructure.FILE &&
339                 (transferMode == TransferMode.STREAM || transferMode == TransferMode.BLOCK) &&
340                 (transferType == TransferType.ASCII || transferType == TransferType.IMAGE);
341     }
342 
343     /**
344      * 
345      * @return True if the current mode for data connection is Stream
346      */
347     public boolean isStreamFile() {
348         return transferMode == TransferMode.STREAM &&
349                 transferStructure == TransferStructure.FILE;
350     }
351 
352     /**
353      * This function must be called after any changes of parameters, ie after MODE, STRU, TYPE
354      * 
355      */
356     private void setCorrectCodec() {
357         try {
358             getDataNetworkHandler().setCorrectCodec();
359         } catch (FtpNoConnectionException e) {
360         }
361     }
362 
363     /**
364      * Unbind passive connection when close the Data Channel (from channelInactive())
365      * 
366      */
367     public void unbindPassive() {
368         if (isBind && passiveMode) {
369             isBind = false;
370             InetSocketAddress local = getLocalAddress();
371             if (dataChannel != null && dataChannel.isActive()) {
372                 WaarpSslUtility.closingSslChannel(dataChannel);
373             }
374             session.getConfiguration().getFtpInternalConfiguration()
375                     .unbindPassive(local);
376             // Previous mode was Passive so remove the current configuration if
377             // any
378             InetAddress remote = remoteAddress.getAddress();
379             session.getConfiguration().delFtpSession(remote, local);
380         }
381         dataChannel = null;
382         dataNetworkHandler = null;
383     }
384 
385     /**
386      * Initialize the socket from Server side (only used in Passive)
387      * 
388      * @return True if OK
389      * @throws Reply425Exception
390      */
391     public boolean initPassiveConnection() throws Reply425Exception {
392         unbindPassive();
393         if (passiveMode) {
394             // Connection is enable but the client will do the real connection
395             session.getConfiguration().getFtpInternalConfiguration()
396                     .bindPassive(getLocalAddress(), session.isDataSsl());
397             isBind = true;
398             return true;
399         }
400         // Connection is already prepared
401         return true;
402     }
403 
404     /**
405      * Return the current Data Channel
406      * 
407      * @return the current Data Channel
408      * @throws FtpNoConnectionException
409      */
410     public Channel getCurrentDataChannel() throws FtpNoConnectionException {
411         if (dataChannel == null) {
412             throw new FtpNoConnectionException("No Data Connection active");
413         }
414         return dataChannel;
415     }
416 
417     /**
418      * 
419      * @return the DataNetworkHandler
420      * @throws FtpNoConnectionException
421      */
422     public DataNetworkHandler getDataNetworkHandler()
423             throws FtpNoConnectionException {
424         if (dataNetworkHandler == null) {
425             throw new FtpNoConnectionException("No Data Connection active");
426         }
427         return dataNetworkHandler;
428     }
429 
430     /**
431      * 
432      * @param dataNetworkHandler
433      *            the {@link DataNetworkHandler} to set
434      */
435     public void setDataNetworkHandler(DataNetworkHandler dataNetworkHandler) {
436         this.dataNetworkHandler = dataNetworkHandler;
437     }
438 
439     /**
440      * 
441      * @param configuration
442      * @return a new Passive Port
443      */
444     public static int getNewPassivePort(FtpConfiguration configuration) {
445         return configuration.getNextRangePort();
446     }
447 
448     /**
449      * @return The current status in String of the different parameters
450      */
451     public String getStatus() {
452         StringBuilder builder = new StringBuilder("Data connection: ")
453                 .append((isActive() ? "connected " : "not connected "))
454                 .append((isBind() ? "bind " : "not bind "))
455                 .append((isPassiveMode() ? "passive mode" : "active mode"))
456                 .append('\n')
457                 .append("Mode: ").append(transferMode.name()).append(" localPort: ")
458                 .append(getLocalPort()).append(" remotePort: ").append(getRemotePort()).append('\n')
459                 .append("Structure: ").append(transferStructure.name()).append('\n')
460                 .append("Type: ").append(transferType.name()).append(' ').append(transferSubType.name());
461         return builder.toString();
462     }
463 
464     /**
465 	 *
466 	 */
467     @Override
468     public String toString() {
469         return getStatus().replace('\n', ' ');
470     }
471 
472     /**
473      * 
474      * @return the FtpTransferControl
475      */
476     public FtpTransferControl getFtpTransferControl() {
477         return transferControl;
478     }
479 
480     /**
481      * Set the new connected Data Channel
482      * 
483      * @param dataChannel
484      *            the new Data Channel
485      * @throws InterruptedException
486      * @throws Reply425Exception
487      */
488     public void setNewOpenedDataChannel(Channel dataChannel)
489             throws InterruptedException, Reply425Exception {
490         this.dataChannel = dataChannel;
491         if (dataChannel == null) {
492             String curmode = null;
493             if (isPassiveMode()) {
494                 curmode = "passive";
495             } else {
496                 curmode = "active";
497             }
498             // Cannot open connection
499             throw new Reply425Exception("Cannot open " + curmode +
500                     " data connection");
501         }
502         isBind = true;
503     }
504 }