From f487d4f48a241f92a4280b0070aeee10576173b9 Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Mon, 26 Nov 2012 22:26:07 +0000
Subject: [PATCH] Don't allow input or output streams to be used after hanging
 up.

---
 src/net/sf/briar/plugins/modem/Modem.java     |  6 +-
 src/net/sf/briar/plugins/modem/ModemImpl.java | 60 +++++++++++--------
 2 files changed, 36 insertions(+), 30 deletions(-)

diff --git a/src/net/sf/briar/plugins/modem/Modem.java b/src/net/sf/briar/plugins/modem/Modem.java
index 3d55e3c24c..87ac4532c0 100644
--- a/src/net/sf/briar/plugins/modem/Modem.java
+++ b/src/net/sf/briar/plugins/modem/Modem.java
@@ -6,20 +6,18 @@ import java.io.OutputStream;
 
 /**
  * A modem that can be used for multiple sequential incoming and outgoing
- * calls.
+ * calls. If an exception is thrown, a new modem instance must be created.
  */
 interface Modem {
 
 	/**
 	 * Call this method after creating the modem and before making any calls.
-	 * If an exception is thrown while using the modem, this method must be
-	 * called again.
 	 */
 	void init() throws IOException;
 
 	/**
 	 * Initiates an outgoing call and returns true if the call connects. If the
-	 * call does not connect the modem is hung up and can continue to be used.
+	 * call does not connect the modem is hung up.
 	 */
 	boolean dial(String number) throws IOException;
 
diff --git a/src/net/sf/briar/plugins/modem/ModemImpl.java b/src/net/sf/briar/plugins/modem/ModemImpl.java
index c008fad2f5..0a2a5a1041 100644
--- a/src/net/sf/briar/plugins/modem/ModemImpl.java
+++ b/src/net/sf/briar/plugins/modem/ModemImpl.java
@@ -40,7 +40,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
 
 	private int lineLen = 0;
 
-	private volatile BlockingQueue<byte[]> received = null;
+	private volatile ModemInputStream inputStream = null;
+	private volatile ModemOutputStream outputStream = null;
 
 	ModemImpl(Executor executor, Callback callback, String portName) {
 		this.executor = executor;
@@ -55,10 +56,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
 	public void init() throws IOException {
 		if(LOG.isLoggable(INFO)) LOG.info("Initialising");
 		try {
-			// Open the serial port
 			if(!port.openPort())
 				throw new IOException("Failed to open serial port");
-			// Find a suitable baud rate
 			boolean foundBaudRate = false;
 			for(int baudRate : BAUD_RATES) {
 				if(port.setParams(baudRate, 8, 1, 0)) {
@@ -68,9 +67,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
 			}
 			if(!foundBaudRate)
 				throw new IOException("Could not find a suitable baud rate");
-			// Listen for incoming data and hangup events
 			port.addEventListener(this);
-			// Initialise the modem
 			port.purgePort(PURGE_RXCLEAR | PURGE_TXCLEAR);
 			port.writeBytes("ATZ\r\n".getBytes("US-ASCII")); // Reset
 			port.writeBytes("ATE0\r\n".getBytes("US-ASCII")); // Echo off
@@ -79,11 +76,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
 			throw new IOException(e.toString());
 		}
 		try {
-			// Wait for the modem to respond "OK"
 			synchronized(initialised) {
 				if(!initialised.get()) initialised.wait(OK_TIMEOUT);
-				if(!initialised.get())
-					throw new IOException("Modem did not respond");
 			}
 		} catch(InterruptedException e) {
 			if(LOG.isLoggable(WARNING))
@@ -92,7 +86,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
 			Thread.currentThread().interrupt();
 			throw new IOException("Interrupted while initialising modem");
 		}
-		received = new LinkedBlockingQueue<byte[]>();
+		if(!initialised.get())
+			throw new IOException("Modem did not respond");
 	}
 
 	public boolean dial(String number) throws IOException {
@@ -125,11 +120,11 @@ class ModemImpl implements Modem, SerialPortEventListener {
 	}
 
 	public InputStream getInputStream() {
-		return new ModemInputStream(received);
+		return inputStream;
 	}
 
 	public OutputStream getOutputStream() {
-		return new ModemOutputStream();
+		return outputStream;
 	}
 
 	public void hangUp() throws IOException {
@@ -140,8 +135,9 @@ class ModemImpl implements Modem, SerialPortEventListener {
 			tryToClose(port);
 			throw new IOException(e.toString());
 		}
-		received.add(new byte[0]); // Empty buffer indicates EOF
-		received = new LinkedBlockingQueue<byte[]>();
+		inputStream.closed = true;
+		inputStream.received.add(new byte[0]); // Poison pill
+		outputStream.closed = true;
 		connected.set(false);
 		offHook.release();
 	}
@@ -150,7 +146,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
 		try {
 			if(ev.isRXCHAR()) {
 				byte[] b = port.readBytes();
-				if(connected.get()) received.add(b);
+				if(connected.get()) inputStream.received.add(b);
 				else handleText(b);
 			} else if(ev.isDSR() && ev.getEventValue() == 0) {
 				if(LOG.isLoggable(INFO)) LOG.info("Remote end hung up");
@@ -175,22 +171,26 @@ class ModemImpl implements Modem, SerialPortEventListener {
 				lineLen = 0;
 				if(LOG.isLoggable(INFO)) LOG.info("Modem status: " + s);
 				if(s.startsWith("CONNECT")) {
+					inputStream = new ModemInputStream();
+					outputStream = new ModemOutputStream();
+					synchronized(connected) {
+						if(connected.getAndSet(true))
+							throw new IOException("Connected twice");
+						connected.notifyAll();
+					}
 					// There might be data in the buffer as well as text
 					int off = i + 1;
 					if(off < b.length) {
 						byte[] data = new byte[b.length - off];
 						System.arraycopy(b, off, data, 0, data.length);
-						received.add(data);
-					}
-					synchronized(connected) {
-						if(!connected.getAndSet(true))
-							connected.notifyAll();
+						inputStream.received.add(data);
 					}
 					return;
 				} else if(s.equals("OK")) {
 					synchronized(initialised) {
-						if(!initialised.getAndSet(true))
-							initialised.notifyAll();
+						if(initialised.getAndSet(true))
+							throw new IOException("Initialised twice");
+						initialised.notifyAll();
 					}
 				} else if(s.equals("RING")) {
 					executor.execute(new Runnable() {
@@ -253,21 +253,23 @@ class ModemImpl implements Modem, SerialPortEventListener {
 		private byte[] buf = null;
 		private int offset = 0;
 
-		private ModemInputStream(BlockingQueue<byte[]> received) {
-			this.received = received;
+		private volatile boolean closed = false;
+
+		private ModemInputStream() {
+			this.received = new LinkedBlockingQueue<byte[]>();
 		}
 
 		@Override
 		public int read() throws IOException {
+			if(closed) throw new IOException("Connection closed");
 			getBufferIfNecessary();
-			if(buf.length == 0) return -1;
 			return buf[offset++];
 		}
 
 		@Override
 		public int read(byte[] b) throws IOException {
+			if(closed) throw new IOException("Connection closed");
 			getBufferIfNecessary();
-			if(buf.length == 0) return -1;
 			int len = Math.min(b.length, buf.length - offset);
 			System.arraycopy(buf, offset, b, 0, len);
 			offset += len;
@@ -276,8 +278,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
 
 		@Override
 		public int read(byte[] b, int off, int len) throws IOException {
+			if(closed) throw new IOException("Connection closed");
 			getBufferIfNecessary();
-			if(buf.length == 0) return -1;
 			len = Math.min(len, buf.length - offset);
 			System.arraycopy(buf, offset, b, off, len);
 			offset += len;
@@ -295,6 +297,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
 					Thread.currentThread().interrupt();
 					throw new IOException(e.toString());
 				}
+				if(buf.length == 0) throw new IOException("Connection closed");
 				offset = 0;
 			}
 		}
@@ -302,8 +305,11 @@ class ModemImpl implements Modem, SerialPortEventListener {
 
 	private class ModemOutputStream extends OutputStream {
 
+		private volatile boolean closed = false;
+
 		@Override
 		public void write(int b) throws IOException {
+			if(closed) throw new IOException("Connection closed");
 			try {
 				port.writeByte((byte) b);
 			} catch(SerialPortException e) {
@@ -314,6 +320,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
 
 		@Override
 		public void write(byte[] b) throws IOException {
+			if(closed) throw new IOException("Connection closed");
 			try {
 				port.writeBytes(b);
 			} catch(SerialPortException e) {
@@ -324,6 +331,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
 
 		@Override
 		public void write(byte[] b, int off, int len) throws IOException {
+			if(closed) throw new IOException("Connection closed");
 			if(len < b.length) {
 				byte[] copy = new byte[len];
 				System.arraycopy(b, off, copy, 0, len);
-- 
GitLab