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.session;
19  
20  import java.io.File;
21  import java.net.InetAddress;
22  import java.net.InetSocketAddress;
23  
24  import io.netty.channel.Channel;
25  
26  import org.waarp.common.command.CommandInterface;
27  import org.waarp.common.command.ReplyCode;
28  import org.waarp.common.command.exception.CommandAbstractException;
29  import org.waarp.common.command.exception.Reply425Exception;
30  import org.waarp.common.file.FileParameterInterface;
31  import org.waarp.common.file.Restart;
32  import org.waarp.common.file.SessionInterface;
33  import org.waarp.common.future.WaarpFuture;
34  import org.waarp.common.logging.WaarpLogger;
35  import org.waarp.common.logging.WaarpLoggerFactory;
36  import org.waarp.ftp.core.command.AbstractCommand;
37  import org.waarp.ftp.core.command.FtpArgumentCode;
38  import org.waarp.ftp.core.command.FtpArgumentCode.TransferSubType;
39  import org.waarp.ftp.core.command.internal.ConnectionCommand;
40  import org.waarp.ftp.core.config.FtpConfiguration;
41  import org.waarp.ftp.core.control.BusinessHandler;
42  import org.waarp.ftp.core.control.NetworkHandler;
43  import org.waarp.ftp.core.data.FtpDataAsyncConn;
44  import org.waarp.ftp.core.exception.FtpNoConnectionException;
45  import org.waarp.ftp.core.file.FtpAuth;
46  import org.waarp.ftp.core.file.FtpDir;
47  
48  /**
49   * Main class that stores any information that must be accessible from anywhere during the
50   * connection of one user.
51   * 
52   * @author Frederic Bregier
53   * 
54   */
55  public class FtpSession implements SessionInterface {
56      /**
57       * Internal Logger
58       */
59      private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(FtpSession.class);
60      /**
61       * Business Handler
62       */
63      private final BusinessHandler businessHandler;
64  
65      /**
66       * Associated global configuration
67       */
68      private final FtpConfiguration configuration;
69  
70      /**
71       * Associated Binary connection
72       */
73      private volatile FtpDataAsyncConn dataConn = null;
74  
75      /**
76       * Ftp Authentication
77       */
78      private FtpAuth ftpAuth = null;
79  
80      /**
81       * Ftp DirInterface configuration and access
82       */
83      private FtpDir ftpDir = null;
84  
85      /**
86       * Previous Command
87       */
88      private AbstractCommand previousCommand = null;
89  
90      /**
91       * Current Command
92       */
93      private AbstractCommand currentCommand = null;
94      /**
95       * Is the current command finished
96       */
97      private volatile boolean isCurrentCommandFinished = true;
98  
99      /**
100      * Associated Reply Code
101      */
102     private ReplyCode replyCode = null;
103 
104     /**
105      * Real text for answer
106      */
107     private String answer = null;
108 
109     /**
110      * Current Restart information
111      */
112     private Restart restart = null;
113 
114     /**
115      * Is the control ready to accept command
116      */
117     private final WaarpFuture isReady = new WaarpFuture(true);
118 
119     /**
120      * Is the current session using SSL on Control
121      */
122     private volatile boolean isSsl = false;
123     /**
124      * Is the current session will using SSL on Control
125      */
126     private volatile WaarpFuture waitForSsl = null;
127     /**
128      * WIll all data be using SSL
129      */
130     private volatile boolean isDataSsl = false;
131 
132     /**
133      * Constructor
134      * 
135      * @param configuration
136      * @param handler
137      */
138     public FtpSession(FtpConfiguration configuration, BusinessHandler handler) {
139         this.configuration = configuration;
140         businessHandler = handler;
141     }
142 
143     /**
144      * @return the businessHandler
145      */
146     public BusinessHandler getBusinessHandler() {
147         return businessHandler;
148     }
149 
150     /**
151      * Get the configuration
152      * 
153      * @return the configuration
154      */
155     public FtpConfiguration getConfiguration() {
156         return configuration;
157     }
158 
159     public FtpDir getDir() {
160         return ftpDir;
161     }
162 
163     /**
164      * @return the Data Connection
165      */
166     public FtpDataAsyncConn getDataConn() {
167         return dataConn;
168     }
169 
170     public FtpAuth getAuth() {
171         return ftpAuth;
172     }
173 
174     public Restart getRestart() {
175         return restart;
176     }
177 
178     /**
179      * This function is called when the Command Channel is connected (from channelConnected of the
180      * NetworkHandler)
181      */
182     public void setControlConnected() {
183         dataConn = new FtpDataAsyncConn(this);
184         // AuthInterface must be done before FtpFile
185         ftpAuth = businessHandler.getBusinessNewAuth();
186         ftpDir = businessHandler.getBusinessNewDir();
187         restart = businessHandler.getBusinessNewRestart();
188     }
189 
190     /**
191      * Special initialization (FtpExec with Https session)
192      * 
193      * @param auth
194      * @param dir
195      * @param restart
196      */
197     public void setSpecialInit(FtpAuth auth, FtpDir dir, Restart restart) {
198         this.ftpAuth = auth;
199         this.ftpDir = dir;
200         this.restart = restart;
201     }
202 
203     /**
204      * @return the Control channel
205      */
206     public Channel getControlChannel() {
207         return getNetworkHandler().getControlChannel();
208     }
209 
210     /**
211      * 
212      * @return The network handler associated with control
213      */
214     public NetworkHandler getNetworkHandler() {
215         if (businessHandler != null) {
216             return businessHandler.getNetworkHandler();
217         }
218         return null;
219     }
220 
221     /**
222      * Set the new current command
223      * 
224      * @param command
225      */
226     public void setNextCommand(CommandInterface command) {
227         previousCommand = currentCommand;
228         currentCommand = (AbstractCommand) command;
229         isCurrentCommandFinished = false;
230     }
231 
232     /**
233      * @return the currentCommand
234      */
235     public AbstractCommand getCurrentCommand() {
236         return currentCommand;
237     }
238 
239     /**
240      * @return the previousCommand
241      */
242     public AbstractCommand getPreviousCommand() {
243         return previousCommand;
244     }
245 
246     /**
247      * Set the previous command as the new current command (used after a incorrect sequence of
248      * commands or unknown command)
249      * 
250      */
251     public void setPreviousAsCurrentCommand() {
252         currentCommand = previousCommand;
253         isCurrentCommandFinished = true;
254     }
255 
256     /**
257      * 
258      * @return True if the Current Command is already Finished (ready to accept a new one)
259      */
260     public boolean isCurrentCommandFinished() {
261         return isCurrentCommandFinished;
262     }
263 
264     /**
265      * Set the Current Command as finished
266      */
267     public void setCurrentCommandFinished() {
268         this.isCurrentCommandFinished = true;
269     }
270 
271     /**
272      * @return the answer
273      */
274     public String getAnswer() {
275         if (answer == null) {
276             if (replyCode == null) {
277                 answer = ReplyCode.REPLY_000_SPECIAL_NOSTATUS.getMesg();
278             } else {
279                 answer = replyCode.getMesg();
280             }
281         }
282         return answer;
283     }
284 
285     /**
286      * @param replyCode
287      *            the replyCode to set
288      * @param answer
289      */
290     public void setReplyCode(ReplyCode replyCode, String answer) {
291         this.replyCode = replyCode;
292         if (answer != null) {
293             this.answer = ReplyCode.getFinalMsg(replyCode.getCode(), answer);
294         } else {
295             this.answer = replyCode.getMesg();
296         }
297     }
298 
299     /**
300      * @param exception
301      */
302     public void setReplyCode(CommandAbstractException exception) {
303         this.setReplyCode(exception.code, exception.message);
304     }
305 
306     /**
307      * Set Exit code after an error
308      * 
309      * @param answer
310      */
311     public void setExitErrorCode(String answer) {
312         this.setReplyCode(ReplyCode.REPLY_421_SERVICE_NOT_AVAILABLE_CLOSING_CONTROL_CONNECTION,
313                 answer);
314     }
315 
316     /**
317      * Set Exit normal code
318      * 
319      * @param answer
320      */
321     public void setExitNormalCode(String answer) {
322         this.setReplyCode(ReplyCode.REPLY_221_CLOSING_CONTROL_CONNECTION,
323                 answer);
324     }
325 
326     /**
327      * @return the replyCode
328      */
329     public ReplyCode getReplyCode() {
330         return replyCode;
331     }
332 
333     public void clear() {
334         if (dataConn != null) {
335             dataConn.clear();
336         }
337         if (ftpDir != null) {
338             ftpDir.clear();
339         }
340         if (ftpAuth != null) {
341             ftpAuth.clear();
342         }
343         previousCommand = null;
344         replyCode = null;
345         answer = null;
346         isReady.cancel();
347     }
348 
349     /**
350      * @return True if the Control is ready to accept command
351      */
352     public boolean isReady() {
353         return isReady.awaitUninterruptibly().isSuccess();
354     }
355 
356     /**
357      * @param isReady
358      *            the isReady to set
359      */
360     public void setReady(boolean isReady) {
361         if (isReady) {
362             this.isReady.setSuccess();
363         } else {
364             this.isReady.cancel();
365         }
366     }
367 
368     @Override
369     public String toString() {
370         String mesg = "FtpSession: ";
371         if (ftpAuth != null) {
372             mesg += "User: " + ftpAuth.getUser() + "/" + ftpAuth.getAccount() + " ";
373         }
374         if (previousCommand != null) {
375             mesg += "PRVCMD: " + previousCommand.getCommand() + " " +
376                     previousCommand.getArg() + " ";
377         }
378         if (currentCommand != null) {
379             mesg += "CMD: " + currentCommand.getCommand() + " " +
380                     currentCommand.getArg() + " ";
381         }
382         if (replyCode != null) {
383             mesg += "Reply: " + (answer != null ? answer : replyCode.getMesg()) +
384                     " ";
385         }
386         if (dataConn != null) {
387             mesg += dataConn.toString();
388         }
389         if (ftpDir != null) {
390             try {
391                 mesg += " PWD: " + ftpDir.getPwd();
392             } catch (CommandAbstractException e) {
393             }
394         }
395         if (getControlChannel() != null) {
396             mesg += " Control: " + getControlChannel();
397         }
398         try {
399             if (getDataConn().getCurrentDataChannel() != null) {
400                 mesg += " Data: " + getDataConn().getCurrentDataChannel();
401             }
402         } catch (FtpNoConnectionException e) {
403         }
404         return mesg + "\n";
405     }
406 
407     public int getBlockSize() {
408         return restart.getMaxSize(configuration.getBLOCKSIZE());
409     }
410 
411     public FileParameterInterface getFileParameter() {
412         return configuration.getFileParameter();
413     }
414 
415     /**
416      * 
417      * @param path
418      * @return the basename from the given path
419      */
420     public static String getBasename(String path) {
421         File file = new File(path);
422         return file.getName();
423     }
424 
425     /**
426      * Reinitialize the authentication to the connection step
427      * 
428      */
429     public void reinitFtpAuth() {
430         AbstractCommand connectioncommand = new ConnectionCommand(this);
431         setNextCommand(connectioncommand);
432         getAuth().clear();
433         getDataConn().clear();
434         getDataConn().getFtpTransferControl().resetWaitForOpenedDataChannel();
435     }
436 
437     /**
438      * Reinitialize all connection parameters, including authentification
439      */
440     public void rein() {
441         // reset to default
442         if (getDataConn().isPassiveMode()) {
443             // Previous mode was Passive so remove the current configuration
444             InetSocketAddress local = getDataConn().getLocalAddress();
445             InetAddress remote = getDataConn().getRemoteAddress().getAddress();
446             getConfiguration().delFtpSession(remote, local);
447         }
448         getDataConn().setMode(FtpArgumentCode.TransferMode.STREAM);
449         getDataConn().setStructure(FtpArgumentCode.TransferStructure.FILE);
450         getDataConn().setType(FtpArgumentCode.TransferType.ASCII);
451         getDataConn().setSubType(TransferSubType.NONPRINT);
452         reinitFtpAuth();
453     }
454 
455     /**
456      * Try to open a connection. Do the intermediate reply if any (150) and the final one (125)
457      * 
458      * @throws Reply425Exception
459      *             if the connection cannot be opened
460      */
461     public void openDataConnection() throws Reply425Exception {
462         getDataConn().getFtpTransferControl().openDataConnection();
463         getNetworkHandler().writeIntermediateAnswer();
464     }
465 
466     @Override
467     public String getUniqueExtension() {
468         return configuration.getUniqueExtension();
469     }
470 
471     public boolean isSsl() {
472         return isSsl;
473     }
474 
475     public void setSsl(boolean isSsl) {
476         this.isSsl = isSsl;
477         if (waitForSsl != null) {
478             if (isSsl) {
479                 waitForSsl.setSuccess();
480             } else {
481                 waitForSsl.cancel();
482             }
483         }
484     }
485 
486     public void prepareSsl() {
487         waitForSsl = new WaarpFuture(true);
488     }
489 
490     public boolean isSslReady() {
491         if (waitForSsl != null) {
492             for (int i = 0; i < 10; i++) {
493                 if (waitForSsl.awaitUninterruptibly(100)) {
494                     break;
495                 }
496                 Thread.yield();
497             }
498             logger.debug("DEBUG : " + (waitForSsl != null ? waitForSsl.isDone() : "not Finished") + ":" + isSsl + ":"
499                     + getControlChannel());
500         }
501         return isSsl;
502     }
503 
504     public boolean isDataSsl() {
505         return isDataSsl;
506     }
507 
508     public void setDataSsl(boolean isDataSsl) {
509         this.isDataSsl = isDataSsl;
510     }
511 
512 }