diff --git a/src/syncterm/ssh.c b/src/syncterm/ssh.c
index c69013b8a37428c3b10acbe1c37e0fe630e34111..71dac97afaf268b48cf835002b0fe0395236596f 100644
--- a/src/syncterm/ssh.c
+++ b/src/syncterm/ssh.c
@@ -30,6 +30,14 @@ int             sftp_channel = -1;
 bool            sftp_active;
 sftpc_state_t   sftp_state;
 
+static void
+FlushData(CRYPT_SESSION sess)
+{
+	cl.SetAttribute(sess, CRYPT_OPTION_NET_READTIMEOUT, 30);
+	cl.SetAttribute(sess, CRYPT_OPTION_NET_WRITETIMEOUT, 30);
+	cl.FlushData(sess);
+}
+
 void
 cryptlib_error_message(int status, const char *msg)
 {
@@ -55,17 +63,21 @@ cryptlib_error_message(int status, const char *msg)
 static void
 close_sftp_channel(void)
 {
-	if (sftp_state == NULL)
-		return;
+	sftpc_state_t oldstate;
 	pthread_mutex_lock(&ssh_mutex);
 	if (sftp_channel != -1) {
-		if (cryptStatusOK(cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sftp_channel)))
+		FlushData(ssh_session);
+		if (cryptStatusOK(cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sftp_channel))) {
 			cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0);
+		}
 		sftp_channel = -1;
 	}
-	pthread_mutex_unlock(&ssh_mutex);
-	sftpc_finish(sftp_state);
+	oldstate = sftp_state;
 	sftp_state = NULL;
+	pthread_mutex_unlock(&ssh_mutex);
+	if (oldstate != NULL) {
+		sftpc_finish(oldstate);
+	}
 }
 
 static void
@@ -73,13 +85,35 @@ close_ssh_channel(void)
 {
 	pthread_mutex_lock(&ssh_mutex);
 	if (ssh_channel != -1) {
-		if (cryptStatusOK(cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, ssh_channel)))
+		FlushData(ssh_session);
+		if (cryptStatusOK(cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, ssh_channel))) {
 			cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0);
+		}
 		ssh_channel = -1;
 	}
 	pthread_mutex_unlock(&ssh_mutex);
 }
 
+static bool
+check_channel_open(int *chan)
+{
+	int open = 0;
+	int status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, *chan);
+	if (cryptStatusError(status)) {
+		open = 0;
+	}
+	else {
+		status = cl.GetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_OPEN, &open);
+		if (cryptStatusError(status)) {
+			open = 0;
+		}
+	}
+	if (!open) {
+		cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0);
+	}
+	return open;
+}
+
 void
 ssh_input_thread(void *args)
 {
@@ -88,26 +122,86 @@ ssh_input_thread(void *args)
 	size_t buffered;
 	size_t buffer;
 	int    chan;
+	sftpc_state_t oldstate = NULL;
 
 	SetThreadName("SSH Input");
+	conn_api.input_thread_running = 1;
 	while (ssh_active && !conn_api.terminate) {
+		pthread_mutex_lock(&ssh_mutex);
+		FlushData(ssh_session);
+		if (ssh_channel != -1) {
+			if (!check_channel_open(&ssh_channel))
+				ssh_channel = -1;
+		}
+		if (sftp_channel != -1) {
+			if (!check_channel_open(&sftp_channel)) {
+				oldstate = sftp_state;
+				sftp_state = NULL;
+				sftp_channel = -1;
+			}
+		}
+		if (ssh_channel == -1 && sftp_channel == -1) {
+			if (oldstate != NULL) {
+				sftpc_finish(oldstate);
+				oldstate = NULL;
+			}
+			pthread_mutex_unlock(&ssh_mutex);
+			break;
+		}
+		pthread_mutex_unlock(&ssh_mutex);
+		if (oldstate != NULL) {
+			sftpc_finish(oldstate);
+			oldstate = NULL;
+		}
 		if (!socket_readable(ssh_sock, 100))
 			continue;
 
 		pthread_mutex_lock(&ssh_mutex);
-		conn_api.input_thread_running = 1;
-		cl.FlushData(ssh_session);
+		FlushData(ssh_session);
+
+		// Check channels are active...
+		if (ssh_channel != -1) {
+			if (!check_channel_open(&ssh_channel))
+				ssh_channel = -1;
+		}
+		if (sftp_channel != -1) {
+			if (!check_channel_open(&sftp_channel)) {
+				pthread_mutex_lock(&ssh_mutex);
+				sftpc_state_t oldstate = sftp_state;
+				sftp_state = NULL;
+				sftp_channel = -1;
+				pthread_mutex_unlock(&ssh_mutex);
+				sftpc_finish(oldstate);
+			}
+		}
+		if (ssh_channel == -1 && sftp_channel == -1) {
+			fprintf(stderr, "All my friends are gone!\n");
+			pthread_mutex_unlock(&ssh_mutex);
+			break;
+		}
+
+		cl.SetAttribute(ssh_session, CRYPT_OPTION_NET_READTIMEOUT, 0);
 		popstatus = cl.PopData(ssh_session, conn_api.rd_buf, conn_api.rd_buf_size, &rd);
-		gchstatus = cl.GetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &chan);
+		if (cryptStatusOK(popstatus)) {
+			gchstatus = cl.GetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &chan);
+		}
+		else {
+			gchstatus = CRYPT_OK;
+			chan = -1;
+		}
 		pthread_mutex_unlock(&ssh_mutex);
 
                 // Handle case where there was socket activity without readable data (ie: rekey)
