diff --git a/src/sftp/objects.mk b/src/sftp/objects.mk
index 27981a4603b36acaf411577bbbccc79ec4849f2f..2123ef195a44a1cc76058e8eb0a658c37fab4bad 100644
--- a/src/sftp/objects.mk
+++ b/src/sftp/objects.mk
@@ -2,4 +2,5 @@ MT_OBJS = \
 	$(MTOBJODIR)$(DIRSEP)sftp_pkt$(OFILE) \
 	$(MTOBJODIR)$(DIRSEP)sftp_str$(OFILE) \
 	$(MTOBJODIR)$(DIRSEP)sftp_client$(OFILE) \
+	$(MTOBJODIR)$(DIRSEP)sftp_attr$(OFILE) \
 
diff --git a/src/sftp/sftp.h b/src/sftp/sftp.h
index 64acc26251c7a7a16176602ed62ee808e281e916..2d12851cded3225da2b6c88443cc807c583582d1 100644
--- a/src/sftp/sftp.h
+++ b/src/sftp/sftp.h
@@ -8,34 +8,33 @@
 
 // draft-ietf-secsh-filexfer-02
 
-#define SSH_FXP_INIT           UINT8_C(1)
-#define SSH_FXP_VERSION        UINT8_C(2)
-#define SSH_FXP_OPEN           UINT8_C(3)
-#define SSH_FXP_CLOSE          UINT8_C(4)
-#define SSH_FXP_READ           UINT8_C(5)
-#define SSH_FXP_WRITE          UINT8_C(6)
-#define SSH_FXP_LSTAT          UINT8_C(7)
-#define SSH_FXP_FSTAT          UINT8_C(8)
-#define SSH_FXP_SETSTAT        UINT8_C(9)
-#define SSH_FXP_FSETSTAT       UINT8_C(10)
-#define SSH_FXP_OPENDIR        UINT8_C(11)
-#define SSH_FXP_READDIR        UINT8_C(12)
-#define SSH_FXP_REMOVE         UINT8_C(13)
-#define SSH_FXP_MKDIR          UINT8_C(14)
-#define SSH_FXP_RMDIR          UINT8_C(15)
-#define SSH_FXP_REALPATH       UINT8_C(16)
-#define SSH_FXP_STAT           UINT8_C(17)
-#define SSH_FXP_RENAME         UINT8_C(18)
-#define SSH_FXP_READLINK       UINT8_C(19)
-#define SSH_FXP_SYMLINK        UINT8_C(20)
-#define SSH_FXP_STATUS         UINT8_C(101)
-#define SSH_FXP_HANDLE         UINT8_C(102)
-#define SSH_FXP_DATA           UINT8_C(103)
-#define SSH_FXP_NAME           UINT8_C(104)
-#define SSH_FXP_ATTRS          UINT8_C(105)
-#define SSH_FXP_EXTENDED       UINT8_C(200)
-#define SSH_FXP_EXTENDED_REPLY UINT8_C(201)
-
+#define SSH_FXP_INIT             UINT8_C(1)
+#define SSH_FXP_VERSION          UINT8_C(2)
+#define SSH_FXP_OPEN             UINT8_C(3)
+#define SSH_FXP_CLOSE            UINT8_C(4)
+#define SSH_FXP_READ             UINT8_C(5)
+#define SSH_FXP_WRITE            UINT8_C(6)
+#define SSH_FXP_LSTAT            UINT8_C(7)
+#define SSH_FXP_FSTAT            UINT8_C(8)
+#define SSH_FXP_SETSTAT          UINT8_C(9)
+#define SSH_FXP_FSETSTAT         UINT8_C(10)
+#define SSH_FXP_OPENDIR          UINT8_C(11)
+#define SSH_FXP_READDIR          UINT8_C(12)
+#define SSH_FXP_REMOVE           UINT8_C(13)
+#define SSH_FXP_MKDIR            UINT8_C(14)
+#define SSH_FXP_RMDIR            UINT8_C(15)
+#define SSH_FXP_REALPATH         UINT8_C(16)
+#define SSH_FXP_STAT             UINT8_C(17)
+#define SSH_FXP_RENAME           UINT8_C(18)
+#define SSH_FXP_READLINK         UINT8_C(19)
+#define SSH_FXP_SYMLINK          UINT8_C(20)
+#define SSH_FXP_STATUS           UINT8_C(101)
+#define SSH_FXP_HANDLE           UINT8_C(102)
+#define SSH_FXP_DATA             UINT8_C(103)
+#define SSH_FXP_NAME             UINT8_C(104)
+#define SSH_FXP_ATTRS            UINT8_C(105)
+#define SSH_FXP_EXTENDED         UINT8_C(200)
+#define SSH_FXP_EXTENDED_REPLY   UINT8_C(201)
 
 #define SSH_FX_OK                UINT32_C(0)
 #define SSH_FX_EOF               UINT32_C(1)
