Browse Source

Finish up pairing code although it doesn't seem to be testable at this
point. :(

Douglas William Thrift 13 years ago
parent
commit
b27a9a34a5

+ 28 - 30
src/net/douglasthrift/bigscreenbot/BigScreenBot.java

@@ -132,7 +132,7 @@ public class BigScreenBot extends Bot
         {
             settings.load();
 
-            remote = new Remote(settings);
+            remote = new Remote(verbose, settings);
         }
         catch (IOException exception)
         {
@@ -239,13 +239,10 @@ public class BigScreenBot extends Bot
                     @Override
                     public void run()
                     {
-                        synchronized (remote)
-                        {
-                            if (arguments.length == 2)
-                                remote.fling(arguments[1], arguments[0]);
-                            else
-                                remote.fling(arguments[0]);
-                        }
+                        if (arguments.length == 2)
+                            remote.fling(arguments[1], arguments[0]);
+                        else
+                            remote.fling(arguments[0]);
 
                         sendMessage(channel, sender, String.format("flung URL (\"%1$s\") to device(s)", arguments[0]));
                     }
@@ -352,10 +349,7 @@ public class BigScreenBot extends Bot
                         {
                             List<String> devices;
 
-                            synchronized (remote)
-                            {
-                                devices = remote.listDevices();
-                            }
+                            devices = remote.listDevices();
 
                             if (devices.isEmpty())
                             {
@@ -382,37 +376,38 @@ public class BigScreenBot extends Bot
                         {
                             try
                             {
-                                synchronized (remote)
+                                if (remote.startPairDevice(arguments[0], new Remote.Pairing()
                                 {
-                                    remote.startPairDevice(arguments[0]);
-                                }
+                                    @Override
+                                    public void prompt()
+                                    {
+                                        sendMessage(channel, sender, String.format("enter the code from the device (\"%1$s\") to finish pairing", arguments[0]));
+                                    }
+
+                                    @Override
+                                    public void interrupted(InterruptedException exception)
+                                    {
+                                        sendMessage(channel, sender, String.format("pairing with device (\"%1$s\") interrupted", arguments[0]));
+                                    }
+                                }))
+                                    sendMessage(channel, sender, String.format("paired with device (\"%1$s\")", arguments[0]));
                             }
                             catch (MalformedURLException exception)
                             {
                                 sendMessage(channel, sender, String.format("invalid device name (\"%1$s\")", arguments[0]));
-
-                                return;
                             }
                             catch (UnknownHostException exception)
                             {
                                 sendMessage(channel, sender, String.format("could not find device (\"%1$s\")", arguments[0]));
-
-                                return;
                             }
                             catch (IOException exception)
                             {
                                 error(channel, sender, exception);
-
-                                return;
                             }
                             catch (PoloException exception)
                             {
                                 error(channel, sender, exception);
-
-                                return;
                             }
-
-                            sendMessage(channel, sender, String.format("enter the code from the device (\"%1$s\") to finish pairing", arguments[0]));
                         }
                     }.start();
 
@@ -425,12 +420,15 @@ public class BigScreenBot extends Bot
                         @Override
                         public void run()
                         {
-                            synchronized (remote)
+                            try
                             {
-                                remote.finishPairDevice(arguments[0], arguments[1]);
+                                if (!remote.finishPairDevice(arguments[0], arguments[1]))
+                                    sendMessage(channel, sender, String.format("have not started pairing with device (\"%1$s\")", arguments[0]));
+                            }
+                            catch (MalformedURLException exception)
+                            {
+                                sendMessage(channel, sender, String.format("invalid device name (\"%1$s\")", arguments[0]));
                             }
-
-                            sendMessage(channel, sender, String.format("paired with device (\"%1$s\")", arguments[0]));
                         }
                     }.start();
                 }
@@ -729,7 +727,7 @@ public class BigScreenBot extends Bot
     private void error(String channel, String sender, Exception exception)
     {
         error(exception);
-        sendMessage(channel, sender, "an error occurred");
+        sendMessage(channel, sender, "an error occurred: " + exception.getMessage());
     }
 
     private void help(String channel, String sender, boolean admin, String command)

+ 137 - 21
src/net/douglasthrift/bigscreenbot/Remote.java

@@ -31,6 +31,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.net.MalformedURLException;
 import java.net.UnknownHostException;
 import java.net.URL;
@@ -49,7 +50,10 @@ import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.jmdns.JmDNS;
 import javax.jmdns.ServiceInfo;
@@ -67,13 +71,26 @@ import com.google.anymote.device.MessageReceiver;
 
 import com.google.polo.exception.PoloException;
 
+import com.google.polo.pairing.ClientPairingSession;
 import com.google.polo.pairing.PairingContext;
+import com.google.polo.pairing.PairingListener;
+import com.google.polo.pairing.PairingSession;
+
+import com.google.polo.pairing.message.EncodingOption;
 
 import com.google.polo.ssl.DummySSLSocketFactory;
 import com.google.polo.ssl.SslUtil;
 
