mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-21 07:09:56 +01:00
Invitation API and two (untested) implementations.
This commit is contained in:
@@ -23,4 +23,28 @@ public interface BatchPlugin extends Plugin {
|
|||||||
* Returns null if a writer could not be created.
|
* Returns null if a writer could not be created.
|
||||||
*/
|
*/
|
||||||
BatchTransportWriter createWriter(ContactId c);
|
BatchTransportWriter createWriter(ContactId c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the invitation process from the inviter's side. Returns null if
|
||||||
|
* no connection can be established within the given timeout.
|
||||||
|
*/
|
||||||
|
BatchTransportWriter sendInvitation(int code, long timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the invitation process from the invitee's side. Returns null if
|
||||||
|
* no connection can be established within the given timeout.
|
||||||
|
*/
|
||||||
|
BatchTransportReader acceptInvitation(int code, long timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Continues the invitation process from the invitee's side. Returns null
|
||||||
|
* if no connection can be established within the given timeout.
|
||||||
|
*/
|
||||||
|
BatchTransportWriter sendInvitationResponse(int code, long timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Continues the invitation process from the inviter's side. Returns null
|
||||||
|
* if no connection can be established within the given timeout.
|
||||||
|
*/
|
||||||
|
BatchTransportReader acceptInvitationResponse(int code, long timeout);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,4 +15,16 @@ public interface StreamPlugin extends Plugin {
|
|||||||
* Returns null if a connection could not be created.
|
* Returns null if a connection could not be created.
|
||||||
*/
|
*/
|
||||||
StreamTransportConnection createConnection(ContactId c);
|
StreamTransportConnection createConnection(ContactId c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the invitation process from the inviter's side. Returns null if
|
||||||
|
* no connection can be established within the given timeout.
|
||||||
|
*/
|
||||||
|
StreamTransportConnection sendInvitation(int code, long timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the invitation process from the invitee's side. Returns null if
|
||||||
|
* no connection can be established within the given timeout.
|
||||||
|
*/
|
||||||
|
StreamTransportConnection acceptInvitation(int code, long timeout);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ class DatabaseCleanerImpl implements DatabaseCleaner, Runnable {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
wait(msBetweenSweeps);
|
wait(msBetweenSweeps);
|
||||||
} catch(InterruptedException ignored) {}
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
if(LOG.isLoggable(Level.WARNING))
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
|||||||
@@ -362,7 +362,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
while(closed) {
|
while(closed) {
|
||||||
try {
|
try {
|
||||||
connections.wait();
|
connections.wait();
|
||||||
} catch(InterruptedException ignored) {}
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
txn = connections.poll();
|
txn = connections.poll();
|
||||||
}
|
}
|
||||||
@@ -433,7 +436,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " open connections");
|
+ " open connections");
|
||||||
try {
|
try {
|
||||||
connections.wait();
|
connections.wait();
|
||||||
} catch(InterruptedException ignored) {}
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
for(Connection c : connections) c.close();
|
for(Connection c : connections) c.close();
|
||||||
openConnections -= connections.size();
|
openConnections -= connections.size();
|
||||||
connections.clear();
|
connections.clear();
|
||||||
|
|||||||
@@ -12,15 +12,17 @@ import java.util.MissingResourceException;
|
|||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
import net.sf.briar.api.i18n.FontManager;
|
import net.sf.briar.api.i18n.FontManager;
|
||||||
import net.sf.briar.api.i18n.I18n;
|
import net.sf.briar.api.i18n.I18n;
|
||||||
import net.sf.briar.util.FileUtils;
|
import net.sf.briar.util.FileUtils;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
// Needs to be public for installer
|
// Needs to be public for installer
|
||||||
public class I18nImpl implements I18n {
|
public class I18nImpl implements I18n {
|
||||||
|
|
||||||
@@ -70,6 +72,9 @@ public class I18nImpl implements I18n {
|
|||||||
"ProgressMonitor.progressText"
|
"ProgressMonitor.progressText"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(I18nImpl.class.getName());
|
||||||
|
|
||||||
private final Object bundleLock = new Object();
|
private final Object bundleLock = new Object();
|
||||||
private final ClassLoader loader = I18n.class.getClassLoader();
|
private final ClassLoader loader = I18n.class.getClassLoader();
|
||||||
private final Set<Listener> listeners = new HashSet<Listener>();
|
private final Set<Listener> listeners = new HashSet<Listener>();
|
||||||
@@ -97,7 +102,10 @@ public class I18nImpl implements I18n {
|
|||||||
for(String key : uiManagerKeys) {
|
for(String key : uiManagerKeys) {
|
||||||
try {
|
try {
|
||||||
UIManager.put(key, bundle.getString(key));
|
UIManager.put(key, bundle.getString(key));
|
||||||
} catch(MissingResourceException ignored) {}
|
} catch(MissingResourceException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ public abstract class AbstractPlugin implements Plugin {
|
|||||||
|
|
||||||
protected final Executor executor;
|
protected final Executor executor;
|
||||||
|
|
||||||
// This field must only be accessed with this's lock held
|
protected boolean started = false; // Locking: this
|
||||||
protected boolean started = false;
|
|
||||||
|
|
||||||
protected AbstractPlugin(Executor executor) {
|
protected AbstractPlugin(Executor executor) {
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ class PollerImpl implements Poller, Runnable {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
wait(p.time - now);
|
wait(p.time - now);
|
||||||
} catch(InterruptedException ignored) {}
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import net.sf.briar.api.ContactId;
|
|||||||
import net.sf.briar.api.TransportConfig;
|
import net.sf.briar.api.TransportConfig;
|
||||||
import net.sf.briar.api.TransportId;
|
import net.sf.briar.api.TransportId;
|
||||||
import net.sf.briar.api.TransportProperties;
|
import net.sf.briar.api.TransportProperties;
|
||||||
import net.sf.briar.api.plugins.StreamPluginCallback;
|
|
||||||
import net.sf.briar.api.plugins.StreamPlugin;
|
import net.sf.briar.api.plugins.StreamPlugin;
|
||||||
|
import net.sf.briar.api.plugins.StreamPluginCallback;
|
||||||
import net.sf.briar.api.transport.StreamTransportConnection;
|
import net.sf.briar.api.transport.StreamTransportConnection;
|
||||||
import net.sf.briar.plugins.AbstractPlugin;
|
import net.sf.briar.plugins.AbstractPlugin;
|
||||||
import net.sf.briar.util.OsUtils;
|
import net.sf.briar.util.OsUtils;
|
||||||
@@ -36,11 +36,12 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(BluetoothPlugin.class.getName());
|
Logger.getLogger(BluetoothPlugin.class.getName());
|
||||||
|
|
||||||
|
private final Object discoveryLock = new Object();
|
||||||
private final StreamPluginCallback callback;
|
private final StreamPluginCallback callback;
|
||||||
private final long pollingInterval;
|
private final long pollingInterval;
|
||||||
|
|
||||||
private LocalDevice localDevice = null;
|
private LocalDevice localDevice = null; // Locking: this
|
||||||
private StreamConnectionNotifier streamConnectionNotifier = null;
|
private StreamConnectionNotifier socket = null; // Locking: this
|
||||||
|
|
||||||
BluetoothPlugin(Executor executor, StreamPluginCallback callback,
|
BluetoothPlugin(Executor executor, StreamPluginCallback callback,
|
||||||
long pollingInterval) {
|
long pollingInterval) {
|
||||||
@@ -72,40 +73,35 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
}
|
}
|
||||||
throw new IOException(e.getMessage());
|
throw new IOException(e.getMessage());
|
||||||
}
|
}
|
||||||
executor.execute(createBinder());
|
executor.execute(createContactSocketBinder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void stop() throws IOException {
|
public synchronized void stop() throws IOException {
|
||||||
super.stop();
|
super.stop();
|
||||||
if(streamConnectionNotifier != null) {
|
if(socket != null) {
|
||||||
streamConnectionNotifier.close();
|
socket.close();
|
||||||
streamConnectionNotifier = null;
|
socket = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Runnable createBinder() {
|
private Runnable createContactSocketBinder() {
|
||||||
return new Runnable() {
|
return new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
bind();
|
bindContactSocket();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bind() {
|
private void bindContactSocket() {
|
||||||
String uuid;
|
String uuid;
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if(!started) return;
|
if(!started) return;
|
||||||
uuid = getUuid();
|
uuid = getUuid();
|
||||||
|
makeDeviceDiscoverable();
|
||||||
}
|
}
|
||||||
// Try to make the device discoverable (requires root on Linux)
|
// Bind the socket
|
||||||
try {
|
String url = "btspp://localhost:" + uuid + ";name=RFCOMM";
|
||||||
localDevice.setDiscoverable(DiscoveryAgent.GIAC);
|
|
||||||
} catch(BluetoothStateException e) {
|
|
||||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
|
||||||
}
|
|
||||||
// Bind the port
|
|
||||||
String url = "btspp://localhost:" + uuid + ";name=" + uuid;
|
|
||||||
StreamConnectionNotifier scn;
|
StreamConnectionNotifier scn;
|
||||||
try {
|
try {
|
||||||
scn = (StreamConnectionNotifier) Connector.open(url);
|
scn = (StreamConnectionNotifier) Connector.open(url);
|
||||||
@@ -123,10 +119,10 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
streamConnectionNotifier = scn;
|
socket = scn;
|
||||||
setLocalBluetoothAddress(localDevice.getBluetoothAddress());
|
setLocalBluetoothAddress(localDevice.getBluetoothAddress());
|
||||||
}
|
}
|
||||||
startListener();
|
startContactAccepterThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized String getUuid() {
|
private synchronized String getUuid() {
|
||||||
@@ -144,32 +140,13 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startListener() {
|
private synchronized void makeDeviceDiscoverable() {
|
||||||
new Thread() {
|
assert started;
|
||||||
@Override
|
// Try to make the device discoverable (requires root on Linux)
|
||||||
public void run() {
|
try {
|
||||||
listen();
|
localDevice.setDiscoverable(DiscoveryAgent.GIAC);
|
||||||
}
|
} catch(BluetoothStateException e) {
|
||||||
}.start();
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
}
|
|
||||||
|
|
||||||
private void listen() {
|
|
||||||
while(true) {
|
|
||||||
StreamConnectionNotifier scn;
|
|
||||||
StreamConnection s;
|
|
||||||
synchronized(this) {
|
|
||||||
if(!started) return;
|
|
||||||
scn = streamConnectionNotifier;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
s = scn.acceptAndOpen();
|
|
||||||
} catch(IOException e) {
|
|
||||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BluetoothTransportConnection conn =
|
|
||||||
new BluetoothTransportConnection(s);
|
|
||||||
callback.incomingConnectionCreated(conn);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,6 +157,35 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
callback.setLocalProperties(p);
|
callback.setLocalProperties(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startContactAccepterThread() {
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
acceptContactConnections();
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void acceptContactConnections() {
|
||||||
|
while(true) {
|
||||||
|
StreamConnectionNotifier scn;
|
||||||
|
StreamConnection s;
|
||||||
|
synchronized(this) {
|
||||||
|
if(!started) return;
|
||||||
|
scn = socket;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
s = scn.acceptAndOpen();
|
||||||
|
} catch(IOException e) {
|
||||||
|
// This is expected when the socket is closed
|
||||||
|
if(LOG.isLoggable(Level.INFO)) LOG.info(e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback.incomingConnectionCreated(
|
||||||
|
new BluetoothTransportConnection(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean shouldPoll() {
|
public boolean shouldPoll() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -202,58 +208,53 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void connectAndCallBack() {
|
private void connectAndCallBack() {
|
||||||
Map<ContactId, String> discovered = discover();
|
Map<ContactId, String> discovered = discoverContactUrls();
|
||||||
for(Entry<ContactId, String> e : discovered.entrySet()) {
|
for(Entry<ContactId, String> e : discovered.entrySet()) {
|
||||||
ContactId c = e.getKey();
|
ContactId c = e.getKey();
|
||||||
String url = e.getValue();
|
String url = e.getValue();
|
||||||
StreamTransportConnection conn = connect(c, url);
|
StreamTransportConnection s = connect(c, url);
|
||||||
if(conn != null) callback.outgoingConnectionCreated(c, conn);
|
if(s != null) callback.outgoingConnectionCreated(c, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<ContactId, String> discover() {
|
private Map<ContactId, String> discoverContactUrls() {
|
||||||
DiscoveryAgent discoveryAgent;
|
DiscoveryAgent discoveryAgent;
|
||||||
Map<String, ContactId> addresses;
|
Map<ContactId, TransportProperties> remote;
|
||||||
Map<ContactId, String> uuids;
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if(!started) return Collections.emptyMap();
|
if(!started) return Collections.emptyMap();
|
||||||
discoveryAgent = localDevice.getDiscoveryAgent();
|
discoveryAgent = localDevice.getDiscoveryAgent();
|
||||||
addresses = new HashMap<String, ContactId>();
|
remote = callback.getRemoteProperties();
|
||||||
uuids = new HashMap<ContactId, String>();
|
}
|
||||||
Map<ContactId, TransportProperties> remote =
|
Map<String, ContactId> addresses = new HashMap<String, ContactId>();
|
||||||
callback.getRemoteProperties();
|
Map<ContactId, String> uuids = new HashMap<ContactId, String>();
|
||||||
for(Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
for(Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
||||||
ContactId c = e.getKey();
|
ContactId c = e.getKey();
|
||||||
TransportProperties p = e.getValue();
|
TransportProperties p = e.getValue();
|
||||||
String address = p.get("address");
|
String address = p.get("address");
|
||||||
String uuid = p.get("uuid");
|
String uuid = p.get("uuid");
|
||||||
if(address != null && uuid != null) {
|
if(address != null && uuid != null) {
|
||||||
addresses.put(address, c);
|
addresses.put(address, c);
|
||||||
uuids.put(c, uuid);
|
uuids.put(c, uuid);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BluetoothListener listener =
|
ContactListener listener = new ContactListener(discoveryAgent,
|
||||||
new BluetoothListener(discoveryAgent, addresses, uuids);
|
addresses, uuids);
|
||||||
try {
|
synchronized(discoveryLock) {
|
||||||
synchronized(listener) {
|
try {
|
||||||
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
|
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
|
||||||
listener.wait();
|
return listener.waitForUrls();
|
||||||
|
} catch(BluetoothStateException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
} catch(BluetoothStateException e) {
|
}
|
||||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
|
||||||
} catch(InterruptedException ignored) {}
|
|
||||||
return listener.getUrls();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private StreamTransportConnection connect(ContactId c, String url) {
|
private StreamTransportConnection connect(ContactId c, String url) {
|
||||||
|
synchronized(this) {
|
||||||
|
if(!started) return null;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
synchronized(this) {
|
|
||||||
if(!started) return null;
|
|
||||||
Map<ContactId, TransportProperties> remote =
|
|
||||||
callback.getRemoteProperties();
|
|
||||||
if(!remote.containsKey(c)) return null;
|
|
||||||
}
|
|
||||||
StreamConnection s = (StreamConnection) Connector.open(url);
|
StreamConnection s = (StreamConnection) Connector.open(url);
|
||||||
return new BluetoothTransportConnection(s);
|
return new BluetoothTransportConnection(s);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
@@ -263,8 +264,137 @@ class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public StreamTransportConnection createConnection(ContactId c) {
|
public StreamTransportConnection createConnection(ContactId c) {
|
||||||
Map<ContactId, String> discovered = discover();
|
String url = discoverContactUrls().get(c);
|
||||||
String url = discovered.get(c);
|
|
||||||
return url == null ? null : connect(c, url);
|
return url == null ? null : connect(c, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StreamTransportConnection sendInvitation(int code, long timeout) {
|
||||||
|
return createInvitationConnection(code, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamTransportConnection acceptInvitation(int code, long timeout) {
|
||||||
|
return createInvitationConnection(code, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StreamTransportConnection createInvitationConnection(int code,
|
||||||
|
long timeout) {
|
||||||
|
// The invitee's device may not be discoverable, so both parties must
|
||||||
|
// try to initiate connections
|
||||||
|
String uuid = convertInvitationCodeToUuid(code);
|
||||||
|
ConnectionCallback c = new ConnectionCallback(uuid, timeout);
|
||||||
|
startOutgoingInvitationThread(c);
|
||||||
|
startIncomingInvitationThread(c);
|
||||||
|
StreamConnection s = c.waitForConnection();
|
||||||
|
return s == null ? null : new BluetoothTransportConnection(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String convertInvitationCodeToUuid(int code) {
|
||||||
|
byte[] b = new byte[16];
|
||||||
|
new Random(code).nextBytes(b);
|
||||||
|
return StringUtils.toHexString(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startOutgoingInvitationThread(final ConnectionCallback c) {
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
createInvitationConnection(c);
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createInvitationConnection(ConnectionCallback c) {
|
||||||
|
DiscoveryAgent discoveryAgent;
|
||||||
|
synchronized(this) {
|
||||||
|
if(!started) return;
|
||||||
|
discoveryAgent = localDevice.getDiscoveryAgent();
|
||||||
|
}
|
||||||
|
// Try to discover the other party until the invitation times out
|
||||||
|
long end = System.currentTimeMillis() + c.getTimeout();
|
||||||
|
String url = null;
|
||||||
|
while(url == null && System.currentTimeMillis() < end) {
|
||||||
|
InvitationListener listener = new InvitationListener(discoveryAgent,
|
||||||
|
c.getUuid());
|
||||||
|
synchronized(discoveryLock) {
|
||||||
|
try {
|
||||||
|
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
|
||||||
|
url = listener.waitForUrl();
|
||||||
|
} catch(BluetoothStateException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized(this) {
|
||||||
|
if(!started) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(url == null) return;
|
||||||
|
// Try to connect to the other party
|
||||||
|
try {
|
||||||
|
StreamConnection s = (StreamConnection) Connector.open(url);
|
||||||
|
c.addConnection(s);
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startIncomingInvitationThread(final ConnectionCallback c) {
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
bindInvitationSocket(c);
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindInvitationSocket(ConnectionCallback c) {
|
||||||
|
synchronized(this) {
|
||||||
|
if(!started) return;
|
||||||
|
makeDeviceDiscoverable();
|
||||||
|
}
|
||||||
|
// Bind the socket
|
||||||
|
String url = "btspp://localhost:" + c.getUuid() + ";name=RFCOMM";
|
||||||
|
StreamConnectionNotifier scn;
|
||||||
|
try {
|
||||||
|
scn = (StreamConnectionNotifier) Connector.open(url);
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startInvitationAccepterThread(c, scn);
|
||||||
|
// Close the socket when the invitation times out
|
||||||
|
try {
|
||||||
|
Thread.sleep(c.getTimeout());
|
||||||
|
scn.close();
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startInvitationAccepterThread(final ConnectionCallback c,
|
||||||
|
final StreamConnectionNotifier scn) {
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
acceptInvitationConnection(c, scn);
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void acceptInvitationConnection(ConnectionCallback c,
|
||||||
|
StreamConnectionNotifier scn) {
|
||||||
|
synchronized(this) {
|
||||||
|
if(!started) return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
StreamConnection s = scn.acceptAndOpen();
|
||||||
|
c.addConnection(s);
|
||||||
|
} catch(IOException e) {
|
||||||
|
// This is expected when the socket is closed
|
||||||
|
if(LOG.isLoggable(Level.INFO)) LOG.info(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package net.sf.briar.plugins.bluetooth;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.microedition.io.StreamConnection;
|
||||||
|
|
||||||
|
class ConnectionCallback {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(ConnectionCallback.class.getName());
|
||||||
|
|
||||||
|
private final String uuid;
|
||||||
|
private final long timeout;
|
||||||
|
private final long end;
|
||||||
|
|
||||||
|
private StreamConnection connection = null; // Locking: this
|
||||||
|
|
||||||
|
ConnectionCallback(String uuid, long timeout) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.timeout = timeout;
|
||||||
|
end = System.currentTimeMillis() + timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
long getTimeout() {
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized StreamConnection waitForConnection() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
while(connection == null && now < end) {
|
||||||
|
try {
|
||||||
|
wait(end - now);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
now = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void addConnection(StreamConnection s) {
|
||||||
|
if(connection == null) {
|
||||||
|
connection = s;
|
||||||
|
notifyAll();
|
||||||
|
} else {
|
||||||
|
// Redundant connection
|
||||||
|
try {
|
||||||
|
s.close();
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.sf.briar.plugins.bluetooth;
|
package net.sf.briar.plugins.bluetooth;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@@ -8,6 +9,7 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.bluetooth.BluetoothStateException;
|
import javax.bluetooth.BluetoothStateException;
|
||||||
|
import javax.bluetooth.DataElement;
|
||||||
import javax.bluetooth.DeviceClass;
|
import javax.bluetooth.DeviceClass;
|
||||||
import javax.bluetooth.DiscoveryAgent;
|
import javax.bluetooth.DiscoveryAgent;
|
||||||
import javax.bluetooth.DiscoveryListener;
|
import javax.bluetooth.DiscoveryListener;
|
||||||
@@ -17,12 +19,10 @@ import javax.bluetooth.UUID;
|
|||||||
|
|
||||||
import net.sf.briar.api.ContactId;
|
import net.sf.briar.api.ContactId;
|
||||||
|
|
||||||
class BluetoothListener implements DiscoveryListener {
|
class ContactListener implements DiscoveryListener {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(BluetoothListener.class.getName());
|
Logger.getLogger(ContactListener.class.getName());
|
||||||
|
|
||||||
private static final int[] ATTRIBUTES = { 0x100 }; // Service name
|
|
||||||
|
|
||||||
private final AtomicInteger searches = new AtomicInteger(1);
|
private final AtomicInteger searches = new AtomicInteger(1);
|
||||||
private final DiscoveryAgent discoveryAgent;
|
private final DiscoveryAgent discoveryAgent;
|
||||||
@@ -30,7 +30,9 @@ class BluetoothListener implements DiscoveryListener {
|
|||||||
private final Map<ContactId, String> uuids;
|
private final Map<ContactId, String> uuids;
|
||||||
private final Map<ContactId, String> urls;
|
private final Map<ContactId, String> urls;
|
||||||
|
|
||||||
BluetoothListener(DiscoveryAgent discoveryAgent,
|
private boolean finished = false; // Locking: this
|
||||||
|
|
||||||
|
ContactListener(DiscoveryAgent discoveryAgent,
|
||||||
Map<String, ContactId> addresses, Map<ContactId, String> uuids) {
|
Map<String, ContactId> addresses, Map<ContactId, String> uuids) {
|
||||||
this.discoveryAgent = discoveryAgent;
|
this.discoveryAgent = discoveryAgent;
|
||||||
this.addresses = addresses;
|
this.addresses = addresses;
|
||||||
@@ -38,7 +40,14 @@ class BluetoothListener implements DiscoveryListener {
|
|||||||
urls = Collections.synchronizedMap(new HashMap<ContactId, String>());
|
urls = Collections.synchronizedMap(new HashMap<ContactId, String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<ContactId, String> getUrls() {
|
public synchronized Map<ContactId, String> waitForUrls() {
|
||||||
|
while(!finished) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
return urls;
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +61,7 @@ class BluetoothListener implements DiscoveryListener {
|
|||||||
UUID[] uuids = new UUID[] { new UUID(uuid, false) };
|
UUID[] uuids = new UUID[] { new UUID(uuid, false) };
|
||||||
// Try to discover the services associated with the UUID
|
// Try to discover the services associated with the UUID
|
||||||
try {
|
try {
|
||||||
discoveryAgent.searchServices(ATTRIBUTES, uuids, device, this);
|
discoveryAgent.searchServices(null, uuids, device, this);
|
||||||
} catch(BluetoothStateException e) {
|
} catch(BluetoothStateException e) {
|
||||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -62,6 +71,7 @@ class BluetoothListener implements DiscoveryListener {
|
|||||||
public void inquiryCompleted(int discoveryType) {
|
public void inquiryCompleted(int discoveryType) {
|
||||||
if(searches.decrementAndGet() == 0) {
|
if(searches.decrementAndGet() == 0) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
|
finished = true;
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,16 +83,33 @@ class BluetoothListener implements DiscoveryListener {
|
|||||||
RemoteDevice device = record.getHostDevice();
|
RemoteDevice device = record.getHostDevice();
|
||||||
ContactId c = addresses.get(device.getBluetoothAddress());
|
ContactId c = addresses.get(device.getBluetoothAddress());
|
||||||
if(c == null) continue;
|
if(c == null) continue;
|
||||||
// Store the URL
|
// Do we have a UUID for this contact?
|
||||||
String url = record.getConnectionURL(
|
String uuid = uuids.get(c);
|
||||||
|
if(uuid == null) return;
|
||||||
|
// Does this service have a URL?
|
||||||
|
String serviceUrl = record.getConnectionURL(
|
||||||
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
|
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
|
||||||
if(url != null) urls.put(c, url);
|
if(serviceUrl == null) continue;
|
||||||
|
// Does this service have the UUID we're looking for?
|
||||||
|
DataElement classIds = record.getAttributeValue(0x1);
|
||||||
|
if(classIds == null) continue;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Enumeration<DataElement> e =
|
||||||
|
(Enumeration<DataElement>) classIds.getValue();
|
||||||
|
for(DataElement classId : Collections.list(e)) {
|
||||||
|
UUID serviceUuid = (UUID) classId.getValue();
|
||||||
|
if(uuid.equals(serviceUuid.toString())) {
|
||||||
|
// The UUID matches - store the URL
|
||||||
|
urls.put(c, serviceUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void serviceSearchCompleted(int transaction, int response) {
|
public void serviceSearchCompleted(int transaction, int response) {
|
||||||
if(searches.decrementAndGet() == 0) {
|
if(searches.decrementAndGet() == 0) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
|
finished = true;
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package net.sf.briar.plugins.bluetooth;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.bluetooth.BluetoothStateException;
|
||||||
|
import javax.bluetooth.DataElement;
|
||||||
|
import javax.bluetooth.DeviceClass;
|
||||||
|
import javax.bluetooth.DiscoveryAgent;
|
||||||
|
import javax.bluetooth.DiscoveryListener;
|
||||||
|
import javax.bluetooth.RemoteDevice;
|
||||||
|
import javax.bluetooth.ServiceRecord;
|
||||||
|
import javax.bluetooth.UUID;
|
||||||
|
|
||||||
|
class InvitationListener implements DiscoveryListener {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(InvitationListener.class.getName());
|
||||||
|
|
||||||
|
private final AtomicInteger searches = new AtomicInteger(1);
|
||||||
|
private final DiscoveryAgent discoveryAgent;
|
||||||
|
private final String uuid;
|
||||||
|
|
||||||
|
private String url = null; // Locking: this
|
||||||
|
private boolean finished = false; // Locking: this
|
||||||
|
|
||||||
|
InvitationListener(DiscoveryAgent discoveryAgent, String uuid) {
|
||||||
|
this.discoveryAgent = discoveryAgent;
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized String waitForUrl() {
|
||||||
|
while(!finished) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deviceDiscovered(RemoteDevice device, DeviceClass deviceClass) {
|
||||||
|
UUID[] uuids = new UUID[] { new UUID(uuid, false) };
|
||||||
|
// Try to discover the services associated with the UUID
|
||||||
|
try {
|
||||||
|
discoveryAgent.searchServices(null, uuids, device, this);
|
||||||
|
searches.incrementAndGet();
|
||||||
|
} catch(BluetoothStateException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inquiryCompleted(int discoveryType) {
|
||||||
|
if(searches.decrementAndGet() == 0) {
|
||||||
|
synchronized(this) {
|
||||||
|
finished = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void servicesDiscovered(int transaction, ServiceRecord[] services) {
|
||||||
|
for(ServiceRecord record : services) {
|
||||||
|
// Does this service have a URL?
|
||||||
|
String serviceUrl = record.getConnectionURL(
|
||||||
|
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
|
||||||
|
if(serviceUrl == null) continue;
|
||||||
|
// Does this service have the UUID we're looking for?
|
||||||
|
DataElement classIds = record.getAttributeValue(0x1);
|
||||||
|
if(classIds == null) continue;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Enumeration<DataElement> e =
|
||||||
|
(Enumeration<DataElement>) classIds.getValue();
|
||||||
|
for(DataElement classId : Collections.list(e)) {
|
||||||
|
UUID serviceUuid = (UUID) classId.getValue();
|
||||||
|
if(uuid.equals(serviceUuid.toString())) {
|
||||||
|
// The UUID matches - store the URL
|
||||||
|
synchronized(this) {
|
||||||
|
url = serviceUrl;
|
||||||
|
finished = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serviceSearchCompleted(int transaction, int response) {
|
||||||
|
if(searches.decrementAndGet() == 0) {
|
||||||
|
synchronized(this) {
|
||||||
|
finished = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
components/net/sf/briar/plugins/file/FileListener.java
Normal file
41
components/net/sf/briar/plugins/file/FileListener.java
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package net.sf.briar.plugins.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
class FileListener {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(FileListener.class.getName());
|
||||||
|
|
||||||
|
private final String filename;
|
||||||
|
private final long end;
|
||||||
|
|
||||||
|
private File file = null; // Locking: this
|
||||||
|
|
||||||
|
FileListener(String filename, long timeout) {
|
||||||
|
this.filename = filename;
|
||||||
|
end = System.currentTimeMillis() + timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized File waitForFile() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
while(file == null && now < end) {
|
||||||
|
try {
|
||||||
|
wait(end - now);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
now = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void addFile(File f) {
|
||||||
|
if(filename.equals(f.getName())) {
|
||||||
|
file = f;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,14 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import net.sf.briar.api.ContactId;
|
import net.sf.briar.api.ContactId;
|
||||||
import net.sf.briar.api.plugins.BatchPluginCallback;
|
|
||||||
import net.sf.briar.api.plugins.BatchPlugin;
|
import net.sf.briar.api.plugins.BatchPlugin;
|
||||||
|
import net.sf.briar.api.plugins.BatchPluginCallback;
|
||||||
import net.sf.briar.api.transport.BatchTransportReader;
|
import net.sf.briar.api.transport.BatchTransportReader;
|
||||||
import net.sf.briar.api.transport.BatchTransportWriter;
|
import net.sf.briar.api.transport.BatchTransportWriter;
|
||||||
import net.sf.briar.api.transport.TransportConstants;
|
import net.sf.briar.api.transport.TransportConstants;
|
||||||
@@ -26,7 +27,11 @@ abstract class FilePlugin extends AbstractPlugin implements BatchPlugin {
|
|||||||
|
|
||||||
protected final BatchPluginCallback callback;
|
protected final BatchPluginCallback callback;
|
||||||
|
|
||||||
|
private final Object listenerLock = new Object();
|
||||||
|
private FileListener listener = null; // Locking: listenerLock
|
||||||
|
|
||||||
protected abstract File chooseOutputDirectory();
|
protected abstract File chooseOutputDirectory();
|
||||||
|
protected abstract Collection<File> findFilesByName(String filename);
|
||||||
protected abstract void writerFinished(File f);
|
protected abstract void writerFinished(File f);
|
||||||
protected abstract void readerFinished(File f);
|
protected abstract void readerFinished(File f);
|
||||||
|
|
||||||
@@ -40,12 +45,28 @@ abstract class FilePlugin extends AbstractPlugin implements BatchPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BatchTransportWriter createWriter(ContactId c) {
|
public BatchTransportWriter createWriter(ContactId c) {
|
||||||
|
return createWriter(createConnectionFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createConnectionFilename() {
|
||||||
|
StringBuilder s = new StringBuilder(12);
|
||||||
|
for(int i = 0; i < 8; i++) s.append((char) ('a' + Math.random() * 26));
|
||||||
|
s.append(".dat");
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package access for testing
|
||||||
|
boolean isPossibleConnectionFilename(String filename) {
|
||||||
|
return filename.toLowerCase().matches("[a-z]{8}\\.dat");
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatchTransportWriter createWriter(String filename) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if(!started) return null;
|
if(!started) return null;
|
||||||
}
|
}
|
||||||
File dir = chooseOutputDirectory();
|
File dir = chooseOutputDirectory();
|
||||||
if(dir == null || !dir.exists() || !dir.isDirectory()) return null;
|
if(dir == null || !dir.exists() || !dir.isDirectory()) return null;
|
||||||
File f = new File(dir, createFilename());
|
File f = new File(dir, filename);
|
||||||
try {
|
try {
|
||||||
long capacity = getCapacity(dir.getPath());
|
long capacity = getCapacity(dir.getPath());
|
||||||
if(capacity < TransportConstants.MIN_CONNECTION_LENGTH) return null;
|
if(capacity < TransportConstants.MIN_CONNECTION_LENGTH) return null;
|
||||||
@@ -58,13 +79,6 @@ abstract class FilePlugin extends AbstractPlugin implements BatchPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createFilename() {
|
|
||||||
StringBuilder s = new StringBuilder(12);
|
|
||||||
for(int i = 0; i < 8; i++) s.append((char) ('a' + Math.random() * 26));
|
|
||||||
s.append(".dat");
|
|
||||||
return s.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getCapacity(String path) throws IOException {
|
private long getCapacity(String path) throws IOException {
|
||||||
return FileSystemUtils.freeSpaceKb(path) * 1024L;
|
return FileSystemUtils.freeSpaceKb(path) * 1024L;
|
||||||
}
|
}
|
||||||
@@ -74,9 +88,60 @@ abstract class FilePlugin extends AbstractPlugin implements BatchPlugin {
|
|||||||
executor.execute(new ReaderCreator(f));
|
executor.execute(new ReaderCreator(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BatchTransportWriter sendInvitation(int code, long timeout) {
|
||||||
|
return createWriter(createInvitationFilename(code, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchTransportReader acceptInvitation(int code, long timeout) {
|
||||||
|
String filename = createInvitationFilename(code, false);
|
||||||
|
return createInvitationReader(filename, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchTransportWriter sendInvitationResponse(int code, long timeout) {
|
||||||
|
return createWriter(createInvitationFilename(code, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchTransportReader acceptInvitationResponse(int code,
|
||||||
|
long timeout) {
|
||||||
|
String filename = createInvitationFilename(code, true);
|
||||||
|
return createInvitationReader(filename, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatchTransportReader createInvitationReader(String filename,
|
||||||
|
long timeout) {
|
||||||
|
Collection<File> files;
|
||||||
|
synchronized(listenerLock) {
|
||||||
|
// Find any matching files that have already arrived
|
||||||
|
files = findFilesByName(filename);
|
||||||
|
if(files.isEmpty()) {
|
||||||
|
// Wait for a matching file to arrive
|
||||||
|
listener = new FileListener(filename, timeout);
|
||||||
|
File f = listener.waitForFile();
|
||||||
|
if(f != null) files.add(f);
|
||||||
|
listener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return the first match that can be opened
|
||||||
|
for(File f : files) {
|
||||||
|
try {
|
||||||
|
FileInputStream in = new FileInputStream(f);
|
||||||
|
return new FileTransportReader(f, in, FilePlugin.this);
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createInvitationFilename(int code, boolean response) {
|
||||||
|
assert code >= 0;
|
||||||
|
assert code < 10 * 1000 * 1000;
|
||||||
|
return String.format("%c%7d.dat", response ? 'b' : 'a', code);
|
||||||
|
}
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
boolean isPossibleConnectionFilename(String filename) {
|
boolean isPossibleInvitationFilename(String filename) {
|
||||||
return filename.toLowerCase().matches("[a-z]{8}\\.dat");
|
return filename.toLowerCase().matches("[ab][0-9]{7}.dat");
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReaderCreator implements Runnable {
|
private class ReaderCreator implements Runnable {
|
||||||
@@ -88,14 +153,21 @@ abstract class FilePlugin extends AbstractPlugin implements BatchPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
if(!isPossibleConnectionFilename(f.getName())) return;
|
String filename = f.getName();
|
||||||
if(f.length() < TransportConstants.MIN_CONNECTION_LENGTH) return;
|
if(isPossibleInvitationFilename(filename)) {
|
||||||
try {
|
synchronized(listenerLock) {
|
||||||
FileInputStream in = new FileInputStream(f);
|
if(listener != null) listener.addFile(f);
|
||||||
callback.readerCreated(new FileTransportReader(f, in,
|
}
|
||||||
FilePlugin.this));
|
}
|
||||||
} catch(IOException e) {
|
if(isPossibleConnectionFilename(f.getName())) {
|
||||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
try {
|
||||||
|
FileInputStream in = new FileInputStream(f);
|
||||||
|
callback.readerCreated(new FileTransportReader(f, in,
|
||||||
|
FilePlugin.this));
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,14 @@ package net.sf.briar.plugins.file;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(PollingRemovableDriveMonitor.class.getName());
|
||||||
|
|
||||||
private final RemovableDriveFinder finder;
|
private final RemovableDriveFinder finder;
|
||||||
private final long pollingInterval;
|
private final long pollingInterval;
|
||||||
private final Object pollingLock = new Object();
|
private final Object pollingLock = new Object();
|
||||||
@@ -47,7 +52,10 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
|||||||
synchronized(pollingLock) {
|
synchronized(pollingLock) {
|
||||||
try {
|
try {
|
||||||
pollingLock.wait(pollingInterval);
|
pollingLock.wait(pollingInterval);
|
||||||
} catch(InterruptedException ignored) {}
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(!running) return;
|
if(!running) return;
|
||||||
List<File> newDrives = finder.findRemovableDrives();
|
List<File> newDrives = finder.findRemovableDrives();
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package net.sf.briar.plugins.file;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@@ -85,8 +87,29 @@ implements RemovableDriveMonitor.Callback {
|
|||||||
callback.showMessage("REMOVABLE_DRIVE_WRITE_FINISHED");
|
callback.showMessage("REMOVABLE_DRIVE_WRITE_FINISHED");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<File> findFilesByName(String filename) {
|
||||||
|
Collection<File> matches = new ArrayList<File>();
|
||||||
|
try {
|
||||||
|
for(File drive : finder.findRemovableDrives()) {
|
||||||
|
File[] files = drive.listFiles();
|
||||||
|
if(files != null) {
|
||||||
|
for(File f : files) {
|
||||||
|
if(f.isFile() && filename.equals(f.getName()))
|
||||||
|
matches.add(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
public void driveInserted(File root) {
|
public void driveInserted(File root) {
|
||||||
File[] files = root.listFiles();
|
File[] files = root.listFiles();
|
||||||
if(files != null) for(File f : files) createReaderFromFile(f);
|
if(files != null) {
|
||||||
|
for(File f : files) if(f.isFile()) createReaderFromFile(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.sf.briar.plugins.socket;
|
package net.sf.briar.plugins.socket;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
@@ -11,6 +12,7 @@ import net.sf.briar.api.ContactId;
|
|||||||
import net.sf.briar.api.TransportId;
|
import net.sf.briar.api.TransportId;
|
||||||
import net.sf.briar.api.TransportProperties;
|
import net.sf.briar.api.TransportProperties;
|
||||||
import net.sf.briar.api.plugins.StreamPluginCallback;
|
import net.sf.briar.api.plugins.StreamPluginCallback;
|
||||||
|
import net.sf.briar.api.transport.StreamTransportConnection;
|
||||||
|
|
||||||
class SimpleSocketPlugin extends SocketPlugin {
|
class SimpleSocketPlugin extends SocketPlugin {
|
||||||
|
|
||||||
@@ -67,7 +69,8 @@ class SimpleSocketPlugin extends SocketPlugin {
|
|||||||
TransportProperties p) {
|
TransportProperties p) {
|
||||||
assert started;
|
assert started;
|
||||||
assert p != null;
|
assert p != null;
|
||||||
String host = p.get("host");
|
String host = p.get("external");
|
||||||
|
if(host == null) host = p.get("internal");
|
||||||
String portString = p.get("port");
|
String portString = p.get("port");
|
||||||
if(host == null || portString == null) return null;
|
if(host == null || portString == null) return null;
|
||||||
int port;
|
int port;
|
||||||
@@ -85,12 +88,22 @@ class SimpleSocketPlugin extends SocketPlugin {
|
|||||||
if(!(s instanceof InetSocketAddress))
|
if(!(s instanceof InetSocketAddress))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
InetSocketAddress i = (InetSocketAddress) s;
|
InetSocketAddress i = (InetSocketAddress) s;
|
||||||
String host = i.getAddress().getHostAddress();
|
InetAddress addr = i.getAddress();
|
||||||
String port = String.valueOf(i.getPort());
|
|
||||||
// FIXME: Special handling for private IP addresses?
|
|
||||||
TransportProperties p = callback.getLocalProperties();
|
TransportProperties p = callback.getLocalProperties();
|
||||||
p.put("host", host);
|
if(addr.isLinkLocalAddress() || addr.isSiteLocalAddress())
|
||||||
p.put("port", port);
|
p.put("internal", addr.getHostAddress());
|
||||||
|
else p.put("external", addr.getHostAddress());
|
||||||
|
p.put("port", String.valueOf(i.getPort()));
|
||||||
callback.setLocalProperties(p);
|
callback.setLocalProperties(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StreamTransportConnection sendInvitation(int code, long timeout) {
|
||||||
|
// FIXME
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamTransportConnection acceptInvitation(int code, long timeout) {
|
||||||
|
// FIXME
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ abstract class SocketPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
|
|
||||||
protected final StreamPluginCallback callback;
|
protected final StreamPluginCallback callback;
|
||||||
|
|
||||||
// This field must only be accessed with this's lock held
|
protected ServerSocket socket = null; // Locking: this
|
||||||
protected ServerSocket socket = null;
|
|
||||||
|
|
||||||
protected abstract void setLocalSocketAddress(SocketAddress s);
|
protected abstract void setLocalSocketAddress(SocketAddress s);
|
||||||
|
|
||||||
@@ -85,10 +84,10 @@ abstract class SocketPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
socket = ss;
|
socket = ss;
|
||||||
setLocalSocketAddress(ss.getLocalSocketAddress());
|
setLocalSocketAddress(ss.getLocalSocketAddress());
|
||||||
}
|
}
|
||||||
startListener();
|
startListenerThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startListener() {
|
private void startListenerThread() {
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -108,7 +107,8 @@ abstract class SocketPlugin extends AbstractPlugin implements StreamPlugin {
|
|||||||
try {
|
try {
|
||||||
s = ss.accept();
|
s = ss.accept();
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
|
// This is expected when the socket is closed
|
||||||
|
if(LOG.isLoggable(Level.INFO)) LOG.info(e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SocketTransportConnection conn = new SocketTransportConnection(s);
|
SocketTransportConnection conn = new SocketTransportConnection(s);
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package net.sf.briar.transport;
|
|||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A thread that calls the writeFullFrame() method of a PaddedConnectionWriter
|
* A thread that calls the writeFullFrame() method of a PaddedConnectionWriter
|
||||||
* at regular intervals. The interval between calls is determined by a target
|
* at regular intervals. The interval between calls is determined by a target
|
||||||
@@ -10,6 +13,9 @@ import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
|||||||
*/
|
*/
|
||||||
class FrameScheduler extends Thread {
|
class FrameScheduler extends Thread {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(FrameScheduler.class.getName());
|
||||||
|
|
||||||
private final PaddedConnectionWriter writer;
|
private final PaddedConnectionWriter writer;
|
||||||
private final int millisPerFrame;
|
private final int millisPerFrame;
|
||||||
|
|
||||||
@@ -27,7 +33,10 @@ class FrameScheduler extends Thread {
|
|||||||
if(nextCall > now) {
|
if(nextCall > now) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(nextCall - now);
|
Thread.sleep(nextCall - now);
|
||||||
} catch(InterruptedException ignored) {}
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lastCall = System.currentTimeMillis();
|
lastCall = System.currentTimeMillis();
|
||||||
if(!writer.writeFullFrame()) return;
|
if(!writer.writeFullFrame()) return;
|
||||||
|
|||||||
@@ -219,7 +219,10 @@ abstract class StreamConnection implements DatabaseListener {
|
|||||||
while(writerFlags == 0) {
|
while(writerFlags == 0) {
|
||||||
try {
|
try {
|
||||||
wait();
|
wait();
|
||||||
} catch(InterruptedException ignored) {}
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
flags = writerFlags;
|
flags = writerFlags;
|
||||||
writerFlags = 0;
|
writerFlags = 0;
|
||||||
@@ -256,7 +259,10 @@ abstract class StreamConnection implements DatabaseListener {
|
|||||||
while(writerFlags == 0) {
|
while(writerFlags == 0) {
|
||||||
try {
|
try {
|
||||||
wait();
|
wait();
|
||||||
} catch(InterruptedException ignored) {}
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
flags = writerFlags;
|
flags = writerFlags;
|
||||||
writerFlags = 0;
|
writerFlags = 0;
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ public class LockFairnessTest extends TestCase {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(sleepTime);
|
Thread.sleep(sleepTime);
|
||||||
finished.add(this);
|
finished.add(this);
|
||||||
} catch(InterruptedException ignored) {
|
} catch(InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
lock.readLock().unlock();
|
lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
@@ -99,7 +100,8 @@ public class LockFairnessTest extends TestCase {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(sleepTime);
|
Thread.sleep(sleepTime);
|
||||||
finished.add(this);
|
finished.add(this);
|
||||||
} catch(InterruptedException ignored) {
|
} catch(InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
lock.writeLock().unlock();
|
lock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -737,9 +737,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
for(int i = 0; i < ids.length; i++) {
|
for(int i = 0; i < ids.length; i++) {
|
||||||
db.addOutstandingBatch(txn, contactId, ids[i],
|
db.addOutstandingBatch(txn, contactId, ids[i],
|
||||||
Collections.<MessageId>emptySet());
|
Collections.<MessageId>emptySet());
|
||||||
try {
|
Thread.sleep(5);
|
||||||
Thread.sleep(5);
|
|
||||||
} catch(InterruptedException ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The contact acks the batches in reverse order. The first
|
// The contact acks the batches in reverse order. The first
|
||||||
@@ -779,9 +777,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
for(int i = 0; i < ids.length; i++) {
|
for(int i = 0; i < ids.length; i++) {
|
||||||
db.addOutstandingBatch(txn, contactId, ids[i],
|
db.addOutstandingBatch(txn, contactId, ids[i],
|
||||||
Collections.<MessageId>emptySet());
|
Collections.<MessageId>emptySet());
|
||||||
try {
|
Thread.sleep(5);
|
||||||
Thread.sleep(5);
|
|
||||||
} catch(InterruptedException ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The contact acks the batches in the order they were sent - nothing
|
// The contact acks the batches in the order they were sent - nothing
|
||||||
@@ -946,9 +942,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
};
|
};
|
||||||
t.start();
|
t.start();
|
||||||
// Do whatever the transaction needs to do
|
// Do whatever the transaction needs to do
|
||||||
try {
|
Thread.sleep(10);
|
||||||
Thread.sleep(10);
|
|
||||||
} catch(InterruptedException ignored) {}
|
|
||||||
transactionFinished.set(true);
|
transactionFinished.set(true);
|
||||||
// Commit the transaction
|
// Commit the transaction
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -981,9 +975,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
};
|
};
|
||||||
t.start();
|
t.start();
|
||||||
// Do whatever the transaction needs to do
|
// Do whatever the transaction needs to do
|
||||||
try {
|
Thread.sleep(10);
|
||||||
Thread.sleep(10);
|
|
||||||
} catch(InterruptedException ignored) {}
|
|
||||||
transactionFinished.set(true);
|
transactionFinished.set(true);
|
||||||
// Abort the transaction
|
// Abort the transaction
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
|
|||||||
@@ -1,63 +1,79 @@
|
|||||||
package net.sf.briar.plugins.bluetooth;
|
package net.sf.briar.plugins.bluetooth;
|
||||||
|
|
||||||
import java.io.PrintStream;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Scanner;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import net.sf.briar.api.ContactId;
|
import net.sf.briar.api.ContactId;
|
||||||
import net.sf.briar.api.TransportConfig;
|
import net.sf.briar.api.TransportConfig;
|
||||||
import net.sf.briar.api.TransportProperties;
|
import net.sf.briar.api.TransportProperties;
|
||||||
import net.sf.briar.api.plugins.StreamPluginCallback;
|
import net.sf.briar.api.plugins.StreamPluginCallback;
|
||||||
import net.sf.briar.api.transport.StreamTransportConnection;
|
import net.sf.briar.api.transport.StreamTransportConnection;
|
||||||
import net.sf.briar.plugins.ImmediateExecutor;
|
|
||||||
|
|
||||||
// This is not a JUnit test - it has to be run manually while the server test
|
// This is not a JUnit test - it has to be run manually while the server test
|
||||||
// is running on another machine
|
// is running on another machine
|
||||||
public class BluetoothClientTest {
|
public class BluetoothClientTest extends BluetoothTest {
|
||||||
|
|
||||||
public static final String RESPONSE = "Carrots!";
|
private final String serverAddress;
|
||||||
|
|
||||||
|
BluetoothClientTest(String serverAddress) {
|
||||||
|
this.serverAddress = serverAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() throws IOException {
|
||||||
|
ContactId contactId = new ContactId(0);
|
||||||
|
ClientCallback callback = new ClientCallback();
|
||||||
|
// Store the server's Bluetooth address and UUID
|
||||||
|
TransportProperties p = new TransportProperties();
|
||||||
|
p.put("address", serverAddress);
|
||||||
|
p.put("uuid", BluetoothServerTest.UUID);
|
||||||
|
callback.remote.put(contactId, p);
|
||||||
|
// Create the plugin
|
||||||
|
Executor e = Executors.newCachedThreadPool();
|
||||||
|
BluetoothPlugin plugin = new BluetoothPlugin(e, callback, 0L);
|
||||||
|
// Start the plugin
|
||||||
|
System.out.println("Starting plugin");
|
||||||
|
plugin.start();
|
||||||
|
// Try to connect to the server
|
||||||
|
System.out.println("Creating connection");
|
||||||
|
StreamTransportConnection s = plugin.createConnection(contactId);
|
||||||
|
if(s == null) {
|
||||||
|
System.out.println("Connection failed");
|
||||||
|
} else {
|
||||||
|
System.out.println("Connection created");
|
||||||
|
receiveChallengeAndSendResponse(s);
|
||||||
|
}
|
||||||
|
// Try to send an invitation
|
||||||
|
System.out.println("Sending invitation");
|
||||||
|
s = plugin.sendInvitation(123, INVITATION_TIMEOUT);
|
||||||
|
if(s == null) {
|
||||||
|
System.out.println("Connection failed");
|
||||||
|
} else {
|
||||||
|
System.out.println("Connection created");
|
||||||
|
receiveChallengeAndSendResponse(s);
|
||||||
|
}
|
||||||
|
// Try to accept an invitation
|
||||||
|
System.out.println("Accepting invitation");
|
||||||
|
s = plugin.acceptInvitation(456, INVITATION_TIMEOUT);
|
||||||
|
if(s == null) {
|
||||||
|
System.out.println("Connection failed");
|
||||||
|
} else {
|
||||||
|
System.out.println("Connection created");
|
||||||
|
sendChallengeAndReceiveResponse(s);
|
||||||
|
}
|
||||||
|
// Stop the plugin
|
||||||
|
System.out.println("Stopping plugin");
|
||||||
|
plugin.stop();
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
if(args.length != 1) {
|
if(args.length != 1) {
|
||||||
System.err.println("Please specify the server's Bluetooth address");
|
System.err.println("Please specify the server's Bluetooth address");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
ContactId contactId = new ContactId(0);
|
new BluetoothClientTest(args[0]).run();
|
||||||
ClientCallback callback = new ClientCallback();
|
|
||||||
// Store the server's Bluetooth address and UUID
|
|
||||||
TransportProperties p = new TransportProperties();
|
|
||||||
p.put("address", args[0]);
|
|
||||||
p.put("uuid", BluetoothServerTest.UUID);
|
|
||||||
callback.remote.put(contactId, p);
|
|
||||||
// Create the plugin
|
|
||||||
BluetoothPlugin plugin =
|
|
||||||
new BluetoothPlugin(new ImmediateExecutor(), callback, 0L);
|
|
||||||
// Start the plugin
|
|
||||||
System.out.println("Starting plugin");
|
|
||||||
plugin.start();
|
|
||||||
// Try to connect to the server
|
|
||||||
System.out.println("Creating connection");
|
|
||||||
StreamTransportConnection conn = plugin.createConnection(contactId);
|
|
||||||
if(conn == null) {
|
|
||||||
System.out.println("Connection failed");
|
|
||||||
} else {
|
|
||||||
System.out.println("Connection created");
|
|
||||||
Scanner in = new Scanner(conn.getInputStream());
|
|
||||||
String challenge = in.nextLine();
|
|
||||||
System.out.println("Received challenge: " + challenge);
|
|
||||||
if(BluetoothServerTest.CHALLENGE.equals(challenge)) {
|
|
||||||
PrintStream out = new PrintStream(conn.getOutputStream());
|
|
||||||
out.println(RESPONSE);
|
|
||||||
System.out.println("Sent response: " + RESPONSE);
|
|
||||||
} else {
|
|
||||||
System.out.println("Incorrect challenge");
|
|
||||||
}
|
|
||||||
conn.dispose(true);
|
|
||||||
}
|
|
||||||
// Stop the plugin
|
|
||||||
System.out.println("Stopping plugin");
|
|
||||||
plugin.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ClientCallback implements StreamPluginCallback {
|
private static class ClientCallback implements StreamPluginCallback {
|
||||||
|
|||||||
@@ -1,32 +1,27 @@
|
|||||||
package net.sf.briar.plugins.bluetooth;
|
package net.sf.briar.plugins.bluetooth;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Scanner;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import net.sf.briar.api.ContactId;
|
import net.sf.briar.api.ContactId;
|
||||||
import net.sf.briar.api.TransportConfig;
|
import net.sf.briar.api.TransportConfig;
|
||||||
import net.sf.briar.api.TransportProperties;
|
import net.sf.briar.api.TransportProperties;
|
||||||
import net.sf.briar.api.plugins.StreamPluginCallback;
|
import net.sf.briar.api.plugins.StreamPluginCallback;
|
||||||
import net.sf.briar.api.transport.StreamTransportConnection;
|
import net.sf.briar.api.transport.StreamTransportConnection;
|
||||||
import net.sf.briar.plugins.ImmediateExecutor;
|
|
||||||
|
|
||||||
//This is not a JUnit test - it has to be run manually while the server test
|
//This is not a JUnit test - it has to be run manually while the server test
|
||||||
//is running on another machine
|
//is running on another machine
|
||||||
public class BluetoothServerTest {
|
public class BluetoothServerTest extends BluetoothTest {
|
||||||
|
|
||||||
public static final String UUID = "CABBA6E5CABBA6E5CABBA6E5CABBA6E5";
|
void run() throws Exception {
|
||||||
public static final String CHALLENGE = "Potatoes!";
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
ServerCallback callback = new ServerCallback();
|
ServerCallback callback = new ServerCallback();
|
||||||
// Store the UUID
|
// Store the UUID
|
||||||
callback.config.put("uuid", UUID);
|
callback.config.put("uuid", UUID);
|
||||||
// Create the plugin
|
// Create the plugin
|
||||||
BluetoothPlugin plugin =
|
Executor e = Executors.newCachedThreadPool();
|
||||||
new BluetoothPlugin(new ImmediateExecutor(), callback, 0L);
|
BluetoothPlugin plugin = new BluetoothPlugin(e, callback, 0L);
|
||||||
// Start the plugin
|
// Start the plugin
|
||||||
System.out.println("Starting plugin");
|
System.out.println("Starting plugin");
|
||||||
plugin.start();
|
plugin.start();
|
||||||
@@ -35,12 +30,35 @@ public class BluetoothServerTest {
|
|||||||
synchronized(callback) {
|
synchronized(callback) {
|
||||||
callback.wait();
|
callback.wait();
|
||||||
}
|
}
|
||||||
|
// Try to accept an invitation
|
||||||
|
System.out.println("Accepting invitation");
|
||||||
|
StreamTransportConnection s = plugin.acceptInvitation(123,
|
||||||
|
INVITATION_TIMEOUT);
|
||||||
|
if(s == null) {
|
||||||
|
System.out.println("Connection failed");
|
||||||
|
} else {
|
||||||
|
System.out.println("Connection created");
|
||||||
|
sendChallengeAndReceiveResponse(s);
|
||||||
|
}
|
||||||
|
// Try to send an invitation
|
||||||
|
System.out.println("Sending invitation");
|
||||||
|
s = plugin.sendInvitation(456, INVITATION_TIMEOUT);
|
||||||
|
if(s == null) {
|
||||||
|
System.out.println("Connection failed");
|
||||||
|
} else {
|
||||||
|
System.out.println("Connection created");
|
||||||
|
receiveChallengeAndSendResponse(s);
|
||||||
|
}
|
||||||
// Stop the plugin
|
// Stop the plugin
|
||||||
System.out.println("Stopping plugin");
|
System.out.println("Stopping plugin");
|
||||||
plugin.stop();
|
plugin.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ServerCallback implements StreamPluginCallback {
|
public static void main(String[] args) throws Exception {
|
||||||
|
new BluetoothServerTest().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ServerCallback implements StreamPluginCallback {
|
||||||
|
|
||||||
private TransportConfig config = new TransportConfig();
|
private TransportConfig config = new TransportConfig();
|
||||||
private TransportProperties local = new TransportProperties();
|
private TransportProperties local = new TransportProperties();
|
||||||
@@ -77,24 +95,9 @@ public class BluetoothServerTest {
|
|||||||
|
|
||||||
public void showMessage(String... message) {}
|
public void showMessage(String... message) {}
|
||||||
|
|
||||||
public void incomingConnectionCreated(StreamTransportConnection conn) {
|
public void incomingConnectionCreated(StreamTransportConnection s) {
|
||||||
System.out.println("Connection received");
|
System.out.println("Connection received");
|
||||||
try {
|
sendChallengeAndReceiveResponse(s);
|
||||||
PrintStream out = new PrintStream(conn.getOutputStream());
|
|
||||||
out.println(CHALLENGE);
|
|
||||||
System.out.println("Sent challenge: " + CHALLENGE);
|
|
||||||
Scanner in = new Scanner(conn.getInputStream());
|
|
||||||
String response = in.nextLine();
|
|
||||||
System.out.println("Received response: " + response);
|
|
||||||
if(BluetoothClientTest.RESPONSE.equals(response)) {
|
|
||||||
System.out.println("Correct response");
|
|
||||||
} else {
|
|
||||||
System.out.println("Incorrect response");
|
|
||||||
}
|
|
||||||
conn.dispose(true);
|
|
||||||
} catch(IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
|
|||||||
54
test/net/sf/briar/plugins/bluetooth/BluetoothTest.java
Normal file
54
test/net/sf/briar/plugins/bluetooth/BluetoothTest.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package net.sf.briar.plugins.bluetooth;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import net.sf.briar.api.transport.StreamTransportConnection;
|
||||||
|
|
||||||
|
abstract class BluetoothTest {
|
||||||
|
|
||||||
|
protected static final String UUID = "CABBA6E5CABBA6E5CABBA6E5CABBA6E5";
|
||||||
|
protected static final String CHALLENGE = "Carrots!";
|
||||||
|
protected static final String RESPONSE = "Potatoes!";
|
||||||
|
protected static final long INVITATION_TIMEOUT = 30 * 1000;
|
||||||
|
|
||||||
|
void sendChallengeAndReceiveResponse(StreamTransportConnection s) {
|
||||||
|
try {
|
||||||
|
PrintStream out = new PrintStream(s.getOutputStream());
|
||||||
|
out.println(CHALLENGE);
|
||||||
|
System.out.println("Sent challenge: " + CHALLENGE);
|
||||||
|
Scanner in = new Scanner(s.getInputStream());
|
||||||
|
String response = in.nextLine();
|
||||||
|
System.out.println("Received response: " + response);
|
||||||
|
if(BluetoothClientTest.RESPONSE.equals(response)) {
|
||||||
|
System.out.println("Correct response");
|
||||||
|
} else {
|
||||||
|
System.out.println("Incorrect response");
|
||||||
|
}
|
||||||
|
s.dispose(true);
|
||||||
|
} catch(IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
s.dispose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receiveChallengeAndSendResponse(StreamTransportConnection s) {
|
||||||
|
try {
|
||||||
|
Scanner in = new Scanner(s.getInputStream());
|
||||||
|
String challenge = in.nextLine();
|
||||||
|
System.out.println("Received challenge: " + challenge);
|
||||||
|
if(BluetoothServerTest.CHALLENGE.equals(challenge)) {
|
||||||
|
PrintStream out = new PrintStream(s.getOutputStream());
|
||||||
|
out.println(RESPONSE);
|
||||||
|
System.out.println("Sent response: " + RESPONSE);
|
||||||
|
} else {
|
||||||
|
System.out.println("Incorrect challenge");
|
||||||
|
}
|
||||||
|
s.dispose(true);
|
||||||
|
} catch(IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
s.dispose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -339,35 +339,6 @@ public class RemovableDrivePluginTest extends TestCase {
|
|||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSmallFileIsIgnored() throws Exception {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final BatchPluginCallback callback =
|
|
||||||
context.mock(BatchPluginCallback.class);
|
|
||||||
final RemovableDriveFinder finder =
|
|
||||||
context.mock(RemovableDriveFinder.class);
|
|
||||||
final RemovableDriveMonitor monitor =
|
|
||||||
context.mock(RemovableDriveMonitor.class);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(monitor).start(with(any(Callback.class)));
|
|
||||||
}});
|
|
||||||
|
|
||||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(
|
|
||||||
new ImmediateExecutor(), callback, finder, monitor);
|
|
||||||
plugin.start();
|
|
||||||
|
|
||||||
File f = new File(testDir, "abcdefgh.dat");
|
|
||||||
OutputStream out = new FileOutputStream(f);
|
|
||||||
out.write(new byte[TransportConstants.MIN_CONNECTION_LENGTH - 1]);
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
assertEquals(TransportConstants.MIN_CONNECTION_LENGTH - 1, f.length());
|
|
||||||
plugin.driveInserted(testDir);
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReaderIsCreated() throws Exception {
|
public void testReaderIsCreated() throws Exception {
|
||||||
Mockery context = new Mockery();
|
Mockery context = new Mockery();
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ public class SimpleSocketPluginTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testIncomingConnection() throws Exception {
|
public void testIncomingConnection() throws Exception {
|
||||||
StreamCallback callback = new StreamCallback();
|
StreamCallback callback = new StreamCallback();
|
||||||
callback.local.put("host", "127.0.0.1");
|
callback.local.put("internal", "127.0.0.1");
|
||||||
callback.local.put("port", "0");
|
callback.local.put("port", "0");
|
||||||
SimpleSocketPlugin plugin =
|
SimpleSocketPlugin plugin =
|
||||||
new SimpleSocketPlugin(new ImmediateExecutor(), callback, 0L);
|
new SimpleSocketPlugin(new ImmediateExecutor(), callback, 0L);
|
||||||
plugin.start();
|
plugin.start();
|
||||||
// The plugin should have bound a socket and stored the port number
|
// The plugin should have bound a socket and stored the port number
|
||||||
String host = callback.local.get("host");
|
String host = callback.local.get("internal");
|
||||||
assertNotNull(host);
|
assertNotNull(host);
|
||||||
assertEquals("127.0.0.1", host);
|
assertEquals("127.0.0.1", host);
|
||||||
String portString = callback.local.get("port");
|
String portString = callback.local.get("port");
|
||||||
@@ -84,7 +84,7 @@ public class SimpleSocketPluginTest extends TestCase {
|
|||||||
}.start();
|
}.start();
|
||||||
// Tell the plugin about the port
|
// Tell the plugin about the port
|
||||||
TransportProperties p = new TransportProperties();
|
TransportProperties p = new TransportProperties();
|
||||||
p.put("host", "127.0.0.1");
|
p.put("internal", "127.0.0.1");
|
||||||
p.put("port", String.valueOf(port));
|
p.put("port", String.valueOf(port));
|
||||||
callback.remote.put(contactId, p);
|
callback.remote.put(contactId, p);
|
||||||
// Connect to the port
|
// Connect to the port
|
||||||
|
|||||||
Reference in New Issue
Block a user