@@ -47,6 +46,19 @@
 #define SSH_FX_CONNECTION_LOST   UINT32_C(7)
 #define SSH_FX_OP_UNSUPPORTED    UINT32_C(8)
 
+#define SSH_FXF_READ             UINT32_C(0x00000001)
+#define SSH_FXF_WRITE            UINT32_C(0x00000002)
+#define SSH_FXF_APPEND           UINT32_C(0x00000004)
+#define SSH_FXF_CREAT            UINT32_C(0x00000008)
+#define SSH_FXF_TRUNC            UINT32_C(0x00000010)
+#define SSH_FXF_EXCL             UINT32_C(0x00000020)
+
+#define SSH_FILEXFER_ATTR_SIZE        UINT32_C(0x00000001)
+#define SSH_FILEXFER_ATTR_UIDGID      UINT32_C(0x00000002)
+#define SSH_FILEXFER_ATTR_PERMISSIONS UINT32_C(0x00000004)
+#define SSH_FILEXFER_ATTR_ACMODTIME   UINT32_C(0x00000008)
+#define SSH_FILEXFER_ATTR_EXTENDED    UINT32_C(0x80000000)
+        
 #define SFTP_MIN_PACKET_ALLOC 4096
 #define SFTP_VERSION 3
 
@@ -76,16 +88,8 @@ struct sftp_extended_file_attribute {
 	struct sftp_string *data;
 };
 
-struct sftp_file_attributes {
-	uint32_t flags;
-	uint64_t size;
-	uint32_t uid;
-	uint32_t gid;
-	uint32_t perm;
-	uint32_t atime;
-	uint32_t mtime;
-	struct sftp_extended_file_attribute ext[];
-};
+struct sftp_file_attributes;
+typedef struct sftp_file_attributes *sftp_file_attr_t;
 
 typedef struct sftp_client_state {
 	bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data);
@@ -102,6 +106,14 @@ typedef struct sftp_client_state {
 	uint32_t err_code;
 } *sftpc_state_t;
 
+enum sftp_handle_type {
+	SFTP_HANDLE_TYPE_DIR,
+	SFTP_HANDLE_TYPE_FILE,
+};
+
+typedef sftp_str_t sftp_filehandle_t;
+typedef sftp_str_t sftp_dirhandle_t;
+
 /* sftp_pkt.c */
 const char * const sftp_get_type_name(uint8_t type);
 bool sftp_have_pkt_sz(sftp_rx_pkt_t pkt);
@@ -135,5 +147,28 @@ sftpc_state_t sftpc_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_dat
 bool sftpc_init(sftpc_state_t state);
 bool sftpc_recv(sftpc_state_t state, uint8_t *buf, uint32_t sz);
 bool sftpc_realpath(sftpc_state_t state, char *path, sftp_str_t *ret);