+import com.google.polo.wire.WireFormat;
+
 public class Remote implements Closeable
 {
+    public static interface Pairing
+    {
+        public void prompt();
+        public void interrupted(InterruptedException exception);
+    }
+
     private static final String STORE = "bigscreenbot.keystore";
     private static final char[] PASSWORD = "b1GsSC33Nb0T".toCharArray();
     private static final char[] NULL = new char[]{};
@@ -81,18 +98,52 @@ public class Remote implements Closeable
     private static final String LOCAL_ALIAS = "anymote-remote";
     private static final String REMOTE_ALIAS = "anymote-server-%1$X";
 
+    private static class Secret
+    {
+        private String secret;
+        private Thread thread;
+
+        public Secret()
+        {
+            thread = Thread.currentThread();
+        }
+
+        public synchronized void set(String secret)
+        {
+            this.secret = secret;
+
+            notify();
+        }
+
+        public synchronized String get() throws InterruptedException
+        {
+            while (secret == null)
+                wait();
+
+            return secret;
+        }
+
+        public void interrupt()
+        {
+            thread.interrupt();
+        }
+    }
+
     static
     {
         new Fixer();
     }
 
+    private boolean verbose;
     private Settings settings;
     private JmDNS mdns;
     private KeyStore store;
     private SSLSocketFactory factory;
+    private Map<InetSocketAddress, Secret> secrets = Collections.synchronizedMap(new HashMap<InetSocketAddress, Secret>());
 
-    public Remote(Settings settings) throws UnknownHostException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException, GeneralSecurityException
+    public Remote(boolean verbose, Settings settings) throws UnknownHostException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException, GeneralSecurityException
     {
+        this.verbose = verbose;
         this.settings = settings;
 
         String interfaze = settings.getProperty("interface");
@@ -156,34 +207,84 @@ public class Remote implements Closeable
         return devices;
     }
 
-    public void startPairDevice(String device) throws MalformedURLException, UnknownHostException, IOException, PoloException
+    public boolean startPairDevice(String device, final Pairing pairing) throws MalformedURLException, UnknownHostException, IOException, PoloException
     {
-        ServiceInfo info = mdns.getServiceInfo(TYPE, device);
-        InetAddress address;
-        int port;
+        SSLSocket socket = (SSLSocket)factory.createSocket();
+        final InetSocketAddress address = getDevice(device);
 
-        if (info != null)
-        {
-            address = info.getInetAddresses()[0];
-            port = info.getPort();
-        }
-        else
-        {
-            URL url = new URL("http://" + device);
+        socket.connect(address);
 
-            address = InetAddress.getByName(url.getHost());
-            port = url.getPort();
+        PairingContext context = PairingContext.fromSslSocket(socket, false);
+        ClientPairingSession session = new ClientPairingSession(WireFormat.PROTOCOL_BUFFERS.getWireInterface(context), context, "AnyMote", "Big Screen Bot");
+        EncodingOption option = new EncodingOption(EncodingOption.EncodingType.ENCODING_HEXADECIMAL, 4);
 
-            if (port == -1)
-                port = 9551;
-        }
+        session.addInputEncoding(option);
+        session.addOutputEncoding(option);
 
-        SSLSocket socket = (SSLSocket)factory.createSocket(address, port);
-        PairingContext context = PairingContext.fromSslSocket(socket, false);
+        return session.doPair(new PairingListener()
+        {
+            @Override
+            public void onLogMessage(LogLevel level, String message)
+            {
+                if (verbose)
+                    System.out.println(level + ": " + message);
+            }
+
+            @Override
+            public void onPerformInputDeviceRole(PairingSession session)
+            {
+                pairing.prompt();
+
+                Secret secret = new Secret(), oldSecret = secrets.put(address, secret);
+
+                if (oldSecret != null)
+                    oldSecret.interrupt();
+
+                try
+                {
+                    session.setSecret(session.getEncoder().decodeToBytes(secret.get()));
+                }
+                catch (InterruptedException exception)
+                {
+                    session.teardown();
+                    pairing.interrupted(exception);
+                }
+            }
+
+            @Override
+            public void onPerformOutputDeviceRole(PairingSession session, byte[] gamma) throws PoloException
+            {
+                throw new PoloException("This should not happen!");
+            }
+
+            @Override
+            public void onSessionCreated(PairingSession session)
+            {
+                if (verbose)
+                    System.out.println(session + " created.");
+            }
+
+            @Override
+            public void onSessionEnded(PairingSession session)
+            {
+                if (verbose)
+                    System.out.println(session + " ended.");
+            }
+        });
+
+        // TODO: store server certificate
     }
 
-    public void finishPairDevice(String device, String code)
+    public boolean finishPairDevice(String device, String code) throws MalformedURLException
     {
+        Secret secret = secrets.remove(getDevice(device));
+
+        if (secret != null)
+            secret.set(code);
+        else
+            return false;
+
+        return true;
     }
 
     public void fling(String url)
@@ -194,6 +295,21 @@ public class Remote implements Closeable
     {
     }
 
+    private InetSocketAddress getDevice(String device) throws MalformedURLException
+    {
+        ServiceInfo info = mdns.getServiceInfo(TYPE, device);
+
+        if (info != null)
+            return new InetSocketAddress(info.getInetAddresses()[0], info.getPort());
+        else
+        {
+            URL url = new URL("http://" + device);
+            int port = url.getPort();
+
+            return new InetSocketAddress(url.getHost(), port == -1 ? 9551 : port);
+        }
+    }
+
     private void store() throws FileNotFoundException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException
     {
         FileOutputStream stream = null;