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.openr66.database.data;
21  
22  import com.fasterxml.jackson.core.Base64Variants;
23  import com.fasterxml.jackson.databind.JsonNode;
24  import com.fasterxml.jackson.databind.node.ArrayNode;
25  import com.fasterxml.jackson.databind.node.ObjectNode;
26  import org.waarp.common.database.DbConstant;
27  import org.waarp.common.database.DbPreparedStatement;
28  import org.waarp.common.database.DbSession;
29  import org.waarp.common.database.exception.WaarpDatabaseException;
30  import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
31  import org.waarp.common.database.exception.WaarpDatabaseNoDataException;
32  import org.waarp.common.database.exception.WaarpDatabaseSqlException;
33  import org.waarp.common.digest.FilesystemBasedDigest;
34  import org.waarp.common.json.JsonHandler;
35  import org.waarp.common.logging.WaarpLogger;
36  import org.waarp.common.logging.WaarpLoggerFactory;
37  import org.waarp.common.role.RoleDefault;
38  import org.waarp.common.utility.ParametersChecker;
39  import org.waarp.common.utility.WaarpStringUtils;
40  import org.waarp.openr66.context.R66Session;
41  import org.waarp.openr66.dao.AbstractDAO;
42  import org.waarp.openr66.dao.DAOFactory;
43  import org.waarp.openr66.dao.Filter;
44  import org.waarp.openr66.dao.HostDAO;
45  import org.waarp.openr66.dao.database.DBHostDAO;
46  import org.waarp.openr66.dao.database.StatementExecutor;
47  import org.waarp.openr66.dao.exception.DAOConnectionException;
48  import org.waarp.openr66.dao.exception.DAONoDataException;
49  import org.waarp.openr66.pojo.Host;
50  import org.waarp.openr66.protocol.configuration.Configuration;
51  import org.waarp.openr66.protocol.networkhandler.NetworkTransaction;
52  
53  import java.net.InetSocketAddress;
54  import java.net.SocketAddress;
55  import java.sql.SQLException;
56  import java.sql.Types;
57  import java.util.ArrayList;
58  import java.util.List;
59  import java.util.regex.Matcher;
60  import java.util.regex.Pattern;
61  
62  /**
63   * Host Authentication Table object
64   */
65  public class DbHostAuth extends AbstractDbDataDao<Host> {
66    private static final String CHECKED = "checked";
67  
68    private static final String CANNOT_FIND_HOST = "Cannot find host";
69  
70    public static final String DEFAULT_CLIENT_ADDRESS = "0.0.0.0";
71  
72    /**
73     * Internal Logger
74     */
75    private static final WaarpLogger logger =
76        WaarpLoggerFactory.getLogger(DbHostAuth.class);
77    private static final byte[] VALUE_0_BYTE = {};
78    private static final DbHostAuth[] DBHOSTAUTH_0_SIZE = new DbHostAuth[0];
79    private static final Pattern BACKSLASH =
80        Pattern.compile("\"", Pattern.LITERAL);
81    private static final Pattern COMMA = Pattern.compile(",", Pattern.LITERAL);
82  
83    public enum Columns {
84      ADDRESS, PORT, ISSSL, HOSTKEY, ADMINROLE, ISCLIENT, ISACTIVE, ISPROXIFIED,
85      UPDATEDINFO, HOSTID
86    }
87  
88    public static final int[] dbTypes = {
89        Types.NVARCHAR, Types.INTEGER, Types.BIT, Types.VARBINARY, Types.BIT,
90        Types.BIT, Types.BIT, Types.BIT, Types.INTEGER, Types.NVARCHAR
91    };
92  
93    public static final String table = " HOSTS ";
94  
95    protected static final String selectAllFields =
96        Columns.ADDRESS.name() + ',' + Columns.PORT.name() + ',' +
97        Columns.ISSSL.name() + ',' + Columns.HOSTKEY.name() + ',' +
98        Columns.ADMINROLE.name() + ',' + Columns.ISCLIENT.name() + ',' +
99        Columns.ISACTIVE.name() + ',' + Columns.ISPROXIFIED.name() + ',' +
100       Columns.UPDATEDINFO.name() + ',' + Columns.HOSTID.name();
101 
102   public static final Columns[] indexes = {
103       Columns.UPDATEDINFO
104   };
105 
106   @Override
107   protected final void initObject() {
108     //nothing
109   }
110 
111   @Override
112   protected final String getTable() {
113     return table;
114   }
115 
116   @Override
117   protected final AbstractDAO<Host> getDao(final boolean isCacheable)
118       throws DAOConnectionException {
119     return DAOFactory.getInstance().getHostDAO(isCacheable);
120   }
121 
122   @Override
123   protected final String getPrimaryKey() {
124     if (pojo != null) {
125       return pojo.getHostid();
126     }
127     throw new IllegalArgumentException("pojo is null");
128   }
129 
130   @Override
131   protected final String getPrimaryField() {
132     return Columns.HOSTID.name();
133   }
134 
135   /**
136    * @param hostid
137    * @param address
138    * @param port
139    * @param isSSL
140    * @param hostkey
141    * @param adminrole
142    * @param isClient
143    */
144   public DbHostAuth(final String hostid, final String address, final int port,
145                     final boolean isSSL, final byte[] hostkey,
146                     final boolean adminrole, final boolean isClient)
147       throws WaarpDatabaseSqlException {
148     pojo = new Host(hostid, address, port, hostkey, isSSL, isClient, false,
149                     adminrole);
150     if (hostkey != null) {
151       try {
152         // Save as crypted with the local Key and HEX
153         pojo.setHostkey(
154             Configuration.configuration.getCryptoKey().cryptToHex(hostkey)
155                                        .getBytes(WaarpStringUtils.UTF8));
156       } catch (final Exception e) {
157         logger.warn("Error while cyphering hostkey" + " : {}", e.getMessage());
158         pojo.setHostkey(VALUE_0_BYTE);
159       }
160     }
161     if (port < 0) {
162       pojo.setClient(true);
163       pojo.setAddress(DEFAULT_CLIENT_ADDRESS);
164     }
165     checkValues();
166     isSaved = false;
167   }
168 
169   private DbHostAuth(final Host host) {
170     if (host == null) {
171       throw new IllegalArgumentException(
172           "Argument in constructor cannot be null");
173     }
174     this.pojo = host;
175   }
176 
177   public DbHostAuth(final ObjectNode source) throws WaarpDatabaseSqlException {
178     pojo = new Host();
179     setFromJson(source, false);
180   }
181 
182   @Override
183   protected final void checkValues() throws WaarpDatabaseSqlException {
184     pojo.checkValues();
185   }
186 
187   @Override
188   public final void setFromJson(final ObjectNode node,
189                                 final boolean ignorePrimaryKey)
190       throws WaarpDatabaseSqlException {
191     super.setFromJson(node, ignorePrimaryKey);
192     if (pojo.getHostkey() == null || pojo.getHostkey().length == 0 ||
193         ParametersChecker.isEmpty(pojo.getAddress()) ||
194         ParametersChecker.isEmpty(pojo.getHostid())) {
195       throw new WaarpDatabaseSqlException(
196           "Not enough argument to create the object");
197     }
198     if (pojo.getHostkey() != null) {
199       try {
200         // Save as crypted with the local Key and Base64
201         pojo.setHostkey(Configuration.configuration.getCryptoKey().cryptToHex(
202             pojo.getHostkey()).getBytes(WaarpStringUtils.UTF8));
203       } catch (final Exception e) {
204         pojo.setHostkey(VALUE_0_BYTE);
205       }
206       isSaved = false;
207     }
208     if (pojo.getPort() < 0) {
209       pojo.setClient(true);
210       pojo.setAddress(DEFAULT_CLIENT_ADDRESS);
211       isSaved = false;
212     }
213     if (!ignorePrimaryKey) {
214       try {
215         insert();
216       } catch (final WaarpDatabaseException e) {
217         try {
218           update();
219         } catch (final WaarpDatabaseException ex) {
220           logger.error("Cannot save item: {}", ex.getMessage());
221         }
222       }
223     }
224   }
225 
226   @Override
227   protected final void setFromJson(final String field, final JsonNode value) {
228     if (value == null) {
229       return;
230     }
231     for (final Columns column : Columns.values()) {
232       if (column.name().equalsIgnoreCase(field)) {
233         switch (column) {
234           case ADDRESS:
235             pojo.setAddress(value.asText());
236             break;
237           case ADMINROLE:
238             pojo.setAdmin(value.asBoolean());
239             break;
240           case HOSTKEY:
241             // Change from Base64 to Byte for HostKey
242             try {
243               final byte[] key =
244                   Base64Variants.getDefaultVariant().decode(value.asText());
245               pojo.setHostkey(key);
246             } catch (final IllegalArgumentException e) {
247               logger.warn("HostKey not in Base64 from Jackson: {}",
248                           e.getMessage());
249               pojo.setHostkey(value.asText().getBytes(WaarpStringUtils.UTF8));
250             }
251             break;
252           case ISACTIVE:
253             pojo.setActive(value.asBoolean());
254             break;
255           case ISCLIENT:
256             pojo.setClient(value.asBoolean());
257             break;
258           case ISPROXIFIED:
259             pojo.setProxified(value.asBoolean());
260             break;
261           case ISSSL:
262             pojo.setSSL(value.asBoolean());
263             break;
264           case PORT:
265             pojo.setPort(value.asInt());
266             break;
267           case UPDATEDINFO:
268             pojo.setUpdatedInfo(
269                 org.waarp.openr66.pojo.UpdatedInfo.valueOf(value.asInt()));
270             break;
271           case HOSTID:
272             pojo.setHostid(value.asText());
273             break;
274         }
275       }
276     }
277   }
278 
279   /**
280    * @param hostid
281    *
282    * @throws WaarpDatabaseException
283    */
284   public DbHostAuth(final String hostid) throws WaarpDatabaseException {
285     if (hostid == null) {
286       throw new WaarpDatabaseException("No host id passed");
287     }
288     HostDAO hostAccess = null;
289     try {
290       hostAccess = DAOFactory.getInstance().getHostDAO(true);
291       pojo = hostAccess.select(hostid);
292     } catch (final DAOConnectionException e) {
293       throw new WaarpDatabaseException(e);
294     } catch (final DAONoDataException e) {
295       throw new WaarpDatabaseNoDataException(CANNOT_FIND_HOST, e);
296     } finally {
297       DAOFactory.closeDAO(hostAccess);
298     }
299   }
300 
301   /**
302    * Delete all entries (used when purge and reload)
303    *
304    * @return the previous existing array of DbRule
305    *
306    * @throws WaarpDatabaseException
307    */
308   public static DbHostAuth[] deleteAll() throws WaarpDatabaseException {
309     HostDAO hostAccess = null;
310     final List<DbHostAuth> res = new ArrayList<DbHostAuth>();
311     List<Host> hosts;
312     try {
313       hostAccess = DAOFactory.getInstance().getHostDAO(false);
314       hosts = hostAccess.getAll();
315       hostAccess.deleteAll();
316     } catch (final DAOConnectionException e) {
317       throw new WaarpDatabaseException(e);
318     } finally {
319       DAOFactory.closeDAO(hostAccess);
320     }
321     for (final Host host : hosts) {
322       final DbHostAuth hostAuth = new DbHostAuth(host);
323       hostAuth.isSaved = false;
324       res.add(hostAuth);
325     }
326     return res.toArray(new DbHostAuth[0]);
327   }
328 
329   /**
330    * Private constructor for Commander only
331    */
332   private DbHostAuth() {
333     pojo = new Host();
334   }
335 
336   /**
337    * Get All DbHostAuth from database or from internal hashMap in case of no
338    * database support
339    *
340    * @return the array of DbHostAuth
341    *
342    * @throws WaarpDatabaseNoConnectionException
343    * @throws WaarpDatabaseSqlException
344    */
345   public static DbHostAuth[] getAllHosts()
346       throws WaarpDatabaseNoConnectionException {
347     HostDAO hostAccess = null;
348     final List<DbHostAuth> res = new ArrayList<DbHostAuth>();
349     List<Host> hosts;
350     try {
351       hostAccess = DAOFactory.getInstance().getHostDAO(false);
352       hosts = hostAccess.getAll();
353     } catch (final DAOConnectionException e) {
354       throw new WaarpDatabaseNoConnectionException(e);
355     } finally {
356       DAOFactory.closeDAO(hostAccess);
357     }
358     for (final Host host : hosts) {
359       final DbHostAuth dbHostAuth = new DbHostAuth(host);
360       dbHostAuth.isSaved = true;
361       res.add(dbHostAuth);
362     }
363     return res.toArray(DBHOSTAUTH_0_SIZE);
364   }
365 
366   /**
367    * For instance from Commander when getting updated information
368    *
369    * @param preparedStatement
370    *
371    * @return the next updated DbHostAuth
372    *
373    * @throws WaarpDatabaseNoConnectionException
374    * @throws WaarpDatabaseSqlException
375    */
376   public static DbHostAuth getFromStatement(
377       final DbPreparedStatement preparedStatement)
378       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
379     final DbHostAuth dbHostAuth = new DbHostAuth();
380     AbstractDAO<Host> hostDAO = null;
381     try {
382       hostDAO = dbHostAuth.getDao(false);
383       dbHostAuth.pojo = ((StatementExecutor<Host>) hostDAO).getFromResultSet(
384           preparedStatement.getResultSet());
385       return dbHostAuth;
386     } catch (final SQLException e) {
387       DbConstant.error(e);
388       throw new WaarpDatabaseSqlException("Getting values in error", e);
389     } catch (final DAOConnectionException e) {
390       throw new WaarpDatabaseSqlException("Getting values in error", e);
391     } finally {
392       DAOFactory.closeDAO(hostDAO);
393     }
394   }
395 
396   public static DbHostAuth[] getUpdatedPreparedStatement()
397       throws WaarpDatabaseNoConnectionException {
398     final List<Filter> filters = new ArrayList<Filter>(1);
399     filters.add(new Filter(DBHostDAO.UPDATED_INFO_FIELD, "=",
400                            org.waarp.openr66.pojo.UpdatedInfo.fromLegacy(
401                                UpdatedInfo.TOSUBMIT).ordinal()));
402     HostDAO hostAccess = null;
403     List<Host> hosts;
404     try {
405       hostAccess = DAOFactory.getInstance().getHostDAO(false);
406       hosts = hostAccess.find(filters);
407     } catch (final DAOConnectionException e) {
408       throw new WaarpDatabaseNoConnectionException(e);
409     } finally {
410       DAOFactory.closeDAO(hostAccess);
411     }
412     final DbHostAuth[] res = new DbHostAuth[hosts.size()];
413     int i = 0;
414     for (final Host host : hosts) {
415       res[i] = new DbHostAuth(host);
416       i++;
417     }
418     return res;
419   }
420 
421   /**
422    * @param session
423    * @param host
424    * @param addr
425    * @param ssl
426    * @param active
427    *
428    * @return the DbPreparedStatement according to the filter
429    *
430    * @throws WaarpDatabaseNoConnectionException
431    * @throws WaarpDatabaseSqlException
432    */
433   public static DbPreparedStatement getFilterPrepareStament(
434       final DbSession session, final String host, final String addr,
435       final boolean ssl, final boolean active)
436       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
437     final DbPreparedStatement preparedStatement =
438         new DbPreparedStatement(session);
439     final String request =
440         "SELECT " + selectAllFields + " FROM " + table + " WHERE ";
441     String condition = null;
442     if (ParametersChecker.isNotEmpty(host)) {
443       condition = Columns.HOSTID.name() + " = '" + host + "' ";
444     }
445     if (ParametersChecker.isNotEmpty(addr)) {
446       if (condition != null) {
447         condition += " AND ";
448       } else {
449         condition = "";
450       }
451       condition += Columns.ADDRESS.name() + " = '" + addr + "' ";
452     }
453     if (condition != null) {
454       condition += " AND ";
455     } else {
456       condition = "";
457     }
458     condition += Columns.ISSSL.name() + " = ? AND ";
459     condition += Columns.ISACTIVE.name() + " = ? ";
460     preparedStatement.createPrepareStatement(
461         request + condition + " ORDER BY " + Columns.HOSTID.name());
462     try {
463       preparedStatement.getPreparedStatement().setBoolean(1, ssl);
464       preparedStatement.getPreparedStatement().setBoolean(2, active);
465     } catch (final SQLException e) {
466       preparedStatement.realClose();
467       throw new WaarpDatabaseSqlException(e);
468     }
469     return preparedStatement;
470   }
471 
472   /**
473    * @param session
474    * @param host
475    * @param addr
476    *
477    * @return the DbPreparedStatement according to the filter
478    *
479    * @throws WaarpDatabaseNoConnectionException
480    * @throws WaarpDatabaseSqlException
481    */
482   public static DbPreparedStatement getFilterPrepareStament(
483       final DbSession session, final String host, final String addr)
484       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
485     final DbPreparedStatement preparedStatement =
486         new DbPreparedStatement(session);
487     final String request = "SELECT " + selectAllFields + " FROM " + table;
488     String condition = null;
489     if (ParametersChecker.isNotEmpty(host)) {
490       condition = Columns.HOSTID.name() + " = '" + host + "' ";
491     }
492     if (ParametersChecker.isNotEmpty(addr)) {
493       if (condition != null) {
494         condition += " AND ";
495       } else {
496         condition = "";
497       }
498       condition += Columns.ADDRESS.name() + " = '" + addr + "' ";
499     }
500     if (condition != null) {
501       condition = " WHERE " + condition;
502     } else {
503       condition = "";
504     }
505     preparedStatement.createPrepareStatement(
506         request + condition + " ORDER BY " + Columns.HOSTID.name());
507     return preparedStatement;
508   }
509 
510   @Override
511   public final void changeUpdatedInfo(final UpdatedInfo info) {
512     isSaved = false;
513     pojo.setUpdatedInfo(org.waarp.openr66.pojo.UpdatedInfo.fromLegacy(info));
514   }
515 
516   /**
517    * @return the isActive
518    */
519   public final boolean isActive() {
520     return pojo.isActive();
521   }
522 
523   /**
524    * @param isActive the isActive to set
525    */
526   public final void setActive(final boolean isActive) {
527     isSaved = false;
528     pojo.setActive(isActive);
529   }
530 
531   /**
532    * @return the isProxified
533    */
534   public final boolean isProxified() {
535     return pojo.isProxified();
536   }
537 
538   /**
539    * @param isProxified the isProxified to set
540    */
541   public final void setProxified(final boolean isProxified) {
542     isSaved = false;
543     pojo.setProxified(isProxified);
544   }
545 
546   /**
547    * Is the given key a valid one
548    *
549    * @param newkey
550    *
551    * @return True if the key is valid (or any key is valid)
552    */
553   public final boolean isKeyValid(final byte[] newkey) {
554     // It is valid to not have a key
555     // Check before if any key is passed or if account is active
556     if (pojo.getHostkey() == null) {
557       return true;
558     }
559     // Check before if any key is passed or if account is active
560     if (newkey == null || !isActive()) {
561       return false;
562     }
563     try {
564       return FilesystemBasedDigest.equalPasswd(
565           Configuration.configuration.getCryptoKey()
566                                      .decryptHexInBytes(pojo.getHostkey()),
567           newkey);
568     } catch (final Exception e) {
569       logger.info("Error while checking key", e);
570       return false;
571     }
572   }
573 
574   /**
575    * @return the hostkey
576    */
577   public final byte[] getHostkey() {
578     if (pojo.getHostkey() == null) {
579       return null;
580     }
581     try {
582       return Configuration.configuration.getCryptoKey()
583                                         .decryptHexInBytes(pojo.getHostkey());
584     } catch (final Exception e) {
585       logger.info("Error while checking key", e);
586       return VALUE_0_BYTE;
587     }
588   }
589 
590   /**
591    * @return the adminrole
592    */
593   public final boolean isAdminrole() {
594     return pojo.isAdmin();
595   }
596 
597   /**
598    * Test if the address is 0.0.0.0 for a client or isClient
599    *
600    * @return True if the address is a client address (0.0.0.0) or isClient
601    */
602   public final boolean isClient() {
603     return pojo.isClient() || isNoAddress();
604   }
605 
606   /**
607    * True if the address is a client address (0.0.0.0) or if the port is < 0
608    *
609    * @return True if the address is a client address (0.0.0.0) or if the port
610    *     is < 0
611    */
612   public final boolean isNoAddress() {
613     return pojo.getAddress().equals(DEFAULT_CLIENT_ADDRESS) ||
614            pojo.getPort() < 0;
615   }
616 
617   /**
618    * @return the SocketAddress from the address and port
619    *
620    * @throws IllegalArgumentException when the address is for a Client
621    *     and
622    *     therefore cannot be checked
623    */
624   public final SocketAddress getSocketAddress()
625       throws IllegalArgumentException {
626     if (isNoAddress()) {
627       throw new IllegalArgumentException("Not a server");
628     }
629     return new InetSocketAddress(pojo.getAddress(), pojo.getPort());
630   }
631 
632   /**
633    * @return True if this Host ref is with SSL support
634    */
635   public final boolean isSsl() {
636     return pojo.isSSL();
637   }
638 
639   /**
640    * @return the hostid
641    */
642   public final String getHostid() {
643     return pojo.getHostid();
644   }
645 
646   /**
647    * @return the address
648    */
649   public final String getAddress() {
650     return pojo.getAddress();
651   }
652 
653   /**
654    * @return the port
655    */
656   public final int getPort() {
657     return pojo.getPort();
658   }
659 
660   private static String getVersion(final String host) {
661     String remoteHost = host;
662     String alias = "";
663     if (Configuration.configuration.getAliases().containsKey(remoteHost)) {
664       remoteHost = Configuration.configuration.getAliases().get(remoteHost);
665       alias += "(Alias: " + remoteHost + ") ";
666     }
667     if (Configuration.configuration.getReverseAliases()
668                                    .containsKey(remoteHost)) {
669       final StringBuilder alias2 = new StringBuilder("(ReverseAlias: ");
670       final String[] list =
671           Configuration.configuration.getReverseAliases().get(remoteHost);
672       boolean found = false;
673       for (final String string : list) {
674         if (string.equals(host)) {
675           continue;
676         }
677         found = true;
678         alias2.append(string).append(' ');
679       }
680       if (found) {
681         alias += alias2 + ") ";
682       }
683     }
684     if (Configuration.configuration.getBusinessWhiteSet()
685                                    .contains(remoteHost)) {
686       alias += "(Business: Allowed) ";
687     }
688     if (Configuration.configuration.getRoles().containsKey(remoteHost)) {
689       final RoleDefault item =
690           Configuration.configuration.getRoles().get(remoteHost);
691       alias += "(Role: " + item + ") ";
692     }
693     return alias +
694            (Configuration.configuration.getVersions().containsKey(remoteHost)?
695                Configuration.configuration.getVersions().get(remoteHost)
696                                           .toString() : "Version Unknown");
697   }
698 
699   @Override
700   public final String toString() {
701     return "HostAuth: " + getHostid() + " address: " + getAddress() + ':' +
702            getPort() + " isSSL: " + isSsl() + " admin: " + isAdminrole() +
703            " isClient: " + isClient() + " isActive: " + isActive() +
704            " isProxified: " + isProxified() + " (" +
705            (pojo.getHostkey() != null? pojo.getHostkey().length : 0) +
706            ") Version: " + getVersion(getHostid());
707   }
708 
709   /**
710    * Write selected DbHostAuth to a Json String
711    *
712    * @param preparedStatement
713    *
714    * @return the associated Json String
715    *
716    * @throws WaarpDatabaseNoConnectionException
717    * @throws WaarpDatabaseSqlException
718    */
719   public static String getJson(final DbPreparedStatement preparedStatement,
720                                final int limit)
721       throws WaarpDatabaseNoConnectionException, WaarpDatabaseSqlException {
722     final ArrayNode arrayNode = JsonHandler.createArrayNode();
723     try {
724       preparedStatement.executeQuery();
725       int nb = 0;
726       while (preparedStatement.getNext()) {
727         final DbHostAuth host = getFromStatement(preparedStatement);
728         final ObjectNode node = host.getInternalJson();
729         arrayNode.add(node);
730         nb++;
731         if (nb >= limit) {
732           break;
733         }
734       }
735     } finally {
736       preparedStatement.realClose();
737     }
738     return JsonHandler.writeAsString(arrayNode);
739   }
740 
741   private ObjectNode getInternalJson() {
742     final ObjectNode node = getJson();
743     try {
744       node.put(Columns.HOSTKEY.name(),
745                new String(getHostkey(), WaarpStringUtils.UTF8));
746     } catch (final Exception e1) {
747       node.put(Columns.HOSTKEY.name(), "");
748     }
749     int nb;
750     try {
751       nb = NetworkTransaction.nbAttachedConnection(getSocketAddress(),
752                                                    getHostid());
753     } catch (final Exception e) {
754       nb = -1;
755     }
756     node.put("Connection", nb);
757     node.put("Version", COMMA.matcher(BACKSLASH.matcher(getVersion(getHostid()))
758                                                .replaceAll(
759                                                    Matcher.quoteReplacement(
760                                                        "")))
761                              .replaceAll(Matcher.quoteReplacement(", ")));
762     return node;
763   }
764 
765   /**
766    * @return the Json string for this
767    */
768   public final String getJsonAsString() {
769     final ObjectNode node = getInternalJson();
770     return JsonHandler.writeAsString(node);
771   }
772 
773   /**
774    * @param session
775    * @param body
776    * @param crypted True if the Key is kept crypted, False it will be
777    *     in
778    *     clear form
779    *
780    * @return the runner in Html format specified by body by replacing all
781    *     instance of fields
782    */
783   public final String toSpecializedHtml(final R66Session session,
784                                         final String body,
785                                         final boolean crypted) {
786     final StringBuilder builder = new StringBuilder(body);
787     WaarpStringUtils.replace(builder, "XXXHOSTXXX", getHostid());
788     WaarpStringUtils.replace(builder, "XXXADDRXXX", getAddress());
789     WaarpStringUtils.replace(builder, "XXXPORTXXX",
790                              Integer.toString(getPort()));
791     if (crypted) {
792       WaarpStringUtils.replace(builder, "XXXKEYXXX",
793                                new String(getHostkey(), WaarpStringUtils.UTF8));
794     } else {
795       try {
796         WaarpStringUtils.replace(builder, "XXXKEYXXX", new String(getHostkey(),
797                                                                   WaarpStringUtils.UTF8));
798       } catch (final Exception e) {
799         WaarpStringUtils.replace(builder, "XXXKEYXXX", "BAD DECRYPT");
800       }
801     }
802     WaarpStringUtils.replace(builder, "XXXSSLXXX", isSsl()? CHECKED : "");
803     WaarpStringUtils.replace(builder, "XXXADMXXX", isAdminrole()? CHECKED : "");
804     WaarpStringUtils.replace(builder, "XXXISCXXX", isClient()? CHECKED : "");
805     WaarpStringUtils.replace(builder, "XXXISAXXX", isActive()? CHECKED : "");
806     WaarpStringUtils.replace(builder, "XXXISPXXX", isProxified()? CHECKED : "");
807     WaarpStringUtils.replace(builder, "XXXVERSIONXXX",
808                              COMMA.matcher(getVersion(getHostid()))
809                                   .replaceAll(Matcher.quoteReplacement(", ")));
810     int nb;
811     try {
812       nb = NetworkTransaction.nbAttachedConnection(getSocketAddress(),
813                                                    getHostid());
814     } catch (final Exception e) {
815       nb = -1;
816     }
817     WaarpStringUtils.replace(builder, "XXXCONNXXX",
818                              nb > 0? "(" + nb + " Connected) " : "");
819     return builder.toString();
820   }
821 
822   /**
823    * @return True if any of the server has the isProxified property
824    */
825   public static boolean hasProxifiedHosts() {
826     final List<Filter> filters = new ArrayList<Filter>();
827     filters.add(new Filter(DBHostDAO.IS_PROXIFIED_FIELD, "=", true));
828 
829     HostDAO hostAccess = null;
830     try {
831       hostAccess = DAOFactory.getInstance().getHostDAO(false);
832       return hostAccess.count(filters) > 0;
833     } catch (final DAOConnectionException e) {
834       logger.error("DAO Access error", e);
835       return false;
836     } finally {
837       DAOFactory.closeDAO(hostAccess);
838     }
839   }
840 }