+bool sftpc_open(sftpc_state_t state, char *path, uint32_t flags, sftp_file_attr_t attr, sftp_dirhandle_t *handle);
+bool sftpc_close(sftpc_state_t state, sftp_filehandle_t *handle);
+bool sftpc_read(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, uint32_t len, sftp_str_t *ret);
+bool sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp_str_t data);
+
+/* sftp_attr.c */
+sftp_file_attr_t sftp_fattr_alloc(void);
+void sftp_fattr_free(sftp_file_attr_t fattr);
+void sftp_fattr_set_size(sftp_file_attr_t fattr, uint64_t sz);
+bool sftp_fattr_get_size(sftp_file_attr_t fattr, uint64_t *sz);
+void sftp_fattr_set_uid_gid(sftp_file_attr_t fattr, uint32_t uid, uint32_t gid);
+bool sftp_fattr_get_uid(sftp_file_attr_t fattr, uint32_t *uid);
+bool sftp_fattr_get_gid(sftp_file_attr_t fattr, uint32_t *gid);
+void sftp_fattr_set_permissions(sftp_file_attr_t fattr, uint64_t perm);
+bool sftp_fattr_get_permissions(sftp_file_attr_t fattr, uint64_t *perm);
+void sftp_fattr_set_times(sftp_file_attr_t fattr, uint32_t atime, uint32_t mtime);
+bool sftp_fattr_get_atime(sftp_file_attr_t fattr, uint32_t *atime);
+bool sftp_fattr_get_mtime(sftp_file_attr_t fattr, uint32_t *mtime);
+bool sftp_fattr_add_ext(sftp_file_attr_t *fattr, sftp_str_t type, sftp_str_t data);
+sftp_str_t sftp_fattr_get_ext_type(sftp_file_attr_t fattr, uint32_t index);
+sftp_str_t sftp_fattr_get_ext_data(sftp_file_attr_t fattr, uint32_t index);
+uint32_t sftp_fattr_get_ext_count(sftp_file_attr_t fattr);
+bool sftp_appendfattr(sftp_tx_pkt_t *pktp, sftp_file_attr_t fattr);
 
 #endif
diff --git a/src/sftp/sftp.vcxproj b/src/sftp/sftp.vcxproj
index 432358d1fcfd255b7584e7b7d8862654e3a47fbb..52aad85399f967036611b9697ac940360c3f5fe1 100644
--- a/src/sftp/sftp.vcxproj
+++ b/src/sftp/sftp.vcxproj
@@ -112,10 +112,11 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="sftp_client.c" />
+    <ClCompile Include="sftp_attr.c" />
     <ClCompile Include="sftp_pkt.c" />
     <ClCompile Include="sftp_str.c" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/sftp/sftp_client.c b/src/sftp/sftp_client.c
index 965954778eba0d8eb12cbd6fb5367da006c7330a..012db6e674351231b77cff51b5dd836a0f195f28 100644
--- a/src/sftp/sftp_client.c
+++ b/src/sftp/sftp_client.c
@@ -42,7 +42,13 @@ append64(sftpc_state_t state, uint64_t u)
 }
 
 static bool
-appendstring(sftpc_state_t state, sftp_str_t *s)
+appendfattr(sftpc_state_t state, sftp_file_attr_t fattr)
+{
+	return sftp_appendfattr(&state->txp, fattr);
+}
+
+static bool
+appendandfreestring(sftpc_state_t state, sftp_str_t *s)
 {
 	bool ret = sftp_appendstring(&state->txp, *s);
 	free_sftp_str(*s);
@@ -50,6 +56,13 @@ appendstring(sftpc_state_t state, sftp_str_t *s)
 	return ret;
 }
 
+static bool
+appendstring(sftpc_state_t state, sftp_str_t s)
+{
+	bool ret = sftp_appendstring(&state->txp, s);
+	return ret;
+}
+
 void
 sftpc_finish(sftpc_state_t state)
 {
@@ -134,12 +147,27 @@ appendheader(sftpc_state_t state, uint8_t type)
 	return true;
 }
 
