mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-11-22 09:12:17 -05:00
add missing File NetFtpFileStorage.cs, added HttpClient based Http implementation
This commit is contained in:
parent
0e40033d85
commit
497be64633
@ -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
|
||||||
|
@ -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;
|
||||||
|
287
src/Kp2aBusinessLogic/Io/HttpFileStorage.cs
Normal file
287
src/Kp2aBusinessLogic/Io/HttpFileStorage.cs
Normal 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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
399
src/Kp2aBusinessLogic/Io/NetFtpFileStorage.cs
Normal file
399
src/Kp2aBusinessLogic/Io/NetFtpFileStorage.cs
Normal 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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
@ -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" />
|
||||||
|
Loading…
Reference in New Issue
Block a user