diff options
Diffstat (limited to 'src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java')
-rw-r--r-- | src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java b/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java new file mode 100644 index 0000000..7c4ca80 --- /dev/null +++ b/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java @@ -0,0 +1,157 @@ +package de.bwlehrpool.bwlp_guac; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.guacamole.protocol.GuacamoleConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.bwlehrpool.bwlp_guac.JsonClient.State; + +public class AvailableClient { + + private static final Logger LOGGER = LoggerFactory.getLogger(AvailableClient.class); + + private static final AtomicLong CON_ID = new AtomicLong(); + + private final String clientip; + + private String password; + + private State state; + + private String inUseBy; + + private WrappedConnection connection; + + private long deadline; + + private long lastConnectionCheck; + + private boolean connectionOk; + + private final Object connCheckLock = new Object(); + + public AvailableClient(JsonClient source) { + this.clientip = source.clientip; + update(source); + } + + /** + * Update this client's state, resetting "in use" if appropriate. Ie, the + * password changed, which would mean the remote VNC server was restarted, so it + * can't possibly be in use by the old user anymore. + */ + public synchronized void update(JsonClient source) { + if (this.password == null || !this.password.equals(source.password)) { + if (source.state != State.OCCUPIED) { + if (this.inUseBy != null) { + LOGGER.info("Client " + this + "is available again"); + this.inUseBy = null; + this.connection = null; + } + } + this.lastConnectionCheck = 0; + this.password = source.password; + } + this.state = source.state; + this.deadline = 0; + } + + /** + * Try to reserve this client for the given user. + */ + public synchronized boolean claim(String user) { + if (this.inUseBy != null || this.password == null || this.state != State.IDLE || user == null) + return false; + this.inUseBy = user; + this.connection = new WrappedConnection(this.clientip + "/" + CON_ID.incrementAndGet(), this); + this.state = State.OCCUPIED; + return true; + } + + public synchronized boolean isInUseBy(String user) { + return (this.inUseBy != null && this.inUseBy.equals(user)); + } + + public synchronized WrappedConnection getConnection(String expectedOwner) { + if (isInUseBy(expectedOwner)) + return this.connection; + return null; + } + + public synchronized void releaseConnection(String expectedOwner) { + if (isInUseBy(expectedOwner)) { + LOGGER.info("Prematurely releasing client " + this); + this.inUseBy = null; + this.connection = null; + } + } + + /** + * If this connection is not returned by the sat server anymore, we keep it + * around for another 5 minutes just in case. + */ + public boolean isTimeout(long NOW) { + if (deadline == 0) { + deadline = NOW + 300000; + return false; + } + return deadline < NOW; + } + + @Override + public String toString() { + return clientip + "/" + state + "/" + inUseBy; + } + + public GuacamoleConfiguration toGuacConfig() { + GuacamoleConfiguration cfg = new GuacamoleConfiguration(); + cfg.setProtocol("vnc"); + cfg.setParameter("hostname", this.clientip); + cfg.setParameter("port", Integer.toString(5900)); // TODO + cfg.setParameter("password", password); + return cfg; + } + + /** + * Check if the given VNC credentials actually work, so we avoid assigning a + * bogus entry to a user. + */ + public boolean checkConnection(int retries) { + long now = System.currentTimeMillis(); + synchronized (connCheckLock) { + if (now < this.lastConnectionCheck) { + this.lastConnectionCheck = 0; + } + if (now - this.lastConnectionCheck < 5000) + return this.connectionOk; + for (;;) { + try (VncConnection vnc = new VncConnection(this.clientip, 5900)) { + LOGGER.debug("VNC Version for " + this.clientip + " is " + vnc.handshake()); + if (vnc.tryLogin(this.password)) { + this.lastConnectionCheck = now; + return this.connectionOk = true; + } + } catch (IOException e) { + LOGGER.info("Connection error VNC @ " + this, e); + if (retries-- > 0) { + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + break; + } + continue; + } + } + break; + } + this.lastConnectionCheck = now; + this.password = null; // Render invalid, so ConnectionManager::getForUser() doesn't turn into an infinite loop + return this.connectionOk = false; + } + } + +} |