+static bool
+appendfhandle(sftpc_state_t state, sftp_filehandle_t handle)
+{
+	return appendstring(state, (sftp_str_t)handle);
+}
+
+static bool
+appenddhandle(sftpc_state_t state, sftp_dirhandle_t handle)
+{
+	return appendstring(state, (sftp_str_t)handle);
+}
+
 static bool
 get_result(sftpc_state_t state)
 {
 	uint8_t *txbuf;
 	size_t txsz;
 
+	assert(state->thread == pthread_self());
+	if (state->thread != pthread_self())
+		return false;
 	if (!sftp_prep_tx_packet(state->txp, &txbuf, &txsz))
 		return false;
 	if (!state->send_cb(txbuf, txsz, state->cb_data))
@@ -213,7 +241,7 @@ sftpc_realpath(sftpc_state_t state, char *path, sftp_str_t *ret)
 	if (!appendheader(state, SSH_FXP_REALPATH))
 		return false;
 	sftp_str_t pstr = sftp_strdup(path);
-	if (!appendstring(state, &pstr))
+	if (!appendandfreestring(state, &pstr))
 		return false;
 	if (!get_result(state))
 		return false;
@@ -229,3 +257,129 @@ sftpc_realpath(sftpc_state_t state, char *path, sftp_str_t *ret)
 	handle_error(state);
 	return false;
 }
+
+static bool
+parse_handle(sftpc_state_t state, sftp_str_t *handle)
+{
+	assert(state);
+	if (state == NULL)
+		return false;
+	assert(state->rxp);
+	if (state->rxp == NULL)
+		return false;
+	assert(state->rxp->type == SSH_FXP_HANDLE);
+	if (state->rxp->type != SSH_FXP_HANDLE)
+		return false;
+	assert(handle);
+	if (handle == NULL)
+		return false;
+	*handle = getstring(state);
+	if (*handle == NULL)
+		return false;
+	return true;
+}
+
+bool
+sftpc_open(sftpc_state_t state, char *path, uint32_t flags, sftp_file_attr_t attr, sftp_dirhandle_t *handle)
+{
+	assert(handle);
+	if (handle == NULL)
+		return false;
+	assert(!*handle);
+	if (*handle != NULL)
+		return false;
+	if (!appendheader(state, SSH_FXP_OPEN))
+		return false;
+	sftp_str_t pstr = sftp_strdup(path);
+	if (!appendandfreestring(state, &pstr))
+		return false;
+	if (!append32(state, flags))
+		return false;
+	sftp_file_attr_t a = attr;
+	if (a == NULL) {
+		a = sftp_fattr_alloc();
+		if (a == NULL)
+			return false;
+	}
+	if (!appendfattr(state, a)) {
+		if (a != attr)
+			sftp_fattr_free(a);
+		return false;
+	}
+	if (a != attr)
+		sftp_fattr_free(a);
+	if (!get_result(state))
+		return false;
+	if (state->rxp->type == SSH_FXP_HANDLE) {
+		if (!parse_handle(state, (sftp_str_t *)handle)) {
+			response_handled(state);
+			return false;
+		}
+		response_handled(state);
+		return true;
+	}
+	handle_error(state);
+	return false;
+}
+
+bool
+sftpc_close(sftpc_state_t state, sftp_filehandle_t *handle)
+{
+	if (!appendheader(state, SSH_FXP_CLOSE))
+		return false;
+	if (!appendfhandle(state, *handle))
+		return false;
+	if (!get_result(state))
+		return false;
+	handle_error(state);
+	free_sftp_str(*handle);
+	*handle = NULL;
+	return state->err_code == SSH_FX_OK;
+}
+
+bool
+sftpc_read(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, uint32_t len, sftp_str_t *ret)
+{
+	assert(ret);
+	if (ret == NULL)
+		return false;
+	assert(*ret == NULL);
+	if (*ret != NULL)
+		return false;
+	if (!appendheader(state, SSH_FXP_READ))
+		return false;
+	if (!appendfhandle(state, handle))
+		return false;
+	if (!append64(state, offset))
+		return false;
+	if (!append32(state, len))
+		return false;
+	if (!get_result(state))
+		return false;
+	if (state->rxp->type == SSH_FXP_DATA) {
+		*ret = getstring(state);
+		return *ret != NULL;
+	}
+	handle_error(state);
+	return false;
+}
+
+bool
+sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp_str_t data)
+{
+	assert(data);
+	if (data == NULL)
+		return false;
+	if (!appendheader(state, SSH_FXP_WRITE))
+		return false;
+	if (!appendfhandle(state, handle))
+		return false;
+	if (!append64(state, offset))
+		return false;
+	if (!appendstring(state, data))
+		return false;
+	if (!get_result(state))
+		return false;
+	handle_error(state);
+	return state->err_code == SSH_FX_OK;
+}
diff --git a/src/syncterm/ssh.c b/src/syncterm/ssh.c
index 5957bbcf074acb58b3d410bbe3846a99e6c6821f..f4065954639e29489747d2c0b569045a6b1dd4b9 100644
--- a/src/syncterm/ssh.c
+++ b/src/syncterm/ssh.c
@@ -250,7 +250,7 @@ error_popup(struct bbslist *bbs, const char *blurb, int status)
 	char str[1024];
 	sprintf(str, "Error %d %s", status, blurb);
 	if (!bbs->hidepopups)
