add missing File NetFtpFileStorage.cs, added HttpClient based Http implementation
This commit is contained in:
		| @@ -51,6 +51,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FtpClientTest", "FtpClientT | ||||
| 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}" | ||||
| 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 | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		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|Win32.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 | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
|   | ||||
| @@ -3,6 +3,7 @@ using System.Collections.Generic; | ||||
| using System.Globalization; | ||||
| using System.IO; | ||||
| using System.Net; | ||||
| using System.Net.Http; | ||||
| using System.Security; | ||||
| using Android; | ||||
| using Android.App; | ||||
| @@ -12,6 +13,7 @@ using Android.OS; | ||||
| using Java.IO; | ||||
| using KeePassLib.Serialization; | ||||
| using KeePassLib.Utility; | ||||
| using ModernHttpClient; | ||||
| using File = System.IO.File; | ||||
| using FileNotFoundException = System.IO.FileNotFoundException; | ||||
| 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> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <Reference Include="ModernHttpClient"> | ||||
|       <HintPath>..\Components\modernhttpclient-2.4.2\lib\android\ModernHttpClient.dll</HintPath> | ||||
|     </Reference> | ||||
|     <Reference Include="Mono.Android" /> | ||||
|     <Reference Include="Mono.Security" /> | ||||
|     <Reference Include="mscorlib" /> | ||||
|     <Reference Include="OkHttp"> | ||||
|       <HintPath>..\Components\modernhttpclient-2.4.2\lib\android\OkHttp.dll</HintPath> | ||||
|     </Reference> | ||||
|     <Reference Include="System" /> | ||||
|     <Reference Include="System.Core" /> | ||||
|     <Reference Include="System.Net.Http" /> | ||||
|     <Reference Include="System.Xml.Linq" /> | ||||
|     <Reference Include="System.Xml" /> | ||||
|     <Reference Include="Xamarin.Insights"> | ||||
| @@ -83,6 +90,7 @@ | ||||
|     <Compile Include="Io\FileStorageSetupActivity.cs" /> | ||||
|     <Compile Include="Io\FileStorageSetupInitiatorActivity.cs" /> | ||||
|     <Compile Include="Io\GDriveFileStorage.cs" /> | ||||
|     <Compile Include="Io\HttpFileStorage.cs" /> | ||||
|     <Compile Include="Io\IFileStorage.cs" /> | ||||
|     <Compile Include="Io\IoUtil.cs" /> | ||||
|     <Compile Include="Io\JavaFileStorage.cs" /> | ||||
| @@ -149,6 +157,12 @@ | ||||
|   <ItemGroup> | ||||
|     <Folder Include="Resources\" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <XamarinComponentReference Include="modernhttpclient"> | ||||
|       <Visible>False</Visible> | ||||
|       <Version>2.4.2</Version> | ||||
|     </XamarinComponentReference> | ||||
|   </ItemGroup> | ||||
|   <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.  | ||||
|        Other similar extension points exist, see Microsoft.Common.targets. | ||||
|   | ||||
| @@ -286,7 +286,7 @@ namespace keepass2android | ||||
|  | ||||
| 			ioc.Obfuscate(false); | ||||
|  | ||||
| 			App.Kp2a.GetFileStorage(ioc).ResolveAccount(ioc); | ||||
| 			//TODO enable for 1.1 release App.Kp2a.GetFileStorage(ioc).ResolveAccount(ioc); | ||||
|  | ||||
| 			return ioc; | ||||
| 		} | ||||
|   | ||||
| @@ -219,7 +219,7 @@ namespace keepass2android | ||||
| 					TextView textView = (TextView)view; | ||||
| 					IOConnectionInfo ioc = new IOConnectionInfo {Path = path}; | ||||
| 					var fileStorage = _app.GetFileStorage(ioc); | ||||
| 					fileStorage.ResolveAccount(ioc); | ||||
| 					//TODO enable for 1.1 release fileStorage.ResolveAccount(ioc); | ||||
| 					textView.Text = fileStorage.GetDisplayName(ioc); | ||||
| 					textView.Tag = ioc.Path; | ||||
| 					return true; | ||||
|   | ||||
| @@ -84,6 +84,7 @@ | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <Reference Include="System" /> | ||||
|     <Reference Include="System.Net.Http" /> | ||||
|     <Reference Include="System.Xml" /> | ||||
|     <Reference Include="System.Core" /> | ||||
|     <Reference Include="Mono.Android" /> | ||||
| @@ -1679,4 +1680,9 @@ | ||||
|     </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'))" /> | ||||
|   </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> | ||||
| @@ -1,5 +1,7 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <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.v4" version="22.2.1.0" targetFramework="MonoAndroid50" /> | ||||
|   <package id="Xamarin.Android.Support.v7.AppCompat" version="22.2.1.0" targetFramework="MonoAndroid50" /> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Philipp Crocoll
					Philipp Crocoll