Squashed commit of not-yet-approved PR #2038
https://github.com/PhilippC/keepass2android/pull/2038 User-defined SFTP connection timeout The addition of SFTP query parameter options are needed to support custom private key functionality. Squashed commits from hyproman:sftp-conn-timeout: commit 9c6b96e8198f1b912acdc1248af775f8fed58e1c commit ebe59d9bc337a46bf0646677eb38b13ddde21f14 commit 69eb0bfd1a7010a2e442c36d10a16d1710c958de commit 9394947c12bedb8667b7b94d0b1457f9e0451e18
This commit is contained in:
@@ -97,7 +97,28 @@ public class FileEntry {
|
|||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder s = new StringBuilder("JavaFileStorage.FileEntry{").append(displayName).append("|")
|
||||||
|
.append("path=").append(path).append(",sz=").append(sizeInBytes)
|
||||||
|
.append(",").append(isDirectory ? "dir" : "file")
|
||||||
|
.append(",lastMod=").append(lastModifiedTime);
|
||||||
|
|
||||||
|
StringBuilder perms = new StringBuilder();
|
||||||
|
if (canRead)
|
||||||
|
perms.append("r");
|
||||||
|
if (canWrite)
|
||||||
|
perms.append("w");
|
||||||
|
if (perms.length() > 0) {
|
||||||
|
s.append(",").append(perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userData != null && userData.length() > 0)
|
||||||
|
s.append(",userData=").append(userData);
|
||||||
|
|
||||||
|
return s.append("}").toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,12 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import com.jcraft.jsch.Channel;
|
import com.jcraft.jsch.Channel;
|
||||||
import com.jcraft.jsch.ChannelSftp;
|
import com.jcraft.jsch.ChannelSftp;
|
||||||
@@ -28,6 +33,9 @@ import android.os.Bundle;
|
|||||||
public class SftpStorage extends JavaFileStorageBase {
|
public class SftpStorage extends JavaFileStorageBase {
|
||||||
|
|
||||||
public static final int DEFAULT_SFTP_PORT = 22;
|
public static final int DEFAULT_SFTP_PORT = 22;
|
||||||
|
public static final int UNSET_SFTP_CONNECT_TIMEOUT = -1;
|
||||||
|
private static final String SFTP_CONNECT_TIMEOUT_OPTION_NAME = "connectTimeout";
|
||||||
|
|
||||||
JSch jsch;
|
JSch jsch;
|
||||||
|
|
||||||
public class ConnectionInfo
|
public class ConnectionInfo
|
||||||
@@ -37,7 +45,33 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
public String password;
|
public String password;
|
||||||
public String localPath;
|
public String localPath;
|
||||||
public int port;
|
public int port;
|
||||||
|
public int connectTimeoutSec = UNSET_SFTP_CONNECT_TIMEOUT;
|
||||||
|
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "ConnectionInfo{host=" + host + ",port=" + port + ",user=" + username +
|
||||||
|
",pwd=<hidden>,path=" + localPath + ",connectTimeout=" + connectTimeoutSec +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasOptions() {
|
||||||
|
return connectTimeoutSec != UNSET_SFTP_CONNECT_TIMEOUT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> buildOptionMap(ConnectionInfo ci) {
|
||||||
|
return buildOptionMap(ci.connectTimeoutSec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> buildOptionMap(int connectTimeoutSec) {
|
||||||
|
Map<String, String> opts = new HashMap<>();
|
||||||
|
if (connectTimeoutSec != UNSET_SFTP_CONNECT_TIMEOUT) {
|
||||||
|
opts.put(SFTP_CONNECT_TIMEOUT_OPTION_NAME, String.valueOf(connectTimeoutSec));
|
||||||
|
}
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Context _appContext;
|
Context _appContext;
|
||||||
|
|
||||||
public SftpStorage(Context appContext) {
|
public SftpStorage(Context appContext) {
|
||||||
@@ -64,15 +98,15 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openFileForRead(String path) throws Exception {
|
public InputStream openFileForRead(String path) throws Exception {
|
||||||
|
ConnectionInfo cInfo = splitStringToConnectionInfo(path);
|
||||||
ChannelSftp c = init(path);
|
ChannelSftp c = init(cInfo);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] buff = new byte[8000];
|
byte[] buff = new byte[8000];
|
||||||
|
|
||||||
int bytesRead = 0;
|
int bytesRead = 0;
|
||||||
|
|
||||||
InputStream in = c.get(extractSessionPath(path));
|
InputStream in = c.get(cInfo.localPath);
|
||||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||||
|
|
||||||
while ((bytesRead = in.read(buff)) != -1) {
|
while ((bytesRead = in.read(buff)) != -1) {
|
||||||
@@ -104,14 +138,15 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
public void uploadFile(String path, byte[] data, boolean writeTransactional)
|
public void uploadFile(String path, byte[] data, boolean writeTransactional)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
ChannelSftp c = init(path);
|
ConnectionInfo cInfo = splitStringToConnectionInfo(path);
|
||||||
|
ChannelSftp c = init(cInfo);
|
||||||
try {
|
try {
|
||||||
InputStream in = new ByteArrayInputStream(data);
|
InputStream in = new ByteArrayInputStream(data);
|
||||||
String targetPath = extractSessionPath(path);
|
String targetPath = cInfo.localPath;
|
||||||
if (writeTransactional)
|
if (writeTransactional)
|
||||||
{
|
{
|
||||||
//upload to temporary location:
|
//upload to temporary location:
|
||||||
String tmpPath = targetPath+".tmp";
|
String tmpPath = targetPath + ".tmp";
|
||||||
c.put(in, tmpPath);
|
c.put(in, tmpPath);
|
||||||
//remove previous file:
|
//remove previous file:
|
||||||
try
|
try
|
||||||
@@ -127,9 +162,9 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
c.put(in, targetPath);
|
c.put(in, targetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
tryDisconnect(c);
|
tryDisconnect(c);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
tryDisconnect(c);
|
tryDisconnect(c);
|
||||||
@@ -141,53 +176,95 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
@Override
|
@Override
|
||||||
public String createFolder(String parentPath, String newDirName)
|
public String createFolder(String parentPath, String newDirName)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
ConnectionInfo cInfo = splitStringToConnectionInfo(parentPath);
|
||||||
try {
|
try {
|
||||||
ChannelSftp c = init(parentPath);
|
ChannelSftp c = init(cInfo);
|
||||||
String newPath = concatPaths(parentPath, newDirName);
|
String newPath = concatPaths(cInfo.localPath, newDirName);
|
||||||
c.mkdir(extractSessionPath(newPath));
|
c.mkdir(newPath);
|
||||||
tryDisconnect(c);
|
tryDisconnect(c);
|
||||||
return newPath;
|
|
||||||
|
return buildFullPath(cInfo.host, cInfo.port, newPath,
|
||||||
|
cInfo.username, cInfo.password, cInfo.connectTimeoutSec);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw convertException(e);
|
throw convertException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String extractUserPwdHostPort(String path) {
|
||||||
|
String withoutProtocol = path
|
||||||
|
.substring(getProtocolPrefix().length());
|
||||||
|
return withoutProtocol.substring(0, withoutProtocol.indexOf("/"));
|
||||||
|
}
|
||||||
|
|
||||||
private String extractSessionPath(String newPath) {
|
private String extractSessionPath(String newPath) {
|
||||||
String withoutProtocol = newPath
|
String withoutProtocol = newPath
|
||||||
.substring(getProtocolPrefix().length());
|
.substring(getProtocolPrefix().length());
|
||||||
return withoutProtocol.substring(withoutProtocol.indexOf("/"));
|
int pathStartIdx = withoutProtocol.indexOf("/");
|
||||||
|
int pathEndIdx = withoutProtocol.indexOf("?");
|
||||||
|
if (pathEndIdx < 0) {
|
||||||
|
pathEndIdx = withoutProtocol.length();
|
||||||
|
}
|
||||||
|
return withoutProtocol.substring(pathStartIdx, pathEndIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractUserPwdHost(String path) {
|
private Map<String, String> extractOptionsMap(String path) throws UnsupportedEncodingException {
|
||||||
String withoutProtocol = path
|
String withoutProtocol = path
|
||||||
.substring(getProtocolPrefix().length());
|
.substring(getProtocolPrefix().length());
|
||||||
return withoutProtocol.substring(0,withoutProtocol.indexOf("/"));
|
|
||||||
|
Map<String, String> options = new HashMap<>();
|
||||||
|
|
||||||
|
int extraOptsIdx = withoutProtocol.indexOf("?");
|
||||||
|
if (extraOptsIdx > 0 && extraOptsIdx + 1 < withoutProtocol.length()) {
|
||||||
|
String optsString = withoutProtocol.substring(extraOptsIdx + 1);
|
||||||
|
String[] parts = optsString.split("&");
|
||||||
|
for (String p : parts) {
|
||||||
|
int sepIdx = p.indexOf('=');
|
||||||
|
if (sepIdx > 0) {
|
||||||
|
String key = decode(p.substring(0, sepIdx));
|
||||||
|
String value = decode(p.substring(sepIdx + 1));
|
||||||
|
options.put(key, value);
|
||||||
|
} else {
|
||||||
|
options.put(decode(p), "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String concatPaths(String parentPath, String newDirName) {
|
private String concatPaths(String parentPath, String newDirName) {
|
||||||
String res = parentPath;
|
StringBuilder fp = new StringBuilder(parentPath);
|
||||||
if (!res.endsWith("/"))
|
if (!parentPath.endsWith("/"))
|
||||||
res += "/";
|
fp.append("/");
|
||||||
res += newDirName;
|
return fp.append(newDirName).toString();
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String createFilePath(String parentPath, String newFileName)
|
public String createFilePath(final String parentUri, String newFileName)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
if (parentPath.endsWith("/") == false)
|
|
||||||
parentPath += "/";
|
String parentPath = parentUri;
|
||||||
return parentPath + newFileName;
|
String params = null;
|
||||||
|
int paramsIdx = parentUri.lastIndexOf("?");
|
||||||
|
if (paramsIdx > 0) {
|
||||||
|
params = parentUri.substring(paramsIdx);
|
||||||
|
parentPath = parentPath.substring(0, paramsIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newPath = concatPaths(parentPath, newFileName);
|
||||||
|
|
||||||
|
if (params != null) {
|
||||||
|
newPath += params;
|
||||||
|
}
|
||||||
|
return newPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FileEntry> listFiles(String parentPath) throws Exception {
|
public List<FileEntry> listFiles(String parentPath) throws Exception {
|
||||||
|
ConnectionInfo cInfo = splitStringToConnectionInfo(parentPath);
|
||||||
|
ChannelSftp c = init(cInfo);
|
||||||
|
|
||||||
ChannelSftp c = init(parentPath);
|
|
||||||
return listFiles(parentPath, c);
|
return listFiles(parentPath, c);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFromAttrs(FileEntry fileEntry, SftpATTRS attrs) {
|
private void setFromAttrs(FileEntry fileEntry, SftpATTRS attrs) {
|
||||||
@@ -211,23 +288,27 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
if (sftpEx.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
|
if (sftpEx.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
|
||||||
return new FileNotFoundException(sftpEx.getMessage());
|
return new FileNotFoundException(sftpEx.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileEntry getFileEntry(String filename) throws Exception {
|
public FileEntry getFileEntry(String filename) throws Exception {
|
||||||
|
ConnectionInfo cInfo = splitStringToConnectionInfo(filename);
|
||||||
ChannelSftp c = init(filename);
|
ChannelSftp c = init(cInfo);
|
||||||
try {
|
try {
|
||||||
FileEntry fileEntry = new FileEntry();
|
FileEntry fileEntry = new FileEntry();
|
||||||
String sessionPath = extractSessionPath(filename);
|
SftpATTRS attr = c.stat(cInfo.localPath);
|
||||||
SftpATTRS attr = c.stat(sessionPath);
|
|
||||||
setFromAttrs(fileEntry, attr);
|
setFromAttrs(fileEntry, attr);
|
||||||
|
|
||||||
|
// Full URI
|
||||||
fileEntry.path = filename;
|
fileEntry.path = filename;
|
||||||
fileEntry.displayName = getFilename(sessionPath);
|
|
||||||
|
fileEntry.displayName = getFilename(cInfo.localPath);
|
||||||
|
|
||||||
tryDisconnect(c);
|
tryDisconnect(c);
|
||||||
|
|
||||||
return fileEntry;
|
return fileEntry;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logDebug("Exception in getFileEntry! " + e);
|
logDebug("Exception in getFileEntry! " + e);
|
||||||
@@ -238,8 +319,9 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(String path) throws Exception {
|
public void delete(String path) throws Exception {
|
||||||
|
ConnectionInfo cInfo = splitStringToConnectionInfo(path);
|
||||||
|
ChannelSftp c = init(cInfo);
|
||||||
|
|
||||||
ChannelSftp c = init(path);
|
|
||||||
delete(path, c);
|
delete(path, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,10 +345,11 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
tryDisconnect(c);
|
tryDisconnect(c);
|
||||||
throw convertException(e);
|
throw convertException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<FileEntry> listFiles(String path, ChannelSftp c) throws Exception {
|
private List<FileEntry> listFiles(String path, ChannelSftp c) throws Exception {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<FileEntry> res = new ArrayList<FileEntry>();
|
List<FileEntry> res = new ArrayList<FileEntry>();
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@@ -282,7 +365,7 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
||(lsEntry.getFilename().equals(".."))
|
||(lsEntry.getFilename().equals(".."))
|
||||||
)
|
)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FileEntry fileEntry = new FileEntry();
|
FileEntry fileEntry = new FileEntry();
|
||||||
fileEntry.displayName = lsEntry.getFilename();
|
fileEntry.displayName = lsEntry.getFilename();
|
||||||
fileEntry.path = createFilePath(path, fileEntry.displayName);
|
fileEntry.path = createFilePath(path, fileEntry.displayName);
|
||||||
@@ -312,16 +395,15 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
throws UnsupportedEncodingException {
|
throws UnsupportedEncodingException {
|
||||||
return java.net.URLDecoder.decode(encodedString, UTF_8);
|
return java.net.URLDecoder.decode(encodedString, UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encode(final String unencoded)
|
protected String encode(final String unencoded)
|
||||||
throws UnsupportedEncodingException {
|
throws UnsupportedEncodingException {
|
||||||
return java.net.URLEncoder.encode(unencoded, UTF_8);
|
return java.net.URLEncoder.encode(unencoded, UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelSftp init(String filename) throws JSchException, UnsupportedEncodingException {
|
ChannelSftp init(ConnectionInfo cInfo) throws JSchException, UnsupportedEncodingException {
|
||||||
jsch = new JSch();
|
jsch = new JSch();
|
||||||
ConnectionInfo ci = splitStringToConnectionInfo(filename);
|
|
||||||
|
|
||||||
String base_dir = getBaseDir();
|
String base_dir = getBaseDir();
|
||||||
jsch.setKnownHosts(base_dir + "/known_hosts");
|
jsch.setKnownHosts(base_dir + "/known_hosts");
|
||||||
@@ -340,13 +422,13 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Session session = jsch.getSession(ci.username, ci.host, ci.port);
|
Session session = jsch.getSession(cInfo.username, cInfo.host, cInfo.port);
|
||||||
UserInfo ui = new SftpUserInfo(ci.password,_appContext);
|
UserInfo ui = new SftpUserInfo(cInfo.password, _appContext);
|
||||||
session.setUserInfo(ui);
|
session.setUserInfo(ui);
|
||||||
|
|
||||||
session.setConfig("PreferredAuthentications", "publickey,password");
|
session.setConfig("PreferredAuthentications", "publickey,password");
|
||||||
|
|
||||||
session.connect();
|
sessionConnect(session, cInfo);
|
||||||
|
|
||||||
Channel channel = session.openChannel("sftp");
|
Channel channel = session.openChannel("sftp");
|
||||||
channel.connect();
|
channel.connect();
|
||||||
@@ -357,6 +439,14 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sessionConnect(Session session, ConnectionInfo ci) throws JSchException {
|
||||||
|
if (ci.connectTimeoutSec != UNSET_SFTP_CONNECT_TIMEOUT) {
|
||||||
|
session.connect(ci.connectTimeoutSec * 1000);
|
||||||
|
} else {
|
||||||
|
session.connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getBaseDir() {
|
private String getBaseDir() {
|
||||||
return _appContext.getFilesDir().getAbsolutePath();
|
return _appContext.getFilesDir().getAbsolutePath();
|
||||||
}
|
}
|
||||||
@@ -389,7 +479,7 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
public ConnectionInfo splitStringToConnectionInfo(String filename)
|
public ConnectionInfo splitStringToConnectionInfo(String filename)
|
||||||
throws UnsupportedEncodingException {
|
throws UnsupportedEncodingException {
|
||||||
ConnectionInfo ci = new ConnectionInfo();
|
ConnectionInfo ci = new ConnectionInfo();
|
||||||
ci.host = extractUserPwdHost(filename);
|
ci.host = extractUserPwdHostPort(filename);
|
||||||
String userPwd = ci.host.substring(0, ci.host.indexOf('@'));
|
String userPwd = ci.host.substring(0, ci.host.indexOf('@'));
|
||||||
ci.username = decode(userPwd.substring(0, userPwd.indexOf(":")));
|
ci.username = decode(userPwd.substring(0, userPwd.indexOf(":")));
|
||||||
ci.password = decode(userPwd.substring(userPwd.indexOf(":")+1));
|
ci.password = decode(userPwd.substring(userPwd.indexOf(":")+1));
|
||||||
@@ -398,10 +488,21 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
int portSeparatorIndex = ci.host.indexOf(":");
|
int portSeparatorIndex = ci.host.indexOf(":");
|
||||||
if (portSeparatorIndex >= 0)
|
if (portSeparatorIndex >= 0)
|
||||||
{
|
{
|
||||||
ci.port = Integer.parseInt(ci.host.substring(portSeparatorIndex+1));
|
ci.port = Integer.parseInt(ci.host.substring(portSeparatorIndex + 1));
|
||||||
ci.host = ci.host.substring(0, portSeparatorIndex);
|
ci.host = ci.host.substring(0, portSeparatorIndex);
|
||||||
}
|
}
|
||||||
ci.localPath = extractSessionPath(filename);
|
ci.localPath = extractSessionPath(filename);
|
||||||
|
|
||||||
|
Map<String, String> options = extractOptionsMap(filename);
|
||||||
|
|
||||||
|
if (options.containsKey(SFTP_CONNECT_TIMEOUT_OPTION_NAME)) {
|
||||||
|
String optVal = options.get(SFTP_CONNECT_TIMEOUT_OPTION_NAME);
|
||||||
|
try {
|
||||||
|
ci.connectTimeoutSec = Integer.parseInt(optVal);
|
||||||
|
} catch (NumberFormatException nan) {
|
||||||
|
logDebug(SFTP_CONNECT_TIMEOUT_OPTION_NAME + " option not a number: " + optVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
return ci;
|
return ci;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,12 +539,20 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ConnectionInfo ci = splitStringToConnectionInfo(path);
|
ConnectionInfo ci = splitStringToConnectionInfo(path);
|
||||||
return getProtocolPrefix()+ci.username+"@"+ci.host+ci.localPath;
|
StringBuilder dName = new StringBuilder(getProtocolPrefix())
|
||||||
|
.append(ci.username)
|
||||||
|
.append("@")
|
||||||
|
.append(ci.host)
|
||||||
|
.append(ci.localPath);
|
||||||
|
if (ci.hasOptions()) {
|
||||||
|
appendOptions(dName, buildOptionMap(ci));
|
||||||
|
}
|
||||||
|
return dName.toString();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return extractSessionPath(path);
|
return extractSessionPath(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -465,22 +574,65 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
@Override
|
@Override
|
||||||
public void onActivityResult(FileStorageSetupActivity activity,
|
public void onActivityResult(FileStorageSetupActivity activity,
|
||||||
int requestCode, int resultCode, Intent data) {
|
int requestCode, int resultCode, Intent data) {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String buildFullPath( String host, int port, String localPath, String username, String password) throws UnsupportedEncodingException
|
public String buildFullPath(String host, int port, String localPath,
|
||||||
{
|
String username, String password,
|
||||||
if (port != DEFAULT_SFTP_PORT)
|
int connectTimeoutSec)
|
||||||
host += ":"+String.valueOf(port);
|
throws UnsupportedEncodingException {
|
||||||
return getProtocolPrefix()+encode(username)+":"+encode(password)+"@"+host+localPath;
|
StringBuilder uri = new StringBuilder(getProtocolPrefix())
|
||||||
|
.append(encode(username)).append(":").append(encode(password))
|
||||||
|
.append("@").append(host);
|
||||||
|
|
||||||
|
if (port != DEFAULT_SFTP_PORT) {
|
||||||
|
uri.append(":").append(port);
|
||||||
|
}
|
||||||
|
if (localPath != null && localPath.startsWith("/")) {
|
||||||
|
uri.append(localPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> opts = buildOptionMap(connectTimeoutSec);
|
||||||
|
appendOptions(uri, opts);
|
||||||
|
|
||||||
|
return uri.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendOptions(StringBuilder uri, Map<String, String> opts)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
|
||||||
|
boolean first = true;
|
||||||
|
// Sort for stability/consistency
|
||||||
|
Set<Map.Entry<String, String>> sortedEntries = new TreeSet<>(new EntryComparator<>());
|
||||||
|
sortedEntries.addAll(opts.entrySet());
|
||||||
|
for (Map.Entry<String, String> me : sortedEntries) {
|
||||||
|
if (first) {
|
||||||
|
uri.append("?");
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
uri.append("&");
|
||||||
|
}
|
||||||
|
uri.append(encode(me.getKey())).append("=").append(encode(me.getValue()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareFileUsage(Context appContext, String path) {
|
public void prepareFileUsage(Context appContext, String path) {
|
||||||
//nothing to do
|
//nothing to do
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A comparator that compares Map.Entry objects by their keys, via natural ordering.
|
||||||
|
*
|
||||||
|
* @param <T> the Map.Entry key type, that must implement Comparable.
|
||||||
|
*/
|
||||||
|
private static class EntryComparator<T extends Comparable<T>> implements Comparator<Map.Entry<T, ?>> {
|
||||||
|
@Override
|
||||||
|
public int compare(Map.Entry<T, ?> o1, Map.Entry<T, ?> o2) {
|
||||||
|
return o1.getKey().compareTo(o2.getKey());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -538,12 +538,12 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
|||||||
}
|
}
|
||||||
|
|
||||||
static JavaFileStorage createStorageToTest(Context ctx, Context appContext, boolean simulateRestart) {
|
static JavaFileStorage createStorageToTest(Context ctx, Context appContext, boolean simulateRestart) {
|
||||||
//storageToTest = new SftpStorage(ctx.getApplicationContext());
|
storageToTest = new SftpStorage(ctx.getApplicationContext());
|
||||||
//storageToTest = new PCloudFileStorage(ctx, "yCeH59Ffgtm");
|
//storageToTest = new PCloudFileStorage(ctx, "yCeH59Ffgtm");
|
||||||
//storageToTest = new SkyDriveFileStorage("000000004010C234", appContext);
|
//storageToTest = new SkyDriveFileStorage("000000004010C234", appContext);
|
||||||
|
|
||||||
|
|
||||||
storageToTest = new GoogleDriveAppDataFileStorage();
|
//storageToTest = new GoogleDriveAppDataFileStorage();
|
||||||
/*storageToTest = new WebDavStorage(new ICertificateErrorHandler() {
|
/*storageToTest = new WebDavStorage(new ICertificateErrorHandler() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onValidationError(String error) {
|
public boolean onValidationError(String error) {
|
||||||
@@ -743,7 +743,19 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
|||||||
int port = Integer.parseInt(etPort.getText().toString());
|
int port = Integer.parseInt(etPort.getText().toString());
|
||||||
EditText etInitDir = ((EditText)view.findViewById(R.id.sftp_initial_dir));
|
EditText etInitDir = ((EditText)view.findViewById(R.id.sftp_initial_dir));
|
||||||
String initialDir = etInitDir.getText().toString();
|
String initialDir = etInitDir.getText().toString();
|
||||||
onReceivePathForFileSelect(requestCode, sftpStorage.buildFullPath( host, port, initialDir, user, pwd));
|
EditText etConnectTimeout = ((EditText)view.findViewById(R.id.sftp_connect_timeout));
|
||||||
|
int connectTimeout = SftpStorage.UNSET_SFTP_CONNECT_TIMEOUT;
|
||||||
|
String ctStr = etConnectTimeout.getText().toString();
|
||||||
|
if (!ctStr.isEmpty()) {
|
||||||
|
try {
|
||||||
|
int ct = Integer.parseInt(ctStr);
|
||||||
|
if (connectTimeout != ct) {
|
||||||
|
connectTimeout = ct;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException parseEx) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onReceivePathForFileSelect(requestCode, sftpStorage.buildFullPath( host, port, initialDir, user, pwd, connectTimeout));
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
android:id="@+id/sftp_host"
|
android:id="@+id/sftp_host"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:inputType="textNoSuggestions"
|
android:inputType="textNoSuggestions"
|
||||||
android:text=""
|
android:text=""
|
||||||
@@ -26,9 +27,10 @@
|
|||||||
android:id="@+id/sftp_port"
|
android:id="@+id/sftp_port"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="4"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:text="22"
|
android:text="22"
|
||||||
android:hint="@string/hint_sftp_port" />
|
android:hint="@string/hint_sftp_port" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<EditText
|
<EditText
|
||||||
@@ -63,6 +65,14 @@
|
|||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="/home/philipp"
|
android:text="/home/philipp"
|
||||||
/>
|
/>
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/sftp_connect_timeout"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:inputType="number"
|
||||||
|
android:text=""
|
||||||
|
android:hint="@string/hint_sftp_connect_timeout" />
|
||||||
<Button android:id="@+id/send_public_key"
|
<Button android:id="@+id/send_public_key"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -333,6 +333,7 @@
|
|||||||
|
|
||||||
<string name="hint_sftp_host">host</string>
|
<string name="hint_sftp_host">host</string>
|
||||||
<string name="hint_sftp_port">port</string>
|
<string name="hint_sftp_port">port</string>
|
||||||
|
<string name="hint_sftp_connect_timeout">connection timeout seconds</string>
|
||||||
|
|
||||||
<string name="select_storage_type">Select the storage type:</string>
|
<string name="select_storage_type">Select the storage type:</string>
|
||||||
|
|
||||||
|
|||||||
@@ -221,7 +221,6 @@ public class FileChooserActivity extends FragmentActivity {
|
|||||||
|
|
||||||
public static final String EXTRA_RESULT_FILE_EXISTS = CLASSNAME + ".result_file_exists";
|
public static final String EXTRA_RESULT_FILE_EXISTS = CLASSNAME + ".result_file_exists";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CONTROLS
|
* CONTROLS
|
||||||
|
|||||||
@@ -269,7 +269,6 @@ public class FragmentFiles extends Fragment implements
|
|||||||
FileChooserActivity.EXTRA_MAX_FILE_COUNT, 1000);
|
FileChooserActivity.EXTRA_MAX_FILE_COUNT, 1000);
|
||||||
mFileAdapter = new BaseFileAdapter(getActivity(), mFilterMode,
|
mFileAdapter = new BaseFileAdapter(getActivity(), mFilterMode,
|
||||||
mIsMultiSelection);
|
mIsMultiSelection);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* History.
|
* History.
|
||||||
@@ -2268,12 +2267,15 @@ public class FragmentFiles extends Fragment implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mIsSaveDialog) {
|
if (mIsSaveDialog) {
|
||||||
mTextSaveas.setText(BaseFileProviderUtils.getFileName(cursor));
|
String fileName = BaseFileProviderUtils.getFileName(cursor);
|
||||||
|
Uri uri = BaseFileProviderUtils.getUri(cursor);
|
||||||
|
|
||||||
|
mTextSaveas.setText(fileName);
|
||||||
/*
|
/*
|
||||||
* Always set tag after setting text, or tag will be reset to
|
* Always set tag after setting text, or tag will be reset to
|
||||||
* null.
|
* null.
|
||||||
*/
|
*/
|
||||||
mTextSaveas.setTag(BaseFileProviderUtils.getUri(cursor));
|
mTextSaveas.setTag(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDoubleTapToChooseFiles) {
|
if (mDoubleTapToChooseFiles) {
|
||||||
@@ -2286,10 +2288,12 @@ public class FragmentFiles extends Fragment implements
|
|||||||
if (mIsMultiSelection)
|
if (mIsMultiSelection)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mIsSaveDialog)
|
if (mIsSaveDialog) {
|
||||||
checkSaveasFilenameAndFinish();
|
checkSaveasFilenameAndFinish();
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
finish(BaseFileProviderUtils.getUri(cursor));
|
finish(BaseFileProviderUtils.getUri(cursor));
|
||||||
|
}
|
||||||
}// single tap to choose files
|
}// single tap to choose files
|
||||||
}// onItemClick()
|
}// onItemClick()
|
||||||
};// mViewFilesOnItemClickListener
|
};// mViewFilesOnItemClickListener
|
||||||
|
|||||||
@@ -15,4 +15,24 @@ public class FileEntry {
|
|||||||
isDirectory = false;
|
isDirectory = false;
|
||||||
canRead = canWrite = true;
|
canRead = canWrite = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder s = new StringBuilder("kp2afilechooser.FileEntry{")
|
||||||
|
.append(displayName).append("|")
|
||||||
|
.append("path=").append(path).append(",sz=").append(sizeInBytes)
|
||||||
|
.append(",").append(isDirectory ? "dir" : "file")
|
||||||
|
.append(",lastMod=").append(lastModifiedTime);
|
||||||
|
|
||||||
|
StringBuilder perms = new StringBuilder();
|
||||||
|
if (canRead)
|
||||||
|
perms.append("r");
|
||||||
|
if (canWrite)
|
||||||
|
perms.append("w");
|
||||||
|
if (perms.length() > 0) {
|
||||||
|
s.append(",").append(perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.append("}").toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public class Kp2aFileChooserBridge {
|
|||||||
.buildUpon()
|
.buildUpon()
|
||||||
.appendPath(defaultPath)
|
.appendPath(defaultPath)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -306,10 +306,9 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
|
|||||||
String parentPath = getParentPath(path);
|
String parentPath = getParentPath(path);
|
||||||
|
|
||||||
|
|
||||||
if (parentPath == null)
|
if (parentPath == null) {
|
||||||
{
|
|
||||||
if (Utils.doLog())
|
if (Utils.doLog())
|
||||||
Log.d(CLASSNAME, "parent file is null");
|
Log.d(CLASSNAME, "parent file is null");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
FileEntry e;
|
FileEntry e;
|
||||||
@@ -501,10 +500,10 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
|
|||||||
RowBuilder newRow = matrixCursor.newRow();
|
RowBuilder newRow = matrixCursor.newRow();
|
||||||
newRow.add(id);// _ID
|
newRow.add(id);// _ID
|
||||||
newRow.add(BaseFile
|
newRow.add(BaseFile
|
||||||
.genContentIdUriBase(
|
.genContentIdUriBase(
|
||||||
getAuthority())
|
getAuthority())
|
||||||
.buildUpon().appendPath(f.path)
|
.buildUpon().appendPath(f.path)
|
||||||
.build().toString());
|
.build().toString());
|
||||||
newRow.add(f.path);
|
newRow.add(f.path);
|
||||||
if (f.displayName == null)
|
if (f.displayName == null)
|
||||||
Log.w("KP2AJ", "displayName is null for " + f.path);
|
Log.w("KP2AJ", "displayName is null for " + f.path);
|
||||||
@@ -549,7 +548,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
|
|||||||
//puts the file entry in the cache for later reuse with retrieveFileInfo
|
//puts the file entry in the cache for later reuse with retrieveFileInfo
|
||||||
private void updateFileEntryCache(FileEntry f) {
|
private void updateFileEntryCache(FileEntry f) {
|
||||||
if (f != null)
|
if (f != null)
|
||||||
fileEntryMap.put(f.path, f);
|
fileEntryMap.put(f.path, f);
|
||||||
}
|
}
|
||||||
//removes the file entry from the cache (if cached). Should be called whenever the file changes
|
//removes the file entry from the cache (if cached). Should be called whenever the file changes
|
||||||
private void removeFromCache(String filename, boolean recursive) {
|
private void removeFromCache(String filename, boolean recursive) {
|
||||||
@@ -584,7 +583,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
|
|||||||
|
|
||||||
//returns the file entry from the cache if present or queries the concrete provider method to return the file info
|
//returns the file entry from the cache if present or queries the concrete provider method to return the file info
|
||||||
private FileEntry getFileEntryCached(String filename) {
|
private FileEntry getFileEntryCached(String filename) {
|
||||||
//check if enry is cached:
|
//check if entry is cached:
|
||||||
FileEntry cachedEntry = fileEntryMap.get(filename);
|
FileEntry cachedEntry = fileEntryMap.get(filename);
|
||||||
if (cachedEntry != null)
|
if (cachedEntry != null)
|
||||||
{
|
{
|
||||||
@@ -728,7 +727,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
|
|||||||
if (targetParent != null && targetParent.startsWith(source))
|
if (targetParent != null && targetParent.startsWith(source))
|
||||||
{
|
{
|
||||||
if (Utils.doLog())
|
if (Utils.doLog())
|
||||||
Log.d("KP2A_FC_P", source+" is parent of "+target);
|
Log.d("KP2A_FC_P", source + " is parent of " + target);
|
||||||
return BaseFileProviderUtils.newClosedCursor();
|
return BaseFileProviderUtils.newClosedCursor();
|
||||||
}
|
}
|
||||||
if (Utils.doLog())
|
if (Utils.doLog())
|
||||||
@@ -768,28 +767,37 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
|
|||||||
|
|
||||||
private String getParentPath(String path)
|
private String getParentPath(String path)
|
||||||
{
|
{
|
||||||
path = removeTrailingSlash(path);
|
String params = null;
|
||||||
if (path.indexOf("://") == -1)
|
int paramsIdx = path.lastIndexOf("?");
|
||||||
{
|
if (paramsIdx > 0) {
|
||||||
Log.d("KP2A_FC_P", "invalid path: " + path);
|
params = path.substring(paramsIdx);
|
||||||
return null;
|
path = path.substring(0, paramsIdx);
|
||||||
}
|
}
|
||||||
String pathWithoutProtocol = path.substring(path.indexOf("://")+3);
|
|
||||||
int lastSlashPos = path.lastIndexOf("/");
|
path = removeTrailingSlash(path);
|
||||||
if (pathWithoutProtocol.indexOf("/") == -1)
|
if (path.indexOf("://") == -1)
|
||||||
{
|
{
|
||||||
Log.d("KP2A_FC_P", "parent of " + path +" is null");
|
Log.d("KP2A_FC_P", "invalid path: " + path);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else
|
String pathWithoutProtocol = path.substring(path.indexOf("://") + 3);
|
||||||
{
|
int lastSlashPos = path.lastIndexOf("/");
|
||||||
String parent = path.substring(0, lastSlashPos)+"/";
|
if (pathWithoutProtocol.indexOf("/") == -1)
|
||||||
Log.d("KP2A_FC_P", "parent of " + path +" is "+parent);
|
{
|
||||||
return parent;
|
Log.d("KP2A_FC_P", "parent of " + path + " is null");
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String parent = path.substring(0, lastSlashPos) + "/";
|
||||||
|
if (params != null) {
|
||||||
|
parent += params;
|
||||||
|
}
|
||||||
|
Log.d("KP2A_FC_P", "parent of " + path +" is " + parent);
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected abstract FileEntry getFileEntry(String path, StringBuilder errorMessageBuilder) throws Exception;
|
protected abstract FileEntry getFileEntry(String path, StringBuilder errorMessageBuilder) throws Exception;
|
||||||
|
|
||||||
|
|||||||
@@ -90,13 +90,17 @@ namespace keepass2android
|
|||||||
|
|
||||||
if (!defaultPath.EndsWith(_schemeSeparator))
|
if (!defaultPath.EndsWith(_schemeSeparator))
|
||||||
{
|
{
|
||||||
var fileStorage = new Keepass2android.Javafilestorage.SftpStorage(activity.ApplicationContext);
|
var fileStorage = new SftpStorage(activity.ApplicationContext);
|
||||||
SftpStorage.ConnectionInfo ci = fileStorage.SplitStringToConnectionInfo(defaultPath);
|
SftpStorage.ConnectionInfo ci = fileStorage.SplitStringToConnectionInfo(defaultPath);
|
||||||
dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text = ci.Host;
|
dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text = ci.Host;
|
||||||
dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text = ci.Port.ToString();
|
dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text = ci.Port.ToString();
|
||||||
dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text = ci.Username;
|
dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text = ci.Username;
|
||||||
dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Text = ci.Password;
|
dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Text = ci.Password;
|
||||||
dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text = ci.LocalPath;
|
dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text = ci.LocalPath;
|
||||||
|
if (ci.ConnectTimeoutSec != SftpStorage.UnsetSftpConnectTimeout)
|
||||||
|
{
|
||||||
|
dlgContents.FindViewById<EditText>(Resource.Id.sftp_connect_timeout).Text = ci.ConnectTimeoutSec.ToString();
|
||||||
|
}
|
||||||
if (string.IsNullOrEmpty(ci.Password))
|
if (string.IsNullOrEmpty(ci.Password))
|
||||||
{
|
{
|
||||||
spinner.SetSelection(1);
|
spinner.SetSelection(1);
|
||||||
@@ -109,7 +113,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
string host = dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text;
|
string host = dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text;
|
||||||
string portText = dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text;
|
string portText = dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text;
|
||||||
int port = Keepass2android.Javafilestorage.SftpStorage.DefaultSftpPort;
|
int port = SftpStorage.DefaultSftpPort;
|
||||||
if (!string.IsNullOrEmpty(portText))
|
if (!string.IsNullOrEmpty(portText))
|
||||||
int.TryParse(portText, out port);
|
int.TryParse(portText, out port);
|
||||||
string user = dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text;
|
string user = dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text;
|
||||||
@@ -117,8 +121,14 @@ namespace keepass2android
|
|||||||
string initialPath = dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text;
|
string initialPath = dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text;
|
||||||
if (string.IsNullOrEmpty(initialPath))
|
if (string.IsNullOrEmpty(initialPath))
|
||||||
initialPath = "/";
|
initialPath = "/";
|
||||||
string sftpPath = new Keepass2android.Javafilestorage.SftpStorage(activity.ApplicationContext).BuildFullPath(host, port, initialPath, user,
|
string connectTimeoutText = dlgContents.FindViewById<EditText>(Resource.Id.sftp_connect_timeout).Text;
|
||||||
password);
|
int connectTimeout = SftpStorage.UnsetSftpConnectTimeout;
|
||||||
|
if (!string.IsNullOrEmpty(connectTimeoutText)) {
|
||||||
|
int.TryParse(connectTimeoutText, out connectTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
string sftpPath = new SftpStorage(activity.ApplicationContext)
|
||||||
|
.BuildFullPath(host, port, initialPath, user, password, connectTimeout);
|
||||||
onStartBrowse(sftpPath);
|
onStartBrowse(sftpPath);
|
||||||
});
|
});
|
||||||
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());
|
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());
|
||||||
|
|||||||
@@ -88,6 +88,18 @@
|
|||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="/"
|
android:text="/"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/connect_timeout"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="4dip"
|
||||||
|
android:layout_marginTop="4dip"
|
||||||
|
android:text="@string/connect_timeout" />
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/sftp_connect_timeout"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:inputType="number" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -586,6 +586,7 @@
|
|||||||
<string name="hint_sftp_host">host (ex: 192.168.0.1)</string>
|
<string name="hint_sftp_host">host (ex: 192.168.0.1)</string>
|
||||||
<string name="hint_sftp_port">port</string>
|
<string name="hint_sftp_port">port</string>
|
||||||
<string name="initial_directory">Initial directory (optional):</string>
|
<string name="initial_directory">Initial directory (optional):</string>
|
||||||
|
<string name="connect_timeout">Connection timeout seconds (optional):"</string>
|
||||||
<string name="enter_sftp_login_title">Enter SFTP login data:</string>
|
<string name="enter_sftp_login_title">Enter SFTP login data:</string>
|
||||||
<string name="sftp_auth_mode">Authentication mode</string>
|
<string name="sftp_auth_mode">Authentication mode</string>
|
||||||
<string name="send_public_key">Send public key...</string>
|
<string name="send_public_key">Send public key...</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user