-		if (popstatus == CRYPT_ERROR_TIMEOUT)
+		if (popstatus == CRYPT_ERROR_TIMEOUT) {
+			FlushData(ssh_session);
 			continue;
+		}
 
 		// A final read on a channel just occured... figure out which is missing...
 		if (gchstatus == CRYPT_ERROR_NOTFOUND) {
+			pthread_mutex_lock(&ssh_mutex);
 			if (ssh_channel != -1) {
+				FlushData(ssh_session);
 				status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, ssh_channel);
 				if (status == CRYPT_ERROR_NOTFOUND) {
 					chan = ssh_channel;
@@ -115,51 +209,70 @@ ssh_input_thread(void *args)
 			}
 			if (chan == -1) {
 				if (sftp_channel != -1) {
+					FlushData(ssh_session);
 					status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, ssh_channel);
 					if (status == CRYPT_ERROR_NOTFOUND) {
 						chan = sftp_channel;
 					}
 				}
 			}
+			pthread_mutex_unlock(&ssh_mutex);
 		}
 
 		if (cryptStatusError(popstatus)) {
 			if ((popstatus == CRYPT_ERROR_COMPLETE) || (popstatus == CRYPT_ERROR_READ)) { /* connection closed */
-				if (chan == ssh_channel)
+				pthread_mutex_lock(&ssh_mutex);
+				if (chan == ssh_channel) {
+					pthread_mutex_unlock(&ssh_mutex);
 					break;
+				}
+				pthread_mutex_unlock(&ssh_mutex);
 			}
 			else {
 				cryptlib_error_message(popstatus, "recieving data");
 				break;
 			}
 		}
