UPD: Correct reping (with 5min rate limiting)
authorFelix Dörre <felix@dogcraft.de>
Sat, 31 Jan 2015 22:14:34 +0000 (23:14 +0100)
committerFelix Dörre <felix@dogcraft.de>
Sat, 31 Jan 2015 22:14:34 +0000 (23:14 +0100)
Thereby Fix times against SQL.. (only UTC everywhere... )

... execpt somewhere in mysql or blargh... I don't know... output is
UTC.

doc/tableStructure.sql
src/org/cacert/gigi/Gigi.java
src/org/cacert/gigi/database/DatabaseConnection.java
src/org/cacert/gigi/database/GigiResultSet.java
src/org/cacert/gigi/dbObjects/Certificate.java
src/org/cacert/gigi/dbObjects/DomainPingConfiguration.java
src/org/cacert/gigi/pages/account/domain/DomainOverview.java
src/org/cacert/gigi/pages/account/domain/PingConfigForm.java
src/org/cacert/gigi/ping/PingerDaemon.java

index 7ff0769c2e336a966b5769d6da5a8791f8e4d839..867a1c63704dc3bd70582d3a397c1a8f9fbc2373 100644 (file)
@@ -92,7 +92,6 @@ CREATE TABLE `pingconfig` (
   `domainid` int(11) NOT NULL,
   `type` enum('email', 'ssl', 'http', 'dns') NOT NULL,
   `info` varchar(255) NOT NULL,
-  `reping` enum('y','n') NOT NULL DEFAULT 'n',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
index 4e96e9a0477f568dbf1570f446463964f20d902b..8c4a234c5a30a3f14ab9fa26eb12e7fd3745a573 100644 (file)
@@ -19,6 +19,7 @@ import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
 import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.dbObjects.DomainPingConfiguration;
 import org.cacert.gigi.dbObjects.User;
 import org.cacert.gigi.localisation.Language;
 import org.cacert.gigi.output.Menu;
@@ -400,7 +401,17 @@ public class Gigi extends HttpServlet {
         return instance.reveresePages.get(p).replaceFirst("/?\\*$", "");
     }
 
-    public static void notifyPinger() {
+    /**
+     * Requests Pinging of domains.
+     * 
+     * @param toReping
+     *            if not null, the {@link DomainPingConfiguration} to test, if
+     *            null, just re-check if there is something to do.
+     */
+    public static void notifyPinger(DomainPingConfiguration toReping) {
+        if (toReping != null) {
+            instance.pinger.queue(toReping);
+        }
         instance.pinger.interrupt();
     }
 
index abeb78d0d765d7888af97e79a8bd1550e65a869e..661565d843a0a3f1b428a8f114d6c92cfc34129f 100644 (file)
@@ -34,7 +34,7 @@ public class DatabaseConnection {
     private void tryConnect() {
         try {
             c = DriverManager.getConnection(credentials.getProperty("sql.url") + "?zeroDateTimeBehavior=convertToNull", credentials.getProperty("sql.user"), credentials.getProperty("sql.password"));
-            PreparedStatement ps = c.prepareStatement("SET SESSION wait_timeout=?;");
+            PreparedStatement ps = c.prepareStatement("SET SESSION wait_timeout=?, time_zone='+0:00';");
             ps.setInt(1, CONNECTION_TIMEOUT);
             ps.execute();
             ps.close();
index 9dc83bd774e007d5335f24815ea3234a3c7f502a..48157ffaaa15b2c389a5f04a9e5c250dbdd8c3d5 100644 (file)
@@ -3,7 +3,6 @@ package org.cacert.gigi.database;
 import java.sql.Date;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Time;
 import java.sql.Timestamp;
 
 public class GigiResultSet {
@@ -59,15 +58,6 @@ public class GigiResultSet {
         }
     }
 
-    public Time getTime(int columnIndex) {
-        try {
-            return target.getTime(columnIndex);
-        } catch (SQLException e) {
-            handleSQL(e);
-            throw new Error(e);
-        }
-    }
-
     public String getString(String columnLabel) {
         try {
             return target.getString(columnLabel);
@@ -113,15 +103,6 @@ public class GigiResultSet {
         }
     }
 
-    public Time getTime(String columnLabel) {
-        try {
-            return target.getTime(columnLabel);
-        } catch (SQLException e) {
-            handleSQL(e);
-            throw new Error(e);
-        }
-    }
-
     public boolean next() {
         try {
             return target.next();
index df940dbc06816cf33eaaf5151dbe343705e6af62..ced044cd8d34a102fa68875e0b9d6604d7fbfdea 100644 (file)
@@ -219,10 +219,10 @@ public class Certificate {
 
         crtName = rs.getString(1);
         serial = rs.getString(4);
-        if (rs.getTime(2) == null) {
+        if (rs.getTimestamp(2) == null) {
             return CertificateStatus.DRAFT;
         }
-        if (rs.getTime(2) != null && rs.getTime(3) == null) {
+        if (rs.getTimestamp(2) != null && rs.getTimestamp(3) == null) {
             return CertificateStatus.ISSUED;
         }
         return CertificateStatus.REVOKED;
index 1146cca0c5c8b0b451c1f75c334cff0f71e87a91..cf1292b8a072ffa744f86e7a203c7fdc00d81514 100644 (file)
@@ -1,5 +1,9 @@
 package org.cacert.gigi.dbObjects;
 
+import java.util.Date;
+
+import org.cacert.gigi.Gigi;
+import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.database.DatabaseConnection;
 import org.cacert.gigi.database.GigiPreparedStatement;
 import org.cacert.gigi.database.GigiResultSet;
@@ -59,10 +63,33 @@ public class DomainPingConfiguration implements IdCachable {
         return res;
     }
 
-    public void requestReping() {
-        GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE pingconfig set reping='y' WHERE id=?");
+    public Date getLastExecution() {
+        GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `when` AS stamp from domainPinglog WHERE configId=? ORDER BY `when` DESC LIMIT 1");
+        ps.setInt(1, id);
+        GigiResultSet rs = ps.executeQuery();
+        if (rs.next()) {
+            return new Date(rs.getTimestamp("stamp").getTime());
+        }
+        return new Date(0);
+    }
+
+    public Date getLastSuccess() {
+        GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `when` AS stamp from domainPinglog WHERE configId=? AND state='success' ORDER BY `when` DESC LIMIT 1");
         ps.setInt(1, id);
-        ps.execute();
+        GigiResultSet rs = ps.executeQuery();
+        if (rs.next()) {
+            return new Date(rs.getTimestamp("stamp").getTime());
+        }
+        return new Date(0);
+    }
+
+    public synchronized void requestReping() throws GigiApiException {
+        Date lastExecution = getLastExecution();
+        if (lastExecution.getTime() + 5 * 60 * 1000 < System.currentTimeMillis()) {
+            Gigi.notifyPinger(this);
+            return;
+        }
+        throw new GigiApiException("Reping is only allowed after 5 minutes");
     }
 
 }
index 1677029252fea60efe71a0209d48175287464a4f..906e2a16b88e266cc2c11c1e08b7fc04b2d057d3 100644 (file)
@@ -6,7 +6,6 @@ import java.util.HashMap;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.cacert.gigi.Gigi;
 import org.cacert.gigi.GigiApiException;
 import org.cacert.gigi.dbObjects.Domain;
 import org.cacert.gigi.dbObjects.DomainPingConfiguration;
@@ -71,8 +70,12 @@ public class DomainOverview extends Page {
             if (dpc.getTarget() != d) {
                 return;
             }
-            dpc.requestReping();
-            Gigi.notifyPinger();
+            try {
+                dpc.requestReping();
+            } catch (GigiApiException e) {
+                e.format(resp.getWriter(), getLanguage(req));
+                return;
+            }
             resp.sendRedirect(PATH + i);
         }
         if (req.getParameter("adddomain") != null) {
index 91ea7cc0f2bfbb4b185094eb8172f5feffcf83f1..5b5da852ca0356709b0e17aeacd0ab99e84651e0 100644 (file)
@@ -133,7 +133,7 @@ public class PingConfigForm extends Form {
 
             }
         }
-        Gigi.notifyPinger();
+        Gigi.notifyPinger(null);
         return false;
     }
 
index 2a3167438851969d613ea8575ebd4d5ae0850c72..ceb88e9a68b80447207749c4797a3599cd8049c6 100644 (file)
@@ -2,42 +2,56 @@ package org.cacert.gigi.ping;
 
 import java.security.KeyStore;
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Queue;
 
 import org.cacert.gigi.database.DatabaseConnection;
 import org.cacert.gigi.database.GigiPreparedStatement;
 import org.cacert.gigi.database.GigiResultSet;
 import org.cacert.gigi.dbObjects.Domain;
-import org.cacert.gigi.dbObjects.User;
+import org.cacert.gigi.dbObjects.DomainPingConfiguration;
+import org.cacert.gigi.dbObjects.DomainPingConfiguration.PingType;
 import org.cacert.gigi.util.RandomToken;
 
 public class PingerDaemon extends Thread {
 
-    HashMap<String, DomainPinger> pingers = new HashMap<>();
+    HashMap<PingType, DomainPinger> pingers = new HashMap<>();
 
     private GigiPreparedStatement searchNeededPings;
 
     private GigiPreparedStatement enterPingResult;
 
-    private GigiPreparedStatement updatePingStatus;
-
     private KeyStore truststore;
 
+    private Queue<DomainPingConfiguration> toExecute = new LinkedList<>();
+
     public PingerDaemon(KeyStore truststore) {
         this.truststore = truststore;
     }
 
     @Override
     public void run() {
-        searchNeededPings = DatabaseConnection.getInstance().prepare("SELECT pingconfig.*, domains.domain, domains.memid FROM pingconfig LEFT JOIN domainPinglog ON domainPinglog.configId=pingconfig.id INNER JOIN domains ON domains.id=pingconfig.domainid WHERE ( pingconfig.reping='y' OR domainPinglog.configId IS NULL) AND domains.deleted IS NULL GROUP BY pingconfig.id");
+        searchNeededPings = DatabaseConnection.getInstance().prepare("SELECT pingconfig.id FROM pingconfig LEFT JOIN domainPinglog ON domainPinglog.configId=pingconfig.id INNER JOIN domains ON domains.id=pingconfig.domainid WHERE ( domainPinglog.configId IS NULL) AND domains.deleted IS NULL GROUP BY pingconfig.id");
         enterPingResult = DatabaseConnection.getInstance().prepare("INSERT INTO domainPinglog SET configId=?, state=?, result=?, challenge=?");
-        updatePingStatus = DatabaseConnection.getInstance().prepare("UPDATE pingconfig SET reping='n' WHERE id=?");
-        pingers.put("email", new EmailPinger());
-        pingers.put("ssl", new SSLPinger(truststore));
-        pingers.put("http", new HTTPFetch());
-        pingers.put("dns", new DNSPinger());
+        pingers.put(PingType.EMAIL, new EmailPinger());
+        pingers.put(PingType.SSL, new SSLPinger(truststore));
+        pingers.put(PingType.HTTP, new HTTPFetch());
+        pingers.put(PingType.DNS, new DNSPinger());
 
         while (true) {
-            execute();
+            synchronized (this) {
+                DomainPingConfiguration conf;
+                while ((conf = toExecute.peek()) != null) {
+                    handle(conf);
+                    toExecute.remove();
+                }
+                notifyAll();
+            }
+
+            GigiResultSet rs = searchNeededPings.executeQuery();
+            while (rs.next()) {
+                handle(DomainPingConfiguration.getById(rs.getInt("id")));
+            }
             try {
                 Thread.sleep(5000);
             } catch (InterruptedException e) {
@@ -45,29 +59,35 @@ public class PingerDaemon extends Thread {
         }
     }
 
-    private void execute() {
-
-        GigiResultSet rs = searchNeededPings.executeQuery();
-        while (rs.next()) {
-            String type = rs.getString("type");
-            String config = rs.getString("info");
-            DomainPinger dp = pingers.get(type);
-            if (dp != null) {
-                String token = null;
-                if (dp instanceof EmailPinger) {
-                    token = RandomToken.generateToken(16);
-                    config = config + ":" + token;
-                }
-                updatePingStatus.setInt(1, rs.getInt("id"));
-                updatePingStatus.execute();
-                enterPingResult.setInt(1, rs.getInt("id"));
-                String resp = dp.ping(Domain.getById(rs.getInt("domainid")), config, User.getById(rs.getInt("memid")));
-                enterPingResult.setString(2, DomainPinger.PING_STILL_PENDING == resp ? "open" : DomainPinger.PING_SUCCEDED.equals(resp) ? "success" : "failed");
-                enterPingResult.setString(3, resp);
-                enterPingResult.setString(4, token);
-                enterPingResult.execute();
+    private void handle(DomainPingConfiguration conf) {
+        PingType type = conf.getType();
+        String config = conf.getInfo();
+        DomainPinger dp = pingers.get(type);
+        if (dp != null) {
+            String token = null;
+            if (dp instanceof EmailPinger) {
+                token = RandomToken.generateToken(16);
+                config = config + ":" + token;
             }
+            enterPingResult.setInt(1, conf.getId());
+            Domain target = conf.getTarget();
+            String resp = dp.ping(target, config, target.getOwner());
+            enterPingResult.setString(2, DomainPinger.PING_STILL_PENDING == resp ? "open" : DomainPinger.PING_SUCCEDED.equals(resp) ? "success" : "failed");
+            enterPingResult.setString(3, resp);
+            enterPingResult.setString(4, token);
+            enterPingResult.execute();
         }
     }
 
+    public synchronized void queue(DomainPingConfiguration toReping) {
+        interrupt();
+        toExecute.add(toReping);
+        while (toExecute.size() > 0) {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
 }