View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, 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 Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.gateway.ftp.adminssl;
21  
22  import io.netty.buffer.ByteBuf;
23  import io.netty.buffer.Unpooled;
24  import io.netty.channel.Channel;
25  import io.netty.channel.ChannelFuture;
26  import io.netty.channel.ChannelHandlerContext;
27  import io.netty.channel.SimpleChannelInboundHandler;
28  import io.netty.handler.codec.http.DefaultFullHttpResponse;
29  import io.netty.handler.codec.http.FullHttpRequest;
30  import io.netty.handler.codec.http.FullHttpResponse;
31  import io.netty.handler.codec.http.HttpHeaderNames;
32  import io.netty.handler.codec.http.HttpHeaderValues;
33  import io.netty.handler.codec.http.HttpMethod;
34  import io.netty.handler.codec.http.HttpResponse;
35  import io.netty.handler.codec.http.HttpResponseStatus;
36  import io.netty.handler.codec.http.HttpUtil;
37  import io.netty.handler.codec.http.HttpVersion;
38  import io.netty.handler.codec.http.QueryStringDecoder;
39  import io.netty.handler.codec.http.cookie.Cookie;
40  import io.netty.handler.codec.http.cookie.DefaultCookie;
41  import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
42  import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
43  import io.netty.handler.traffic.TrafficCounter;
44  import org.waarp.common.command.ReplyCode;
45  import org.waarp.common.command.exception.CommandAbstractException;
46  import org.waarp.common.crypto.ssl.WaarpSslUtility;
47  import org.waarp.common.database.DbAdmin;
48  import org.waarp.common.database.DbPreparedStatement;
49  import org.waarp.common.database.DbSession;
50  import org.waarp.common.database.exception.WaarpDatabaseException;
51  import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
52  import org.waarp.common.database.exception.WaarpDatabaseSqlException;
53  import org.waarp.common.file.DirInterface;
54  import org.waarp.common.logging.WaarpLogger;
55  import org.waarp.common.logging.WaarpLoggerFactory;
56  import org.waarp.common.utility.ThreadLocalRandom;
57  import org.waarp.common.utility.Version;
58  import org.waarp.common.utility.WaarpStringUtils;
59  import org.waarp.ftp.core.session.FtpSession;
60  import org.waarp.ftp.core.utils.FtpChannelUtils;
61  import org.waarp.gateway.ftp.config.FileBasedConfiguration;
62  import org.waarp.gateway.ftp.control.FtpConstraintLimitHandler;
63  import org.waarp.gateway.ftp.database.DbConstantFtp;
64  import org.waarp.gateway.ftp.database.data.DbTransferLog;
65  import org.waarp.gateway.ftp.exec.AbstractExecutor;
66  import org.waarp.gateway.ftp.exec.AbstractExecutor.CommandExecutor;
67  import org.waarp.gateway.ftp.file.FileBasedAuth;
68  import org.waarp.gateway.kernel.http.HttpWriteCacheEnable;
69  
70  import java.io.IOException;
71  import java.util.List;
72  import java.util.Map;
73  import java.util.Set;
74  import java.util.concurrent.ConcurrentHashMap;
75  
76  /**
77   * HttpSslHandler for FTP Gateway
78   */
79  public class HttpSslHandler
80      extends SimpleChannelInboundHandler<FullHttpRequest> {
81    private static final String XXXFILEXXX = "XXXFILEXXX";
82    private static final String ACTION2 = "ACTION";
83    private static final String XXXRESULTXXX = "XXXRESULTXXX";
84    /**
85     * Internal Logger
86     */
87    private static final WaarpLogger logger =
88        WaarpLoggerFactory.getLogger(HttpSslHandler.class);
89    /**
90     * Session Management
91     */
92    private static final ConcurrentHashMap<String, FileBasedAuth> sessions =
93        new ConcurrentHashMap<String, FileBasedAuth>();
94    private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
95  
96    private final FtpSession ftpSession =
97        new FtpSession(FileBasedConfiguration.fileBasedConfiguration, null);
98    private FileBasedAuth authentHttp = new FileBasedAuth(ftpSession);
99  
100   private FullHttpRequest request;
101   private boolean newSession;
102   private Cookie admin;
103   private final StringBuilder responseContent = new StringBuilder();
104   private String uriRequest;
105   private Map<String, List<String>> params;
106   private boolean forceClose;
107   private boolean shutdown;
108 
109   private static final String FTPSESSIONString = "FTPSESSION";
110 
111   private enum REQUEST {
112     Logon("Logon.html"), index("index.html"), error("error.html"),
113     Transfer("Transfer_head.html", "Transfer_body.html", "Transfer_end.html"),
114     Rule("Rule.html"),
115     User("User_head.html", "User_body.html", "User_end.html"),
116     System("System.html");
117 
118     private final String header;
119     private final String body;
120     private final String end;
121 
122     /**
123      * Constructor for a unique file
124      *
125      * @param uniquefile
126      */
127     REQUEST(final String uniquefile) {
128       header = uniquefile;
129       body = null;
130       end = null;
131     }
132 
133     /**
134      * @param header
135      * @param body
136      * @param end
137      */
138     REQUEST(final String header, final String body, final String end) {
139       this.header = header;
140       this.body = body;
141       this.end = end;
142     }
143 
144     /**
145      * Reader for a unique file
146      *
147      * @return the content of the unique file
148      */
149     public final String readFileUnique() {
150       return WaarpStringUtils.readFile(
151           FileBasedConfiguration.fileBasedConfiguration.getHttpBasePath() +
152           header);
153     }
154 
155     public final String readHeader() {
156       return WaarpStringUtils.readFile(
157           FileBasedConfiguration.fileBasedConfiguration.getHttpBasePath() +
158           header);
159     }
160 
161     public final String readBody() {
162       return WaarpStringUtils.readFile(
163           FileBasedConfiguration.fileBasedConfiguration.getHttpBasePath() +
164           body);
165     }
166 
167     public final String readEnd() {
168       return WaarpStringUtils.readFile(
169           FileBasedConfiguration.fileBasedConfiguration.getHttpBasePath() +
170           end);
171     }
172   }
173 
174   public static final int LIMITROW = 48;// better if it can be divided by 4
175 
176   /**
177    * The Database connection attached to this NetworkChannel shared among all
178    * associated LocalChannels in the
179    * session
180    */
181   private DbSession dbSession;
182 
183   private String getTrimValue(final String varname) {
184     String value = params.get(varname).get(0).trim();
185     if (value.length() == 0) {
186       value = null;
187     }
188     return value;
189   }
190 
191   private String index() {
192     final String index = REQUEST.index.readFileUnique();
193     final StringBuilder builder = new StringBuilder(index);
194     WaarpStringUtils.replace(builder, "XXXLOCALXXX",
195                              FileBasedConfiguration.fileBasedConfiguration.getFtpInternalConfiguration()
196                                                                           .getNumberSessions() +
197                              " " + Thread.activeCount());
198     final TrafficCounter trafficCounter =
199         FileBasedConfiguration.fileBasedConfiguration.getFtpInternalConfiguration()
200                                                      .getGlobalTrafficShapingHandler()
201                                                      .trafficCounter();
202     WaarpStringUtils.replace(builder, "XXXBANDWIDTHXXX", "IN:" +
203                                                          trafficCounter.lastReadThroughput() /
204                                                          131072 +
205                                                          "Mbits&nbsp;<br>&nbsp;OUT:" +
206                                                          trafficCounter.lastWriteThroughput() /
207                                                          131072 + "Mbits");
208     WaarpStringUtils.replaceAll(builder, "XXXHOSTIDXXX",
209                                 FileBasedConfiguration.fileBasedConfiguration.getHostId());
210     WaarpStringUtils.replaceAll(builder, "XXXADMINXXX",
211                                 "Administrator Connected");
212     WaarpStringUtils.replace(builder, "XXXVERSIONXXX",
213                              Version.fullIdentifier());
214     return builder.toString();
215   }
216 
217   private String error(final String mesg) {
218     final String index = REQUEST.error.readFileUnique();
219     return index.replace("XXXERRORMESGXXX", mesg);
220   }
221 
222   private String logon() {
223     return REQUEST.Logon.readFileUnique();
224   }
225 
226   private String system() {
227     getParams();
228     final FtpConstraintLimitHandler handler =
229         FileBasedConfiguration.fileBasedConfiguration.getConstraintLimitHandler();
230     if (params == null) {
231       final String system = REQUEST.System.readFileUnique();
232       final StringBuilder builder = new StringBuilder(system);
233       WaarpStringUtils.replace(builder, "XXXXCHANNELLIMITRXXX", Long.toString(
234           FileBasedConfiguration.fileBasedConfiguration.getServerGlobalReadLimit()));
235       WaarpStringUtils.replace(builder, "XXXXCPULXXX",
236                                Double.toString(handler.getCpuLimit()));
237       WaarpStringUtils.replace(builder, "XXXXCONLXXX",
238                                Integer.toString(handler.getChannelLimit()));
239       WaarpStringUtils.replace(builder, XXXRESULTXXX, "");
240       return builder.toString();
241     }
242     String extraInformation = null;
243     if (params.containsKey(ACTION2)) {
244       final List<String> action = params.get(ACTION2);
245       for (final String act : action) {
246         if ("Disconnect".equalsIgnoreCase(act)) {
247           final String logon = logon();
248           newSession = true;
249           clearSession();
250           forceClose = true;
251           return logon;
252         } else if ("Shutdown".equalsIgnoreCase(act)) {
253           final String error = error("Shutdown in progress");
254           newSession = true;
255           clearSession();
256           forceClose = true;
257           shutdown = true;
258           return error;
259         } else if ("Validate".equalsIgnoreCase(act)) {
260           String bglobalr = getTrimValue("BGLOBR");
261           long lglobal =
262               FileBasedConfiguration.fileBasedConfiguration.getServerGlobalReadLimit();
263           if (bglobalr != null) {
264             lglobal = Long.parseLong(bglobalr);
265           }
266           FileBasedConfiguration.fileBasedConfiguration.changeNetworkLimit(
267               lglobal, lglobal);
268           bglobalr = getTrimValue("CPUL");
269           double dcpu = handler.getCpuLimit();
270           if (bglobalr != null) {
271             dcpu = Double.parseDouble(bglobalr);
272           }
273           handler.setCpuLimit(dcpu);
274           bglobalr = getTrimValue("CONL");
275           int iconn = handler.getChannelLimit();
276           if (bglobalr != null) {
277             iconn = Integer.parseInt(bglobalr);
278           }
279           handler.setChannelLimit(iconn);
280           extraInformation = "Configuration Saved";
281         }
282       }
283     }
284     final String system = REQUEST.System.readFileUnique();
285     final StringBuilder builder = new StringBuilder(system);
286     WaarpStringUtils.replace(builder, "XXXXCHANNELLIMITRXXX", Long.toString(
287         FileBasedConfiguration.fileBasedConfiguration.getServerGlobalReadLimit()));
288     WaarpStringUtils.replace(builder, "XXXXCPULXXX",
289                              Double.toString(handler.getCpuLimit()));
290     WaarpStringUtils.replace(builder, "XXXXCONLXXX",
291                              Integer.toString(handler.getChannelLimit()));
292     if (extraInformation != null) {
293       WaarpStringUtils.replace(builder, XXXRESULTXXX, extraInformation);
294     } else {
295       WaarpStringUtils.replace(builder, XXXRESULTXXX, "");
296     }
297     return builder.toString();
298   }
299 
300   private String rule() {
301     getParams();
302     if (params == null) {
303       final String system = REQUEST.Rule.readFileUnique();
304       final StringBuilder builder = new StringBuilder(system);
305       final CommandExecutor exec = AbstractExecutor.getCommandExecutor();
306       WaarpStringUtils.replace(builder, "XXXSTCXXX",
307                                exec.getStorType() + ' ' + exec.pstorCMD);
308       WaarpStringUtils.replace(builder, "XXXSTDXXX",
309                                Long.toString(exec.getPstorDelay()));
310       WaarpStringUtils.replace(builder, "XXXRTCXXX",
311                                exec.getRetrType() + ' ' + exec.pretrCMD);
312       WaarpStringUtils.replace(builder, "XXXRTDXXX",
313                                Long.toString(exec.getPretrDelay()));
314       WaarpStringUtils.replace(builder, XXXRESULTXXX, "");
315       return builder.toString();
316     }
317     String extraInformation = null;
318     if (params.containsKey(ACTION2)) {
319       final List<String> action = params.get(ACTION2);
320       for (final String act : action) {
321         if ("Update".equalsIgnoreCase(act)) {
322           final CommandExecutor exec = AbstractExecutor.getCommandExecutor();
323           String bglobalr = getTrimValue("std");
324           long lglobal = exec.getPstorDelay();
325           if (bglobalr != null) {
326             lglobal = Long.parseLong(bglobalr);
327           }
328           exec.setPstorDelay(lglobal);
329           bglobalr = getTrimValue("rtd");
330           lglobal = exec.getPretrDelay();
331           if (bglobalr != null) {
332             lglobal = Long.parseLong(bglobalr);
333           }
334           exec.setPretrDelay(lglobal);
335           bglobalr = getTrimValue("stc");
336           String store = exec.getStorType() + ' ' + exec.pstorCMD;
337           if (bglobalr != null) {
338             store = bglobalr;
339           }
340           bglobalr = getTrimValue("rtc");
341           String retr = exec.getRetrType() + ' ' + exec.pretrCMD;
342           if (bglobalr != null) {
343             retr = bglobalr;
344           }
345           AbstractExecutor.initializeExecutor(retr, exec.getPretrDelay(), store,
346                                               exec.getPstorDelay());
347           extraInformation = "Configuration Saved";
348         }
349       }
350     }
351     final String system = REQUEST.Rule.readFileUnique();
352     final StringBuilder builder = new StringBuilder(system);
353     final CommandExecutor exec = AbstractExecutor.getCommandExecutor();
354     WaarpStringUtils.replace(builder, "XXXSTCXXX",
355                              exec.getStorType() + ' ' + exec.pstorCMD);
356     WaarpStringUtils.replace(builder, "XXXSTDXXX",
357                              Long.toString(exec.getPstorDelay()));
358     WaarpStringUtils.replace(builder, "XXXRTCXXX",
359                              exec.getRetrType() + ' ' + exec.pretrCMD);
360     WaarpStringUtils.replace(builder, "XXXRTDXXX",
361                              Long.toString(exec.getPretrDelay()));
362     if (extraInformation != null) {
363       WaarpStringUtils.replace(builder, XXXRESULTXXX, extraInformation);
364     } else {
365       WaarpStringUtils.replace(builder, XXXRESULTXXX, "");
366     }
367     return builder.toString();
368   }
369 
370   private String transfer() {
371     getParams();
372     final String head = REQUEST.Transfer.readHeader();
373     String end = REQUEST.Transfer.readEnd();
374     String body = REQUEST.Transfer.readBody();
375     if (params == null) {
376       end = end.replace(XXXRESULTXXX, "");
377       body = FileBasedConfiguration.fileBasedConfiguration.getHtmlTransfer(body,
378                                                                            LIMITROW);
379       return head + body + end;
380     }
381     String message = "";
382     final List<String> parms = params.get(ACTION2);
383     if (parms != null) {
384       final String parm = parms.get(0);
385       boolean purgeAll = false;
386       boolean purgeCorrect = false;
387       boolean delete = false;
388       if ("PurgeCorrectTransferLogs".equalsIgnoreCase(parm)) {
389         purgeCorrect = true;
390       } else if ("PurgeAllTransferLogs".equalsIgnoreCase(parm)) {
391         purgeAll = true;
392       } else if ("Delete".equalsIgnoreCase(parm)) {
393         delete = true;
394       }
395       if (purgeCorrect || purgeAll) {
396         DbPreparedStatement preparedStatement = null;
397         ReplyCode status = null;
398         String action = "purgeAll";
399 
400         if (purgeCorrect) {
401           status = ReplyCode.REPLY_226_CLOSING_DATA_CONNECTION;
402           action = "purge";
403         }
404         try {
405           preparedStatement =
406               DbTransferLog.getStatusPrepareStament(dbSession, status, 0);
407         } catch (final WaarpDatabaseNoConnectionException e) {
408           message = "Error during " + action;
409         } catch (final WaarpDatabaseSqlException e) {
410           message = "Error during " + action;
411         }
412         if (preparedStatement != null) {
413           try {
414             final FileBasedConfiguration config =
415                 FileBasedConfiguration.fileBasedConfiguration;
416             final String filename =
417                 config.getBaseDirectory() + DirInterface.SEPARATOR +
418                 config.getAdminName() + DirInterface.SEPARATOR +
419                 config.getHostId() + "_logs_" + System.currentTimeMillis() +
420                 ".xml";
421             message = DbTransferLog.saveDbTransferLogFile(preparedStatement,
422                                                           filename);
423           } finally {
424             preparedStatement.realClose();
425           }
426         }
427       } else if (delete) {
428         final String user = getTrimValue("user");
429         final String acct = getTrimValue("account");
430         final String specid = getTrimValue("specialid");
431         final long specialId = Long.parseLong(specid);
432         try {
433           final DbTransferLog log =
434               new DbTransferLog(dbSession, user, acct, specialId);
435           final FileBasedConfiguration config =
436               FileBasedConfiguration.fileBasedConfiguration;
437           final String filename =
438               config.getBaseDirectory() + DirInterface.SEPARATOR +
439               config.getAdminName() + DirInterface.SEPARATOR +
440               config.getHostId() + "_log_" + System.currentTimeMillis() +
441               ".xml";
442           message = log.saveDbTransferLog(filename);
443         } catch (final WaarpDatabaseException e) {
444           message = "Error during delete 1 Log";
445         }
446       } else {
447         message = "No Action";
448       }
449       end = end.replace(XXXRESULTXXX, message);
450     }
451     end = end.replace(XXXRESULTXXX, "");
452     body = FileBasedConfiguration.fileBasedConfiguration.getHtmlTransfer(body,
453                                                                          LIMITROW);
454     return head + body + end;
455   }
456 
457   private String user() {
458     getParams();
459     final String head = REQUEST.User.readHeader();
460     String end = REQUEST.User.readEnd();
461     String body = REQUEST.User.readBody();
462     final FileBasedConfiguration config =
463         FileBasedConfiguration.fileBasedConfiguration;
464     final String filedefault =
465         config.getBaseDirectory() + DirInterface.SEPARATOR +
466         config.getAdminName() + DirInterface.SEPARATOR + "authentication.xml";
467     if (params == null) {
468       end = end.replace(XXXRESULTXXX, "");
469       end = end.replace(XXXFILEXXX, filedefault);
470       body = FileBasedConfiguration.fileBasedConfiguration.getHtmlAuth(body);
471       return head + body + end;
472     }
473     final List<String> parms = params.get(ACTION2);
474     if (parms != null) {
475       final String parm = parms.get(0);
476       if ("ImportExport".equalsIgnoreCase(parm)) {
477         String file = getTrimValue("file");
478         final String exportImport = getTrimValue("export");
479         String message = "";
480         final boolean purge;
481         purge = params.containsKey("purge");
482         final boolean replace;
483         replace = params.containsKey("replace");
484         if (file == null) {
485           file = filedefault;
486         }
487         end = end.replace(XXXFILEXXX, file);
488         if ("import".equalsIgnoreCase(exportImport)) {
489           if (!config.initializeAuthent(file, purge)) {
490             message += "Cannot initialize Authentication from " + file;
491           } else {
492             message += "Initialization of Authentication OK from " + file;
493             if (replace) {
494               if (!config.saveAuthenticationFile(
495                   config.getAuthenticationFile())) {
496                 message += " but cannot replace server authenticationFile";
497               } else {
498                 message += " and replacement done";
499               }
500             }
501           }
502         } else {
503           // export
504           if (!config.saveAuthenticationFile(file)) {
505             message += "Authentications CANNOT be saved into " + file;
506           } else {
507             message += "Authentications saved into " + file;
508           }
509         }
510         end = end.replace(XXXRESULTXXX, message);
511       } else {
512         end = end.replace(XXXFILEXXX, filedefault);
513       }
514     }
515     end = end.replace(XXXRESULTXXX, "");
516     body = FileBasedConfiguration.fileBasedConfiguration.getHtmlAuth(body);
517     return head + body + end;
518   }
519 
520   private void getParams() {
521     if (request.method() == HttpMethod.GET) {
522       params = null;
523     } else if (request.method() == HttpMethod.POST) {
524       final ByteBuf content = request.content();
525       if (content.isReadable()) {
526         final String param = content.toString(WaarpStringUtils.UTF8);
527         final QueryStringDecoder queryStringDecoder2 =
528             new QueryStringDecoder("/?" + param);
529         params = queryStringDecoder2.parameters();
530       } else {
531         params = null;
532       }
533     }
534   }
535 
536   private void clearSession() {
537     if (admin != null) {
538       final FileBasedAuth auth = sessions.remove(admin.value());
539       admin = null;
540       if (auth != null) {
541         auth.clear();
542       }
543     }
544   }
545 
546   protected final void closeConnection() {
547     if (dbSession != null &&
548         dbSession != DbConstantFtp.gatewayAdmin.getSession()) {
549       DbAdmin.decHttpSession();
550       dbSession.disconnect();
551     }
552     dbSession = null;
553   }
554 
555   private void checkAuthent(final ChannelHandlerContext ctx) {
556     newSession = true;
557     if (request.method() == HttpMethod.GET) {
558       final String logon = logon();
559       responseContent.append(logon);
560       clearSession();
561       writeResponse(ctx);
562       return;
563     } else if (request.method() == HttpMethod.POST) {
564       getParams();
565       if (params == null) {
566         final String logon = logon();
567         responseContent.append(logon);
568         clearSession();
569         writeResponse(ctx);
570         return;
571       }
572     }
573     boolean getMenu = false;
574     if (params.containsKey("Logon")) {
575       String name = null;
576       String password = null;
577       List<String> values;
578       // get values
579       if (params.containsKey("name")) {
580         values = params.get("name");
581         if (values != null) {
582           name = values.get(0);
583           if (name == null || name.length() == 0) {
584             getMenu = true;
585           }
586         }
587       } else {
588         getMenu = true;
589       }
590       // search the nb param
591       if (!getMenu && params.containsKey("passwd")) {
592         values = params.get("passwd");
593         if (values != null) {
594           password = values.get(0);
595           getMenu = password == null || password.length() == 0;
596         } else {
597           getMenu = true;
598         }
599       } else {
600         getMenu = true;
601       }
602       if (!getMenu && name != null) {
603         logger.debug("Name={} vs {} Pass={} vs {}", name, name.equals(
604                          FileBasedConfiguration.fileBasedConfiguration.getAdminName()),
605                      password,
606                      FileBasedConfiguration.fileBasedConfiguration.checkPassword(
607                          password));
608         if (name.equals(
609             FileBasedConfiguration.fileBasedConfiguration.getAdminName()) &&
610             FileBasedConfiguration.fileBasedConfiguration.checkPassword(
611                 password)) {
612           authentHttp.specialNoSessionAuth(
613               FileBasedConfiguration.fileBasedConfiguration.getHostId());
614         } else {
615           getMenu = true;
616         }
617         if (!authentHttp.isIdentified()) {
618           logger.info("Still not authenticated: {}", authentHttp);
619           getMenu = true;
620         }
621       }
622     } else {
623       getMenu = true;
624     }
625     if (getMenu) {
626       final String logon = logon();
627       responseContent.append(logon);
628       clearSession();
629     } else {
630       final String index = index();
631       responseContent.append(index);
632       clearSession();
633       admin = new DefaultCookie(FTPSESSIONString,
634                                 FileBasedConfiguration.fileBasedConfiguration.getHostId() +
635                                 Long.toHexString(RANDOM.nextLong()));
636       sessions.put(admin.value(), authentHttp);
637       logger.debug("CreateSession: {}}:{}", uriRequest, admin);
638     }
639     writeResponse(ctx);
640   }
641 
642   @Override
643   protected void channelRead0(final ChannelHandlerContext ctx,
644                               final FullHttpRequest msg) {
645     request = msg;
646     final QueryStringDecoder queryStringDecoder =
647         new QueryStringDecoder(request.uri());
648     uriRequest = queryStringDecoder.path();
649     if (uriRequest.contains("gre/") || uriRequest.contains("img/") ||
650         uriRequest.contains("res/")) {
651       HttpWriteCacheEnable.writeFile(request, ctx,
652                                      FileBasedConfiguration.fileBasedConfiguration.getHttpBasePath() +
653                                      uriRequest, FTPSESSIONString);
654       return;
655     }
656     checkSession();
657     try {
658       if (!authentHttp.isIdentified()) {
659         logger.debug("Not Authent: {}}:{}", uriRequest, authentHttp);
660         checkAuthent(ctx);
661         return;
662       }
663       String find = uriRequest;
664       if (uriRequest.charAt(0) == '/') {
665         find = uriRequest.substring(1);
666       }
667       find = find.substring(0, find.indexOf('.'));
668       REQUEST req = REQUEST.index;
669       try {
670         req = REQUEST.valueOf(find);
671       } catch (final IllegalArgumentException e1) {
672         req = REQUEST.index;
673         logger.info("NotFound: {}:{}", find, uriRequest);
674       }
675       switch (req) {
676         case System:
677           responseContent.append(system());
678           break;
679         case Rule:
680           responseContent.append(rule());
681           break;
682         case User:
683           responseContent.append(user());
684           break;
685         case Transfer:
686           responseContent.append(transfer());
687           break;
688         default:
689           responseContent.append(index());
690           break;
691       }
692       writeResponse(ctx);
693     } finally {
694       closeConnection();
695     }
696   }
697 
698   private void checkSession() {
699     final String cookieString = request.headers().get(HttpHeaderNames.COOKIE);
700     if (cookieString != null) {
701       final Set<Cookie> cookies = ServerCookieDecoder.LAX.decode(cookieString);
702       if (!cookies.isEmpty()) {
703         for (final Cookie elt : cookies) {
704           if (elt.name().equalsIgnoreCase(FTPSESSIONString)) {
705             admin = elt;
706             break;
707           }
708         }
709       }
710     }
711     // load DbSession
712     try {
713       dbSession = new DbSession(DbConstantFtp.gatewayAdmin, false);
714       DbAdmin.incHttpSession();
715     } catch (final WaarpDatabaseNoConnectionException e1) {
716       // Cannot connect so use default connection
717       logger.warn("Use default database connection");
718       dbSession = DbConstantFtp.gatewayAdmin.getSession();
719     }
720     if (admin != null) {
721       final FileBasedAuth auth = sessions.get(admin.value());
722       if (auth != null) {
723         authentHttp = auth;
724       }
725     } else {
726       logger.info("NoSession: {}:{}", uriRequest, admin);
727     }
728   }
729 
730   private void handleCookies(final HttpResponse response) {
731     final String cookieString = request.headers().get(HttpHeaderNames.COOKIE);
732     if (cookieString != null) {
733       final Set<Cookie> cookies = ServerCookieDecoder.LAX.decode(cookieString);
734       if (!cookies.isEmpty()) {
735         // Reset the sessions if necessary.
736         boolean findSession = false;
737         for (final Cookie cookie : cookies) {
738           if (cookie.name().equalsIgnoreCase(FTPSESSIONString)) {
739             if (newSession) {
740               findSession = false;
741             } else {
742               findSession = true;
743               response.headers().add(HttpHeaderNames.SET_COOKIE,
744                                      ServerCookieEncoder.LAX.encode(cookie));
745             }
746           } else {
747             response.headers().add(HttpHeaderNames.SET_COOKIE,
748                                    ServerCookieEncoder.LAX.encode(cookie));
749           }
750         }
751         newSession = false;
752         if (!findSession && admin != null) {
753           response.headers().add(HttpHeaderNames.SET_COOKIE,
754                                  ServerCookieEncoder.LAX.encode(admin));
755           logger.debug("AddSession: {}:{}", uriRequest, admin);
756         }
757       }
758     } else if (admin != null) {
759       logger.debug("AddSession: {}:{}", uriRequest, admin);
760       response.headers().add(HttpHeaderNames.SET_COOKIE,
761                              ServerCookieEncoder.LAX.encode(admin));
762     }
763   }
764 
765   /**
766    * Write the response
767    *
768    * @param ctx
769    */
770   private void writeResponse(final ChannelHandlerContext ctx) {
771     // Convert the response content to a ByteBuf.
772     final ByteBuf buf = Unpooled.copiedBuffer(responseContent.toString(),
773                                               WaarpStringUtils.UTF8);
774     responseContent.setLength(0);
775 
776     // Decide whether to close the connection or not.
777     final boolean keepAlive = HttpUtil.isKeepAlive(request);
778     final boolean close = HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(
779         request.headers().get(HttpHeaderNames.CONNECTION)) || !keepAlive ||
780                           forceClose;
781 
782     // Build the response object.
783     final FullHttpResponse response =
784         new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
785                                     buf);
786     response.headers().add(HttpHeaderNames.CONTENT_LENGTH,
787                            response.content().readableBytes());
788     response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html");
789     if (keepAlive) {
790       response.headers()
791               .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
792     }
793     if (!close) {
794       // There's no need to add 'Content-Length' header
795       // if this is the last response.
796       response.headers().set(HttpHeaderNames.CONTENT_LENGTH,
797                              String.valueOf(buf.readableBytes()));
798     }
799 
800     handleCookies(response);
801 
802     // Write the response.
803     final ChannelFuture future = ctx.writeAndFlush(response);
804     // Close the connection after the write operation is done if necessary.
805     if (close) {
806       future.addListener(WaarpSslUtility.SSLCLOSE);
807     }
808     if (shutdown) {
809       FtpChannelUtils.teminateServer(
810           FileBasedConfiguration.fileBasedConfiguration);
811       if (!close) {
812         future.addListener(WaarpSslUtility.SSLCLOSE);
813       }
814     }
815   }
816 
817   /**
818    * Send an error and close
819    *
820    * @param ctx
821    * @param status
822    */
823   private void sendError(final ChannelHandlerContext ctx,
824                          final HttpResponseStatus status) {
825     responseContent.setLength(0);
826     responseContent.append(error(status.toString()));
827     final FullHttpResponse response =
828         new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,
829                                     Unpooled.copiedBuffer(
830                                         responseContent.toString(),
831                                         WaarpStringUtils.UTF8));
832     response.headers().add(HttpHeaderNames.CONTENT_LENGTH,
833                            response.content().readableBytes());
834     response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html");
835     clearSession();
836     // Close the connection as soon as the error message is sent.
837     ctx.channel().writeAndFlush(response).addListener(WaarpSslUtility.SSLCLOSE);
838   }
839 
840   @Override
841   public void exceptionCaught(final ChannelHandlerContext ctx,
842                               final Throwable cause) {
843     if (!(cause instanceof CommandAbstractException)) {
844       if (cause instanceof IOException) {
845         // Nothing to do
846         return;
847       }
848       logger.warn("Exception in HttpSslHandler: {}", cause.getMessage());
849     }
850     if (ctx.channel().isActive()) {
851       sendError(ctx, HttpResponseStatus.BAD_REQUEST);
852     }
853   }
854 
855   @Override
856   public void channelActive(final ChannelHandlerContext ctx) throws Exception {
857     final Channel channel = ctx.channel();
858     logger.debug("Add channel to ssl");
859     FileBasedConfiguration.fileBasedConfiguration.getHttpChannelGroup()
860                                                  .add(channel);
861     super.channelActive(ctx);
862   }
863 }