-		else {
+		else if (chan != -1) {
+			pthread_mutex_lock(&ssh_mutex);
 			if (chan == sftp_channel) {
-				if (!sftpc_recv(sftp_state, conn_api.rd_buf, rd)) {
+				if (gchstatus == CRYPT_ERROR_NOTFOUND) {
+					sftp_channel = -1;
+				}
+				/*
+				 * TODO: Can't hold sftp mutex here!
+				 *       It will be held by whatever's waiting for this.
+				 *       and will deadlock.
+				 */
+				if (rd > 0 && !sftpc_recv(sftp_state, conn_api.rd_buf, rd)) {
+					pthread_mutex_unlock(&ssh_mutex);
 					close_sftp_channel();
+					pthread_mutex_lock(&ssh_mutex);
+					FlushData(ssh_session);
+					pthread_mutex_unlock(&ssh_mutex);
 					continue;
 				}
-				if (gchstatus == CRYPT_ERROR_NOTFOUND) {
-					sftp_channel = -1;
+				else {
+					pthread_mutex_unlock(&ssh_mutex);
 				}
 			}
 			else if (chan == ssh_channel) {
-				buffered = 0;
-				while (buffered < rd) {
-					pthread_mutex_lock(&(conn_inbuf.mutex));
-					buffer = conn_buf_wait_free(&conn_inbuf, rd - buffered, 100);
-					buffered += conn_buf_put(&conn_inbuf, conn_api.rd_buf + buffered, buffer);
-					pthread_mutex_unlock(&(conn_inbuf.mutex));
-				}
 				if (gchstatus == CRYPT_ERROR_NOTFOUND) {
 					ssh_channel = -1;
 				}
-			}
-			else {
-				assert(true);
-				cryptlib_error_message(gchstatus, "unknown channel");
+				pthread_mutex_unlock(&ssh_mutex);
+				if (rd > 0) {
+					buffered = 0;
+					while (buffered < rd) {
+						pthread_mutex_lock(&(conn_inbuf.mutex));
+						buffer = conn_buf_wait_free(&conn_inbuf, rd - buffered, 100);
+						buffered += conn_buf_put(&conn_inbuf, conn_api.rd_buf + buffered, buffer);
+						pthread_mutex_unlock(&(conn_inbuf.mutex));
+					}
+				}
 			}
 		}
+		FlushData(ssh_session);
 	}
 	conn_api.input_thread_running = 2;
 }
@@ -171,10 +284,11 @@ ssh_output_thread(void *args)
 	int    ret;
 	size_t sent;
 	int    status;
+	bool   channel_gone = false;
 
 	SetThreadName("SSH Output");
 	conn_api.output_thread_running = 1;
-	while (ssh_active && !conn_api.terminate && ssh_channel != -1) {
+	while (ssh_active && !conn_api.terminate && !channel_gone) {
 		pthread_mutex_lock(&(conn_outbuf.mutex));
 		wr = conn_buf_wait_bytes(&conn_outbuf, 1, 100);
 		if (wr) {
@@ -186,11 +300,16 @@ ssh_output_thread(void *args)
 				pthread_mutex_lock(&ssh_mutex);
 				if (ssh_channel == -1) {
 					pthread_mutex_unlock(&ssh_mutex);
+					channel_gone = true;
 					break;
 				}
+				FlushData(ssh_session);
 				status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, ssh_channel);
-				if (cryptStatusOK(status))
+				if (cryptStatusOK(status)) {
 					status = cl.PushData(ssh_session, conn_api.wr_buf + sent, wr - sent, &ret);
+					if (cryptStatusOK(status))
+						FlushData(ssh_session);
+				}
 				pthread_mutex_unlock(&ssh_mutex);
 				if (cryptStatusError(status)) {
 					if ((status == CRYPT_ERROR_COMPLETE) || (status == CRYPT_ERROR_NOTFOUND)) { /* connection closed */
@@ -201,11 +320,6 @@ ssh_output_thread(void *args)
 				}
 				sent += ret;
 			}
-			if (sent) {
-				pthread_mutex_lock(&ssh_mutex);
-				cl.FlushData(ssh_session);
-				pthread_mutex_unlock(&ssh_mutex);
-			}
 		}
 		else {
 			pthread_mutex_unlock(&(conn_outbuf.mutex));
@@ -219,16 +333,21 @@ static bool
 sftp_send(uint8_t *buf, size_t sz, void *cb_data)
 {
 	size_t sent = 0;
+	int active = 1;
+
+	if (sz == 0)
+		return true;
 	while (ssh_active && sent < sz) {
 		int status;
-		int ret;
+		int ret = 0;
 		pthread_mutex_lock(&ssh_mutex);
-		status = cl.FlushData(ssh_session);
+		FlushData(ssh_session);
 		status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sftp_channel);
 		if (cryptStatusOK(status)) {
-			status = cl.PushData(ssh_session, buf + sent, sz - sent, &ret);
-			if (cryptStatusOK(status))
-				status = cl.FlushData(ssh_session);
+			active = 0;
+			status = cl.GetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_OPEN, &active);
+			if (cryptStatusOK(status) && active)
+				status = cl.PushData(ssh_session, buf + sent, sz - sent, &ret);
 		}
 		pthread_mutex_unlock(&ssh_mutex);
 		if (cryptStatusError(status)) {
@@ -252,11 +371,15 @@ get_public_key(CRYPT_CONTEXT ctx)
 	char *ret;
 	size_t rsz;
 
+	pthread_mutex_lock(&ssh_mutex);
 	status = cl.GetAttributeString(ctx, CRYPT_CTXINFO_SSH_PUBLIC_KEY, NULL, &sz);
+	pthread_mutex_unlock(&ssh_mutex);
 	if (cryptStatusOK(status)) {
 		raw = malloc(sz);
 		if (raw != NULL) {
+			pthread_mutex_lock(&ssh_mutex);
 			status = cl.GetAttributeString(ctx, CRYPT_CTXINFO_SSH_PUBLIC_KEY, raw, &sz);
+			pthread_mutex_unlock(&ssh_mutex);
 			if (cryptStatusOK(status)) {
 				rsz = (sz - 4) * 4 / 3 + 3;
 				ret = malloc(rsz);
@@ -337,88 +460,107 @@ key_not_present(sftp_filehandle_t f, const char *priv)
 	} while(1);
 }
 
-static bool
-add_public_key(struct bbslist *bbs, char *priv)
+static void
+add_public_key(void *vpriv)
 {
 	int status;
-	bool added = false;
-
-	// TODO: Without this sleep, all is woe.
-	while (!conn_api.input_thread_running)
-		SLEEP(1);
-	if (!bbs->hidepopups) {
-		uifc.pop(NULL);
-		uifc.pop("Creating SFTP Channel");
-	}
+	int active;
+	char *priv = vpriv;
+
+	/*
+	 * TODO: We need to wait until the session is established.
+	 *       Best way to do this is a channel property that indicates
+	 *       what type of channel it is.
+	 */
+	SLEEP(1000);
 	pthread_mutex_lock(&ssh_mutex);
+	FlushData(ssh_session);
 	status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, CRYPT_UNUSED);
 	if (cryptStatusError(status)) {
+		pthread_mutex_unlock(&ssh_mutex);
 		cryptlib_error_message(status, "setting new channel");
-	}
-	if (cryptStatusOK(status)) {
+	} else {
 		status = cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TYPE, "subsystem", 9);
-		if (cryptStatusError(status))
+		if (cryptStatusError(status)) {
+			pthread_mutex_unlock(&ssh_mutex);
 			cryptlib_error_message(status, "setting channel type");
-		if (cryptStatusOK(status)) {
+		} else {
 			status = cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ARG1, "sftp", 4);
-			if (cryptStatusError(status))
+			if (cryptStatusError(status)) {
+				pthread_mutex_unlock(&ssh_mutex);
 				cryptlib_error_message(status, "setting subsystem");
-			if (cryptStatusOK(status)) {
+			} else {
 				status = cl.GetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &sftp_channel);
-				if (cryptStatusError(status))
+				if (cryptStatusError(status)) {
+					sftp_channel = -1;
+					pthread_mutex_unlock(&ssh_mutex);
 					cryptlib_error_message(status, "getting new channel");
-				if (cryptStatusOK(status)) {
-					// TODO: Do something...
 				}
 			}
 		}
 	}
 	if (sftp_channel != -1) {
-		pthread_mutex_lock(&ssh_mutex);
-		cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sftp_channel);
+		status = cl.GetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_OPEN, &active);
+		if (cryptStatusError(status) || !active) {
+			cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0);
+			sftp_channel = -1;
+			pthread_mutex_unlock(&ssh_mutex);
+			free(priv);
+			return;
+		}
 		status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 1);
+		if (cryptStatusError(status)) {
+			pthread_mutex_unlock(&ssh_mutex);
+			close_sftp_channel();
+			free(priv);
+			return;
+		}
+		sftp_state = sftpc_begin(sftp_send, NULL);
 		pthread_mutex_unlock(&ssh_mutex);
-		if (cryptStatusOK(status)) {
-			sftp_state = sftpc_begin(sftp_send, NULL);
-			if (sftp_state != NULL) {
-				if (sftpc_init(sftp_state)) {
-					sftp_filehandle_t f = NULL;
-					// TODO: Add permissions?
-
-					if (sftpc_open(sftp_state, ".ssh/authorized_keys", SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND | SSH_FXF_CREAT, NULL, &f)) {
-						/* Read through the file looking for our key */
-						if (key_not_present(f, priv)) {
-							// TODO: Types other than RSA...
-							sftp_str_t ln =  sftp_asprintf("ssh-rsa %s Added by SyncTERM\n", priv);
-							if (ln != NULL) {
-								sftpc_write(sftp_state, f, 0, ln);
-							}
-						}
-						sftpc_close(sftp_state, &f);
+		if (sftp_state == NULL) {
+			close_sftp_channel();
+			free(priv);
+			return;
+		}
+		if (sftpc_init(sftp_state)) {
+			sftp_filehandle_t f = NULL;
+			// TODO: Add permissions?
+
+			if (sftpc_open(sftp_state, ".ssh/authorized_keys", SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND | SSH_FXF_CREAT, NULL, &f)) {
+				/* Read through the file looking for our key */
+				if (key_not_present(f, priv)) {
+					// TODO: Types other than RSA...
+					sftp_str_t ln =  sftp_asprintf("ssh-rsa %s Added by SyncTERM\n", priv);
+					if (ln != NULL) {
+						sftpc_write(sftp_state, f, 0, ln);
+						free_sftp_str(ln);
 					}
 				}
+				sftpc_close(sftp_state, &f);
 			}
+			pthread_mutex_lock(&ssh_mutex);
+			sftpc_state_t oldstate = sftp_state;
+			sftp_state = NULL;
+			pthread_mutex_unlock(&ssh_mutex);
+			sftpc_finish(oldstate);
 		}
-		pthread_mutex_lock(&ssh_mutex);
-		cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sftp_channel);
-		status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0);
-		if (cryptStatusOK(status))
-			sftp_channel = -1;
+		close_sftp_channel();
+	}
+	else {
 		pthread_mutex_unlock(&ssh_mutex);
 	}
-	return added;
+	free(priv);
 }
 #else
-static char *
-get_public_key(CRYPT_CONTEXT ctx)
+static void
+add_public_key(void *vpriv)
 {
-	return NULL;
 }
 
-static bool
-add_public_key(struct bbslist *bbs, char *priv)
+static char *
+get_public_key(CRYPT_CONTEXT ctx)
 {
-	return true;
+	return NULL;
 }
 #endif
 
@@ -647,7 +789,7 @@ ssh_connect(struct bbslist *bbs)
 	}
 	status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT, rows);
 
