add missing File NetFtpFileStorage.cs, added HttpClient based Http implementation

This commit is contained in:
Philipp Crocoll 2016-03-02 05:29:42 +01:00
parent 0e40033d85
commit 497be64633
9 changed files with 758 additions and 2 deletions

View File

@ -51,6 +51,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FtpClientTest", "FtpClientT
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.FtpClient.Android", "netftpandroid\System.Net.FtpClient\System.Net.FtpClient.Android.csproj", "{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.FtpClient.Android", "netftpandroid\System.Net.FtpClient\System.Net.FtpClient.Android.csproj", "{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aUnitTests2", "Kp2aUnitTests2\Kp2aUnitTests2.csproj", "{B6062CC3-C238-40F3-B4BE-DD647300F96C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDavAndroid", "WebDavAndroid\WebDavAndroid.csproj", "{B6B866AF-D69C-4317-9838-51CF743E24B3}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -571,6 +575,48 @@ Global
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU {146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU {146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU {146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Debug|Win32.ActiveCfg = Debug|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Debug|x64.ActiveCfg = Debug|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Release|Any CPU.Build.0 = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Release|Any CPU.Deploy.0 = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Release|Win32.ActiveCfg = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.Release|x64.ActiveCfg = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{B6062CC3-C238-40F3-B4BE-DD647300F96C}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Debug|Win32.ActiveCfg = Debug|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Debug|x64.ActiveCfg = Debug|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Release|Any CPU.Build.0 = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Release|Win32.ActiveCfg = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.Release|x64.ActiveCfg = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{B6B866AF-D69C-4317-9838-51CF743E24B3}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Http;
using System.Security; using System.Security;
using Android; using Android;
using Android.App; using Android.App;
@ -12,6 +13,7 @@ using Android.OS;
using Java.IO; using Java.IO;
using KeePassLib.Serialization; using KeePassLib.Serialization;
using KeePassLib.Utility; using KeePassLib.Utility;
using ModernHttpClient;
using File = System.IO.File; using File = System.IO.File;
using FileNotFoundException = System.IO.FileNotFoundException; using FileNotFoundException = System.IO.FileNotFoundException;
using IOException = System.IO.IOException; using IOException = System.IO.IOException;

View File

@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using KeePassLib;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using ModernHttpClient;
namespace keepass2android.Io
{
public class HttpFileStorage: IFileStorage
{
public IEnumerable<string> SupportedProtocols
{
get
{
yield return "http";
yield return "https";
}
}
HttpClient GetHttpClient(IOConnectionInfo ioc)
{
var handler = new NativeMessageHandler();
//var handler = new HttpClientHandler();
if ((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
{
int backslashPos = ioc.UserName.IndexOf("\\", StringComparison.Ordinal);
if (backslashPos > 0)
{
string domain = ioc.UserName.Substring(0, backslashPos);
string user = ioc.UserName.Substring(backslashPos + 1);
handler.Credentials = new NetworkCredential(user, ioc.Password, domain);
}
else
{
handler.PreAuthenticate = true;
handler.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
}
}
return new HttpClient(handler);
}
public void Delete(IOConnectionInfo ioc)
{
GetHttpClient(ioc).DeleteAsync(ioc.Path).Result.EnsureSuccessStatusCode();
}
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
{
return false;
}
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
{
return null;
}
public Stream OpenFileForRead(IOConnectionInfo ioc)
{
return GetHttpClient(ioc).GetStreamAsync(ioc.Path).Result;
}
class UploadOnCloseMemoryStream : MemoryStream
{
IOConnectionInfo ioc;
private HttpClient client;
public UploadOnCloseMemoryStream(IOConnectionInfo _ioc, HttpClient _client)
{
this.ioc = _ioc;
this.client = _client;
}
public override void Close()
{
base.Close();
var msg = new HttpRequestMessage(HttpMethod.Put, ioc.Path);
msg.Headers.Add("Translate", "f");
msg.Content = new StreamContent(new MemoryStream(this.ToArray()));
client.SendAsync(msg).Result.EnsureSuccessStatusCode();
}
}
public class UntransactedWrite : IWriteTransaction
{
private readonly IOConnectionInfo _ioc;
private readonly HttpClient _client;
public UntransactedWrite(IOConnectionInfo ioc, HttpClient client)
{
_ioc = ioc;
_client = client;
}
public void Dispose()
{
}
public Stream OpenFile()
{
return new UploadOnCloseMemoryStream(_ioc, _client);
}
public void CommitWrite()
{
}
}
public class TransactedWrite : IWriteTransaction
{
private readonly IOConnectionInfo _ioc;
private readonly HttpFileStorage _fileStorage;
private readonly IOConnectionInfo _iocTemp;
public TransactedWrite(IOConnectionInfo ioc, HttpFileStorage fileStorage)
{
_ioc = ioc;
_iocTemp = _ioc.CloneDeep();
_iocTemp.Path += "." + new PwUuid(true).ToHexString().Substring(0, 6) + ".tmp";
_fileStorage = fileStorage;
}
public void Dispose()
{
}
public Stream OpenFile()
{
return new UploadOnCloseMemoryStream(_ioc, _fileStorage.GetHttpClient(_ioc));
}
public void CommitWrite()
{
var client = _fileStorage.GetHttpClient(_ioc);
client.DeleteAsync(_ioc.Path).Result.EnsureSuccessStatusCode();
var msg = new HttpRequestMessage(new HttpMethod("MOVE"), _iocTemp.Path);
msg.Headers.Add("Destination", _ioc.Path);
client.SendAsync(msg).Result.EnsureSuccessStatusCode();
}
}
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
{
if (useFileTransaction)
return new TransactedWrite(ioc, this);
else
return new UntransactedWrite(ioc, GetHttpClient(ioc));
}
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
{
return UrlUtil.StripExtension(
UrlUtil.GetFileName(ioc.Path));
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return ioc.CredSaveMode != IOCredSaveMode.SaveCred;
}
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
{
throw new NotImplementedException();
}
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
{
throw new NotImplementedException();
}
public FileDescription GetFileDescription(IOConnectionInfo ioc)
{
throw new NotImplementedException();
}
public bool RequiresSetup(IOConnectionInfo ioConnection)
{
return false;
}
public string IocToPath(IOConnectionInfo ioc)
{
return ioc.Path;
}
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
{
activity.PerformManualFileSelect(isForSave, requestCode, protocolId);
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
bool alwaysReturnSuccess)
{
Intent intent = new Intent();
activity.IocToIntent(intent, ioc);
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileUsagePrepared, intent);
}
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
{
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
{
}
public void OnResume(IFileStorageSetupActivity activity)
{
}
public void OnStart(IFileStorageSetupActivity activity)
{
}
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
{
}
public string GetDisplayName(IOConnectionInfo ioc)
{
return ioc.GetDisplayName();
}
public string CreateFilePath(string parent, string newFilename)
{
if (!parent.EndsWith("/"))
parent += "/";
return parent + newFilename;
}
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
{
return IoUtil.GetParentPath(ioc);
}
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
{
IOConnectionInfo res = folderPath.CloneDeep();
if (!res.Path.EndsWith("/"))
res.Path += "/";
res.Path += filename;
return res;
}
public bool IsPermanentLocation(IOConnectionInfo ioc)
{
return true;
}
public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{
return false;
}
public void ResolveAccount(IOConnectionInfo ioc)
{
}
}
}

View File

@ -0,0 +1,399 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.FtpClient;
using Android.Content;
using Android.OS;
using Android.Preferences;
using KeePassLib;
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace keepass2android.Io
{
public class NetFtpFileStorage: IFileStorage
{
struct ConnectionSettings
{
public FtpEncryptionMode EncryptionMode {get; set; }
public static ConnectionSettings FromIoc(IOConnectionInfo ioc)
{
if (ioc.Path.StartsWith(Kp2AAccountPathPrefix))
throw new InvalidOperationException("cannot extract settings from account-path");
string path = ioc.Path;
int schemeLength = path.IndexOf("://", StringComparison.Ordinal);
path = path.Substring(schemeLength + 3);
string settings = path.Substring(0, path.IndexOf("/", StringComparison.Ordinal));
return new ConnectionSettings()
{
EncryptionMode = (FtpEncryptionMode) int.Parse(settings)
};
}
}
private const string Kp2AAccountPathPrefix = "__kp2a_account__";
private readonly Context _context;
public NetFtpFileStorage(Context context)
{
_context = context;
}
public IEnumerable<string> SupportedProtocols
{
get { yield return "ftp"; }
}
public void Delete(IOConnectionInfo ioc)
{
using (FtpClient client = GetClient(ioc))
{
client.DeleteFile(IocPathToUri(ioc.Path).PathAndQuery);
}
}
internal FtpClient GetClient(IOConnectionInfo ioc, bool enableCloneClient = true)
{
FtpClient client = new FtpClient();
if ((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
client.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
else
client.Credentials = new NetworkCredential("anonymous", ""); //TODO TEST
Uri uri = IocPathToUri(ioc.Path);
client.Host = uri.Host;
if (!uri.IsDefaultPort) //TODO test
client.Port = uri.Port;
//TODO Validate
//client.ValidateCertificate += app...
// we don't need to be thread safe in a classic sense, but OpenRead and OpenWrite don't
//perform the actual stream operation so we'd need to wrap the stream (or just enable this:)
client.EnableThreadSafeDataConnections = enableCloneClient;
client.EncryptionMode = ConnectionSettings.FromIoc(ioc).EncryptionMode;
client.Connect();
return client;
}
internal Uri IocPathToUri(string path)
{
//remove addition stuff like TLS param
int schemeLength = path.IndexOf("://", StringComparison.Ordinal);
string scheme = path.Substring(0, schemeLength);
path = path.Substring(schemeLength + 3);
string settings = path.Substring(0, path.IndexOf("/", StringComparison.Ordinal));
path = path.Substring(settings.Length + 1);
return new Uri(scheme + "://" + path);
}
private string IocPathFromUri(IOConnectionInfo baseIoc, Uri uri)
{
string basePath = baseIoc.Path;
int schemeLength = basePath.IndexOf("://", StringComparison.Ordinal);
string scheme = basePath.Substring(0, schemeLength);
basePath = basePath.Substring(schemeLength + 3);
string baseSettings = basePath.Substring(0, basePath.IndexOf("/", StringComparison.Ordinal));
return scheme + "://" + baseSettings + "/" + uri.AbsolutePath; //TODO does this contain Query?
}
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
{
return false;
}
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
{
return null;
}
public Stream OpenFileForRead(IOConnectionInfo ioc)
{
using (var cl = GetClient(ioc))
{
return cl.OpenRead(IocPathToUri(ioc.Path).PathAndQuery, FtpDataType.Binary, 0);
}
}
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
{
if (useFileTransaction)
return new UntransactedWrite(ioc, this);
else
return new TransactedWrite(ioc, this);
}
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
{
//TODO does this work when flags are encoded in the iocPath?
return UrlUtil.StripExtension(
UrlUtil.GetFileName(ioc.Path));
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return ioc.CredSaveMode != IOCredSaveMode.SaveCred;
}
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
{
using (var client = GetClient(ioc))
{
client.CreateDirectory(IocPathToUri(ioc.Path).PathAndQuery);
}
}
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
{
using (var client = GetClient(ioc))
{
List<FileDescription> files = new List<FileDescription>();
foreach (FtpListItem item in client.GetListing(IocPathToUri(ioc.Path).PathAndQuery,
FtpListOption.Modify | FtpListOption.Size | FtpListOption.DerefLinks))
{
switch (item.Type)
{
case FtpFileSystemObjectType.Directory:
files.Add(new FileDescription()
{
CanRead = true,
CanWrite = true,
DisplayName = item.Name,
IsDirectory = true,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, new Uri(item.FullName))
});
break;
case FtpFileSystemObjectType.File:
files.Add(new FileDescription()
{
CanRead = true,
CanWrite = true,
DisplayName = item.Name,
IsDirectory = false,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, new Uri(item.FullName)),
SizeInBytes = item.Size
});
break;
}
}
return files;
}
}
public FileDescription GetFileDescription(IOConnectionInfo ioc)
{
//TODO when is this called?
//is it very inefficient to connect for each description?
using (FtpClient client = GetClient(ioc))
{
var uri = IocPathToUri(ioc.Path);
string path = uri.PathAndQuery;
return new FileDescription()
{
CanRead = true,
CanWrite = true,
Path = ioc.Path,
LastModified = client.GetModifiedTime(path),
SizeInBytes = client.GetFileSize(path),
DisplayName = UrlUtil.GetFileName(path),
IsDirectory = false
};
}
}
public bool RequiresSetup(IOConnectionInfo ioConnection)
{
return false;
}
public string IocToPath(IOConnectionInfo ioc)
{
return ioc.Path;
}
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
{
throw new NotImplementedException();
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
bool alwaysReturnSuccess)
{
Intent intent = new Intent();
activity.IocToIntent(intent, ioc);
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileUsagePrepared, intent);
}
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
{
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
{
}
public void OnResume(IFileStorageSetupActivity activity)
{
}
public void OnStart(IFileStorageSetupActivity activity)
{
}
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
{
}
public string GetDisplayName(IOConnectionInfo ioc)
{
var uri = IocPathToUri(ioc.Path);
return uri.ToString(); //TODO is this good?
}
public string CreateFilePath(string parent, string newFilename)
{
if (!parent.EndsWith("/"))
parent += "/";
return parent + newFilename;
}
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
{
return IoUtil.GetParentPath(ioc);
}
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
{
IOConnectionInfo res = folderPath.CloneDeep();
if (!res.Path.EndsWith("/"))
res.Path += "/";
res.Path += filename;
return res;
}
public bool IsPermanentLocation(IOConnectionInfo ioc)
{
return true;
}
public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{
return false;
}
public void ResolveAccount(IOConnectionInfo ioc)
{
string path = ioc.Path;
int schemeLength = path.IndexOf("://", StringComparison.Ordinal);
string scheme = path.Substring(0, schemeLength);
path = path.Substring(schemeLength+3);
if (path.StartsWith(Kp2AAccountPathPrefix))
{
string accountId = path.Substring(0, path.IndexOf("/", StringComparison.Ordinal));
path = path.Substring(accountId.Length + 1);
var prefs = PreferenceManager.GetDefaultSharedPreferences(_context);
string host = prefs.GetString(accountId + "_Host", null);
int port = prefs.GetInt(accountId + "_Port", 0 /* auto*/);
string initialPath = prefs.GetString(accountId + "_InitPath", "");
if (initialPath.StartsWith("/"))
initialPath = initialPath.Substring(1);
if ((!initialPath.EndsWith("/") && (initialPath != "")))
initialPath += "/";
int encMode = prefs.GetInt(accountId + "_EncMode", (int) FtpEncryptionMode.None);
string settings = encMode.ToString();
ioc.Path = scheme + "://" + settings + "/" + host + (port == 0 ? "" : (":" + port)) + "/" + initialPath + path;
}
}
public Stream OpenWrite(IOConnectionInfo ioc)
{
using (var client = GetClient(ioc))
{
return client.OpenWrite(IocPathToUri(ioc.Path).PathAndQuery);
}
}
}
public class TransactedWrite : IWriteTransaction
{
private readonly IOConnectionInfo _ioc;
private readonly NetFtpFileStorage _fileStorage;
private readonly IOConnectionInfo _iocTemp;
private FtpClient _client;
public TransactedWrite(IOConnectionInfo ioc, NetFtpFileStorage fileStorage)
{
_ioc = ioc;
_iocTemp = _ioc.CloneDeep();
_iocTemp.Path += "." + new PwUuid(true).ToHexString().Substring(0, 6) + ".tmp";
_fileStorage = fileStorage;
}
public void Dispose()
{
if (_client != null)
_client.Dispose();
_client = null;
}
public Stream OpenFile()
{
_client = _fileStorage.GetClient(_ioc, false);
return _client.OpenWrite(_fileStorage.IocPathToUri(_iocTemp.Path).PathAndQuery);
}
public void CommitWrite()
{
_client.DeleteFile(_fileStorage.IocPathToUri(_ioc.Path).PathAndQuery);
_client.Rename(_fileStorage.IocPathToUri(_iocTemp.Path).PathAndQuery, _fileStorage.IocPathToUri(_ioc.Path).PathAndQuery);
}
}
public class UntransactedWrite : IWriteTransaction
{
private readonly IOConnectionInfo _ioc;
private readonly NetFtpFileStorage _fileStorage;
public UntransactedWrite(IOConnectionInfo ioc, NetFtpFileStorage fileStorage)
{
_ioc = ioc;
_fileStorage = fileStorage;
}
public void Dispose()
{
}
public Stream OpenFile()
{
return _fileStorage.OpenWrite(_ioc);
}
public void CommitWrite()
{
}
}
}

View File

@ -47,11 +47,18 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="ModernHttpClient">
<HintPath>..\Components\modernhttpclient-2.4.2\lib\android\ModernHttpClient.dll</HintPath>
</Reference>
<Reference Include="Mono.Android" /> <Reference Include="Mono.Android" />
<Reference Include="Mono.Security" /> <Reference Include="Mono.Security" />
<Reference Include="mscorlib" /> <Reference Include="mscorlib" />
<Reference Include="OkHttp">
<HintPath>..\Components\modernhttpclient-2.4.2\lib\android\OkHttp.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Xamarin.Insights"> <Reference Include="Xamarin.Insights">
@ -83,6 +90,7 @@
<Compile Include="Io\FileStorageSetupActivity.cs" /> <Compile Include="Io\FileStorageSetupActivity.cs" />
<Compile Include="Io\FileStorageSetupInitiatorActivity.cs" /> <Compile Include="Io\FileStorageSetupInitiatorActivity.cs" />
<Compile Include="Io\GDriveFileStorage.cs" /> <Compile Include="Io\GDriveFileStorage.cs" />
<Compile Include="Io\HttpFileStorage.cs" />
<Compile Include="Io\IFileStorage.cs" /> <Compile Include="Io\IFileStorage.cs" />
<Compile Include="Io\IoUtil.cs" /> <Compile Include="Io\IoUtil.cs" />
<Compile Include="Io\JavaFileStorage.cs" /> <Compile Include="Io\JavaFileStorage.cs" />
@ -149,6 +157,12 @@
<ItemGroup> <ItemGroup>
<Folder Include="Resources\" /> <Folder Include="Resources\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<XamarinComponentReference Include="modernhttpclient">
<Visible>False</Visible>
<Version>2.4.2</Version>
</XamarinComponentReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -286,7 +286,7 @@ namespace keepass2android
ioc.Obfuscate(false); ioc.Obfuscate(false);
App.Kp2a.GetFileStorage(ioc).ResolveAccount(ioc); //TODO enable for 1.1 release App.Kp2a.GetFileStorage(ioc).ResolveAccount(ioc);
return ioc; return ioc;
} }

View File

@ -219,7 +219,7 @@ namespace keepass2android
TextView textView = (TextView)view; TextView textView = (TextView)view;
IOConnectionInfo ioc = new IOConnectionInfo {Path = path}; IOConnectionInfo ioc = new IOConnectionInfo {Path = path};
var fileStorage = _app.GetFileStorage(ioc); var fileStorage = _app.GetFileStorage(ioc);
fileStorage.ResolveAccount(ioc); //TODO enable for 1.1 release fileStorage.ResolveAccount(ioc);
textView.Text = fileStorage.GetDisplayName(ioc); textView.Text = fileStorage.GetDisplayName(ioc);
textView.Tag = ioc.Path; textView.Tag = ioc.Path;
return true; return true;

View File

@ -84,6 +84,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="Mono.Android" /> <Reference Include="Mono.Android" />
@ -1679,4 +1680,9 @@
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\packages\Xamarin.Insights.1.11.3\build\MonoAndroid10\Xamarin.Insights.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Insights.1.11.3\build\MonoAndroid10\Xamarin.Insights.targets'))" /> <Error Condition="!Exists('..\packages\Xamarin.Insights.1.11.3\build\MonoAndroid10\Xamarin.Insights.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Insights.1.11.3\build\MonoAndroid10\Xamarin.Insights.targets'))" />
</Target> </Target>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
</Project> </Project>

View File

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="MonoAndroid60" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="MonoAndroid60" />
<package id="Xamarin.Android.Support.Design" version="22.2.1.0" targetFramework="MonoAndroid50" /> <package id="Xamarin.Android.Support.Design" version="22.2.1.0" targetFramework="MonoAndroid50" />
<package id="Xamarin.Android.Support.v4" version="22.2.1.0" targetFramework="MonoAndroid50" /> <package id="Xamarin.Android.Support.v4" version="22.2.1.0" targetFramework="MonoAndroid50" />
<package id="Xamarin.Android.Support.v7.AppCompat" version="22.2.1.0" targetFramework="MonoAndroid50" /> <package id="Xamarin.Android.Support.v7.AppCompat" version="22.2.1.0" targetFramework="MonoAndroid50" />