Implemented loading of Keepass 1 (kdb) files. First test passed!
This commit is contained in:
		| @@ -9,5 +9,7 @@ namespace KeePassLib | ||||
| 		void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger); | ||||
|  | ||||
| 		byte[] HashOfLastStream { get; } | ||||
|  | ||||
| 		bool CanWrite { get;  } | ||||
| 	} | ||||
| } | ||||
| @@ -20,7 +20,7 @@ | ||||
|     <DebugType>full</DebugType> | ||||
|     <Optimize>false</Optimize> | ||||
|     <OutputPath>bin\Debug\</OutputPath> | ||||
|     <DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants> | ||||
|     <DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|   </PropertyGroup> | ||||
| @@ -55,6 +55,9 @@ | ||||
|     <Compile Include="database\CheckDatabaseForChanges.cs" /> | ||||
|     <Compile Include="database\edit\EditGroup.cs" /> | ||||
|     <Compile Include="database\edit\MoveElement.cs" /> | ||||
|     <Compile Include="database\DatabaseV1.cs" /> | ||||
|     <Compile Include="database\KdbDatabaseLoader.cs" /> | ||||
|     <Compile Include="database\KdbxDatabaseLoader.cs" /> | ||||
|     <Compile Include="database\SynchronizeCachedDatabase.cs" /> | ||||
|     <Compile Include="Io\BuiltInFileStorage.cs" /> | ||||
|     <Compile Include="Io\CachingFileStorage.cs" /> | ||||
| @@ -112,6 +115,10 @@ | ||||
|       <Project>{545b4a6b-8bba-4fbe-92fc-4ac060122a54}</Project> | ||||
|       <Name>KeePassLib2Android</Name> | ||||
|     </ProjectReference> | ||||
|     <ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj"> | ||||
|       <Project>{70d3844a-d9fa-4a64-b205-a84c6a822196}</Project> | ||||
|       <Name>KP2AKdbLibraryBinding</Name> | ||||
|     </ProjectReference> | ||||
|   </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.  | ||||
|   | ||||
| @@ -112,8 +112,15 @@ namespace keepass2android | ||||
| 			Loaded = true; | ||||
| 			KpDatabase = pwDatabase; | ||||
| 			SearchHelper = new SearchDbHelper(app); | ||||
|  | ||||
| 			CanWrite = databaseLoader.CanWrite; | ||||
| 		} | ||||
|  | ||||
| 		/// <summary> | ||||
| 		/// Indicates whether it is possible to make changes to this database | ||||
| 		/// </summary> | ||||
| 		public bool CanWrite { get; set; } | ||||
|  | ||||
| 		protected  virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseLoader databaseLoader) | ||||
| 		{ | ||||
| 			IFileStorage fileStorage = _app.GetFileStorage(iocInfo); | ||||
|   | ||||
| @@ -1,27 +1,23 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Security.Cryptography; | ||||
| using Android.Content; | ||||
| using Com.Keepassdroid.Database; | ||||
| using Com.Keepassdroid.Database.Exception; | ||||
| using Java.Lang; | ||||
| using KeePassLib; | ||||
| using KeePassLib.Cryptography; | ||||
| using KeePassLib.Cryptography.Cipher; | ||||
| using KeePassLib.Interfaces; | ||||
| using KeePassLib.Keys; | ||||
| using KeePassLib.Security; | ||||
| using Exception = System.Exception; | ||||
| using PwIcon = KeePassLib.PwIcon; | ||||
|  | ||||
| namespace keepass2android | ||||
| { | ||||
| 	class KdbDatabaseLoader: IDatabaseLoader | ||||
| 	{ | ||||
| 		private Context _ctx; | ||||
|  | ||||
| 		public KdbDatabaseLoader(Context ctx) | ||||
| 		{ | ||||
| 			_ctx = ctx; | ||||
| 		} | ||||
| 		private Dictionary<PwUuid, AdditionalGroupData> _groupData = new Dictionary<PwUuid, AdditionalGroupData>(); | ||||
|  | ||||
| 		public void PopulateDatabaseFromStream(PwDatabase db, CompositeKey key, Stream s, IStatusLogger slLogger) | ||||
| 		{ | ||||
| @@ -49,6 +45,8 @@ namespace keepass2android | ||||
| 				var dbv3 = importer.OpenDatabase(hashingStream, password, keyfile); | ||||
|  | ||||
| 				db.Name = dbv3.Name; | ||||
| 				db.RootGroup = ConvertGroup(dbv3.RootGroup); | ||||
| 				 | ||||
| 			} | ||||
| 			catch (InvalidPasswordException e) { | ||||
| 			 | ||||
| @@ -71,6 +69,98 @@ namespace keepass2android | ||||
| 				throw new Exception("hashing didn't work"); //todo remove | ||||
| 		} | ||||
|  | ||||
| 		private PwGroup ConvertGroup(PwGroupV3 groupV3) | ||||
| 		{ | ||||
| 			PwGroup pwGroup = new PwGroup(true, false); | ||||
| 			pwGroup.Name = groupV3.Name; | ||||
| 			 | ||||
| 			pwGroup.CreationTime = ConvertTime(groupV3.TCreation); | ||||
| 			pwGroup.LastAccessTime = ConvertTime(groupV3.TLastAccess); | ||||
| 			pwGroup.LastModificationTime = ConvertTime(groupV3.TLastMod); | ||||
| 			pwGroup.Expires = !PwGroupV3.NeverExpire.Equals(groupV3.TExpire); | ||||
| 			if (pwGroup.Expires) | ||||
| 				pwGroup.ExpiryTime = ConvertTime(groupV3.TExpire); | ||||
|  | ||||
| 			if (groupV3.Icon != null) | ||||
| 				pwGroup.IconId = (PwIcon) groupV3.Icon.IconId; | ||||
| 			_groupData.Add(pwGroup.Uuid, new AdditionalGroupData | ||||
| 				{ | ||||
| 					Flags = groupV3.Flags, | ||||
| 					Id = groupV3.Id.Id | ||||
| 				}); | ||||
|  | ||||
|  | ||||
| 			for (int i = 0; i < groupV3.ChildGroups.Count;i++) | ||||
| 			{ | ||||
| 				pwGroup.AddGroup(ConvertGroup(groupV3.GetGroupAt(i)), true); | ||||
| 			} | ||||
| 			for (int i = 0; i < groupV3.ChildEntries.Count; i++) | ||||
| 			{ | ||||
| 				var entry = groupV3.GetEntryAt(i); | ||||
| 				if (entry.IsMetaStream) | ||||
| 					continue; | ||||
| 				pwGroup.AddEntry(ConvertEntry(entry), true); | ||||
| 			} | ||||
| 			 | ||||
| 			return pwGroup; | ||||
| 		} | ||||
|  | ||||
| 		private PwEntry ConvertEntry(PwEntryV3 entryV3) | ||||
| 		{ | ||||
| 			PwEntry pwEntry = new PwEntry(false, false); | ||||
| 			pwEntry.Uuid = new PwUuid(entryV3.Uuid.ToArray()); | ||||
| 			pwEntry.CreationTime = ConvertTime(entryV3.TCreation); | ||||
| 			pwEntry.LastAccessTime = ConvertTime(entryV3.TLastAccess); | ||||
| 			pwEntry.LastModificationTime = ConvertTime(entryV3.TLastMod); | ||||
|  | ||||
| 			pwEntry.Expires = entryV3.Expires(); | ||||
| 			if (pwEntry.Expires) | ||||
| 				pwEntry.ExpiryTime = ConvertTime(entryV3.TExpire); | ||||
|  | ||||
| 			if (entryV3.Icon != null) | ||||
| 				pwEntry.IconId = (PwIcon) entryV3.Icon.IconId; | ||||
| 			SetFieldIfAvailable(pwEntry, PwDefs.TitleField, false, entryV3.Title); | ||||
| 			SetFieldIfAvailable(pwEntry, PwDefs.UserNameField, false, entryV3.Username); | ||||
| 			SetFieldIfAvailable(pwEntry, PwDefs.UrlField, false, entryV3.Url); | ||||
| 			SetFieldIfAvailable(pwEntry, PwDefs.PasswordField, true, entryV3.Password); | ||||
| 			SetFieldIfAvailable(pwEntry, PwDefs.NotesField, true, entryV3.Additional); | ||||
|  | ||||
| 			if (entryV3.GetBinaryData() != null) | ||||
| 			{ | ||||
| 				pwEntry.Binaries.Set(entryV3.BinaryDesc, new ProtectedBinary(true, entryV3.GetBinaryData())); | ||||
| 			} | ||||
| 			return pwEntry; | ||||
| 		} | ||||
|  | ||||
| 		private void SetFieldIfAvailable(PwEntry pwEntry, string fieldName, bool makeProtected, string value) | ||||
| 		{ | ||||
| 			if (value != null) | ||||
| 			{ | ||||
| 				pwEntry.Strings.Set(fieldName, new ProtectedString(makeProtected, value));	 | ||||
| 			} | ||||
| 			 | ||||
| 		} | ||||
|  | ||||
| 		private DateTime ConvertTime(PwDate date) | ||||
| 		{ | ||||
| 			if (date == null) | ||||
| 				return PwDefs.DtDefaultNow; | ||||
| 			return JavaTimeToCSharp(date.JDate.Time); | ||||
| 		} | ||||
|  | ||||
| 		private DateTime JavaTimeToCSharp(long javatime) | ||||
| 		{ | ||||
| 			return new DateTime(1970, 1, 1).AddMilliseconds(javatime); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		public byte[] HashOfLastStream { get; private set; } | ||||
| 		public bool CanWrite { get { return false; } } | ||||
| 	} | ||||
|  | ||||
| 	internal class AdditionalGroupData	 | ||||
| 	{ | ||||
| 		public int Id { get; set; } | ||||
| 		public int Flags { get; set; } | ||||
| 	} | ||||
| } | ||||
| @@ -27,5 +27,6 @@ namespace keepass2android | ||||
| 		} | ||||
|  | ||||
| 		public byte[] HashOfLastStream { get; private set; } | ||||
| 		public bool CanWrite { get { return true; } } | ||||
| 	} | ||||
| } | ||||
| @@ -19,7 +19,6 @@ using System; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Android.App; | ||||
| using KeePassLib; | ||||
| using KeePassLib.Keys; | ||||
| using KeePassLib.Serialization; | ||||
| @@ -33,6 +32,7 @@ namespace keepass2android | ||||
| 		private readonly string _keyfileOrProvider; | ||||
| 		private readonly IKp2aApp _app; | ||||
| 		private readonly bool _rememberKeyfile; | ||||
| 		IDatabaseLoader _loader; | ||||
| 		 | ||||
| 		public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(finish) | ||||
| 		{ | ||||
| @@ -68,8 +68,8 @@ namespace keepass2android | ||||
| 				} | ||||
|  | ||||
| 				//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess: | ||||
| 				IDatabaseLoader loader = new KdbxDatabaseLoader(KdbpFile.GetFormatToUse(_ioc));  | ||||
| 				TryLoad(databaseStream, loader); | ||||
| 				_loader = new KdbxDatabaseLoader(KdbpFile.GetFormatToUse(_ioc)); | ||||
| 				TryLoad(databaseStream); | ||||
| 			} | ||||
| 			catch (KeyFileException) | ||||
| 			{ | ||||
| @@ -99,7 +99,7 @@ namespace keepass2android | ||||
| 			 | ||||
| 		} | ||||
|  | ||||
| 		private void TryLoad(MemoryStream databaseStream, IDatabaseLoader loader) | ||||
| 		private void TryLoad(MemoryStream databaseStream) | ||||
| 		{ | ||||
| 			//create a copy of the stream so we can try again if we get an exception which indicates we should change parameters | ||||
| 			//This is not optimal in terms of (short-time) memory usage but is hard to avoid because the Keepass library closes streams also in case of errors. | ||||
| @@ -112,14 +112,15 @@ namespace keepass2android | ||||
| 			//now let's go: | ||||
| 			try | ||||
| 			{ | ||||
| 				_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, loader); | ||||
| 				_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _loader); | ||||
| 				SaveFileData(_ioc, _keyfileOrProvider); | ||||
| 				Kp2aLog.Log("LoadDB OK"); | ||||
| 				Finish(true); | ||||
| 			} | ||||
| 			catch (OldFormatException) | ||||
| 			{ | ||||
| 				TryLoad(databaseStream, new KdbDatabaseLoader(Application.Context)); | ||||
| 				_loader = new KdbDatabaseLoader(); | ||||
| 				TryLoad(databaseStream); | ||||
| 			} | ||||
| 			catch (InvalidCompositeKeyException) | ||||
| 			{ | ||||
| @@ -131,7 +132,7 @@ namespace keepass2android | ||||
| 					//retry without password: | ||||
| 					_compositeKey.RemoveUserKey(passwordKey); | ||||
| 					//retry: | ||||
| 					TryLoad(databaseStream, loader); | ||||
| 					TryLoad(databaseStream); | ||||
| 				} | ||||
| 				else throw; | ||||
| 			} | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using Android.App; | ||||
| using KeePassLib; | ||||
| using KeePassLib.Serialization; | ||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| using keepass2android; | ||||
| @@ -14,7 +15,15 @@ namespace Kp2aUnitTests | ||||
| 	{ | ||||
| 		private void RunLoadTest(string filenameWithoutDir, string password, string keyfile) | ||||
| 		{ | ||||
| 			Android.Util.Log.Debug("KP2ATest", "Starting for " + filenameWithoutDir+" with " + password+"/"+keyfile); | ||||
| 			var app = PerformLoad(filenameWithoutDir, password, keyfile); | ||||
|  | ||||
| 			Assert.AreEqual(6,app.GetDb().KpDatabase.RootGroup.Groups.Count()); | ||||
| 			Assert.AreEqual(2,app.GetDb().KpDatabase.RootGroup.Entries.Count()); | ||||
| 		} | ||||
|  | ||||
| 		private IKp2aApp PerformLoad(string filenameWithoutDir, string password, string keyfile) | ||||
| 		{ | ||||
| 			Android.Util.Log.Debug("KP2ATest", "Starting for " + filenameWithoutDir + " with " + password + "/" + keyfile); | ||||
|  | ||||
| 			IKp2aApp app = new TestKp2aApp(); | ||||
| 			app.CreateNewDatabase(); | ||||
| @@ -22,40 +31,43 @@ namespace Kp2aUnitTests | ||||
| 			var key = CreateKey(password, keyfile); | ||||
| 			string loadErrorMessage = ""; | ||||
|  | ||||
| 			LoadDb task = new LoadDb(app, new IOConnectionInfo { Path = TestDbDirectory+filenameWithoutDir }, null, | ||||
| 				key, keyfile, new ActionOnFinish((success, message) => | ||||
| 					{ | ||||
| 						loadErrorMessage = message; | ||||
| 						if (!success) | ||||
| 							Android.Util.Log.Debug("KP2ATest", "error loading db: " + message); | ||||
| 						loadSuccesful = success; 		 | ||||
| 					}) | ||||
| 			LoadDb task = new LoadDb(app, new IOConnectionInfo {Path = TestDbDirectory + filenameWithoutDir}, null, | ||||
| 			                         key, keyfile, new ActionOnFinish((success, message) => | ||||
| 				                         { | ||||
| 					                         loadErrorMessage = message; | ||||
| 					                         if (!success) | ||||
| 						                         Android.Util.Log.Debug("KP2ATest", "error loading db: " + message); | ||||
| 					                         loadSuccesful = success; | ||||
| 				                         }) | ||||
| 				); | ||||
| 			ProgressTask pt = new ProgressTask(app, Application.Context, task); | ||||
| 			Android.Util.Log.Debug("KP2ATest", "Running ProgressTask"); | ||||
| 			pt.Run(); | ||||
| 			pt.JoinWorkerThread(); | ||||
| 			Android.Util.Log.Debug("KP2ATest", "PT.run finished"); | ||||
| 			Assert.IsTrue(loadSuccesful, "didn't succesfully load database :-( "+loadErrorMessage); | ||||
| 			 | ||||
| 			Assert.AreEqual(6,app.GetDb().KpDatabase.RootGroup.Groups.Count()); | ||||
| 			Assert.AreEqual(2,app.GetDb().KpDatabase.RootGroup.Entries.Count()); | ||||
| 			Assert.IsTrue(loadSuccesful, "didn't succesfully load database :-( " + loadErrorMessage); | ||||
| 			return app; | ||||
| 		} | ||||
|  | ||||
| 		 | ||||
|  | ||||
|  | ||||
| 		[TestMethod] | ||||
| 		public void TestLoadWithPasswordOnly() | ||||
| 		{ | ||||
| 			RunLoadTest("passwordonly.kdbx", DefaultPassword, ""); | ||||
| 			 | ||||
|  | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		[TestMethod] | ||||
| 		public void TestLoadKdb1() | ||||
| 		{ | ||||
| 			RunLoadTest("test1.kdb", "12345", ""); | ||||
| 			var app = PerformLoad("keepass.kdb", "test", ""); | ||||
|  | ||||
| 			//contents of the kdb file are a little different because the root group cannot have entries | ||||
| 			Assert.AreEqual(6, app.GetDb().KpDatabase.RootGroup.Groups.Count()); | ||||
| 			PwGroup generalGroup = app.GetDb().KpDatabase.RootGroup.Groups.Single(g => g.Name == "General"); | ||||
| 			Assert.AreEqual(2, generalGroup.Entries.Count()); | ||||
| 		} | ||||
|  | ||||
| 		[TestMethod] | ||||
|   | ||||
| @@ -310,7 +310,7 @@ public class PwDatabaseV3 { | ||||
| 	 *            ID number to check for | ||||
| 	 * @return True if the ID is used, false otherwise | ||||
| 	 */ | ||||
| 	protected boolean isGroupIdUsed(PwGroupId id) { | ||||
| 	protected boolean isGroupIdUsed(PwGroupIdV3 id) { | ||||
| 		List<PwGroupV3> groups = getGroups(); | ||||
| 		 | ||||
| 		for (int i = 0; i < groups.size(); i++) { | ||||
| @@ -382,9 +382,9 @@ public class PwDatabaseV3 { | ||||
| 		groups = grp; | ||||
| 	} | ||||
|  | ||||
| 	public List<PwGroupV3> getGrpRoots() { | ||||
| 	public ArrayList<PwGroupV3> getGrpRoots() { | ||||
| 		int target = 0; | ||||
| 		List<PwGroupV3> kids = new ArrayList<PwGroupV3>(); | ||||
| 		ArrayList<PwGroupV3> kids = new ArrayList<PwGroupV3>(); | ||||
| 		for (int i = 0; i < groups.size(); i++) { | ||||
| 			PwGroupV3 grp = (PwGroupV3) groups.get(i); | ||||
| 			if (grp.level == target) | ||||
| @@ -404,10 +404,10 @@ public class PwDatabaseV3 { | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	public List<PwGroupV3> getGrpChildren(PwGroupV3 parent) { | ||||
| 	public ArrayList<PwGroupV3> getGrpChildren(PwGroupV3 parent) { | ||||
| 		int idx = groups.indexOf(parent); | ||||
| 		int target = parent.level + 1; | ||||
| 		List<PwGroupV3> kids = new ArrayList<PwGroupV3>(); | ||||
| 		ArrayList<PwGroupV3> kids = new ArrayList<PwGroupV3>(); | ||||
| 		while (++idx < groups.size()) { | ||||
| 			PwGroupV3 grp = (PwGroupV3) groups.get(idx); | ||||
| 			if (grp.level < target) | ||||
| @@ -418,8 +418,8 @@ public class PwDatabaseV3 { | ||||
| 		return kids; | ||||
| 	} | ||||
|  | ||||
| 	public List<PwEntryV3> getEntries(PwGroupV3 parent) { | ||||
| 		List<PwEntryV3> kids = new ArrayList<PwEntryV3>(); | ||||
| 	public ArrayList<PwEntryV3> getEntries(PwGroupV3 parent) { | ||||
| 		ArrayList<PwEntryV3> kids = new ArrayList<PwEntryV3>(); | ||||
| 		/* | ||||
| 		 * for( Iterator i = entries.iterator(); i.hasNext(); ) { PwEntryV3 ent | ||||
| 		 * = (PwEntryV3)i.next(); if( ent.groupId == parent.groupId ) kids.add( | ||||
| @@ -443,7 +443,7 @@ public class PwDatabaseV3 { | ||||
| 			PwGroupV3 root = new PwGroupV3(); | ||||
| 			rootGroup = root; | ||||
|  | ||||
| 			List<PwGroupV3> rootChildGroups = getGrpRoots(); | ||||
| 			ArrayList<PwGroupV3> rootChildGroups = getGrpRoots(); | ||||
| 			root.setGroups(rootChildGroups); | ||||
| 			root.childEntries = new ArrayList<PwEntryV3>(); | ||||
| 			root.level = -1; | ||||
|   | ||||
| @@ -90,10 +90,10 @@ protected static final String PMS_TAN_ENTRY = "<TAN>"; | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	public PwIcon getIcon() { | ||||
| 	public PwIconStandard getIcon() { | ||||
| 		return icon; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	public boolean isTan() { | ||||
| 		return getTitle().equals(PMS_TAN_ENTRY) && (getUsername().length() > 0); | ||||
| 	} | ||||
| @@ -148,7 +148,7 @@ protected static final String PMS_TAN_ENTRY = "<TAN>"; | ||||
| 	public int              groupId; | ||||
| 	public 	String 			username; | ||||
| 	private byte[]          password; | ||||
| 	private byte[]          uuid; | ||||
| 	public byte[]          uuid; | ||||
| 	public String title; | ||||
| 	public String url; | ||||
| 	public String additional; | ||||
|   | ||||
| @@ -1,5 +1,2 @@ | ||||
| package com.keepassdroid.database; | ||||
|  | ||||
| public abstract class PwGroupId { | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|  */ | ||||
| package com.keepassdroid.database; | ||||
|  | ||||
| public class PwGroupIdV3 extends PwGroupId { | ||||
| public class PwGroupIdV3 { | ||||
|  | ||||
| 	private int id; | ||||
| 	 | ||||
|   | ||||
| @@ -46,17 +46,29 @@ public class PwGroupV3 { | ||||
| 	public PwGroupV3() { | ||||
| 	} | ||||
|  | ||||
| 	public List<PwGroupV3> childGroups = new ArrayList<PwGroupV3>(); | ||||
| 	//Mono for Android binding somehow doesn't return List<PwGroupV3> but only IList and casting of the contents doesn't work. | ||||
| 	//therefore, getGroupAt() must be used in C#, see below | ||||
| 	public ArrayList<PwGroupV3> childGroups = new ArrayList<PwGroupV3>(); | ||||
|  | ||||
| 	public List<PwEntryV3> childEntries = new ArrayList<PwEntryV3>(); | ||||
| 	public ArrayList<PwEntryV3> childEntries = new ArrayList<PwEntryV3>(); | ||||
| 	public String name = ""; | ||||
| 	public PwIconStandard icon; | ||||
| 	 | ||||
| 	  | ||||
| 	public PwGroupV3 getGroupAt(int i) | ||||
| 	{ | ||||
| 		return childGroups.get(i); | ||||
| 	} | ||||
| 	public PwEntryV3 getEntryAt(int i) | ||||
| 	{ | ||||
| 		return childEntries.get(i); | ||||
| 	} | ||||
|  | ||||
| 	public PwIcon getIcon() { | ||||
| 	public PwIconStandard getIcon() { | ||||
| 		return icon; | ||||
| 	} | ||||
|  | ||||
| 	public void super_initNewGroup(String nm, PwGroupId newId) { | ||||
| 	public void super_initNewGroup(String nm, PwGroupIdV3 newId) { | ||||
| 		setId(newId); | ||||
| 		name = nm; | ||||
| 	} | ||||
| @@ -113,7 +125,7 @@ public class PwGroupV3 { | ||||
| 	/** Used by KeePass internally, don't use */ | ||||
| 	public int flags; | ||||
|  | ||||
| 	public void setGroups(List<PwGroupV3> groups) { | ||||
| 	public void setGroups(ArrayList<PwGroupV3> groups) { | ||||
| 		childGroups = groups; | ||||
| 	} | ||||
|  | ||||
| @@ -121,11 +133,11 @@ public class PwGroupV3 { | ||||
| 		return parent; | ||||
| 	} | ||||
|  | ||||
| 	public PwGroupId getId() { | ||||
| 	public PwGroupIdV3 getId() { | ||||
| 		return new PwGroupIdV3(groupId); | ||||
| 	} | ||||
|  | ||||
| 	public void setId(PwGroupId id) { | ||||
| 	public void setId(PwGroupIdV3 id) { | ||||
| 		PwGroupIdV3 id3 = (PwGroupIdV3) id; | ||||
| 		groupId = id3.getId(); | ||||
| 	} | ||||
| @@ -144,7 +156,7 @@ public class PwGroupV3 { | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	public void initNewGroup(String nm, PwGroupId newId) { | ||||
| 	public void initNewGroup(String nm, PwGroupIdV3 newId) { | ||||
| 		super_initNewGroup(nm, newId); | ||||
|  | ||||
| 		Date now = Calendar.getInstance().getTime(); | ||||
|   | ||||
| @@ -1,12 +0,0 @@ | ||||
| package com.keepassdroid.database; | ||||
|  | ||||
| public abstract class PwIcon { | ||||
| 	 | ||||
| 	public boolean isMetaStreamIcon() { | ||||
| 		return false; | ||||
| 	} | ||||
| 	 | ||||
| 	public void writeBytes() { | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| @@ -19,8 +19,8 @@ | ||||
|  */ | ||||
| package com.keepassdroid.database; | ||||
|  | ||||
| public class PwIconStandard extends PwIcon { | ||||
| 	public final int iconId; | ||||
| public class PwIconStandard  { | ||||
| 	public /*final*/ int iconId; | ||||
| 	 | ||||
| 	public static PwIconStandard FIRST = new PwIconStandard(1); | ||||
| 	 | ||||
| @@ -30,7 +30,6 @@ public class PwIconStandard extends PwIcon { | ||||
| 		this.iconId = iconId; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean isMetaStreamIcon() { | ||||
| 		return iconId == 0; | ||||
| 	} | ||||
|   | ||||
| @@ -45,6 +45,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  | ||||
| package com.keepassdroid.database.load; | ||||
|  | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.UnsupportedEncodingException; | ||||
| @@ -135,10 +136,29 @@ public class ImporterV3  { | ||||
|  | ||||
|  | ||||
| 		// Load entire file, most of it's encrypted. | ||||
| 		int fileSize = inStream.available(); | ||||
| 		byte[] filebuf = new byte[fileSize + 16]; // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer | ||||
| 		inStream.read(filebuf, 0, fileSize); | ||||
| 		 | ||||
| 		ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | ||||
|  | ||||
| 		int nRead; | ||||
| 		byte[] data = new byte[16384]; | ||||
|  | ||||
| 		while ((nRead = inStream.read(data, 0, data.length)) != -1) { | ||||
| 		  buffer.write(data, 0, nRead); | ||||
| 		} | ||||
|  | ||||
| 		buffer.flush(); | ||||
| 		 | ||||
| 		int fileSize = buffer.size(); | ||||
| 		 | ||||
| 	 // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer | ||||
| 		for (int i=0;i<16;i++) | ||||
| 		{ | ||||
| 			buffer.write(0); | ||||
| 		} | ||||
|  | ||||
| 		inStream.close(); | ||||
| 		 | ||||
| 		byte[] filebuf = buffer.toByteArray(); | ||||
|  | ||||
| 		// Parse header (unencrypted) | ||||
| 		if( fileSize < PwDbHeaderV3.BUF_SIZE ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Philipp Crocoll
					Philipp Crocoll