-		uifcmsg("Error %s", str);
+		uifcmsg(str, str);
 	conn_api.terminate = 1;
 	if (!bbs->hidepopups)
 		uifc.pop(NULL);
@@ -301,8 +301,7 @@ ssh_connect(struct bbslist *bbs)
 		if(cryptStatusError(status)) {
 			error_popup(bbs, "creating context", status);
 		}
-		status = cl.KeysetClose(ssh_keyset);
-		if (cryptStatusError(status)) {
+		if (cryptStatusError(cl.KeysetClose(ssh_keyset))) {
 			error_popup(bbs, "closing keyset", status);
 		}
 	}
@@ -314,7 +313,7 @@ ssh_connect(struct bbslist *bbs)
 				error_popup(bbs, "creating context", status);
 				break;
 			}
-			status = cl.SetAttributeString(ssh_context, CRYPT_CTXINFO_LABEL, KEY_LABEL, 10);
+			status = cl.SetAttributeString(ssh_context, CRYPT_CTXINFO_LABEL, KEY_LABEL, strlen(KEY_LABEL));
 			if (cryptStatusError(status)) {
 				error_popup(bbs, "setting label", status);
 				break;
@@ -417,12 +416,15 @@ ssh_connect(struct bbslist *bbs)
 			return -1;
 		}
 
-		if (!bbs->hidepopups)
-			uifc.pop("Setting Private Key");
-		status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_PRIVATEKEY, ssh_context);
-		if (cryptStatusError(status)) {
-			error_popup(bbs, "setting private key", status);
-			return -1;
+		if (ssh_context != -1) {
+			if (!bbs->hidepopups)
+				uifc.pop("Setting Private Key");
+			status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_PRIVATEKEY, ssh_context);
+			cl.DestroyContext(ssh_context);
+			if (cryptStatusError(status)) {
+				error_popup(bbs, "setting private key", status);
+				return -1;
+			}
 		}
 	}
 
@@ -625,8 +627,14 @@ ssh_connect(struct bbslist *bbs)
 			if (sftp_state != NULL) {
 				if (sftpc_init(sftp_state)) {
 					sftp_str_t ret = NULL;
-					if (sftpc_realpath(sftp_state, ".", &ret)) {
-						fprintf(stderr, "Home dir: %.*s\n", ret->len, ret->c_str);
+					sftp_filehandle_t f = NULL;
+
+					if (sftpc_open(sftp_state, ".ssh/authorized_keys", SSH_FXF_READ, NULL, &f)) {
+						if (sftpc_read(sftp_state, f, 0, 1024, &ret)) {
+							fprintf(stderr, "First lines... %s\n", ret->c_str);
+							free_sftp_str(ret);
+						}
+						sftpc_close(sftp_state, &f);
 					}
 				}
 			}