-	cl.SetAttribute(ssh_session, CRYPT_OPTION_NET_READTIMEOUT, 1);
+	cl.SetAttribute(ssh_session, CRYPT_OPTION_NET_READTIMEOUT, 30);
 
 	/* Activate the session */
 	if (!bbs->hidepopups) {
@@ -676,7 +818,7 @@ ssh_connect(struct bbslist *bbs)
 		error_popup(bbs, "activating session", status);
 		return -1;
 	}
-	cl.FlushData(ssh_session);
+	FlushData(ssh_session);
 
 	ssh_active = true;
 	if (!bbs->hidepopups) {
@@ -695,7 +837,7 @@ ssh_connect(struct bbslist *bbs)
 		uifc.pop("Getting SSH Channel");
 	}
 	status = cl.GetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &ssh_channel);
-	if (cryptStatusError(status)) {
+	if (cryptStatusError(status) || ssh_channel == -1) {
 		free(pubkey);
 		error_popup(bbs, "getting ssh channel", status);
 		return -1;
@@ -770,9 +912,7 @@ ssh_connect(struct bbslist *bbs)
 
 	_beginthread(ssh_output_thread, 0, NULL);
 	_beginthread(ssh_input_thread, 0, NULL);
-
-	add_public_key(bbs, pubkey);
-	free(pubkey);
+	_beginthread(add_public_key, 0, pubkey);
 
 	if (!bbs->hidepopups)
 		uifc.pop(NULL); // TODO: Why is this called twice?