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