Compare commits
312 Commits
2.5.0.0bet
...
2.5.0.0bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1033dd78b0 | ||
|
|
4aaae3f59e | ||
|
|
1424633a58 | ||
|
|
c9b98094e5 | ||
|
|
5707026985 | ||
|
|
bfcfb842fb | ||
|
|
8fe2230891 | ||
|
|
41cc0b1a5a | ||
|
|
cc8c525dab | ||
|
|
92bc3c2838 | ||
|
|
1d065e7bc5 | ||
|
|
fb1b90a281 | ||
|
|
268f716104 | ||
|
|
b7328875f1 | ||
|
|
02ee58efa7 | ||
|
|
6754881847 | ||
|
|
6e4c5d8c26 | ||
|
|
544648c2eb | ||
|
|
e62f8bf56b | ||
|
|
7f5138b08b | ||
|
|
a367aeaf12 | ||
|
|
fc6453beba | ||
|
|
8a981b7a79 | ||
|
|
d7f9a02699 | ||
|
|
7c9153ea04 | ||
|
|
2c16fe3335 | ||
|
|
eb14d27ca5 | ||
|
|
7024178069 | ||
|
|
c85ce3e0e8 | ||
|
|
067c5ff1cf | ||
|
|
429bd99f1c | ||
|
|
7b5bb0fd97 | ||
|
|
ae50f424d3 | ||
|
|
53cc1ad673 | ||
|
|
ceb80c6cac | ||
|
|
71bd3ab780 | ||
|
|
8a40a4b3ae | ||
|
|
7772db17ae | ||
|
|
11738d807c | ||
|
|
80b9a8fa50 | ||
|
|
d619f6581e | ||
|
|
697e9aa923 | ||
|
|
15c843fbb9 | ||
|
|
b6b7e61cfb | ||
|
|
d5b0ee9371 | ||
|
|
e86807c3d3 | ||
|
|
08fcb119a9 | ||
|
|
edff33afd1 | ||
|
|
bdfa963bed | ||
|
|
4eff247865 | ||
|
|
3e3d5bd7d8 | ||
|
|
5a74d1be8f | ||
|
|
812eccfe0e | ||
|
|
d00c337382 | ||
|
|
54dbcc95ab | ||
|
|
2e5ddd80ff | ||
|
|
2c44a4d760 | ||
|
|
f8561222d5 | ||
|
|
2c8e3e9c8e | ||
|
|
ccbc6c07ed | ||
|
|
47e820e3d8 | ||
|
|
13dea4b567 | ||
|
|
70d1ce715e | ||
|
|
cc41545c0a | ||
|
|
aded8fab0a | ||
|
|
9cb4d28ad8 | ||
|
|
a1b2235fb3 | ||
|
|
161923c808 | ||
|
|
d5a03424d0 | ||
|
|
3e87c1ab9d | ||
|
|
a6046bc49a | ||
|
|
db98f4b30c | ||
|
|
60eff63569 | ||
|
|
b8e97ec53b | ||
|
|
c2b60b2268 | ||
|
|
cac1f923cc | ||
|
|
b4ac0ef21f | ||
|
|
8b85c22f25 | ||
|
|
582faab29d | ||
|
|
a9f6fa3441 | ||
|
|
54740c1ba2 | ||
|
|
94a8ecbdf1 | ||
|
|
46855e0b70 | ||
|
|
2d3eabe07e | ||
|
|
48bda000e9 | ||
|
|
8aea6fa2da | ||
|
|
c46aa0e5c8 | ||
|
|
ed7f6e4b68 | ||
|
|
825d88014d | ||
|
|
b3ff38fb35 | ||
|
|
1ebba9d8be | ||
|
|
1e4deb2182 | ||
|
|
d5acd04a19 | ||
|
|
fe73a951e3 | ||
|
|
415094154d | ||
|
|
347522b9a6 | ||
|
|
918f563faa | ||
|
|
3b88b0fea6 | ||
|
|
d8e3d52f21 | ||
|
|
041f3eb568 | ||
|
|
4a1d97a622 | ||
|
|
cd0d712f56 | ||
|
|
761d9a1883 | ||
|
|
2c9a6d7c26 | ||
|
|
8105f89088 | ||
|
|
90084ed2f7 | ||
|
|
496725a8fd | ||
|
|
72b0cdf0d7 | ||
|
|
a266d307a6 | ||
|
|
f7fe414597 | ||
|
|
5c1e9bd94f | ||
|
|
747135afae | ||
|
|
675efe3ab8 | ||
|
|
02b6191e66 | ||
|
|
1e07d6a28d | ||
|
|
ab98f03ebf | ||
|
|
4aff5e3a06 | ||
|
|
3ad4e8efe7 | ||
|
|
d04d7737c3 | ||
|
|
8de52cb51e | ||
|
|
c8cb79726b | ||
|
|
d51a227c1d | ||
|
|
94ed03ce35 | ||
|
|
304a5e946d | ||
|
|
ff64b53580 | ||
|
|
9ece794f9a | ||
|
|
b67dc22f10 | ||
|
|
8a1a29caa8 | ||
|
|
a8d07c3e82 | ||
|
|
937135bb36 | ||
|
|
10c32266c2 | ||
|
|
ad45369442 | ||
|
|
1ca770fa72 | ||
|
|
4acd11d2b2 | ||
|
|
38c264e38d | ||
|
|
8da6b882fc | ||
|
|
6e03c2ebfc | ||
|
|
a5cb5ed896 | ||
|
|
32bef7b099 | ||
|
|
af0359132e | ||
|
|
85990879de | ||
|
|
9436a4c3a5 | ||
|
|
efc786e318 | ||
|
|
ed299b35e9 | ||
|
|
0defe9401f | ||
|
|
b66678f028 | ||
|
|
b0cfd4292f | ||
|
|
8e390685cd | ||
|
|
9bcacdca4a | ||
|
|
c1969402f1 | ||
|
|
50bf22a4c7 | ||
|
|
cf93044d3f | ||
|
|
33404add38 | ||
|
|
49ba74c38f | ||
|
|
3ed5db9819 | ||
|
|
4535204d1e | ||
|
|
18c656a555 | ||
|
|
42c28b5b95 | ||
|
|
9fc47fdf05 | ||
|
|
9ef34599ce | ||
|
|
bd3e5c6917 | ||
|
|
7e9bed1e53 | ||
|
|
ff2215929c | ||
|
|
36d53e9788 | ||
|
|
6b9db7d40d | ||
|
|
30f805f5a9 | ||
|
|
64448ef218 | ||
|
|
6e312e0420 | ||
|
|
1f7b86d34f | ||
|
|
ae00fd5782 | ||
|
|
62f6f02467 | ||
|
|
55ee89314f | ||
|
|
b2f985aa03 | ||
|
|
b3a34c0138 | ||
|
|
1f06b09d38 | ||
|
|
758914a80a | ||
|
|
f2566abdcd | ||
|
|
cb6d479350 | ||
|
|
846930a4f9 | ||
|
|
e788d89b18 | ||
|
|
07f68cfe2f | ||
|
|
1c6ef51925 | ||
|
|
9995bc4d9f | ||
|
|
accb931831 | ||
|
|
42ac83c814 | ||
|
|
9ee9063c4d | ||
|
|
8c38b361ea | ||
|
|
cb919b2de5 | ||
|
|
679ea7c58f | ||
|
|
21ebbd25f8 | ||
|
|
3c232ac5b6 | ||
|
|
d36d2408d7 | ||
|
|
9fd59f850c | ||
|
|
4cc7d1e74d | ||
|
|
66f4353c3e | ||
|
|
0acc83b066 | ||
|
|
943d7ca6b9 | ||
|
|
65d1c7376b | ||
|
|
871a624313 | ||
|
|
8cf515120f | ||
|
|
30c5db92e6 | ||
|
|
19ebc40bdb | ||
|
|
4db2e6baf9 | ||
|
|
a9c1369cbf | ||
|
|
dd478d7cd4 | ||
|
|
53e7cc7f72 | ||
|
|
e5d3a0a931 | ||
|
|
23e6b12326 | ||
|
|
2cc530cbec | ||
|
|
b540cde623 | ||
|
|
5a54955941 | ||
|
|
9ffffdceca | ||
|
|
257d0ff315 | ||
|
|
a935b0a089 | ||
|
|
0236bddffd | ||
|
|
384966641a | ||
|
|
3be9c9161c | ||
|
|
a445533ffc | ||
|
|
e073b21544 | ||
|
|
7e746bb01c | ||
|
|
2d8e902a2a | ||
|
|
bb10892b76 | ||
|
|
bdfa5ae707 | ||
|
|
2091b04a11 | ||
|
|
f367c098ec | ||
|
|
98631d37ae | ||
|
|
d504ab7924 | ||
|
|
4fc8c74530 | ||
|
|
4876623f86 | ||
|
|
d5b752e4c0 | ||
|
|
a1ecc5b399 | ||
|
|
306e8b17b2 | ||
|
|
f66500f535 | ||
|
|
b92ef7cb1a | ||
|
|
b30c2656e8 | ||
|
|
0899c7fc5a | ||
|
|
800d0eb04d | ||
|
|
b09bc52b51 | ||
|
|
657c810c1f | ||
|
|
c501d6bdc6 | ||
|
|
f13c595bf9 | ||
|
|
60e2209281 | ||
|
|
24eeff1d61 | ||
|
|
c94291f6e1 | ||
|
|
6faee3cef9 | ||
|
|
bb4e067394 | ||
|
|
81503c6934 | ||
|
|
8858a5cdca | ||
|
|
d994fbafcf | ||
|
|
c3954faa3e | ||
|
|
0650a6f7db | ||
|
|
0ddd08bf6d | ||
|
|
a1720c1c79 | ||
|
|
cd9f82696a | ||
|
|
242427d348 | ||
|
|
3f1ab3623d | ||
|
|
7068b4b4b3 | ||
|
|
7c81685aa6 | ||
|
|
f7fc7984e2 | ||
|
|
b2ef29d131 | ||
|
|
d5dcf697f6 | ||
|
|
9731247f2e | ||
|
|
c2654cd65c | ||
|
|
e032540c0b | ||
|
|
6dda7d1e64 | ||
|
|
f67df77dff | ||
|
|
ecdadaf0bc | ||
|
|
25fb85a5ef | ||
|
|
31426268ea | ||
|
|
f0b5b1bcb5 | ||
|
|
1f2ebf0825 | ||
|
|
fa0afbe947 | ||
|
|
70967a4234 | ||
|
|
87c9aeeb12 | ||
|
|
0a386c3985 | ||
|
|
bfcd4b9f00 | ||
|
|
3d6082a5d9 | ||
|
|
aceeb581d4 | ||
|
|
d46a6a2ea8 | ||
|
|
c1bf96ac5f | ||
|
|
a1bf5f1e70 | ||
|
|
7155e25c1e | ||
|
|
5c4d2e607a | ||
|
|
79750c5320 | ||
|
|
bb353bc9d6 | ||
|
|
5818762aaf | ||
|
|
e5467bc54b | ||
|
|
e574fba0a5 | ||
|
|
82b59662f3 | ||
|
|
260ce95509 | ||
|
|
23df28cb25 | ||
|
|
a68960a30f | ||
|
|
f295aac206 | ||
|
|
d568604117 | ||
|
|
795d6fa334 | ||
|
|
d788d16020 | ||
|
|
c8db1f7c5c | ||
|
|
ac67ff9f21 | ||
|
|
3a71f635f0 | ||
|
|
a9e12cb518 | ||
|
|
b0aac43d6c | ||
|
|
f0d7249679 | ||
|
|
32fb536a92 | ||
|
|
8f8361a176 | ||
|
|
06056128e5 | ||
|
|
259c31b94c | ||
|
|
ffffeb9e85 | ||
|
|
ebe8c90238 | ||
|
|
25f657e665 | ||
|
|
ffd40a5419 | ||
|
|
8f8b265d97 | ||
|
|
d20eef0922 |
1
.gitignore
vendored
@@ -75,3 +75,4 @@ art/logo_512.png
|
||||
# Dir linux
|
||||
.directory
|
||||
*/.directory
|
||||
.idea
|
||||
|
||||
22
.idea/compiler.xml
generated
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
<entry name="!?*.aj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
3
.idea/copyright/profiles_settings.xml
generated
@@ -1,3 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings default="" />
|
||||
</component>
|
||||
6
.idea/encodings.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
20
.idea/gradle.xml
generated
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="LOCAL" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.14.1" />
|
||||
<option name="gradleJvm" value="1.8" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
11
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,11 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AndroidLintAppCompatResource" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="AndroidLintMissingTranslation" enabled="true" level="INFO" enabled_by_default="true" />
|
||||
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
|
||||
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
7
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,7 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" value="Project Default" />
|
||||
<option name="USE_PROJECT_PROFILE" value="true" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
49
.idea/misc.xml
generated
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
<component name="masterDetails">
|
||||
<states>
|
||||
<state key="ProjectJDKs.UI">
|
||||
<settings>
|
||||
<last-edited>1.7</last-edited>
|
||||
<splitter-proportions>
|
||||
<option name="proportions">
|
||||
<list>
|
||||
<option value="0.2" />
|
||||
</list>
|
||||
</option>
|
||||
</splitter-proportions>
|
||||
</settings>
|
||||
</state>
|
||||
</states>
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/modules.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/KeePassDX.iml" filepath="$PROJECT_DIR$/KeePassDX.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/runConfigurations.xml
generated
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
52
CHANGELOG
@@ -1,3 +1,55 @@
|
||||
KeepassDX (2.5.0.0beta9)
|
||||
* Education Screens to learn how to use the app
|
||||
* New designs
|
||||
* New custom font for character visibility
|
||||
* New themes
|
||||
* New icon pack
|
||||
* Change setting organisation
|
||||
* Pro version
|
||||
|
||||
KeepassDX (2.5.0.0beta8)
|
||||
* Hide custom entries protected
|
||||
* Best management of field references (https://keepass.info/help/base/fieldrefs.html)
|
||||
* Change database / default settings
|
||||
* Add Autofill for search
|
||||
* Add sorting by last access and by creation time
|
||||
* Rebuild custom entries
|
||||
* Refactor old code
|
||||
* Fix bugs
|
||||
|
||||
KeepassDX (2.5.0.0beta7)
|
||||
* Rebuild Notifications
|
||||
* Change links to https
|
||||
* Add extended Ascii (ñæËÌÂÝÜ...)
|
||||
* Upgrade custom visibility font
|
||||
* Best fingerprint error management
|
||||
* Add setting to prevent the password copy
|
||||
* Fix bugs
|
||||
|
||||
KeepassDX (2.5.0.0beta6)
|
||||
* Fix crash
|
||||
|
||||
KeepassDX (2.5.0.0beta5)
|
||||
* Autofill (Android O)
|
||||
* Deletion for group
|
||||
* New sorts with (Asc/Dsc, Groups before or after)
|
||||
* Better permission management with dialog at runtime
|
||||
* Setting to change font of field (monospace for a better visibility)
|
||||
* Open kdbx and kdb files from file browser
|
||||
* Change sort of fields
|
||||
* Hide empty fields
|
||||
* Add copy button for Username / Password and extra field
|
||||
* Add 5, 10, 20 seconds and 15 minutes of clipboard timeout
|
||||
* Hide "show password" icon when password not present
|
||||
* New animations for add button
|
||||
* New list to add and delete node with animation
|
||||
* Change view for better cohesion
|
||||
* Upgrade translations
|
||||
* Fix crash for API < Kitkat
|
||||
* Fix fingerprint bugs
|
||||
* Fix many small bugs
|
||||
* Add recycle bin setting (not yet accessible)
|
||||
|
||||
KeepassDX (2.5.0.0beta4)
|
||||
* Show only file name
|
||||
* Setting for full path
|
||||
|
||||
339
COPYING.gpl-2.0
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
16
LICENSE
@@ -1,7 +1,19 @@
|
||||
---
|
||||
|
||||
The KeePass icon was created by Jeremy JAMET and is licensed under the terms of GPLv3.
|
||||
KeePass DX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The KeePass DX icon was created by Jeremy JAMET and is licensed under the terms of GPLv3.
|
||||
|
||||
---
|
||||
|
||||
@@ -54,7 +66,7 @@ Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft <contact@kunzisoft.com>.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
|
||||
396
LICENSES/LICENSE-FONT-AWESOME
Normal file
@@ -0,0 +1,396 @@
|
||||
Attribution 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution 4.0 International Public License ("Public License"). To the
|
||||
extent this Public License may be interpreted as a contract, You are
|
||||
granted the Licensed Rights in consideration of Your acceptance of
|
||||
these terms and conditions, and the Licensor grants You such rights in
|
||||
consideration of benefits the Licensor receives from making the
|
||||
Licensed Material available under these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
d. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
f. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
g. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
h. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
4. If You Share Adapted Material You produce, the Adapter's
|
||||
License You apply must not prevent recipients of the Adapted
|
||||
Material from complying with this Public License.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
|
||||
201
LICENSES/LICENSE_FONT_DROID_SANS_MONO.txt
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
61
ReadMe.md
@@ -1,39 +1,48 @@
|
||||
# Android Keepass DX
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/logo.png"> Keepass DX is a material design Keepass Client for manage keys and passwords in crypt database for your android device.
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/icon.png"> Keepass DX is a multi-format KeePass manager for Android devices. The application allows to create keys and passwords in a secure way by integrating with the Android design standards.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen.jpg" width="220">
|
||||
|
||||
### Features
|
||||
|
||||
- Simplified creation of the database file
|
||||
- Create entries and groups
|
||||
- Open database, copy username / password, open URI / URL
|
||||
- Fingerprint for fast unlocking
|
||||
- Material design with themes
|
||||
- Device integration and AutoFill (In progress)
|
||||
* Create database files / entries and groups
|
||||
* Support for .kdb and .kdbx files (version 1 to 4) with AES - Twofish - ChaCha20 - Argon2 algorithm
|
||||
* Compatible with the majority of alternative programs (KeePass, KeePassX, KeePass XC...)
|
||||
* Allows fast copy of fields and opening of URI / URL
|
||||
* Fingerprint for fast unlocking
|
||||
* Material design with themes
|
||||
* AutoFill and Integration
|
||||
* Precise management of settings
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen1.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen2.jpg" width="220">
|
||||
Keepass DX is opensource and ad-free.
|
||||
|
||||
## What is KeePass?
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screen1.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screen2.jpg" width="220">
|
||||
|
||||
Today you need to remember many passwords. You need a password for the Windows network logon, your e-mail account, your website's FTP password, online passwords (like website member account), etc. etc. etc. The list is endless. Also, you should use different passwords for each account. Because if you use only one password everywhere and someone gets this password you have a problem... A serious problem. The thief would have access to your e-mail account, website, etc. Unimaginable.
|
||||
## What is KeePass DX?
|
||||
|
||||
KeePass is a free open source password manager, which helps you to manage your passwords in a secure way. You can put all your passwords in one database, which is locked with one master key or a key file. So you only have to remember one single master password or select the key file to unlock the whole database. The databases are encrypted using the best and most secure encryption algorithms currently known (AES and Twofish). For more information, see the features page.
|
||||
Today you need to remember many passwords. You need a password for your e-mail account, your website's FTP password, online passwords (like website member account), etc. etc. etc. The list is endless. Also, you should use different passwords for each account. Because if you use only one password everywhere and someone gets this password you have a problem... A serious problem. The thief would have access to your e-mail account, website, etc. Unimaginable.
|
||||
|
||||
KeePass DX is a free open source password manager for Android, which helps you to manage your passwords in a secure way. You can put all your passwords in one database, which is locked with one master key or a key file. So you only have to remember one single master password or select the key file to unlock the whole database. The databases are encrypted using the best and most secure encryption algorithms currently known.
|
||||
|
||||
## Is it really free?
|
||||
|
||||
Yes, KeePass is really free, and more than that: it is open source (OSI certified). You can have a look at its full source and check whether the encryption algorithms are implemented correctly.
|
||||
Yes, KeePass DX is under free license (OSI certified) and without advertising. You can have a look at its full source and check whether the encryption algorithms are implemented correctly.
|
||||
|
||||
## Donation
|
||||
*Note : If you access the application from a store, visual features may not be available to incentivize the contribution to the work of open source projects. These optional visuals are accessible after a donation (and a small congratulation message :) or the purchase of an extended version, but do not worry, the main features remain completely free.*
|
||||
|
||||
Even if the application is free, to maintain the application, you can make donations.
|
||||
## Contributions
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KM6QMDAXZM3UU "Kunzisoft Paypal Donation")
|
||||
You can contribute in different ways to help us on our work.
|
||||
|
||||
[](https://liberapay.com/Kunzisoft/donate "Kunzisoft Liberapay Donation")
|
||||
* Add features by a **pull request**.
|
||||
* Help to **translate** into your language
|
||||
* **[Donate](https://www.kunzisoft.com/donation)** 人◕ ‿‿ ◕人Y for a better service and a quick development of your features.
|
||||
* Buy the **[Pro version](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro)** of KeePass DX
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen4.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen5.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screen4.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screen5.jpg" width="220">
|
||||
|
||||
## Download
|
||||
|
||||
@@ -45,15 +54,11 @@ Even if the application is free, to maintain the application, you can make donat
|
||||
alt="Get it on Google Play"
|
||||
height="80">](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.free)
|
||||
|
||||
### JNI
|
||||
## Other devices
|
||||
|
||||
Native library build instructions:
|
||||
1. Make sure you have the latest MIPS Android NDK installed:
|
||||
https://developer.android.com/tools/sdk/ndk/index.html
|
||||
2. From KeePassDroid/app/src/main/jni, call prep_build.sh to download and unpack the crypto sources.
|
||||
3. The standard gradle files build everything now.
|
||||
- [KeePass XC](https://keepassxc.org/) (https://keepassxc.org/) works with **GNU/Linux**, **Mac** and **Windows**, is updated regulary and under the terms of the GNU General Public License. It's the recommended version for computers.
|
||||
|
||||
This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid) by bpellin.
|
||||
- [KeePass](https://keepass.info/) (https://keepass.info/) is the historical project, with good technical documentation for standardized database files but only running on **Windows**.
|
||||
|
||||
## License
|
||||
|
||||
@@ -63,7 +68,7 @@ This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid
|
||||
|
||||
KeePass DX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -73,3 +78,5 @@ This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid) by bpellin.
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion = 25
|
||||
buildToolsVersion = "27.0.1"
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '27.0.3'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.kunzisoft.keepass"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 25
|
||||
versionCode = 4
|
||||
versionName = "2.5.0.0beta4"
|
||||
targetSdkVersion 27
|
||||
versionCode = 9
|
||||
versionName = "2.5.0.0beta9"
|
||||
multiDexEnabled true
|
||||
|
||||
testApplicationId = "com.keepassdroid.tests"
|
||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||
testInstrumentationRunner = "android.test.InstrumentationTestRunner"
|
||||
|
||||
ndk {
|
||||
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
|
||||
'arm64-v8a', 'mips', 'mips64'
|
||||
}
|
||||
|
||||
buildConfigField "String[]", "ICON_PACKS", "{\"classic\",\"material\"}"
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
@@ -43,36 +46,73 @@ android {
|
||||
applicationIdSuffix = ".libre"
|
||||
versionNameSuffix "-libre"
|
||||
buildConfigField "boolean", "FULL_VERSION", "true"
|
||||
buildConfigField "boolean", "GOOGLE_PLAY_VERSION", "false"
|
||||
buildConfigField "boolean", "CLOSED_STORE", "false"
|
||||
buildConfigField "String[]", "STYLES_DISABLED", "{\"KeepassDXStyle_Dark\",\"KeepassDXStyle_Purple\"}"
|
||||
buildConfigField "String[]", "ICON_PACKS_DISABLED", "{}"
|
||||
|
||||
}
|
||||
pro_google {
|
||||
pro {
|
||||
applicationIdSuffix = ".pro"
|
||||
versionNameSuffix "-pro"
|
||||
buildConfigField "boolean", "FULL_VERSION", "true"
|
||||
buildConfigField "boolean", "GOOGLE_PLAY_VERSION", "true"
|
||||
buildConfigField "boolean", "CLOSED_STORE", "true"
|
||||
buildConfigField "String[]", "STYLES_DISABLED", "{}"
|
||||
buildConfigField "String[]", "ICON_PACKS_DISABLED", "{}"
|
||||
}
|
||||
free_google {
|
||||
free {
|
||||
applicationIdSuffix = ".free"
|
||||
versionNameSuffix "-free"
|
||||
buildConfigField "boolean", "FULL_VERSION", "false"
|
||||
buildConfigField "boolean", "GOOGLE_PLAY_VERSION", "true"
|
||||
buildConfigField "boolean", "CLOSED_STORE", "true"
|
||||
buildConfigField "String[]", "STYLES_DISABLED", "{\"KeepassDXStyle_Dark\",\"KeepassDXStyle_Blue\",\"KeepassDXStyle_Purple\"}"
|
||||
buildConfigField "String[]", "ICON_PACKS_DISABLED", "{\"material\"}"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
libre.res.srcDir 'src/libre/res'
|
||||
pro.res.srcDir 'src/pro/res'
|
||||
free.res.srcDir 'src/free/res'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
def supportVersion = "25.4.0"
|
||||
def supportVersion = "27.1.1"
|
||||
def spongycastleVersion = "1.58.0.0"
|
||||
def permissionDispatcherVersion = "3.1.0"
|
||||
|
||||
dependencies {
|
||||
androidTestCompile "junit:junit:4.12"
|
||||
compile "com.android.support:appcompat-v7:$supportVersion"
|
||||
compile "com.android.support:design:$supportVersion"
|
||||
compile "com.android.support:preference-v7:$supportVersion"
|
||||
compile "com.android.support:preference-v14:$supportVersion"
|
||||
compile "com.android.support:cardview-v7:$supportVersion"
|
||||
compile "com.madgag.spongycastle:core:$spongycastleVersion"
|
||||
compile "com.madgag.spongycastle:prov:$spongycastleVersion"
|
||||
compile "joda-time:joda-time:2.9.9"
|
||||
compile "org.sufficientlysecure:html-textview:3.5"
|
||||
compile "com.nononsenseapps:filepicker:4.1.0"
|
||||
implementation "com.android.support:appcompat-v7:$supportVersion"
|
||||
implementation "com.android.support:design:$supportVersion"
|
||||
implementation "com.android.support:preference-v7:$supportVersion"
|
||||
implementation "com.android.support:preference-v14:$supportVersion"
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation "com.madgag.spongycastle:core:$spongycastleVersion"
|
||||
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
|
||||
// Expandable view
|
||||
implementation 'net.cachapa.expandablelayout:expandablelayout:2.9.2'
|
||||
// Time
|
||||
implementation 'joda-time:joda-time:2.9.9'
|
||||
implementation 'org.sufficientlysecure:html-textview:3.5'
|
||||
implementation 'com.nononsenseapps:filepicker:4.1.0'
|
||||
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.11.0'
|
||||
// Permissions
|
||||
implementation("com.github.hotchemi:permissionsdispatcher:$permissionDispatcherVersion") {
|
||||
// if you don't use android.app.Fragment you can exclude support for them
|
||||
exclude module: "support-v13"
|
||||
}
|
||||
annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$permissionDispatcherVersion"
|
||||
// Apache Commons Collections
|
||||
implementation 'commons-collections:commons-collections:3.2.1'
|
||||
// Base64
|
||||
implementation 'biz.source_code:base64coder:2010-12-19'
|
||||
implementation 'com.google.code.gson:gson:2.8.1'
|
||||
implementation 'com.google.guava:guava:23.0-android'
|
||||
// Icon pack, classic for all, material for libre and pro
|
||||
implementation project(path: ':icon-pack-classic')
|
||||
implementation project(path: ':icon-pack-material')
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.PwIconCustom;
|
||||
import com.keepassdroid.database.PwIconStandard;
|
||||
import com.keepassdroid.database.security.ProtectedBinary;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
|
||||
public class PwEntryTestV4 extends TestCase {
|
||||
public void testAssign() {
|
||||
PwEntryV4 entry = new PwEntryV4();
|
||||
|
||||
entry.additional = "test223";
|
||||
|
||||
entry.autoType = entry.new AutoType();
|
||||
entry.autoType.defaultSequence = "1324";
|
||||
entry.autoType.enabled = true;
|
||||
entry.autoType.obfuscationOptions = 123412432109L;
|
||||
entry.autoType.put("key", "value");
|
||||
|
||||
entry.backgroupColor = "blue";
|
||||
entry.binaries.put("key1", new ProtectedBinary(false, new byte[] {0,1}));
|
||||
entry.customIcon = new PwIconCustom(UUID.randomUUID(), new byte[0]);
|
||||
entry.foregroundColor = "red";
|
||||
entry.history.add(new PwEntryV4());
|
||||
entry.icon = new PwIconStandard(5);
|
||||
entry.overrideURL = "override";
|
||||
entry.parent = new PwGroupV4();
|
||||
entry.strings.put("key2", new ProtectedString(false, "value2"));
|
||||
entry.url = "http://localhost";
|
||||
entry.uuid = UUID.randomUUID();
|
||||
|
||||
PwEntryV4 target = new PwEntryV4();
|
||||
target.assign(entry);
|
||||
|
||||
/* This test is not so useful now that I am not implementing value equality for Entries
|
||||
assertTrue("Entries do not match.", entry.equals(target));
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,11 +17,11 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class AccentTest extends AndroidTestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
@@ -29,7 +29,7 @@ public class OutputTests extends TestSuite {
|
||||
public static Test suite() {
|
||||
|
||||
return new TestSuiteBuilder(AllTests.class)
|
||||
.includePackages("com.keepassdroid.tests.output")
|
||||
.includePackages("com.kunzisoft.keepass.tests.output")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,11 +17,11 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDate;
|
||||
import com.kunzisoft.keepass.database.PwDate;
|
||||
|
||||
public class PwDateTest extends TestCase {
|
||||
public void testDate() {
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -27,8 +27,8 @@ import java.util.Calendar;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwEntryV3;
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.database.PwEntryV3;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class PwEntryTestV3 extends AndroidTestCase {
|
||||
PwEntryV3 mPE;
|
||||
@@ -37,12 +37,12 @@ public class PwEntryTestV3 extends AndroidTestCase {
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mPE = (PwEntryV3) TestData.GetTest1(getContext()).entries.get(0);
|
||||
mPE = (PwEntryV3) TestData.GetTest1(getContext()).getEntryAt(0);
|
||||
|
||||
}
|
||||
|
||||
public void testName() {
|
||||
assertTrue("Name was " + mPE.title, mPE.title.equals("Amazon"));
|
||||
assertTrue("Name was " + mPE.getTitle(), mPE.getTitle().equals("Amazon"));
|
||||
}
|
||||
|
||||
public void testPassword() throws UnsupportedEncodingException {
|
||||
@@ -54,7 +54,7 @@ public class PwEntryTestV3 extends AndroidTestCase {
|
||||
|
||||
public void testCreation() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(mPE.tCreation.getJDate());
|
||||
cal.setTime(mPE.getCreationTime().getDate());
|
||||
|
||||
assertEquals("Incorrect year.", cal.get(Calendar.YEAR), 2009);
|
||||
assertEquals("Incorrect month.", cal.get(Calendar.MONTH), 3);
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import com.kunzisoft.keepass.database.AutoType;
|
||||
import com.kunzisoft.keepass.database.PwEntryV4;
|
||||
import com.kunzisoft.keepass.database.PwGroupV4;
|
||||
import com.kunzisoft.keepass.database.PwIconCustom;
|
||||
import com.kunzisoft.keepass.database.PwIconStandard;
|
||||
import com.kunzisoft.keepass.database.security.ProtectedBinary;
|
||||
import com.kunzisoft.keepass.database.security.ProtectedString;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PwEntryTestV4 extends TestCase {
|
||||
public void testAssign() {
|
||||
PwEntryV4 entry = new PwEntryV4();
|
||||
|
||||
entry.setAdditional("test223");
|
||||
|
||||
entry.setAutoType(new AutoType());
|
||||
entry.getAutoType().defaultSequence = "1324";
|
||||
entry.getAutoType().enabled = true;
|
||||
entry.getAutoType().obfuscationOptions = 123412432109L;
|
||||
entry.getAutoType().put("key", "value");
|
||||
|
||||
entry.setBackgroupColor("blue");
|
||||
entry.putProtectedBinary("key1", new ProtectedBinary(false, new byte[] {0,1}));
|
||||
entry.setCustomIcon(new PwIconCustom(UUID.randomUUID(), new byte[0]));
|
||||
entry.setForegroundColor("red");
|
||||
entry.addToHistory(new PwEntryV4());
|
||||
entry.setIcon(new PwIconStandard(5));
|
||||
entry.setOverrideURL("override");
|
||||
entry.setParent(new PwGroupV4());
|
||||
entry.addExtraField("key2", new ProtectedString(false, "value2"));
|
||||
entry.setUrl("http://localhost");
|
||||
entry.setUUID(UUID.randomUUID());
|
||||
|
||||
PwEntryV4 target = new PwEntryV4();
|
||||
target.updateWith(entry);
|
||||
|
||||
/* This test is not so useful now that I am not implementing value equality for Entries
|
||||
assertTrue("Entries do not match.", entry.equals(target));
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,13 +17,13 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.database.PwGroupV3;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class PwGroupTest extends AndroidTestCase {
|
||||
|
||||
@@ -38,7 +38,7 @@ public class PwGroupTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
public void testGroupName() {
|
||||
assertTrue("Name was " + mPG.name, mPG.name.equals("Internet"));
|
||||
assertTrue("Name was " + mPG.getName(), mPG.getName().equals("Internet"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -29,8 +29,8 @@ import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.kunzisoft.keepass.utils.EmptyUtils;
|
||||
import com.kunzisoft.keepass.utils.UriUtil;
|
||||
|
||||
public class TestUtil {
|
||||
private static final File sdcard = Environment.getExternalStorageDirectory();
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -28,10 +28,10 @@ import java.util.UUID;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDate;
|
||||
import com.keepassdroid.stream.LEDataInputStream;
|
||||
import com.keepassdroid.stream.LEDataOutputStream;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.kunzisoft.keepass.database.PwDate;
|
||||
import com.kunzisoft.keepass.stream.LEDataInputStream;
|
||||
import com.kunzisoft.keepass.stream.LEDataOutputStream;
|
||||
import com.kunzisoft.keepass.utils.Types;
|
||||
|
||||
public class TypesTest extends TestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,9 +17,11 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.crypto;
|
||||
package com.kunzisoft.keepass.tests.crypto;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import com.kunzisoft.keepass.crypto.CipherFactory;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
@@ -33,9 +35,7 @@ import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.crypto.CipherFactory;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class AESTest extends TestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.crypto;
|
||||
package com.kunzisoft.keepass.tests.crypto;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -37,11 +37,11 @@ import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.crypto.CipherFactory;
|
||||
import com.keepassdroid.crypto.engine.AesEngine;
|
||||
import com.keepassdroid.crypto.engine.CipherEngine;
|
||||
import com.keepassdroid.stream.BetterCipherInputStream;
|
||||
import com.keepassdroid.stream.LEDataInputStream;
|
||||
import com.kunzisoft.keepass.crypto.CipherFactory;
|
||||
import com.kunzisoft.keepass.crypto.engine.AesEngine;
|
||||
import com.kunzisoft.keepass.crypto.engine.CipherEngine;
|
||||
import com.kunzisoft.keepass.stream.BetterCipherInputStream;
|
||||
import com.kunzisoft.keepass.stream.LEDataInputStream;
|
||||
|
||||
public class CipherTest extends TestCase {
|
||||
private Random rand = new Random();
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.crypto;
|
||||
package com.kunzisoft.keepass.tests.crypto;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -26,8 +26,8 @@ import java.util.Random;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.crypto.finalkey.AndroidFinalKey;
|
||||
import com.keepassdroid.crypto.finalkey.NativeFinalKey;
|
||||
import com.kunzisoft.keepass.crypto.finalkey.AndroidFinalKey;
|
||||
import com.kunzisoft.keepass.crypto.finalkey.NativeFinalKey;
|
||||
|
||||
public class FinalKeyTest extends TestCase {
|
||||
private Random mRand;
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,20 +17,21 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.edit.DeleteGroup;
|
||||
import com.keepassdroid.search.SearchDbHelper;
|
||||
import com.kunzisoft.keepass.database.Database;
|
||||
import com.kunzisoft.keepass.database.PwDatabase;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV3;
|
||||
import com.kunzisoft.keepass.database.PwEntry;
|
||||
import com.kunzisoft.keepass.database.PwEntryV3;
|
||||
import com.kunzisoft.keepass.database.PwGroup;
|
||||
import com.kunzisoft.keepass.database.edit.DeleteGroup;
|
||||
import com.kunzisoft.keepass.search.SearchDbHelper;
|
||||
|
||||
public class DeleteEntry extends AndroidTestCase {
|
||||
private static final String GROUP1_NAME = "Group1";
|
||||
@@ -54,12 +55,12 @@ public class DeleteEntry extends AndroidTestCase {
|
||||
return;
|
||||
}
|
||||
|
||||
PwDatabaseV3 pm = (PwDatabaseV3) db.pm;
|
||||
PwDatabaseV3 pm = (PwDatabaseV3) db.getPwDatabase();
|
||||
PwGroup group1 = getGroup(pm, GROUP1_NAME);
|
||||
assertNotNull("Could not find group1", group1);
|
||||
|
||||
// Delete the group
|
||||
DeleteGroup task = new DeleteGroup(db, group1, null, true);
|
||||
DeleteGroup task = new DeleteGroup(null, db, group1, null, true);
|
||||
task.run();
|
||||
|
||||
// Verify the entries were deleted
|
||||
@@ -71,24 +72,22 @@ public class DeleteEntry extends AndroidTestCase {
|
||||
|
||||
// Verify the entries were removed from the search index
|
||||
SearchDbHelper dbHelp = new SearchDbHelper(ctx);
|
||||
PwGroup results1 = dbHelp.search(db, ENTRY1_NAME);
|
||||
PwGroup results2 = dbHelp.search(db, ENTRY2_NAME);
|
||||
PwGroup results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME);
|
||||
PwGroup results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME);
|
||||
|
||||
assertEquals("Entry1 was not removed from the search results", 0, results1.childEntries.size());
|
||||
assertEquals("Entry2 was not removed from the search results", 0, results2.childEntries.size());
|
||||
assertEquals("Entry1 was not removed from the search results", 0, results1.numbersOfChildEntries());
|
||||
assertEquals("Entry2 was not removed from the search results", 0, results2.numbersOfChildEntries());
|
||||
|
||||
// Verify the group was deleted
|
||||
group1 = getGroup(pm, GROUP1_NAME);
|
||||
assertNull("Group 1 was not removed.", group1);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private PwEntry getEntry(PwDatabaseV3 pm, String name) {
|
||||
List<PwEntry> entries = pm.entries;
|
||||
private PwEntryV3 getEntry(PwDatabaseV3 pm, String name) {
|
||||
List<PwEntryV3> entries = pm.getEntries();
|
||||
for ( int i = 0; i < entries.size(); i++ ) {
|
||||
PwEntry entry = entries.get(i);
|
||||
PwEntryV3 entry = entries.get(i);
|
||||
if ( entry.getTitle().equals(name) ) {
|
||||
return entry;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,34 +17,36 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.PwEntryV4;
|
||||
|
||||
public class EntryV4 extends TestCase {
|
||||
|
||||
public void testBackup() {
|
||||
PwDatabaseV4 db = new PwDatabaseV4();
|
||||
|
||||
db.historyMaxItems = 2;
|
||||
db.setHistoryMaxItems(2);
|
||||
|
||||
PwEntryV4 entry = new PwEntryV4();
|
||||
entry.setTitle("Title1", db);
|
||||
entry.setUsername("User1", db);
|
||||
entry.startToManageFieldReferences(db);
|
||||
entry.setTitle("Title1");
|
||||
entry.setUsername("User1");
|
||||
entry.createBackup(db);
|
||||
|
||||
entry.setTitle("Title2", db);
|
||||
entry.setUsername("User2", db);
|
||||
entry.setTitle("Title2");
|
||||
entry.setUsername("User2");
|
||||
entry.createBackup(db);
|
||||
|
||||
entry.setTitle("Title3", db);
|
||||
entry.setUsername("User3", db);
|
||||
entry.setTitle("Title3");
|
||||
entry.setUsername("User3");
|
||||
entry.createBackup(db);
|
||||
|
||||
PwEntryV4 backup = entry.history.get(0);
|
||||
PwEntryV4 backup = entry.getHistory().get(0);
|
||||
entry.endToManageFieldReferences();
|
||||
assertEquals("Title2", backup.getTitle());
|
||||
assertEquals("User2", backup.getUsername());
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
@@ -25,9 +25,9 @@ import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.load.ImporterV3;
|
||||
import com.keepassdroid.tests.TestUtil;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV3;
|
||||
import com.kunzisoft.keepass.tests.TestUtil;
|
||||
import com.kunzisoft.keepass.utils.UriUtil;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.File;
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -25,9 +25,9 @@ import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwEncryptionAlgorithm;
|
||||
import com.keepassdroid.database.load.ImporterV3;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV3;
|
||||
import com.kunzisoft.keepass.database.PwEncryptionAlgorithm;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV3;
|
||||
|
||||
public class Kdb3Twofish extends AndroidTestCase {
|
||||
public void testReadTwofish() throws Exception {
|
||||
@@ -40,10 +40,9 @@ public class Kdb3Twofish extends AndroidTestCase {
|
||||
|
||||
PwDatabaseV3 db = importer.openDatabase(is, "12345", null);
|
||||
|
||||
assertTrue(db.algorithm == PwEncryptionAlgorithm.Twofish);
|
||||
assertTrue(db.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish);
|
||||
|
||||
is.close();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -29,16 +29,16 @@ import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.exception.InvalidDBException;
|
||||
import com.keepassdroid.database.exception.PwDbOutputException;
|
||||
import com.keepassdroid.database.load.Importer;
|
||||
import com.keepassdroid.database.load.ImporterFactory;
|
||||
import com.keepassdroid.database.load.ImporterV4;
|
||||
import com.keepassdroid.database.save.PwDbOutput;
|
||||
import com.keepassdroid.database.save.PwDbV4Output;
|
||||
import com.keepassdroid.stream.CopyInputStream;
|
||||
import com.keepassdroid.tests.TestUtil;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.exception.InvalidDBException;
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException;
|
||||
import com.kunzisoft.keepass.database.load.Importer;
|
||||
import com.kunzisoft.keepass.database.load.ImporterFactory;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV4;
|
||||
import com.kunzisoft.keepass.database.save.PwDbOutput;
|
||||
import com.kunzisoft.keepass.database.save.PwDbV4Output;
|
||||
import com.kunzisoft.keepass.stream.CopyInputStream;
|
||||
import com.kunzisoft.keepass.tests.TestUtil;
|
||||
|
||||
public class Kdb4 extends AndroidTestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,18 +17,17 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
|
||||
import java.io.InputStream;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.crypto.CipherFactory;
|
||||
import com.keepassdroid.crypto.engine.AesEngine;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.load.ImporterV4;
|
||||
import com.kunzisoft.keepass.crypto.engine.AesEngine;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV4;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public class Kdb4Header extends AndroidTestCase {
|
||||
public void testReadHeader() throws Exception {
|
||||
@@ -41,9 +40,9 @@ public class Kdb4Header extends AndroidTestCase {
|
||||
|
||||
PwDatabaseV4 db = importer.openDatabase(is, "12345", null);
|
||||
|
||||
assertEquals(6000, db.numKeyEncRounds);
|
||||
assertEquals(6000, db.getNumberKeyEncryptionRounds());
|
||||
|
||||
assertTrue(db.dataCipher.equals(AesEngine.CIPHER_UUID));
|
||||
assertTrue(db.getDataCipher().equals(AesEngine.CIPHER_UUID));
|
||||
|
||||
is.close();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.UUID;
|
||||
@@ -27,15 +27,16 @@ import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
import biz.source_code.base64Coder.Base64Coder;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.load.ImporterV4;
|
||||
import com.keepassdroid.utils.SprEngine;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.kunzisoft.keepass.database.PwDatabase;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.PwEntryV4;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV4;
|
||||
import com.kunzisoft.keepass.utils.SprEngineV4;
|
||||
import com.kunzisoft.keepass.utils.Types;
|
||||
|
||||
public class SprEngineTest extends AndroidTestCase {
|
||||
private PwDatabaseV4 db;
|
||||
private SprEngine spr;
|
||||
private SprEngineV4 spr;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
@@ -51,7 +52,7 @@ public class SprEngineTest extends AndroidTestCase {
|
||||
|
||||
is.close();
|
||||
|
||||
spr = SprEngine.getInstance(db);
|
||||
spr = new SprEngineV4();
|
||||
}
|
||||
|
||||
private final String REF = "{REF:P@I:2B1D56590D961F48A8CE8C392CE6CD35}";
|
||||
@@ -60,7 +61,7 @@ public class SprEngineTest extends AndroidTestCase {
|
||||
public void testRefReplace() {
|
||||
UUID entryUUID = decodeUUID(ENCODE_UUID);
|
||||
|
||||
PwEntryV4 entry = (PwEntryV4) db.entries.get(entryUUID);
|
||||
PwEntryV4 entry = (PwEntryV4) db.getEntryByUUIDId(entryUUID);
|
||||
|
||||
|
||||
assertEquals(RESULT, spr.compile(REF, entry, db));
|
||||
@@ -69,7 +70,7 @@ public class SprEngineTest extends AndroidTestCase {
|
||||
|
||||
private UUID decodeUUID(String encoded) {
|
||||
if (encoded == null || encoded.length() == 0 ) {
|
||||
return PwDatabaseV4.UUID_ZERO;
|
||||
return PwDatabase.UUID_ZERO;
|
||||
}
|
||||
|
||||
byte[] buf = Base64Coder.decode(encoded);
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -25,12 +25,10 @@ import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwDatabaseV3Debug;
|
||||
import com.keepassdroid.database.load.Importer;
|
||||
import com.keepassdroid.tests.TestUtil;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.kunzisoft.keepass.database.Database;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV3Debug;
|
||||
import com.kunzisoft.keepass.database.load.Importer;
|
||||
import com.kunzisoft.keepass.tests.TestUtil;
|
||||
|
||||
public class TestData {
|
||||
private static final String TEST1_KEYFILE = "";
|
||||
@@ -60,10 +58,10 @@ public class TestData {
|
||||
|
||||
InputStream keyIs = TestUtil.getKeyFileInputStream(ctx, keyfile);
|
||||
|
||||
Db.LoadData(ctx, is, password, keyIs, Importer.DEBUG);
|
||||
Db.loadData(ctx, is, password, keyIs, Importer.DEBUG);
|
||||
Uri.Builder b = new Uri.Builder();
|
||||
|
||||
Db.mUri = b.scheme("file").path(filename).build();
|
||||
Db.setUri(b.scheme("file").path(filename).build());
|
||||
|
||||
return Db;
|
||||
|
||||
@@ -74,6 +72,6 @@ public class TestData {
|
||||
GetDb1(ctx);
|
||||
}
|
||||
|
||||
return (PwDatabaseV3Debug) mDb1.pm;
|
||||
return (PwDatabaseV3Debug) mDb1.getPwDatabase();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.output;
|
||||
package com.kunzisoft.keepass.tests.output;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -32,16 +32,16 @@ import java.security.NoSuchAlgorithmException;
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV3Debug;
|
||||
import com.keepassdroid.database.PwDbHeader;
|
||||
import com.keepassdroid.database.PwDbHeaderV3;
|
||||
import com.keepassdroid.database.exception.PwDbOutputException;
|
||||
import com.keepassdroid.database.save.PwDbHeaderOutputV3;
|
||||
import com.keepassdroid.database.save.PwDbV3Output;
|
||||
import com.keepassdroid.database.save.PwDbV3OutputDebug;
|
||||
import com.keepassdroid.stream.NullOutputStream;
|
||||
import com.keepassdroid.tests.TestUtil;
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV3Debug;
|
||||
import com.kunzisoft.keepass.database.PwDbHeader;
|
||||
import com.kunzisoft.keepass.database.PwDbHeaderV3;
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException;
|
||||
import com.kunzisoft.keepass.database.save.PwDbHeaderOutputV3;
|
||||
import com.kunzisoft.keepass.database.save.PwDbV3Output;
|
||||
import com.kunzisoft.keepass.database.save.PwDbV3OutputDebug;
|
||||
import com.kunzisoft.keepass.stream.NullOutputStream;
|
||||
import com.kunzisoft.keepass.tests.TestUtil;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class PwManagerOutputTest extends AndroidTestCase {
|
||||
PwDatabaseV3Debug mPM;
|
||||
@@ -60,7 +60,7 @@ public class PwManagerOutputTest extends AndroidTestCase {
|
||||
pos.outputPlanGroupAndEntries(bos);
|
||||
|
||||
assertTrue("No output", bos.toByteArray().length > 0);
|
||||
assertArrayEquals("Group and entry output doesn't match.", mPM.postHeader, bos.toByteArray());
|
||||
assertArrayEquals("Group and entry output doesn't match.", mPM.getPostHeader(), bos.toByteArray());
|
||||
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class PwManagerOutputTest extends AndroidTestCase {
|
||||
|
||||
byte[] digest = md.digest();
|
||||
assertTrue("No output", digest.length > 0);
|
||||
assertArrayEquals("Hash of groups and entries failed.", mPM.dbHeader.contentsHash, digest);
|
||||
assertArrayEquals("Hash of groups and entries failed.", mPM.getDbHeader().contentsHash, digest);
|
||||
}
|
||||
|
||||
private void assertHeadersEquals(PwDbHeaderV3 expected, PwDbHeaderV3 actual) {
|
||||
@@ -100,10 +100,10 @@ public class PwManagerOutputTest extends AndroidTestCase {
|
||||
PwDbHeaderV3 header = pActual.outputHeader(bActual);
|
||||
|
||||
ByteArrayOutputStream bExpected = new ByteArrayOutputStream();
|
||||
PwDbHeaderOutputV3 outExpected = new PwDbHeaderOutputV3(mPM.dbHeader, bExpected);
|
||||
PwDbHeaderOutputV3 outExpected = new PwDbHeaderOutputV3(mPM.getDbHeader(), bExpected);
|
||||
outExpected.output();
|
||||
|
||||
assertHeadersEquals(mPM.dbHeader, header);
|
||||
assertHeadersEquals(mPM.getDbHeader(), header);
|
||||
assertTrue("No output", bActual.toByteArray().length > 0);
|
||||
assertArrayEquals("Header does not match.", bExpected.toByteArray(), bActual.toByteArray());
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class PwManagerOutputTest extends AndroidTestCase {
|
||||
PwDbHeader hActual = pActual.outputHeader(bActual);
|
||||
byte[] finalKey = pActual.getFinalKey(hActual);
|
||||
|
||||
assertArrayEquals("Keys mismatched", mPM.finalKey, finalKey);
|
||||
assertArrayEquals("Keys mismatched", mPM.getFinalKey(), finalKey);
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.search;
|
||||
package com.kunzisoft.keepass.tests.search;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
@@ -25,10 +25,9 @@ import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.database.Database;
|
||||
import com.kunzisoft.keepass.database.PwGroup;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class SearchTest extends AndroidTestCase {
|
||||
|
||||
@@ -42,23 +41,23 @@ public class SearchTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
public void testSearch() {
|
||||
PwGroup results = mDb.Search("Amazon");
|
||||
assertTrue("Search result not found.", results.childEntries.size() > 0);
|
||||
PwGroup results = mDb.search("Amazon");
|
||||
assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
|
||||
|
||||
}
|
||||
|
||||
public void testBackupIncluded() {
|
||||
updateOmitSetting(false);
|
||||
PwGroup results = mDb.Search("BackupOnly");
|
||||
PwGroup results = mDb.search("BackupOnly");
|
||||
|
||||
assertTrue("Search result not found.", results.childEntries.size() > 0);
|
||||
assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
|
||||
}
|
||||
|
||||
public void testBackupExcluded() {
|
||||
updateOmitSetting(true);
|
||||
PwGroup results = mDb.Search("BackupOnly");
|
||||
PwGroup results = mDb.search("BackupOnly");
|
||||
|
||||
assertFalse("Search result found, but should not have been.", results.childEntries.size() > 0);
|
||||
assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0);
|
||||
}
|
||||
|
||||
private void updateOmitSetting(boolean setting) {
|
||||
@@ -66,7 +65,7 @@ public class SearchTest extends AndroidTestCase {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
editor.putBoolean(ctx.getString(R.string.omitbackup_key), setting);
|
||||
editor.putBoolean("settings_omitbackup_key", setting);
|
||||
editor.commit();
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.stream;
|
||||
package com.kunzisoft.keepass.tests.stream;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -30,8 +30,8 @@ import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.stream.HashedBlockInputStream;
|
||||
import com.keepassdroid.stream.HashedBlockOutputStream;
|
||||
import com.kunzisoft.keepass.stream.HashedBlockInputStream;
|
||||
import com.kunzisoft.keepass.stream.HashedBlockOutputStream;
|
||||
|
||||
public class HashedBlock extends TestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,11 +17,11 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.utils;
|
||||
package com.kunzisoft.keepass.tests.utils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import com.keepassdroid.utils.StrUtil;
|
||||
import com.kunzisoft.keepass.utils.StrUtil;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
BIN
app/src/free/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/free/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
app/src/free/res/mipmap-ldpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/free/res/mipmap-ldpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/free/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/free/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/free/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/free/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
app/src/free/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
app/src/free/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
app/src/free/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
app/src/free/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
app/src/libre/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/libre/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
app/src/libre/res/mipmap-ldpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/libre/res/mipmap-ldpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/libre/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/libre/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/libre/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
app/src/libre/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
app/src/libre/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
app/src/libre/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app/src/libre/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
app/src/libre/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
@@ -16,51 +16,37 @@
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:name="com.keepassdroid.app.App"
|
||||
android:name="com.kunzisoft.keepass.app.App"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup"
|
||||
android:backupAgent="com.keepassdroid.backup.SettingsBackupAgent"
|
||||
android:theme="@style/KeepassDXStyle.Light"
|
||||
android:backupAgent="com.kunzisoft.keepass.backup.SettingsBackupAgent"
|
||||
android:theme="@style/KeepassDXStyle.Night"
|
||||
tools:replace="android:theme">
|
||||
<!-- TODO backup API Key -->
|
||||
<meta-data android:name="com.google.android.backup.api_key"
|
||||
<meta-data
|
||||
android:name="com.google.android.backup.api_key"
|
||||
android:value="" />
|
||||
|
||||
<!-- Folder picker -->
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/nnf_provider_paths" />
|
||||
</provider>
|
||||
<activity
|
||||
android:name="com.keepassdroid.FilePickerStylishActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".KeePass"
|
||||
android:label="@string/app_name">
|
||||
android:name="com.kunzisoft.keepass.fileselect.FileSelectActivity"
|
||||
android:theme="@style/KeepassDXStyle.SplashScreen"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.keepassdroid.fileselect.FileSelectActivity"
|
||||
android:launchMode="singleInstance"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity android:name="com.keepassdroid.AboutActivity"
|
||||
android:launchMode="singleInstance"
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.AboutActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/menu_about" />
|
||||
<activity android:name="com.keepassdroid.PasswordActivity"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.password.PasswordActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@@ -90,42 +76,81 @@
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
||||
</intent-filter>
|
||||
<intent-filter tools:ignore="AppLinkUrlError">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:mimeType="application/octet-stream"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.keepassdroid.GroupActivityV3" android:configChanges="orientation|keyboardHidden">
|
||||
<meta-data android:name="android.app.default_searchable"
|
||||
android:value="com.keepassdroid.search.SearchResults" />
|
||||
</activity>
|
||||
<activity android:name="com.keepassdroid.GroupActivityV4" android:configChanges="orientation|keyboardHidden">
|
||||
<meta-data android:name="android.app.default_searchable"
|
||||
android:value="com.keepassdroid.search.SearchResults"
|
||||
android:exported="false" />
|
||||
<!-- Folder picker -->
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/nnf_provider_paths" />
|
||||
</provider>
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.fileselect.FilePickerStylishActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.keepassdroid.EntryActivity"
|
||||
android:name="com.kunzisoft.keepass.activities.GroupActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="adjustPan">
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:value="com.kunzisoft.keepass.search.SearchResults"
|
||||
android:exported="false"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.EntryActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name="com.keepassdroid.EntryActivityV4"
|
||||
android:name="com.kunzisoft.keepass.activities.EntryEditActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name="com.keepassdroid.EntryEditActivityV3"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name="com.keepassdroid.EntryEditActivityV4"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity android:name="com.keepassdroid.search.SearchResultsActivity" android:launchMode="standard">
|
||||
android:name="com.kunzisoft.keepass.search.SearchResultsActivity"
|
||||
android:launchMode="standard">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable" />
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
<activity android:name="com.keepassdroid.settings.SettingsActivity" />
|
||||
<activity android:name="com.kunzisoft.keepass.settings.SettingsActivity" />
|
||||
<activity android:name="com.kunzisoft.keepass.autofill.AutoFillAuthActivity"
|
||||
android:configChanges="orientation|keyboardHidden" />
|
||||
<activity android:name="com.kunzisoft.keepass.settings.SettingsAutofillActivity" />
|
||||
|
||||
<service android:name="com.keepassdroid.services.TimeoutService" />
|
||||
<service android:name="com.kunzisoft.keepass.timeout.TimeoutService" />
|
||||
<service
|
||||
android:name="com.kunzisoft.keepass.notifications.NotificationCopyingService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name="com.kunzisoft.keepass.autofill.KeeAutofillService"
|
||||
android:label="@string/autofill_service_name"
|
||||
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
|
||||
<meta-data
|
||||
android:name="android.autofill"
|
||||
android:resource="@xml/dataset_service" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.service.autofill.AutofillService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
BIN
app/src/main/assets/fonts/DroidSansMonoSlashed.ttf
Normal file
@@ -1,225 +0,0 @@
|
||||
// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
|
||||
// www.source-code.biz, www.inventec.ch/chdh
|
||||
//
|
||||
// This module is multi-licensed and may be used under the terms
|
||||
// of any of the following licenses:
|
||||
//
|
||||
// EPL, Eclipse Public License, http://www.eclipse.org/legal
|
||||
// LGPL, GNU Lesser General Public License, http://www.gnu.org/licenses/lgpl.html
|
||||
// AL, Apache License, http://www.apache.org/licenses
|
||||
// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
|
||||
//
|
||||
// Please contact the author if you need another license.
|
||||
// This module is provided "as is", without warranties of any kind.
|
||||
|
||||
package biz.source_code.base64Coder;
|
||||
|
||||
/**
|
||||
* A Base64 encoder/decoder.
|
||||
*
|
||||
* <p>
|
||||
* This class is used to encode and decode data in Base64 format as described in RFC 1521.
|
||||
*
|
||||
* <p>
|
||||
* Project home page: <a href="http://www.source-code.biz/base64coder/java/">www.source-code.biz/base64coder/java</a><br>
|
||||
* Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
|
||||
* Multi-licensed: EPL / LGPL / AL / BSD.
|
||||
*/
|
||||
public class Base64Coder {
|
||||
|
||||
// The line separator string of the operating system.
|
||||
private static final String systemLineSeparator = System.getProperty("line.separator");
|
||||
|
||||
// Mapping table from 6-bit nibbles to Base64 characters.
|
||||
private static char[] map1 = new char[64];
|
||||
static {
|
||||
int i=0;
|
||||
for (char c='A'; c<='Z'; c++) map1[i++] = c;
|
||||
for (char c='a'; c<='z'; c++) map1[i++] = c;
|
||||
for (char c='0'; c<='9'; c++) map1[i++] = c;
|
||||
map1[i++] = '+'; map1[i++] = '/'; }
|
||||
|
||||
// Mapping table from Base64 characters to 6-bit nibbles.
|
||||
private static byte[] map2 = new byte[128];
|
||||
static {
|
||||
for (int i=0; i<map2.length; i++) map2[i] = -1;
|
||||
for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
|
||||
|
||||
/**
|
||||
* Encodes a string into Base64 format.
|
||||
* No blanks or line breaks are inserted.
|
||||
* @param s A String to be encoded.
|
||||
* @return A String containing the Base64 encoded data.
|
||||
*/
|
||||
public static String encodeString (String s) {
|
||||
return new String(encode(s.getBytes())); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters.
|
||||
* This method is compatible with <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @return A String containing the Base64 encoded data, broken into lines.
|
||||
*/
|
||||
public static String encodeLines (byte[] in) {
|
||||
return encodeLines(in, 0, in.length, 76, systemLineSeparator); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base 64 format and breaks the output into lines.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @param iOff Offset of the first byte in <code>in</code> to be processed.
|
||||
* @param iLen Number of bytes to be processed in <code>in</code>, starting at <code>iOff</code>.
|
||||
* @param lineLen Line length for the output data. Should be a multiple of 4.
|
||||
* @param lineSeparator The line separator to be used to separate the output lines.
|
||||
* @return A String containing the Base64 encoded data, broken into lines.
|
||||
*/
|
||||
public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) {
|
||||
int blockLen = (lineLen*3) / 4;
|
||||
if (blockLen <= 0) throw new IllegalArgumentException();
|
||||
int lines = (iLen+blockLen-1) / blockLen;
|
||||
int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length();
|
||||
StringBuilder buf = new StringBuilder(bufLen);
|
||||
int ip = 0;
|
||||
while (ip < iLen) {
|
||||
int l = Math.min(iLen-ip, blockLen);
|
||||
buf.append (encode(in, iOff+ip, l));
|
||||
buf.append (lineSeparator);
|
||||
ip += l; }
|
||||
return buf.toString(); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 format.
|
||||
* No blanks or line breaks are inserted in the output.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @return A character array containing the Base64 encoded data.
|
||||
*/
|
||||
public static char[] encode (byte[] in) {
|
||||
return encode(in, 0, in.length); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 format.
|
||||
* No blanks or line breaks are inserted in the output.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @param iLen Number of bytes to process in <code>in</code>.
|
||||
* @return A character array containing the Base64 encoded data.
|
||||
*/
|
||||
public static char[] encode (byte[] in, int iLen) {
|
||||
return encode(in, 0, iLen); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 format.
|
||||
* No blanks or line breaks are inserted in the output.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @param iOff Offset of the first byte in <code>in</code> to be processed.
|
||||
* @param iLen Number of bytes to process in <code>in</code>, starting at <code>iOff</code>.
|
||||
* @return A character array containing the Base64 encoded data.
|
||||
*/
|
||||
public static char[] encode (byte[] in, int iOff, int iLen) {
|
||||
int oDataLen = (iLen*4+2)/3; // output length without padding
|
||||
int oLen = ((iLen+2)/3)*4; // output length including padding
|
||||
char[] out = new char[oLen];
|
||||
int ip = iOff;
|
||||
int iEnd = iOff + iLen;
|
||||
int op = 0;
|
||||
while (ip < iEnd) {
|
||||
int i0 = in[ip++] & 0xff;
|
||||
int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
|
||||
int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
|
||||
int o0 = i0 >>> 2;
|
||||
int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
|
||||
int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
|
||||
int o3 = i2 & 0x3F;
|
||||
out[op++] = map1[o0];
|
||||
out[op++] = map1[o1];
|
||||
out[op] = op < oDataLen ? map1[o2] : '='; op++;
|
||||
out[op] = op < oDataLen ? map1[o3] : '='; op++; }
|
||||
return out; }
|
||||
|
||||
/**
|
||||
* Decodes a string from Base64 format.
|
||||
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||
* @param s A Base64 String to be decoded.
|
||||
* @return A String containing the decoded data.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static String decodeString (String s) {
|
||||
return new String(decode(s)); }
|
||||
|
||||
/**
|
||||
* Decodes a byte array from Base64 format and ignores line separators, tabs and blanks.
|
||||
* CR, LF, Tab and Space characters are ignored in the input data.
|
||||
* This method is compatible with <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>.
|
||||
* @param s A Base64 String to be decoded.
|
||||
* @return An array containing the decoded data bytes.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static byte[] decodeLines (String s) {
|
||||
char[] buf = new char[s.length()];
|
||||
int p = 0;
|
||||
for (int ip = 0; ip < s.length(); ip++) {
|
||||
char c = s.charAt(ip);
|
||||
if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
|
||||
buf[p++] = c; }
|
||||
return decode(buf, 0, p); }
|
||||
|
||||
/**
|
||||
* Decodes a byte array from Base64 format.
|
||||
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||
* @param s A Base64 String to be decoded.
|
||||
* @return An array containing the decoded data bytes.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static byte[] decode (String s) {
|
||||
return decode(s.toCharArray()); }
|
||||
|
||||
/**
|
||||
* Decodes a byte array from Base64 format.
|
||||
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||
* @param in A character array containing the Base64 encoded data.
|
||||
* @return An array containing the decoded data bytes.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static byte[] decode (char[] in) {
|
||||
return decode(in, 0, in.length); }
|
||||
|
||||
/**
|
||||
* Decodes a byte array from Base64 format.
|
||||
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||
* @param in A character array containing the Base64 encoded data.
|
||||
* @param iOff Offset of the first character in <code>in</code> to be processed.
|
||||
* @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>.
|
||||
* @return An array containing the decoded data bytes.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static byte[] decode (char[] in, int iOff, int iLen) {
|
||||
if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
|
||||
while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--;
|
||||
int oLen = (iLen*3) / 4;
|
||||
byte[] out = new byte[oLen];
|
||||
int ip = iOff;
|
||||
int iEnd = iOff + iLen;
|
||||
int op = 0;
|
||||
while (ip < iEnd) {
|
||||
int i0 = in[ip++];
|
||||
int i1 = in[ip++];
|
||||
int i2 = ip < iEnd ? in[ip++] : 'A';
|
||||
int i3 = ip < iEnd ? in[ip++] : 'A';
|
||||
if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
|
||||
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
|
||||
int b0 = map2[i0];
|
||||
int b1 = map2[i1];
|
||||
int b2 = map2[i2];
|
||||
int b3 = map2[i3];
|
||||
if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
|
||||
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
|
||||
int o0 = ( b0 <<2) | (b1>>>4);
|
||||
int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
|
||||
int o2 = ((b2 & 3)<<6) | b3;
|
||||
out[op++] = (byte)o0;
|
||||
if (op<oLen) out[op++] = (byte)o1;
|
||||
if (op<oLen) out[op++] = (byte)o2; }
|
||||
return out; }
|
||||
|
||||
// Dummy constructor.
|
||||
private Base64Coder() {}
|
||||
|
||||
} // end class Base64Coder
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.exception.ContentFileNotFoundException;
|
||||
import com.keepassdroid.database.exception.InvalidDBException;
|
||||
import com.keepassdroid.database.exception.InvalidPasswordException;
|
||||
import com.keepassdroid.database.exception.PwDbOutputException;
|
||||
import com.keepassdroid.database.load.Importer;
|
||||
import com.keepassdroid.database.load.ImporterFactory;
|
||||
import com.keepassdroid.database.save.PwDbOutput;
|
||||
import com.keepassdroid.icons.DrawableFactory;
|
||||
import com.keepassdroid.search.SearchDbHelper;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.SyncFailedException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author bpellin
|
||||
*/
|
||||
public class Database {
|
||||
public Set<PwGroup> dirty = new HashSet<PwGroup>();
|
||||
public PwDatabase pm;
|
||||
public Uri mUri;
|
||||
public SearchDbHelper searchHelper;
|
||||
public boolean readOnly = false;
|
||||
public boolean passwordEncodingError = false;
|
||||
|
||||
public DrawableFactory drawFactory = new DrawableFactory();
|
||||
|
||||
private boolean loaded = false;
|
||||
|
||||
public boolean Loaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public void setLoaded() {
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, InputStream is, String password, InputStream keyInputStream) throws IOException, InvalidDBException {
|
||||
LoadData(ctx, is, password, keyInputStream, new UpdateStatus(), !Importer.DEBUG);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, Uri uri, String password, Uri keyfile) throws IOException, FileNotFoundException, InvalidDBException {
|
||||
LoadData(ctx, uri, password, keyfile, new UpdateStatus(), !Importer.DEBUG);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status) throws IOException, FileNotFoundException, InvalidDBException {
|
||||
LoadData(ctx, uri, password, keyfile, status, !Importer.DEBUG);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status, boolean debug) throws IOException, FileNotFoundException, InvalidDBException {
|
||||
mUri = uri;
|
||||
readOnly = false;
|
||||
if (uri.getScheme().equals("file")) {
|
||||
File file = new File(uri.getPath());
|
||||
readOnly = !file.canWrite();
|
||||
}
|
||||
|
||||
try {
|
||||
passUrisAsInputStreams(ctx, uri, password, keyfile, status, debug, 0);
|
||||
} catch (InvalidPasswordException e) {
|
||||
// Retry with rounds fix
|
||||
try {
|
||||
passUrisAsInputStreams(ctx, uri, password, keyfile, status, debug, getFixRounds(ctx));
|
||||
} catch (Exception e2) {
|
||||
// Rethrow original exception
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getFixRounds(Context ctx) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
return prefs.getLong(ctx.getString(R.string.roundsFix_key), ctx.getResources().getInteger(R.integer.roundsFix_default));
|
||||
}
|
||||
|
||||
|
||||
private void passUrisAsInputStreams(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status, boolean debug, long roundsFix) throws IOException, FileNotFoundException, InvalidDBException {
|
||||
InputStream is, kfIs;
|
||||
try {
|
||||
is = UriUtil.getUriInputStream(ctx, uri);
|
||||
} catch (Exception e) {
|
||||
Log.e("KPD", "Database::LoadData", e);
|
||||
throw ContentFileNotFoundException.getInstance(uri);
|
||||
}
|
||||
|
||||
try {
|
||||
kfIs = UriUtil.getUriInputStream(ctx, keyfile);
|
||||
} catch (Exception e) {
|
||||
Log.e("KPD", "Database::LoadData", e);
|
||||
throw ContentFileNotFoundException.getInstance(keyfile);
|
||||
}
|
||||
LoadData(ctx, is, password, kfIs, status, debug, roundsFix);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, boolean debug) throws IOException, InvalidDBException {
|
||||
LoadData(ctx, is, password, kfIs, new UpdateStatus(), debug);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, UpdateStatus status, boolean debug) throws IOException, InvalidDBException {
|
||||
LoadData(ctx, is, password, kfIs, status, debug, 0);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, UpdateStatus status, boolean debug, long roundsFix) throws IOException, InvalidDBException {
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
|
||||
if ( ! bis.markSupported() ) {
|
||||
throw new IOException("Input stream does not support mark.");
|
||||
}
|
||||
|
||||
// We'll end up reading 8 bytes to identify the header. Might as well use two extra.
|
||||
bis.mark(10);
|
||||
|
||||
Importer imp = ImporterFactory.createImporter(bis, debug);
|
||||
|
||||
bis.reset(); // Return to the start
|
||||
|
||||
pm = imp.openDatabase(bis, password, kfIs, status, roundsFix);
|
||||
if ( pm != null ) {
|
||||
PwGroup root = pm.rootGroup;
|
||||
pm.populateGlobals(root);
|
||||
LoadData(ctx, pm, password, kfIs, status);
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, PwDatabase pm, String password, InputStream keyInputStream, UpdateStatus status) {
|
||||
if ( pm != null ) {
|
||||
passwordEncodingError = !pm.validatePasswordEncoding(password);
|
||||
}
|
||||
searchHelper = new SearchDbHelper(ctx);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public PwGroup Search(String str) {
|
||||
if (searchHelper == null) { return null; }
|
||||
return searchHelper.search(this, str);
|
||||
}
|
||||
|
||||
public void SaveData(Context ctx) throws IOException, PwDbOutputException {
|
||||
SaveData(ctx, mUri);
|
||||
}
|
||||
|
||||
public void SaveData(Context ctx, Uri uri) throws IOException, PwDbOutputException {
|
||||
if (uri.getScheme().equals("file")) {
|
||||
String filename = uri.getPath();
|
||||
File tempFile = new File(filename + ".tmp");
|
||||
FileOutputStream fos = new FileOutputStream(tempFile);
|
||||
//BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||
|
||||
//PwDbV3Output pmo = new PwDbV3Output(pm, bos, App.getCalendar());
|
||||
PwDbOutput pmo = PwDbOutput.getInstance(pm, fos);
|
||||
pmo.output();
|
||||
//bos.flush();
|
||||
//bos.close();
|
||||
fos.close();
|
||||
|
||||
// Force data to disk before continuing
|
||||
try {
|
||||
fos.getFD().sync();
|
||||
} catch (SyncFailedException e) {
|
||||
// Ignore if fsync fails. We tried.
|
||||
}
|
||||
|
||||
File orig = new File(filename);
|
||||
|
||||
if (!tempFile.renameTo(orig)) {
|
||||
throw new IOException("Failed to store database.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
OutputStream os;
|
||||
try {
|
||||
os = ctx.getContentResolver().openOutputStream(uri);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Failed to store database.");
|
||||
}
|
||||
|
||||
PwDbOutput pmo = PwDbOutput.getInstance(pm, os);
|
||||
pmo.output();
|
||||
os.close();
|
||||
}
|
||||
mUri = uri;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
dirty.clear();
|
||||
drawFactory.clear();
|
||||
|
||||
pm = null;
|
||||
mUri = null;
|
||||
loaded = false;
|
||||
passwordEncodingError = false;
|
||||
}
|
||||
|
||||
public void markAllGroupsAsDirty() {
|
||||
for ( PwGroup group : pm.getGroups() ) {
|
||||
dirty.add(group);
|
||||
}
|
||||
|
||||
// TODO: This should probably be abstracted out
|
||||
// The root tree in v3 is not an 'official' tree
|
||||
if ( pm instanceof PwDatabaseV3 ) {
|
||||
dirty.add(pm.rootGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,496 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.compat.ActivityCompat;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.exception.SamsungClipboardException;
|
||||
import com.keepassdroid.intents.Intents;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.MenuUtil;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.keepassdroid.utils.Util;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.keepassdroid.settings.PrefsUtil.isClipboardNotificationsEnable;
|
||||
|
||||
public class EntryActivity extends LockCloseHideActivity {
|
||||
public static final String KEY_ENTRY = "entry";
|
||||
public static final String KEY_REFRESH_POS = "refresh_pos";
|
||||
|
||||
public static final int NOTIFY_USERNAME = 1;
|
||||
public static final int NOTIFY_PASSWORD = 2;
|
||||
|
||||
public static void Launch(Activity act, PwEntry pw, int pos) {
|
||||
Intent i;
|
||||
|
||||
if ( pw instanceof PwEntryV4 ) {
|
||||
i = new Intent(act, EntryActivityV4.class);
|
||||
} else {
|
||||
i = new Intent(act, EntryActivity.class);
|
||||
}
|
||||
|
||||
i.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||
i.putExtra(KEY_REFRESH_POS, pos);
|
||||
|
||||
act.startActivityForResult(i,0);
|
||||
}
|
||||
|
||||
protected PwEntry mEntry;
|
||||
private Timer mTimer = new Timer();
|
||||
private boolean mShowPassword;
|
||||
private int mPos;
|
||||
private NotificationManager mNM;
|
||||
private BroadcastReceiver mIntentReceiver;
|
||||
protected boolean readOnly = false;
|
||||
|
||||
private DateFormat dateFormat;
|
||||
private DateFormat timeFormat;
|
||||
|
||||
protected void setEntryView() {
|
||||
setContentView(R.layout.entry_view);
|
||||
}
|
||||
|
||||
protected void setupEditButtons() {
|
||||
View edit = findViewById(R.id.entry_edit);
|
||||
edit.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
EntryEditActivity.Launch(EntryActivity.this, mEntry);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (readOnly) {
|
||||
edit.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mShowPassword = ! prefs.getBoolean(getString(R.string.maskpass_key), getResources().getBoolean(R.bool.maskpass_default));
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setEntryView();
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
Context appCtx = getApplicationContext();
|
||||
dateFormat = android.text.format.DateFormat.getDateFormat(appCtx);
|
||||
timeFormat = android.text.format.DateFormat.getTimeFormat(appCtx);
|
||||
|
||||
Database db = App.getDB();
|
||||
// Likely the app has been killed exit the activity
|
||||
if ( ! db.Loaded() ) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
readOnly = db.readOnly;
|
||||
|
||||
setResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
Intent i = getIntent();
|
||||
UUID uuid = Types.bytestoUUID(i.getByteArrayExtra(KEY_ENTRY));
|
||||
mPos = i.getIntExtra(KEY_REFRESH_POS, -1);
|
||||
|
||||
mEntry = db.pm.entries.get(uuid);
|
||||
if (mEntry == null) {
|
||||
Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set
|
||||
ActivityCompat.invalidateOptionsMenu(this);
|
||||
|
||||
// Update last access time.
|
||||
mEntry.touch(false, false);
|
||||
|
||||
fillData(false);
|
||||
|
||||
setupEditButtons();
|
||||
|
||||
// If notifications enabled in settings
|
||||
if (isClipboardNotificationsEnable(getApplicationContext())) {
|
||||
// Notification Manager
|
||||
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
|
||||
if (mEntry.getPassword().length() > 0) {
|
||||
// only show notification if password is available
|
||||
Notification password = getNotification(Intents.COPY_PASSWORD, R.string.copy_password);
|
||||
mNM.notify(NOTIFY_PASSWORD, password);
|
||||
}
|
||||
|
||||
if (mEntry.getUsername().length() > 0) {
|
||||
// only show notification if username is available
|
||||
Notification username = getNotification(Intents.COPY_USERNAME, R.string.copy_username);
|
||||
mNM.notify(NOTIFY_USERNAME, username);
|
||||
}
|
||||
}
|
||||
|
||||
mIntentReceiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if ( action != null) {
|
||||
if (action.equals(Intents.COPY_USERNAME)) {
|
||||
String username = mEntry.getUsername();
|
||||
if (username.length() > 0) {
|
||||
timeoutCopyToClipboard(username);
|
||||
}
|
||||
} else if (action.equals(Intents.COPY_PASSWORD)) {
|
||||
String password = mEntry.getPassword();
|
||||
if (password.length() > 0) {
|
||||
timeoutCopyToClipboard(password);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intents.COPY_USERNAME);
|
||||
filter.addAction(Intents.COPY_PASSWORD);
|
||||
registerReceiver(mIntentReceiver, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// These members might never get initialized if the app timed out
|
||||
if ( mIntentReceiver != null ) {
|
||||
unregisterReceiver(mIntentReceiver);
|
||||
}
|
||||
|
||||
if ( mNM != null ) {
|
||||
try {
|
||||
mNM.cancelAll();
|
||||
} catch (SecurityException e) {
|
||||
// Some android devices give a SecurityException when trying to cancel notifications without the WAKE_LOCK permission,
|
||||
// we'll ignore these.
|
||||
}
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private Notification getNotification(String intentText, int descResId) {
|
||||
|
||||
String desc = getString(descResId);
|
||||
|
||||
Intent intent = new Intent(intentText);
|
||||
PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
// no longer supported for api level >22
|
||||
// notify.setLatestEventInfo(this, getString(R.string.app_name), desc, pending);
|
||||
// so instead using compat builder and create new notification
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
Notification notify = builder.setContentIntent(pending).setContentText(desc).setContentTitle(getString(R.string.app_name))
|
||||
.setSmallIcon(R.drawable.notify).setTicker(desc).setWhen(System.currentTimeMillis()).build();
|
||||
|
||||
return notify;
|
||||
}
|
||||
|
||||
private String getDateTime(Date dt) {
|
||||
return dateFormat.format(dt) + " " + timeFormat.format(dt);
|
||||
|
||||
}
|
||||
|
||||
protected void fillData(boolean trimList) {
|
||||
ImageView iv = (ImageView) findViewById(R.id.entry_icon);
|
||||
Database db = App.getDB();
|
||||
db.drawFactory.assignDrawableTo(iv, getResources(), mEntry.getIcon());
|
||||
|
||||
PwDatabase pm = db.pm;
|
||||
|
||||
populateText(R.id.entry_title, mEntry.getTitle(true, pm));
|
||||
populateText(R.id.entry_user_name, mEntry.getUsername(true, pm));
|
||||
|
||||
populateText(R.id.entry_url, mEntry.getUrl(true, pm));
|
||||
populateText(R.id.entry_password, mEntry.getPassword(true, pm));
|
||||
setPasswordStyle();
|
||||
|
||||
populateText(R.id.entry_created, getDateTime(mEntry.getCreationTime()));
|
||||
populateText(R.id.entry_modified, getDateTime(mEntry.getLastModificationTime()));
|
||||
populateText(R.id.entry_accessed, getDateTime(mEntry.getLastAccessTime()));
|
||||
|
||||
Date expires = mEntry.getExpiryTime();
|
||||
if ( mEntry.expires() ) {
|
||||
populateText(R.id.entry_expires, getDateTime(expires));
|
||||
} else {
|
||||
populateText(R.id.entry_expires, R.string.never);
|
||||
}
|
||||
populateText(R.id.entry_comment, mEntry.getNotes(true, pm));
|
||||
|
||||
}
|
||||
|
||||
private void populateText(int viewId, int resId) {
|
||||
TextView tv = (TextView) findViewById(viewId);
|
||||
tv.setText(resId);
|
||||
}
|
||||
|
||||
private void populateText(int viewId, String text) {
|
||||
TextView tv = (TextView) findViewById(viewId);
|
||||
tv.setText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if ( resultCode == KeePass.EXIT_REFRESH || resultCode == KeePass.EXIT_REFRESH_TITLE ) {
|
||||
fillData(true);
|
||||
if ( resultCode == KeePass.EXIT_REFRESH_TITLE ) {
|
||||
Intent ret = new Intent();
|
||||
ret.putExtra(KEY_REFRESH_POS, mPos);
|
||||
setResult(KeePass.EXIT_REFRESH, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
MenuUtil.donationMenuInflater(inflater, menu);
|
||||
inflater.inflate(R.menu.entry, menu);
|
||||
inflater.inflate(R.menu.lock_database, menu);
|
||||
|
||||
MenuItem togglePassword = menu.findItem(R.id.menu_toggle_pass);
|
||||
if ( mShowPassword ) {
|
||||
togglePassword.setTitle(R.string.menu_hide_password);
|
||||
togglePassword.setIcon(R.drawable.ic_visibility_off_white_24dp);
|
||||
} else {
|
||||
togglePassword.setTitle(R.string.menu_showpass);
|
||||
togglePassword.setIcon(R.drawable.ic_visibility_white_24dp);
|
||||
}
|
||||
|
||||
MenuItem gotoUrl = menu.findItem(R.id.menu_goto_url);
|
||||
MenuItem copyUser = menu.findItem(R.id.menu_copy_user);
|
||||
MenuItem copyPass = menu.findItem(R.id.menu_copy_pass);
|
||||
|
||||
// In API >= 11 onCreateOptionsMenu may be called before onCreate completes
|
||||
// so mEntry may not be set
|
||||
if (mEntry == null) {
|
||||
gotoUrl.setVisible(false);
|
||||
copyUser.setVisible(false);
|
||||
copyPass.setVisible(false);
|
||||
}
|
||||
else {
|
||||
String url = mEntry.getUrl();
|
||||
if (EmptyUtils.isNullOrEmpty(url)) {
|
||||
// disable button if url is not available
|
||||
gotoUrl.setVisible(false);
|
||||
}
|
||||
if ( mEntry.getUsername().length() == 0 ) {
|
||||
// disable button if username is not available
|
||||
copyUser.setVisible(false);
|
||||
}
|
||||
if ( mEntry.getPassword().length() == 0 ) {
|
||||
// disable button if password is not available
|
||||
copyPass.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setPasswordStyle() {
|
||||
TextView password = (TextView) findViewById(R.id.entry_password);
|
||||
|
||||
if ( mShowPassword ) {
|
||||
password.setTransformationMethod(null);
|
||||
} else {
|
||||
password.setTransformationMethod(PasswordTransformationMethod.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
case R.id.menu_donate:
|
||||
return MenuUtil.onDonationItemSelected(this);
|
||||
|
||||
case R.id.menu_toggle_pass:
|
||||
if ( mShowPassword ) {
|
||||
item.setTitle(R.string.menu_showpass);
|
||||
item.setIcon(R.drawable.ic_visibility_white_24dp);
|
||||
mShowPassword = false;
|
||||
} else {
|
||||
item.setTitle(R.string.menu_hide_password);
|
||||
item.setIcon(R.drawable.ic_visibility_off_white_24dp);
|
||||
mShowPassword = true;
|
||||
}
|
||||
setPasswordStyle();
|
||||
return true;
|
||||
|
||||
case R.id.menu_goto_url:
|
||||
String url;
|
||||
url = mEntry.getUrl();
|
||||
|
||||
// Default http:// if no protocol specified
|
||||
if ( ! url.contains("://") ) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
|
||||
try {
|
||||
Util.gotoUrl(this, url);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.menu_copy_user:
|
||||
timeoutCopyToClipboard(mEntry.getUsername(true, App.getDB().pm));
|
||||
return true;
|
||||
|
||||
case R.id.menu_copy_pass:
|
||||
timeoutCopyToClipboard(mEntry.getPassword(true, App.getDB().pm));
|
||||
return true;
|
||||
|
||||
case R.id.menu_lock:
|
||||
App.setShutdown();
|
||||
setResult(KeePass.EXIT_LOCK);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
case android.R.id.home :
|
||||
finish(); // close this activity and return to preview activity (if there is any)
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void timeoutCopyToClipboard(String text) {
|
||||
try {
|
||||
Util.copyToClipboard(this, text);
|
||||
} catch (SamsungClipboardException e) {
|
||||
showSamsungDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
String sClipClear = prefs.getString(getString(R.string.clipboard_timeout_key), getString(R.string.clipboard_timeout_default));
|
||||
|
||||
long clipClearTime = Long.parseLong(sClipClear);
|
||||
|
||||
if ( clipClearTime > 0 ) {
|
||||
mTimer.schedule(new ClearClipboardTask(this, text), clipClearTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Setup to allow the toast to happen in the foreground
|
||||
final Handler uiThreadCallback = new Handler();
|
||||
|
||||
// Task which clears the clipboard, and sends a toast to the foreground.
|
||||
private class ClearClipboardTask extends TimerTask {
|
||||
|
||||
private final String mClearText;
|
||||
private final Context mCtx;
|
||||
|
||||
ClearClipboardTask(Context ctx, String clearText) {
|
||||
mClearText = clearText;
|
||||
mCtx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String currentClip = Util.getClipboard(mCtx);
|
||||
|
||||
if ( currentClip.equals(mClearText) ) {
|
||||
try {
|
||||
Util.copyToClipboard(mCtx, "");
|
||||
uiThreadCallback.post(new UIToastTask(mCtx, R.string.ClearClipboard));
|
||||
} catch (SamsungClipboardException e) {
|
||||
uiThreadCallback.post(new UIToastTask(mCtx, R.string.clipboard_error_clear));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showSamsungDialog() {
|
||||
String text = getString(R.string.clipboard_error).concat(System.getProperty("line.separator")).concat(getString(R.string.clipboard_error_url));
|
||||
SpannableString s = new SpannableString(text);
|
||||
TextView tv = new TextView(this);
|
||||
tv.setText(s);
|
||||
tv.setAutoLinkMask(RESULT_OK);
|
||||
tv.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
Linkify.addLinks(s, Linkify.WEB_URLS);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.clipboard_error_title)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.setView(tv)
|
||||
.show();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
import com.keepassdroid.utils.SprEngine;
|
||||
import com.keepassdroid.utils.SprEngineV4;
|
||||
import com.keepassdroid.view.EntrySection;
|
||||
|
||||
|
||||
public class EntryActivityV4 extends EntryActivity {
|
||||
|
||||
@Override
|
||||
protected void setEntryView() {
|
||||
setContentView(R.layout.entry_view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillData(boolean trimList) {
|
||||
super.fillData(trimList);
|
||||
|
||||
ViewGroup group = (ViewGroup) findViewById(R.id.extra_strings);
|
||||
|
||||
if (trimList) {
|
||||
group.removeAllViews();
|
||||
}
|
||||
|
||||
PwEntryV4 entry = (PwEntryV4) mEntry;
|
||||
|
||||
PwDatabase pm = App.getDB().pm;
|
||||
SprEngine spr = SprEngineV4.getInstance(pm);
|
||||
|
||||
// Display custom strings
|
||||
if (entry.strings.size() > 0) {
|
||||
for (Map.Entry<String, ProtectedString> pair : entry.strings.entrySet()) {
|
||||
String key = pair.getKey();
|
||||
|
||||
if (!PwEntryV4.IsStandardString(key)) {
|
||||
String text = pair.getValue().toString();
|
||||
View view = new EntrySection(this, null, key, spr.compile(text, entry, pm));
|
||||
group.addView(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwEntryV3;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.PwIconStandard;
|
||||
import com.keepassdroid.database.edit.AddEntry;
|
||||
import com.keepassdroid.database.edit.OnFinish;
|
||||
import com.keepassdroid.database.edit.RunnableOnFinish;
|
||||
import com.keepassdroid.database.edit.UpdateEntry;
|
||||
import com.keepassdroid.icons.Icons;
|
||||
import com.keepassdroid.utils.MenuUtil;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.keepassdroid.utils.Util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class EntryEditActivity extends LockCloseHideActivity
|
||||
implements IconPickerFragment.IconPickerListener,
|
||||
GeneratePasswordFragment.GeneratePasswordListener {
|
||||
public static final String KEY_ENTRY = "entry";
|
||||
public static final String KEY_PARENT = "parent";
|
||||
|
||||
protected PwEntry mEntry;
|
||||
protected boolean mIsNew;
|
||||
protected int mSelectedIconID = -1;
|
||||
|
||||
public static void Launch(Activity act, PwEntry pw) {
|
||||
Intent i;
|
||||
if (pw instanceof PwEntryV3) {
|
||||
i = new Intent(act, EntryEditActivityV3.class);
|
||||
}
|
||||
else if (pw instanceof PwEntryV4) {
|
||||
i = new Intent(act, EntryEditActivityV4.class);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Not yet implemented.");
|
||||
}
|
||||
|
||||
i.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||
|
||||
act.startActivityForResult(i, 0);
|
||||
}
|
||||
|
||||
public static void Launch(Activity act, PwGroup pw) {
|
||||
Intent i;
|
||||
if (pw instanceof PwGroupV3) {
|
||||
i = new Intent(act, EntryEditActivityV3.class);
|
||||
EntryEditActivityV3.putParentId(i, KEY_PARENT, (PwGroupV3)pw);
|
||||
}
|
||||
else if (pw instanceof PwGroupV4) {
|
||||
i = new Intent(act, EntryEditActivityV4.class);
|
||||
EntryEditActivityV4.putParentId(i, KEY_PARENT, (PwGroupV4)pw);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Not yet implemented.");
|
||||
}
|
||||
|
||||
act.startActivityForResult(i, 0);
|
||||
}
|
||||
|
||||
protected abstract PwGroupId getParentGroupId(Intent i, String key);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.entry_edit);
|
||||
setResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
toolbar.setTitle(getString(R.string.app_name));
|
||||
setSupportActionBar(toolbar);
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
// Likely the app has been killed exit the activity
|
||||
Database db = App.getDB();
|
||||
if ( ! db.Loaded() ) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent i = getIntent();
|
||||
byte[] uuidBytes = i.getByteArrayExtra(KEY_ENTRY);
|
||||
|
||||
PwDatabase pm = db.pm;
|
||||
if ( uuidBytes == null ) {
|
||||
|
||||
PwGroupId parentId = getParentGroupId(i, KEY_PARENT);
|
||||
PwGroup parent = pm.groups.get(parentId);
|
||||
mEntry = PwEntry.getInstance(parent);
|
||||
mIsNew = true;
|
||||
|
||||
} else {
|
||||
UUID uuid = Types.bytestoUUID(uuidBytes);
|
||||
mEntry = pm.entries.get(uuid);
|
||||
mIsNew = false;
|
||||
fillData();
|
||||
}
|
||||
|
||||
View scrollView = findViewById(R.id.entry_scroll);
|
||||
scrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
||||
|
||||
View iconButton = findViewById(R.id.icon_button);
|
||||
iconButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
IconPickerFragment.Launch(EntryEditActivity.this);
|
||||
}
|
||||
});
|
||||
|
||||
// Generate password button
|
||||
View generatePassword = findViewById(R.id.generate_button);
|
||||
generatePassword.setOnClickListener(new OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
GeneratePasswordFragment generatePasswordFragment = new GeneratePasswordFragment();
|
||||
generatePasswordFragment.show(getSupportFragmentManager(), "PasswordGeneratorFragment");
|
||||
}
|
||||
});
|
||||
|
||||
// Save button
|
||||
View save = findViewById(R.id.entry_save);
|
||||
save.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
EntryEditActivity act = EntryEditActivity.this;
|
||||
|
||||
if (!validateBeforeSaving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PwEntry newEntry = populateNewEntry();
|
||||
|
||||
if ( newEntry.getTitle().equals(mEntry.getTitle()) ) {
|
||||
setResult(KeePass.EXIT_REFRESH);
|
||||
} else {
|
||||
setResult(KeePass.EXIT_REFRESH_TITLE);
|
||||
}
|
||||
|
||||
RunnableOnFinish task;
|
||||
OnFinish onFinish = act.new AfterSave(new Handler());
|
||||
|
||||
if ( mIsNew ) {
|
||||
task = AddEntry.getInstance(EntryEditActivity.this, App.getDB(), newEntry, onFinish);
|
||||
} else {
|
||||
task = new UpdateEntry(EntryEditActivity.this, App.getDB(), mEntry, newEntry, onFinish);
|
||||
}
|
||||
ProgressTask pt = new ProgressTask(act, task, R.string.saving_database);
|
||||
pt.run();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean validateBeforeSaving() {
|
||||
// Require title
|
||||
String title = Util.getEditText(this, R.id.entry_title);
|
||||
if ( title.length() == 0 ) {
|
||||
Toast.makeText(this, R.string.error_title_required, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate password
|
||||
String pass = Util.getEditText(this, R.id.entry_password);
|
||||
String conf = Util.getEditText(this, R.id.entry_confpassword);
|
||||
if ( ! pass.equals(conf) ) {
|
||||
Toast.makeText(this, R.string.error_pass_match, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected PwEntry populateNewEntry() {
|
||||
return populateNewEntry(null);
|
||||
}
|
||||
|
||||
protected PwEntry populateNewEntry(PwEntry entry) {
|
||||
PwEntry newEntry;
|
||||
if (entry == null) {
|
||||
newEntry = mEntry.clone(true);
|
||||
}
|
||||
else {
|
||||
newEntry = entry;
|
||||
}
|
||||
|
||||
Date now = Calendar.getInstance().getTime();
|
||||
newEntry.setLastAccessTime(now);
|
||||
newEntry.setLastModificationTime(now);
|
||||
|
||||
PwDatabase db = App.getDB().pm;
|
||||
newEntry.setTitle(Util.getEditText(this, R.id.entry_title), db);
|
||||
if(mSelectedIconID != -1)
|
||||
newEntry.setIcon(new PwIconStandard(mSelectedIconID));
|
||||
newEntry.setUrl(Util.getEditText(this, R.id.entry_url), db);
|
||||
newEntry.setUsername(Util.getEditText(this, R.id.entry_user_name), db);
|
||||
newEntry.setNotes(Util.getEditText(this, R.id.entry_comment), db);
|
||||
newEntry.setPassword(Util.getEditText(this, R.id.entry_password), db);
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
MenuUtil.donationMenuInflater(inflater, menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
case R.id.menu_donate:
|
||||
return MenuUtil.onDonationItemSelected(this);
|
||||
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
protected void fillData() {
|
||||
ImageButton currIconButton = (ImageButton) findViewById(R.id.icon_button);
|
||||
App.getDB().drawFactory.assignDrawableTo(currIconButton, getResources(), mEntry.getIcon());
|
||||
|
||||
populateText(R.id.entry_title, mEntry.getTitle());
|
||||
populateText(R.id.entry_user_name, mEntry.getUsername());
|
||||
populateText(R.id.entry_url, mEntry.getUrl());
|
||||
|
||||
String password = mEntry.getPassword();
|
||||
populateText(R.id.entry_password, password);
|
||||
populateText(R.id.entry_confpassword, password);
|
||||
|
||||
populateText(R.id.entry_comment, mEntry.getNotes());
|
||||
}
|
||||
|
||||
private void populateText(int viewId, String text) {
|
||||
TextView tv = (TextView) findViewById(viewId);
|
||||
tv.setText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iconPicked(Bundle bundle) {
|
||||
mSelectedIconID = bundle.getInt(IconPickerFragment.KEY_ICON_ID);
|
||||
ImageButton currIconButton = (ImageButton) findViewById(R.id.icon_button);
|
||||
currIconButton.setImageResource(Icons.iconToResId(mSelectedIconID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptPassword(Bundle bundle) {
|
||||
String generatedPassword = bundle.getString(GeneratePasswordFragment.KEY_PASSWORD_ID);
|
||||
EditText password = (EditText) findViewById(R.id.entry_password);
|
||||
EditText confPassword = (EditText) findViewById(R.id.entry_confpassword);
|
||||
|
||||
password.setText(generatedPassword);
|
||||
confPassword.setText(generatedPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelPassword(Bundle bundle) {
|
||||
// Do nothing here
|
||||
}
|
||||
|
||||
private final class AfterSave extends OnFinish {
|
||||
|
||||
AfterSave(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ( mSuccess ) {
|
||||
finish();
|
||||
} else {
|
||||
displayMessage(EntryEditActivity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupIdV3;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
|
||||
public class EntryEditActivityV3 extends EntryEditActivity {
|
||||
|
||||
@Override
|
||||
protected PwEntry populateNewEntry(PwEntry entry) {
|
||||
PwEntry newEntry = super.populateNewEntry(entry);
|
||||
|
||||
if (mSelectedIconID == -1) {
|
||||
if (mIsNew) {
|
||||
newEntry.icon = App.getDB().pm.iconFactory.getIcon(0);
|
||||
}
|
||||
else {
|
||||
// Keep previous icon, if no new one was selected
|
||||
newEntry.icon = mEntry.icon;
|
||||
}
|
||||
}
|
||||
else {
|
||||
newEntry.icon = App.getDB().pm.iconFactory.getIcon(mSelectedIconID);
|
||||
}
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
protected static void putParentId(Intent i, String parentKey, PwGroupV3 parent) {
|
||||
i.putExtra(parentKey, parent.groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PwGroupId getParentGroupId(Intent i, String key) {
|
||||
int groupId = i.getIntExtra(key, -1);
|
||||
|
||||
return new PwGroupIdV3(groupId);
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupIdV4;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.keepassdroid.view.EntryEditSection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
public class EntryEditActivityV4 extends EntryEditActivity {
|
||||
|
||||
private ScrollView scroll;
|
||||
private LayoutInflater inflater;
|
||||
|
||||
protected static void putParentId(Intent i, String parentKey, PwGroupV4 parent) {
|
||||
PwGroupId id = parent.getId();
|
||||
PwGroupIdV4 id4 = (PwGroupIdV4) id;
|
||||
|
||||
i.putExtra(parentKey, Types.UUIDtoBytes(id4.getId()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PwGroupId getParentGroupId(Intent i, String key) {
|
||||
byte[] buf = i.getByteArrayExtra(key);
|
||||
UUID id = Types.bytestoUUID(buf);
|
||||
|
||||
return new PwGroupIdV4(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
scroll = (ScrollView) findViewById(R.id.entry_scroll);
|
||||
|
||||
View add = findViewById(R.id.add_advanced);
|
||||
add.setVisibility(View.VISIBLE);
|
||||
add.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
|
||||
EntryEditSection ees = (EntryEditSection) inflater.inflate(R.layout.entry_edit_section, container, false);
|
||||
ees.setData("", new ProtectedString(false, ""));
|
||||
container.addView(ees);
|
||||
|
||||
// Scroll bottom
|
||||
scroll.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scroll.fullScroll(ScrollView.FOCUS_DOWN);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillData() {
|
||||
super.fillData();
|
||||
|
||||
PwEntryV4 entry = (PwEntryV4) mEntry;
|
||||
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
|
||||
if (entry.strings.size() > 0) {
|
||||
for (Entry<String, ProtectedString> pair : entry.strings.entrySet()) {
|
||||
String key = pair.getKey();
|
||||
|
||||
if (!PwEntryV4.IsStandardString(key)) {
|
||||
EntryEditSection ees = (EntryEditSection) inflater.inflate(R.layout.entry_edit_section, container, false);
|
||||
ees.setData(key, pair.getValue());
|
||||
container.addView(ees);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected PwEntry populateNewEntry() {
|
||||
PwEntryV4 newEntry = (PwEntryV4) mEntry.clone(true);
|
||||
newEntry.history = (ArrayList<PwEntryV4>) newEntry.history.clone();
|
||||
newEntry.createBackup((PwDatabaseV4)App.getDB().pm);
|
||||
|
||||
newEntry = (PwEntryV4) super.populateNewEntry(newEntry);
|
||||
|
||||
Map<String, ProtectedString> strings = newEntry.strings;
|
||||
|
||||
// Delete all new standard strings
|
||||
Iterator<Entry<String, ProtectedString>> iter = strings.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<String, ProtectedString> pair = iter.next();
|
||||
if (!PwEntryV4.IsStandardString(pair.getKey())) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
View view = container.getChildAt(i);
|
||||
|
||||
TextView keyView = (TextView)view.findViewById(R.id.title);
|
||||
String key = keyView.getText().toString();
|
||||
|
||||
TextView valueView = (TextView)view.findViewById(R.id.value);
|
||||
String value = valueView.getText().toString();
|
||||
|
||||
CheckBox cb = (CheckBox)view.findViewById(R.id.protection);
|
||||
boolean protect = cb.isChecked();
|
||||
|
||||
strings.put(key, new ProtectedString(protect, value));
|
||||
}
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
public void deleteAdvancedString(View view) {
|
||||
ViewGroup section = (ViewGroup) view.getParent();
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
ViewGroup ees = (ViewGroup) container.getChildAt(i);
|
||||
if (ees == section) {
|
||||
container.removeViewAt(i);
|
||||
container.invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean validateBeforeSaving() {
|
||||
if(!super.validateBeforeSaving()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
EntryEditSection ees = (EntryEditSection) container.getChildAt(i);
|
||||
|
||||
TextView keyView = (TextView) ees.findViewById(R.id.title);
|
||||
CharSequence key = keyView.getText();
|
||||
|
||||
if (key == null || key.length() == 0) {
|
||||
Toast.makeText(this, R.string.error_string_key, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.edit.AddGroup;
|
||||
import com.keepassdroid.dialog.ReadOnlyDialog;
|
||||
import com.keepassdroid.view.ClickView;
|
||||
import com.keepassdroid.view.GroupAddEntryView;
|
||||
import com.keepassdroid.view.GroupRootView;
|
||||
import com.keepassdroid.view.GroupViewOnlyView;
|
||||
|
||||
public abstract class GroupActivity extends GroupBaseActivity
|
||||
implements GroupEditFragment.CreateGroupListener, IconPickerFragment.IconPickerListener {
|
||||
|
||||
private static final String TAG_CREATE_GROUP = "TAG_CREATE_GROUP";
|
||||
|
||||
protected boolean addGroupEnabled = false;
|
||||
protected boolean addEntryEnabled = false;
|
||||
protected boolean isRoot = false;
|
||||
protected boolean readOnly = false;
|
||||
|
||||
private static final String TAG = "Group Activity:";
|
||||
|
||||
public static void Launch(Activity act) {
|
||||
Launch(act, null);
|
||||
}
|
||||
|
||||
public static void Launch(Activity act, PwGroup group) {
|
||||
Intent i;
|
||||
|
||||
// Need to use PwDatabase since tree may be null
|
||||
PwDatabase db = App.getDB().pm;
|
||||
if ( db instanceof PwDatabaseV3 ) {
|
||||
i = new Intent(act, GroupActivityV3.class);
|
||||
|
||||
if ( group != null ) {
|
||||
PwGroupV3 g = (PwGroupV3) group;
|
||||
i.putExtra(KEY_ENTRY, g.groupId);
|
||||
}
|
||||
} else if ( db instanceof PwDatabaseV4 ) {
|
||||
i = new Intent(act, GroupActivityV4.class);
|
||||
|
||||
if ( group != null ) {
|
||||
PwGroupV4 g = (PwGroupV4) group;
|
||||
i.putExtra(KEY_ENTRY, g.uuid.toString());
|
||||
}
|
||||
} else {
|
||||
// Reached if db is null
|
||||
Log.d(TAG, "Tried to launch with null db");
|
||||
return;
|
||||
}
|
||||
|
||||
act.startActivityForResult(i,0);
|
||||
}
|
||||
|
||||
protected abstract PwGroupId retrieveGroupId(Intent i);
|
||||
|
||||
protected void setupButtons() {
|
||||
addGroupEnabled = !readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if ( isFinishing() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
Log.w(TAG, "Creating tree view");
|
||||
Intent intent = getIntent();
|
||||
|
||||
PwGroupId id = retrieveGroupId(intent);
|
||||
|
||||
Database db = App.getDB();
|
||||
readOnly = db.readOnly;
|
||||
PwGroup root = db.pm.rootGroup;
|
||||
if ( id == null ) {
|
||||
mGroup = root;
|
||||
} else {
|
||||
mGroup = db.pm.groups.get(id);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Retrieved tree");
|
||||
if ( mGroup == null ) {
|
||||
Log.w(TAG, "Group was null");
|
||||
return;
|
||||
}
|
||||
|
||||
isRoot = mGroup == root;
|
||||
|
||||
setupButtons();
|
||||
|
||||
if ( addGroupEnabled && addEntryEnabled ) {
|
||||
setContentView(new GroupAddEntryView(this));
|
||||
} else if ( addGroupEnabled ) {
|
||||
setContentView(new GroupRootView(this));
|
||||
} else if ( addEntryEnabled ) {
|
||||
setContentView(new GroupAddEntryView(this));
|
||||
View addGroup = findViewById(R.id.add_group);
|
||||
addGroup.setVisibility(View.GONE);
|
||||
} else {
|
||||
setContentView(new GroupViewOnlyView(this));
|
||||
}
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
toolbar.setTitle("");
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
if ( mGroup.getParent() != null )
|
||||
toolbar.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp);
|
||||
|
||||
Log.w(TAG, "Set view");
|
||||
|
||||
if ( addGroupEnabled ) {
|
||||
// Add Group button
|
||||
View addGroup = findViewById(R.id.add_group);
|
||||
addGroup.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
GroupEditFragment groupEditFragment = new GroupEditFragment();
|
||||
groupEditFragment.show(getSupportFragmentManager(), TAG_CREATE_GROUP);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ( addEntryEnabled ) {
|
||||
// Add Entry button
|
||||
View addEntry = findViewById(R.id.add_entry);
|
||||
addEntry.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
EntryEditActivity.Launch(GroupActivity.this, mGroup);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setGroupTitle();
|
||||
setGroupIcon();
|
||||
|
||||
setListAdapter(new PwGroupListAdapter(this, mGroup));
|
||||
registerForContextMenu(getListView());
|
||||
Log.w(TAG, "Finished creating tree");
|
||||
|
||||
if (isRoot) {
|
||||
showWarnings();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
|
||||
AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
|
||||
ClickView cv = (ClickView) acmi.targetView;
|
||||
cv.onCreateMenu(menu, menuInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) item.getMenuInfo();
|
||||
ClickView cv = (ClickView) acmi.targetView;
|
||||
|
||||
return cv.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void approveCreateGroup(Bundle bundle) {
|
||||
String GroupName = bundle.getString(GroupEditFragment.KEY_NAME);
|
||||
int GroupIconID = bundle.getInt(GroupEditFragment.KEY_ICON_ID);
|
||||
GroupActivity act = GroupActivity.this;
|
||||
Handler handler = new Handler();
|
||||
AddGroup task = AddGroup.getInstance(this, App.getDB(), GroupName, GroupIconID, mGroup, act.new RefreshTask(handler), false);
|
||||
ProgressTask pt = new ProgressTask(act, task, R.string.saving_database);
|
||||
pt.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelCreateGroup(Bundle bundle) {
|
||||
// Do nothing here
|
||||
}
|
||||
|
||||
@Override
|
||||
// For icon in create tree dialog
|
||||
public void iconPicked(Bundle bundle) {
|
||||
GroupEditFragment groupEditFragment = (GroupEditFragment) getSupportFragmentManager().findFragmentByTag(TAG_CREATE_GROUP);
|
||||
if (groupEditFragment != null) {
|
||||
groupEditFragment.iconPicked(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
protected void showWarnings() {
|
||||
if (App.getDB().readOnly) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
if (prefs.getBoolean(getString(R.string.show_read_only_warning), true)) {
|
||||
Dialog dialog = new ReadOnlyDialog(this);
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupIdV4;
|
||||
|
||||
public class GroupActivityV4 extends GroupActivity {
|
||||
|
||||
@Override
|
||||
protected PwGroupId retrieveGroupId(Intent i) {
|
||||
String uuid = i.getStringExtra(KEY_ENTRY);
|
||||
|
||||
if ( uuid == null || uuid.length() == 0 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PwGroupIdV4(UUID.fromString(uuid));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupButtons() {
|
||||
super.setupButtons();
|
||||
addEntryEnabled = !readOnly;
|
||||
}
|
||||
}
|
||||
@@ -1,345 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Build;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.compat.ActivityCompat;
|
||||
import com.keepassdroid.compat.EditorCompat;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.edit.OnFinish;
|
||||
import com.keepassdroid.search.SearchResultsActivity;
|
||||
import com.keepassdroid.utils.MenuUtil;
|
||||
import com.keepassdroid.view.AssignPasswordHelper;
|
||||
import com.keepassdroid.view.ClickView;
|
||||
import com.keepassdroid.view.GroupViewOnlyView;
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public abstract class GroupBaseActivity extends LockCloseListActivity
|
||||
implements AssignMasterKeyDialog.AssignPasswordDialogListener {
|
||||
protected ListView mList;
|
||||
protected ListAdapter mAdapter;
|
||||
|
||||
public static final String KEY_ENTRY = "entry";
|
||||
|
||||
private SharedPreferences prefs;
|
||||
|
||||
protected PwGroup mGroup;
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
refreshIfDirty();
|
||||
}
|
||||
|
||||
public void refreshIfDirty() {
|
||||
Database db = App.getDB();
|
||||
if ( db.dirty.contains(mGroup) ) {
|
||||
db.dirty.remove(mGroup);
|
||||
((BaseAdapter) mAdapter).notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
ClickView cv = (ClickView) mAdapter.getView(position, null, null);
|
||||
cv.onClick();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Likely the app has been killed exit the activity
|
||||
if ( ! App.getDB().Loaded() ) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
ActivityCompat.invalidateOptionsMenu(this);
|
||||
|
||||
setContentView(new GroupViewOnlyView(this));
|
||||
setResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
styleScrollBars();
|
||||
|
||||
}
|
||||
|
||||
protected void styleScrollBars() {
|
||||
ensureCorrectListView();
|
||||
mList.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
||||
mList.setTextFilterEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
protected void setGroupTitle() {
|
||||
if ( mGroup != null ) {
|
||||
String name = mGroup.getName();
|
||||
TextView tv = (TextView) findViewById(R.id.group_name);
|
||||
if ( name != null && name.length() > 0 ) {
|
||||
if ( tv != null ) {
|
||||
tv.setText(name);
|
||||
}
|
||||
} else {
|
||||
if ( tv != null ) {
|
||||
tv.setText(getText(R.string.root));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setGroupIcon() {
|
||||
if (mGroup != null) {
|
||||
ImageView iv = (ImageView) findViewById(R.id.icon);
|
||||
App.getDB().drawFactory.assignDrawableTo(iv, getResources(), mGroup.getIcon());
|
||||
}
|
||||
}
|
||||
|
||||
protected void setListAdapter(ListAdapter adapter) {
|
||||
ensureCorrectListView();
|
||||
mAdapter = adapter;
|
||||
mList.setAdapter(adapter);
|
||||
}
|
||||
|
||||
protected ListView getListView() {
|
||||
ensureCorrectListView();
|
||||
return mList;
|
||||
}
|
||||
|
||||
private void ensureCorrectListView(){
|
||||
mList = (ListView)findViewById(R.id.group_list);
|
||||
if (mList != null) {
|
||||
mList.setOnItemClickListener(
|
||||
new AdapterView.OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
onListItemClick((ListView) parent, v, position, id);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.search, menu);
|
||||
MenuUtil.donationMenuInflater(inflater, menu);
|
||||
inflater.inflate(R.menu.tree, menu);
|
||||
inflater.inflate(R.menu.database, menu);
|
||||
inflater.inflate(R.menu.default_menu, menu);
|
||||
|
||||
// Get the SearchView and set the searchable configuration
|
||||
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
assert searchManager != null;
|
||||
|
||||
MenuItem searchItem = menu.findItem(R.id.menu_search);
|
||||
SearchView searchView = null;
|
||||
if (searchItem != null) {
|
||||
searchView = (SearchView) searchItem.getActionView();
|
||||
}
|
||||
if (searchView != null) {
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, SearchResultsActivity.class)));
|
||||
searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setSortMenuText(Menu menu) {
|
||||
boolean sortByName = false;
|
||||
|
||||
// Will be null if onPrepareOptionsMenu is called before onCreate
|
||||
if (prefs != null) {
|
||||
sortByName = prefs.getBoolean(getString(R.string.sort_key), getResources().getBoolean(R.bool.sort_default));
|
||||
}
|
||||
|
||||
int resId;
|
||||
if ( sortByName ) {
|
||||
resId = R.string.sort_db;
|
||||
} else {
|
||||
resId = R.string.sort_name;
|
||||
}
|
||||
|
||||
menu.findItem(R.id.menu_sort).setTitle(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
if ( ! super.onPrepareOptionsMenu(menu) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setSortMenuText(menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
|
||||
case R.id.menu_search:
|
||||
onSearchRequested();
|
||||
return true;
|
||||
|
||||
case R.id.menu_sort:
|
||||
toggleSort();
|
||||
return true;
|
||||
|
||||
case R.id.menu_lock:
|
||||
App.setShutdown();
|
||||
setResult(KeePass.EXIT_LOCK);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
case R.id.menu_change_master_key:
|
||||
setPassword();
|
||||
return true;
|
||||
|
||||
default:
|
||||
MenuUtil.onDefaultMenuOptionsItemSelected(this, item);
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleSort() {
|
||||
// Toggle setting
|
||||
String sortKey = getString(R.string.sort_key);
|
||||
boolean sortByName = prefs.getBoolean(sortKey, getResources().getBoolean(R.bool.sort_default));
|
||||
Editor editor = prefs.edit();
|
||||
editor.putBoolean(sortKey, ! sortByName);
|
||||
EditorCompat.apply(editor);
|
||||
|
||||
// Refresh menu titles
|
||||
ActivityCompat.invalidateOptionsMenu(this);
|
||||
|
||||
// Mark all groups as dirty now to refresh them on load
|
||||
Database db = App.getDB();
|
||||
db.markAllGroupsAsDirty();
|
||||
// We'll manually refresh this tree so we can remove it
|
||||
db.dirty.remove(mGroup);
|
||||
|
||||
// Tell the adapter to refresh it's list
|
||||
((BaseAdapter) mAdapter).notifyDataSetChanged();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssignKeyDialogPositiveClick(
|
||||
boolean masterPasswordChecked, String masterPassword,
|
||||
boolean keyFileChecked, Uri keyFile) {
|
||||
|
||||
AssignPasswordHelper assignPasswordHelper =
|
||||
new AssignPasswordHelper(this,
|
||||
masterPassword, keyFile);
|
||||
assignPasswordHelper.assignPasswordInDatabase(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssignKeyDialogNegativeClick(
|
||||
boolean masterPasswordChecked, String masterPassword,
|
||||
boolean keyFileChecked, Uri keyFile) {
|
||||
|
||||
}
|
||||
|
||||
private void setPassword() {
|
||||
AssignMasterKeyDialog dialog = new AssignMasterKeyDialog();
|
||||
dialog.show(getSupportFragmentManager(), "passwordDialog");
|
||||
}
|
||||
|
||||
public class RefreshTask extends OnFinish {
|
||||
public RefreshTask(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ( mSuccess) {
|
||||
refreshIfDirty();
|
||||
} else {
|
||||
displayMessage(GroupBaseActivity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
|
||||
/*
|
||||
* ACTION_SEARCH automatically forces a new task. This occurs when you open a kdb file in
|
||||
* another app such as Files or GoogleDrive and then Search for an entry. Here we remove the
|
||||
* FLAG_ACTIVITY_NEW_TASK flag bit allowing search to open it's activity in the current task.
|
||||
*/
|
||||
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||
int flags = intent.getFlags();
|
||||
flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
intent.setFlags(flags);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
super.startActivityForResult(intent, requestCode, options);
|
||||
}
|
||||
}
|
||||
|
||||
public class AfterDeleteGroup extends OnFinish {
|
||||
public AfterDeleteGroup(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ( mSuccess) {
|
||||
refreshIfDirty();
|
||||
} else {
|
||||
mHandler.post(new UIToastTask(GroupBaseActivity.this, "Unrecoverable error: " + mMessage));
|
||||
App.setShutdown();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.icons.Icons;
|
||||
|
||||
public class GroupEditFragment extends DialogFragment
|
||||
implements IconPickerFragment.IconPickerListener {
|
||||
|
||||
public static final String KEY_NAME = "name";
|
||||
public static final String KEY_ICON_ID = "icon_id";
|
||||
|
||||
private CreateGroupListener createGroupListener;
|
||||
|
||||
private int mSelectedIconID;
|
||||
private View root;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
// Verify that the host activity implements the callback interface
|
||||
try {
|
||||
// Instantiate the NoticeDialogListener so we can send events to the host
|
||||
createGroupListener = (CreateGroupListener) context;
|
||||
createGroupListener = (CreateGroupListener) context;
|
||||
} catch (ClassCastException e) {
|
||||
// The activity doesn't implement the interface, throw exception
|
||||
throw new ClassCastException(context.toString()
|
||||
+ " must implement " + GroupEditFragment.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
|
||||
root = inflater.inflate(R.layout.group_edit, null);
|
||||
builder.setView(root)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
TextView nameField = (TextView) root.findViewById(R.id.group_name);
|
||||
String name = nameField.getText().toString();
|
||||
|
||||
if ( name.length() > 0 ) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_NAME, name);
|
||||
bundle.putInt(KEY_ICON_ID, mSelectedIconID);
|
||||
createGroupListener.approveCreateGroup(bundle);
|
||||
|
||||
GroupEditFragment.this.getDialog().cancel();
|
||||
}
|
||||
else {
|
||||
Toast.makeText(getContext(), R.string.error_no_name, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
Bundle bundle = new Bundle();
|
||||
createGroupListener.cancelCreateGroup(bundle);
|
||||
|
||||
GroupEditFragment.this.getDialog().cancel();
|
||||
}
|
||||
});
|
||||
|
||||
final ImageButton iconButton = (ImageButton) root.findViewById(R.id.icon_button);
|
||||
iconButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
IconPickerFragment iconPickerFragment = new IconPickerFragment();
|
||||
iconPickerFragment.show(getFragmentManager(), "IconPickerFragment");
|
||||
}
|
||||
});
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iconPicked(Bundle bundle) {
|
||||
mSelectedIconID = bundle.getInt(IconPickerFragment.KEY_ICON_ID);
|
||||
ImageButton currIconButton = (ImageButton) root.findViewById(R.id.icon_button);
|
||||
currIconButton.setImageResource(Icons.iconToResId(mSelectedIconID));
|
||||
}
|
||||
|
||||
public interface CreateGroupListener {
|
||||
void approveCreateGroup(Bundle bundle);
|
||||
void cancelCreateGroup(Bundle bundle);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import com.keepassdroid.compat.BuildCompat;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
|
||||
/**
|
||||
* Locking Close Activity that sets FLAG_SECURE to prevent screenshots, and from
|
||||
* appearing in the recent app preview
|
||||
* @author Brian Pellin
|
||||
*
|
||||
*/
|
||||
public abstract class LockCloseHideActivity extends LockCloseActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Several gingerbread devices have problems with FLAG_SECURE
|
||||
int ver = BuildCompat.getSdkVersion();
|
||||
if (ver >= BuildCompat.VERSION_CODE_ICE_CREAM_SANDWICH || ver < BuildCompat.VERSION_CODE_GINGERBREAD) {
|
||||
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.settings.PrefsUtil;
|
||||
import com.keepassdroid.stylish.StylishActivity;
|
||||
import com.keepassdroid.timeout.TimeoutHelper;
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
|
||||
|
||||
public abstract class LockingActivity extends StylishActivity {
|
||||
|
||||
private ScreenReceiver screenReceiver;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (PrefsUtil.isLockDatabaseWhenScreenShutOffEnable(this)) {
|
||||
screenReceiver = new ScreenReceiver();
|
||||
registerReceiver(screenReceiver, new IntentFilter((Intent.ACTION_SCREEN_OFF)));
|
||||
} else
|
||||
screenReceiver = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
checkShutdown();
|
||||
TimeoutHelper.resume(this);
|
||||
}
|
||||
|
||||
private void checkShutdown() {
|
||||
if ( App.isShutdown() && App.getDB().Loaded() ) {
|
||||
setResult(KeePass.EXIT_LOCK);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
TimeoutHelper.pause(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if(screenReceiver != null)
|
||||
unregisterReceiver(screenReceiver);
|
||||
}
|
||||
|
||||
public class ScreenReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
if(intent.getAction() != null) {
|
||||
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
|
||||
if (PrefsUtil.isLockDatabaseWhenScreenShutOffEnable(LockingActivity.this)) {
|
||||
App.setShutdown();
|
||||
checkShutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,810 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.compat.BackupManagerCompat;
|
||||
import com.keepassdroid.compat.ClipDataCompat;
|
||||
import com.keepassdroid.compat.EditorCompat;
|
||||
import com.keepassdroid.database.edit.LoadDB;
|
||||
import com.keepassdroid.database.edit.OnFinish;
|
||||
import com.keepassdroid.dialog.PasswordEncodingDialogHelper;
|
||||
import com.keepassdroid.fingerprint.FingerPrintAnimatedVector;
|
||||
import com.keepassdroid.fingerprint.FingerPrintHelper;
|
||||
import com.keepassdroid.settings.PrefsUtil;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.MenuUtil;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.keepassdroid.view.FingerPrintDialog;
|
||||
import com.keepassdroid.view.KeyFileHelper;
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class PasswordActivity extends LockingActivity
|
||||
implements FingerPrintHelper.FingerPrintCallback, UriIntentInitTaskCallback {
|
||||
|
||||
public static final String KEY_DEFAULT_FILENAME = "defaultFileName";
|
||||
private static final String KEY_PASSWORD = "password";
|
||||
private static final String KEY_LAUNCH_IMMEDIATELY = "launchImmediately";
|
||||
|
||||
private Uri mDbUri = null;
|
||||
private Uri mKeyUri = null;
|
||||
SharedPreferences prefs;
|
||||
SharedPreferences prefsNoBackup;
|
||||
|
||||
private FingerPrintHelper fingerPrintHelper;
|
||||
private boolean fingerprintMustBeConfigured = true;
|
||||
private boolean mRememberKeyfile;
|
||||
|
||||
private int mode;
|
||||
private static final String PREF_KEY_VALUE_PREFIX = "valueFor_"; // key is a combination of db file name and this prefix
|
||||
private static final String PREF_KEY_IV_PREFIX = "ivFor_"; // key is a combination of db file name and this prefix
|
||||
|
||||
private View fingerprintContainerView;
|
||||
private View fingerprintImageView;
|
||||
private FingerPrintAnimatedVector fingerPrintAnimatedVector;
|
||||
private TextView fingerprintTextView;
|
||||
private TextView filenameView;
|
||||
private EditText passwordView;
|
||||
private EditText keyFileView;
|
||||
private Button confirmButtonView;
|
||||
private CompoundButton checkboxPasswordView;
|
||||
private CompoundButton checkboxKeyfileView;
|
||||
private CompoundButton checkboxDefaultDatabaseView;
|
||||
|
||||
private KeyFileHelper keyFileHelper;
|
||||
|
||||
public static void Launch(
|
||||
Activity act,
|
||||
String fileName) throws FileNotFoundException {
|
||||
Launch(act, fileName, "");
|
||||
}
|
||||
|
||||
public static void Launch(
|
||||
Activity act,
|
||||
String fileName,
|
||||
String keyFile) throws FileNotFoundException {
|
||||
if (EmptyUtils.isNullOrEmpty(fileName)) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
Uri uri = UriUtil.parseDefaultFile(fileName);
|
||||
assert uri != null;
|
||||
String scheme = uri.getScheme();
|
||||
|
||||
if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
|
||||
File dbFile = new File(uri.getPath());
|
||||
if (!dbFile.exists()) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
Intent i = new Intent(act, PasswordActivity.class);
|
||||
i.putExtra(UriIntentInitTask.KEY_FILENAME, fileName);
|
||||
i.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile);
|
||||
|
||||
act.startActivityForResult(i, 0);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(
|
||||
int requestCode,
|
||||
int resultCode,
|
||||
Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (keyFileHelper != null) {
|
||||
keyFileHelper.onActivityResultCallback(requestCode, resultCode, data,
|
||||
new KeyFileHelper.KeyFileCallback() {
|
||||
@Override
|
||||
public void onKeyFileResultCallback(Uri uri) {
|
||||
if (uri != null) {
|
||||
keyFileView.setText(uri.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case KeePass.EXIT_NORMAL:
|
||||
setEmptyViews();
|
||||
App.getDB().clear();
|
||||
break;
|
||||
|
||||
case KeePass.EXIT_LOCK:
|
||||
setResult(KeePass.EXIT_LOCK);
|
||||
setEmptyViews();
|
||||
finish();
|
||||
App.getDB().clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
prefsNoBackup = PrefsUtil.getNoBackupSharedPreferences(getApplicationContext());
|
||||
|
||||
mRememberKeyfile = prefs.getBoolean(getString(R.string.keyfile_key),
|
||||
getResources().getBoolean(R.bool.keyfile_default));
|
||||
|
||||
setContentView(R.layout.password);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
toolbar.setTitle(getString(R.string.app_name));
|
||||
setSupportActionBar(toolbar);
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
confirmButtonView = (Button) findViewById(R.id.pass_ok);
|
||||
fingerprintContainerView = findViewById(R.id.fingerprint_container);
|
||||
fingerprintImageView = findViewById(R.id.fingerprint_image);
|
||||
fingerprintTextView = (TextView) findViewById(R.id.fingerprint_label);
|
||||
filenameView = (TextView) findViewById(R.id.filename);
|
||||
passwordView = (EditText) findViewById(R.id.password);
|
||||
keyFileView = (EditText) findViewById(R.id.pass_keyfile);
|
||||
checkboxPasswordView = (CompoundButton) findViewById(R.id.password_checkbox);
|
||||
checkboxKeyfileView = (CompoundButton) findViewById(R.id.keyfile_checkox);
|
||||
checkboxDefaultDatabaseView = (CompoundButton) findViewById(R.id.default_database);
|
||||
|
||||
View browseView = findViewById(R.id.browse_button);
|
||||
keyFileHelper = new KeyFileHelper(PasswordActivity.this);
|
||||
browseView.setOnClickListener(keyFileHelper.getOpenFileOnClickViewListener());
|
||||
|
||||
passwordView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
checkboxPasswordView.setChecked(true);
|
||||
}
|
||||
});
|
||||
keyFileView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
checkboxKeyfileView.setChecked(true);
|
||||
}
|
||||
});
|
||||
|
||||
new UriIntentInitTask(this, mRememberKeyfile)
|
||||
.execute(getIntent());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
fingerPrintAnimatedVector = new FingerPrintAnimatedVector(this,
|
||||
(ImageView) fingerprintImageView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostInitTask(Uri dbUri, Uri keyFileUri, Integer errorStringId) {
|
||||
|
||||
mDbUri = dbUri;
|
||||
mKeyUri = keyFileUri;
|
||||
|
||||
Intent intent = getIntent();
|
||||
String password = intent.getStringExtra(KEY_PASSWORD);
|
||||
boolean launch_immediately = intent.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false);
|
||||
|
||||
if (errorStringId != null) {
|
||||
Toast.makeText(PasswordActivity.this, errorStringId, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
populateView();
|
||||
|
||||
confirmButtonView.setOnClickListener(new OkClickHandler());
|
||||
|
||||
if (password != null) {
|
||||
passwordView.setText(password);
|
||||
}
|
||||
|
||||
checkboxDefaultDatabaseView.setOnCheckedChangeListener(new DefaultCheckChange());
|
||||
|
||||
retrieveSettings();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
checkFingerprintAvailability();
|
||||
}
|
||||
|
||||
if (launch_immediately) {
|
||||
verifyCheckboxesAndLoadDatabase(password, mKeyUri);
|
||||
}
|
||||
}
|
||||
|
||||
private void retrieveSettings() {
|
||||
String defaultFilename = prefs.getString(KEY_DEFAULT_FILENAME, "");
|
||||
if (mDbUri!=null
|
||||
&& !EmptyUtils.isNullOrEmpty(mDbUri.getPath())
|
||||
&& UriUtil.equalsDefaultfile(mDbUri, defaultFilename)) {
|
||||
checkboxDefaultDatabaseView.setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateView() {
|
||||
String db = (mDbUri == null) ? "" : mDbUri.toString();
|
||||
if (!db.isEmpty()) {
|
||||
if (PrefsUtil.isFullFilePathEnable(this))
|
||||
filenameView.setText(db);
|
||||
else
|
||||
filenameView.setText(new File(mDbUri.getPath()).getName()); // TODO Encapsulate
|
||||
}
|
||||
|
||||
String key = (mKeyUri == null) ? "" : mKeyUri.toString();
|
||||
if (!key.isEmpty() && mRememberKeyfile) { // Bug KeepassDX #18
|
||||
keyFileView.setText(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// If the application was shutdown make sure to clear the password field, if it
|
||||
// was saved in the instance state
|
||||
if (App.isShutdown()) {
|
||||
setEmptyViews();
|
||||
}
|
||||
|
||||
// Clear the shutdown flag
|
||||
App.clearShutdown();
|
||||
|
||||
// checks if fingerprint is available, will also start listening for fingerprints when available
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
initForFingerprint();
|
||||
checkFingerprintAvailability();
|
||||
if (fingerPrintAnimatedVector != null) {
|
||||
fingerPrintAnimatedVector.startScan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setEmptyViews() {
|
||||
passwordView.setText("");
|
||||
checkboxPasswordView.setChecked(false);
|
||||
// Bug KeepassDX #18
|
||||
if (!mRememberKeyfile) {
|
||||
keyFileView.setText("");
|
||||
checkboxKeyfileView.setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
private Uri getKeyFile(Uri dbUri) {
|
||||
if (mRememberKeyfile) {
|
||||
return App.getFileHistory().getFileByName(dbUri);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// fingerprint related code here
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private void initForFingerprint() {
|
||||
mode = -1;
|
||||
|
||||
fingerPrintHelper = new FingerPrintHelper(this, this);
|
||||
|
||||
// when text entered we can enable the logon/purchase button and if required update encryption/decryption mode
|
||||
passwordView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
final CharSequence s,
|
||||
final int start,
|
||||
final int count,
|
||||
final int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(
|
||||
final CharSequence s,
|
||||
final int start,
|
||||
final int before,
|
||||
final int count) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(final Editable s) {
|
||||
if ( !fingerprintMustBeConfigured ) {
|
||||
final boolean validInput = s.length() > 0;
|
||||
// encrypt or decrypt mode based on how much input or not
|
||||
setFingerPrintTextView(validInput ? R.string.store_with_fingerprint : R.string.scanning_fingerprint);
|
||||
mode = validInput ? toggleMode(Cipher.ENCRYPT_MODE) : toggleMode(Cipher.DECRYPT_MODE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// callback for fingerprint findings
|
||||
fingerPrintHelper.setAuthenticationCallback(new FingerprintManagerCompat.AuthenticationCallback() {
|
||||
@Override
|
||||
public void onAuthenticationError(
|
||||
final int errorCode,
|
||||
final CharSequence errString) {
|
||||
|
||||
// this is triggered on stop/start listening done by helper to switch between modes so don't restart here
|
||||
// errorCode = 5
|
||||
// errString = "Fingerprint operation canceled."
|
||||
//onFingerprintException();
|
||||
//fingerprintTextView.setText(errString);
|
||||
// true false fingerprint readings are handled otherwise with the toast messages, see below in code
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationHelp(
|
||||
final int helpCode,
|
||||
final CharSequence helpString) {
|
||||
|
||||
showError(helpString);
|
||||
checkFingerprintAvailability();
|
||||
fingerprintTextView.setText(helpString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(final FingerprintManagerCompat.AuthenticationResult result) {
|
||||
|
||||
if (mode == Cipher.ENCRYPT_MODE) {
|
||||
|
||||
// newly store the entered password in encrypted way
|
||||
final String password = passwordView.getText().toString();
|
||||
fingerPrintHelper.encryptData(password);
|
||||
|
||||
} else if (mode == Cipher.DECRYPT_MODE) {
|
||||
|
||||
// retrieve the encrypted value from preferences
|
||||
final String encryptedValue = prefsNoBackup.getString(getPreferenceKeyValue(), null);
|
||||
if (encryptedValue != null) {
|
||||
fingerPrintHelper.decryptData(encryptedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
showError(R.string.fingerprint_not_recognized);
|
||||
checkFingerprintAvailability();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getPreferenceKeyValue() {
|
||||
// makes it possible to store passwords uniqly per database
|
||||
return PREF_KEY_VALUE_PREFIX + (mDbUri != null ? mDbUri.getPath() : "");
|
||||
}
|
||||
|
||||
private String getPreferenceKeyIvSpec() {
|
||||
return PREF_KEY_IV_PREFIX + (mDbUri != null ? mDbUri.getPath() : "");
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private synchronized int toggleMode(final int newMode) {
|
||||
if(newMode != mode) {
|
||||
mode = newMode;
|
||||
switch (mode) {
|
||||
case Cipher.ENCRYPT_MODE:
|
||||
fingerPrintHelper.initEncryptData();
|
||||
break;
|
||||
case Cipher.DECRYPT_MODE:
|
||||
final String ivSpecValue = prefsNoBackup.getString(getPreferenceKeyIvSpec(), null);
|
||||
if (ivSpecValue != null)
|
||||
fingerPrintHelper.initDecryptData(ivSpecValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& fingerPrintAnimatedVector != null) {
|
||||
fingerPrintAnimatedVector.stopScan();
|
||||
}
|
||||
|
||||
// stop listening when we go in background
|
||||
if (fingerPrintHelper != null) {
|
||||
fingerPrintHelper.stopListening();
|
||||
}
|
||||
}
|
||||
|
||||
private void setFingerPrintVisibility(final int vis) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fingerprintContainerView.setVisibility(vis);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setFingerPrintTextView(final int textId) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fingerprintTextView.setText(textId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setFingerPrintAlphaImageView(final float alpha) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fingerprintImageView.setAlpha(alpha);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private synchronized void checkFingerprintAvailability() {
|
||||
|
||||
// fingerprint not supported (by API level or hardware) so keep option hidden
|
||||
// or manually disable
|
||||
if (!PrefsUtil.isFingerprintEnable(getApplicationContext())
|
||||
|| !FingerPrintHelper.isFingerprintSupported(FingerprintManagerCompat.from(this))) {
|
||||
setFingerPrintVisibility(View.GONE);
|
||||
}
|
||||
// fingerprint is available but not configured show icon but in disabled state with some information
|
||||
else {
|
||||
// show explanations
|
||||
fingerprintContainerView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
FingerPrintDialog fingerPrintDialog = new FingerPrintDialog();
|
||||
fingerPrintDialog.show(getSupportFragmentManager(), "fingerprintDialog");
|
||||
}
|
||||
});
|
||||
setFingerPrintVisibility(View.VISIBLE);
|
||||
|
||||
if (!fingerPrintHelper.hasEnrolledFingerprints()) {
|
||||
setFingerPrintAlphaImageView(0.3f);
|
||||
// This happens when no fingerprints are registered. Listening won't start
|
||||
setFingerPrintTextView(R.string.configure_fingerprint);
|
||||
}
|
||||
// finally fingerprint available and configured so we can use it
|
||||
else {
|
||||
fingerprintMustBeConfigured = false;
|
||||
setFingerPrintAlphaImageView(1f);
|
||||
|
||||
// fingerprint available but no stored password found yet for this DB so show info don't listen
|
||||
if (!prefsNoBackup.contains(getPreferenceKeyValue())) {
|
||||
setFingerPrintTextView(R.string.no_password_stored);
|
||||
// listen for encryption
|
||||
toggleMode(Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
// all is set here so we can confirm to user and start listening for fingerprints
|
||||
else {
|
||||
setFingerPrintTextView(R.string.scanning_fingerprint);
|
||||
// listen for decryption
|
||||
toggleMode(Cipher.DECRYPT_MODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEncryptedResult(
|
||||
final String value,
|
||||
final String ivSpec) {
|
||||
prefsNoBackup.edit()
|
||||
.putString(getPreferenceKeyValue(), value)
|
||||
.putString(getPreferenceKeyIvSpec(), ivSpec)
|
||||
.apply();
|
||||
// and remove visual input to reset UI
|
||||
confirmButtonView.performClick();
|
||||
setFingerPrintTextView(R.string.encrypted_value_stored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDecryptedResult(final String passwordValue) {
|
||||
// Load database directly
|
||||
String key = keyFileView.getText().toString();
|
||||
loadDatabase(passwordValue, key);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onInvalidKeyException(Exception e) {
|
||||
showError(R.string.fingerprint_invalid_key);
|
||||
prefsNoBackup.edit()
|
||||
.remove(getPreferenceKeyValue())
|
||||
.remove(getPreferenceKeyIvSpec())
|
||||
.apply();
|
||||
e.printStackTrace();
|
||||
checkFingerprintAvailability(); // restarts listening
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onFingerPrintException(Exception e) {
|
||||
showError(R.string.fingerprint_error);
|
||||
e.printStackTrace();
|
||||
checkFingerprintAvailability();
|
||||
}
|
||||
|
||||
private void showError(final int messageId) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(getApplicationContext(), messageId, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showError(final CharSequence message) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class DefaultCheckChange implements CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(
|
||||
CompoundButton buttonView,
|
||||
boolean isChecked) {
|
||||
|
||||
String newDefaultFileName;
|
||||
|
||||
if (isChecked) {
|
||||
newDefaultFileName = mDbUri.toString();
|
||||
} else {
|
||||
newDefaultFileName = "";
|
||||
}
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putString(KEY_DEFAULT_FILENAME, newDefaultFileName);
|
||||
EditorCompat.apply(editor);
|
||||
|
||||
BackupManagerCompat backupManager = new BackupManagerCompat(PasswordActivity.this);
|
||||
backupManager.dataChanged();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class OkClickHandler implements View.OnClickListener {
|
||||
|
||||
public void onClick(View view) {
|
||||
String pass = passwordView.getText().toString();
|
||||
String key = keyFileView.getText().toString();
|
||||
verifyCheckboxesAndLoadDatabase(pass, key);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyCheckboxesAndLoadDatabase(
|
||||
String pass,
|
||||
String keyfile) {
|
||||
verifyCheckboxesAndLoadDatabase(pass, UriUtil.parseDefaultFile(keyfile));
|
||||
}
|
||||
|
||||
private void verifyCheckboxesAndLoadDatabase(
|
||||
String pass,
|
||||
Uri keyfile) {
|
||||
if (!checkboxPasswordView.isChecked()) {
|
||||
pass = "";
|
||||
}
|
||||
if (!checkboxKeyfileView.isChecked()) {
|
||||
keyfile = null;
|
||||
}
|
||||
loadDatabase(pass, keyfile);
|
||||
}
|
||||
|
||||
private void loadDatabase(
|
||||
String pass,
|
||||
String keyfile) {
|
||||
loadDatabase(pass, UriUtil.parseDefaultFile(keyfile));
|
||||
}
|
||||
|
||||
private void loadDatabase(
|
||||
String pass,
|
||||
Uri keyfile) {
|
||||
|
||||
// Clear before we load
|
||||
Database db = App.getDB();
|
||||
db.clear();
|
||||
|
||||
// Clear the shutdown flag
|
||||
App.clearShutdown();
|
||||
|
||||
Handler handler = new Handler();
|
||||
LoadDB task = new LoadDB(db, PasswordActivity.this, mDbUri, pass, keyfile, new AfterLoad(handler, db));
|
||||
ProgressTask pt = new ProgressTask(PasswordActivity.this, task, R.string.loading_database);
|
||||
pt.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
MenuUtil.defaultMenuInflater(getMenuInflater(), menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
break;
|
||||
|
||||
default:
|
||||
return MenuUtil.onDefaultMenuOptionsItemSelected(this, item);
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private final class AfterLoad extends OnFinish {
|
||||
|
||||
private Database db;
|
||||
|
||||
AfterLoad(
|
||||
Handler handler,
|
||||
Database db) {
|
||||
super(handler);
|
||||
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (db.passwordEncodingError) {
|
||||
PasswordEncodingDialogHelper dialog = new PasswordEncodingDialogHelper();
|
||||
dialog.show(PasswordActivity.this, new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(
|
||||
DialogInterface dialog,
|
||||
int which) {
|
||||
GroupActivity.Launch(PasswordActivity.this);
|
||||
}
|
||||
|
||||
});
|
||||
} else if (mSuccess) {
|
||||
GroupActivity.Launch(PasswordActivity.this);
|
||||
} else {
|
||||
displayMessage(PasswordActivity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class UriIntentInitTask extends AsyncTask<Intent, Void, Integer> {
|
||||
|
||||
static final String KEY_FILENAME = "fileName";
|
||||
static final String KEY_KEYFILE = "keyFile";
|
||||
private static final String VIEW_INTENT = "android.intent.action.VIEW";
|
||||
|
||||
private UriIntentInitTaskCallback uriIntentInitTaskCallback;
|
||||
private boolean isKeyFileNeeded;
|
||||
private Uri databaseUri;
|
||||
private Uri keyFileUri;
|
||||
|
||||
UriIntentInitTask(UriIntentInitTaskCallback uriIntentInitTaskCallback, boolean isKeyFileNeeded) {
|
||||
this.uriIntentInitTaskCallback = uriIntentInitTaskCallback;
|
||||
this.isKeyFileNeeded = isKeyFileNeeded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Intent... args) {
|
||||
Intent intent = args[0];
|
||||
String action = intent.getAction();
|
||||
if (action != null && action.equals(VIEW_INTENT)) {
|
||||
Uri incoming = intent.getData();
|
||||
databaseUri = incoming;
|
||||
keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE);
|
||||
|
||||
if (incoming == null) {
|
||||
return R.string.error_can_not_handle_uri;
|
||||
} else if (incoming.getScheme().equals("file")) {
|
||||
String fileName = incoming.getPath();
|
||||
|
||||
if (fileName.length() == 0) {
|
||||
// No file name
|
||||
return R.string.file_not_found;
|
||||
}
|
||||
|
||||
File dbFile = new File(fileName);
|
||||
if (!dbFile.exists()) {
|
||||
// File does not exist
|
||||
return R.string.file_not_found;
|
||||
}
|
||||
|
||||
if (keyFileUri == null) {
|
||||
keyFileUri = getKeyFile(databaseUri);
|
||||
}
|
||||
} else if (incoming.getScheme().equals("content")) {
|
||||
if (keyFileUri == null) {
|
||||
keyFileUri = getKeyFile(databaseUri);
|
||||
}
|
||||
} else {
|
||||
return R.string.error_can_not_handle_uri;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
databaseUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_FILENAME));
|
||||
keyFileUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_KEYFILE));
|
||||
|
||||
if (keyFileUri == null || keyFileUri.toString().length() == 0) {
|
||||
keyFileUri = getKeyFile(databaseUri);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void onPostExecute(Integer result) {
|
||||
uriIntentInitTaskCallback.onPostInitTask(databaseUri, keyFileUri, result);
|
||||
}
|
||||
|
||||
private Uri getKeyFile(Uri dbUri) {
|
||||
if (isKeyFileNeeded) {
|
||||
return App.getFileHistory().getFileByName(dbUri);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.view.PwEntryView;
|
||||
import com.keepassdroid.view.PwGroupView;
|
||||
|
||||
public class PwGroupListAdapter extends BaseAdapter {
|
||||
|
||||
private GroupBaseActivity mAct;
|
||||
private PwGroup mGroup;
|
||||
private List<PwGroup> groupsForViewing;
|
||||
private List<PwEntry> entriesForViewing;
|
||||
private Comparator<PwEntry> entryComp = new PwEntry.EntryNameComparator();
|
||||
private Comparator<PwGroup> groupComp = new PwGroup.GroupNameComparator();
|
||||
private SharedPreferences prefs;
|
||||
|
||||
public PwGroupListAdapter(GroupBaseActivity act, PwGroup group) {
|
||||
mAct = act;
|
||||
mGroup = group;
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(act);
|
||||
|
||||
filterAndSort();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
super.notifyDataSetChanged();
|
||||
|
||||
filterAndSort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetInvalidated() {
|
||||
super.notifyDataSetInvalidated();
|
||||
|
||||
filterAndSort();
|
||||
}
|
||||
|
||||
private void filterAndSort() {
|
||||
entriesForViewing = new ArrayList<PwEntry>();
|
||||
|
||||
for (int i = 0; i < mGroup.childEntries.size(); i++) {
|
||||
PwEntry entry = mGroup.childEntries.get(i);
|
||||
if ( ! entry.isMetaStream() ) {
|
||||
entriesForViewing.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
boolean sortLists = prefs.getBoolean(mAct.getString(R.string.sort_key), mAct.getResources().getBoolean(R.bool.sort_default));
|
||||
if ( sortLists ) {
|
||||
groupsForViewing = new ArrayList<PwGroup>(mGroup.childGroups);
|
||||
|
||||
Collections.sort(entriesForViewing, entryComp);
|
||||
Collections.sort(groupsForViewing, groupComp);
|
||||
} else {
|
||||
groupsForViewing = mGroup.childGroups;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
|
||||
return groupsForViewing.size() + entriesForViewing.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
int size = groupsForViewing.size();
|
||||
|
||||
if ( position < size ) {
|
||||
return createGroupView(position, convertView);
|
||||
} else {
|
||||
return createEntryView(position - size, convertView);
|
||||
}
|
||||
}
|
||||
|
||||
private View createGroupView(int position, View convertView) {
|
||||
PwGroup group = groupsForViewing.get(position);
|
||||
PwGroupView gv;
|
||||
|
||||
if (convertView == null || !(convertView instanceof PwGroupView)) {
|
||||
|
||||
gv = PwGroupView.getInstance(mAct, group);
|
||||
}
|
||||
else {
|
||||
gv = (PwGroupView) convertView;
|
||||
gv.convertView(group);
|
||||
|
||||
}
|
||||
|
||||
return gv;
|
||||
}
|
||||
|
||||
private PwEntryView createEntryView(int position, View convertView) {
|
||||
PwEntry entry = entriesForViewing.get(position);
|
||||
PwEntryView ev;
|
||||
|
||||
if (convertView == null || !(convertView instanceof PwEntryView)) {
|
||||
ev = PwEntryView.getInstance(mAct, entry, position);
|
||||
}
|
||||
else {
|
||||
ev = (PwEntryView) convertView;
|
||||
ev.convertView(entry, position);
|
||||
}
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public class UnavailableFeatureDialog extends DialogFragment {
|
||||
|
||||
private static final String MIN_REQUIRED_VERSION_ARG = "MIN_REQUIRED_VERSION_ARG";
|
||||
private int minVersionRequired = Build.VERSION_CODES.M;
|
||||
|
||||
public static UnavailableFeatureDialog getInstance(int minVersionRequired) {
|
||||
UnavailableFeatureDialog fragment = new UnavailableFeatureDialog();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(MIN_REQUIRED_VERSION_ARG, minVersionRequired);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
if (getArguments() != null && getArguments().containsKey(MIN_REQUIRED_VERSION_ARG))
|
||||
minVersionRequired = getArguments().getInt(MIN_REQUIRED_VERSION_ARG);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
String message = getString(R.string.unavailable_feature_text).concat("\n");
|
||||
if(Build.VERSION.SDK_INT <= minVersionRequired)
|
||||
message = message.concat(getString(R.string.unavailable_feature_version,
|
||||
Build.VERSION.SDK_INT,
|
||||
minVersionRequired));
|
||||
else
|
||||
message = message.concat(getString(R.string.unavailable_feature_hardware));
|
||||
|
||||
builder.setMessage(message)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) { }
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
interface UriIntentInitTaskCallback {
|
||||
void onPostInitTask(Uri dbUri, Uri keyFileUri, Integer errorStringId);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.compat;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
public class ActivityCompat {
|
||||
private static Method invalidateOptMenu;
|
||||
|
||||
static {
|
||||
try {
|
||||
invalidateOptMenu = Activity.class.getMethod("invalidateOptionsMenu", (Class<Activity>[]) null);
|
||||
} catch (Exception e) {
|
||||
// Do nothing if method dosen't exist
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidateOptionsMenu(Activity act) {
|
||||
if (invalidateOptMenu != null) {
|
||||
try {
|
||||
invalidateOptMenu.invoke(act, (Object[]) null);
|
||||
} catch (Exception e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin.
|
||||
*
|
||||
* This file is part of KeePassDroid.
|
||||
*
|
||||
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.compat;
|
||||
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
||||
public class KeyGenParameterSpecCompat {
|
||||
private static Class builder;
|
||||
private static Constructor buildConst;
|
||||
private static Method builderBuild;
|
||||
private static Method setBlockModes;
|
||||
private static Method setUserAuthReq;
|
||||
private static Method setEncPad;
|
||||
|
||||
private static boolean available;
|
||||
|
||||
static {
|
||||
try {
|
||||
builder = Class.forName("android.security.keystore.KeyGenParameterSpec$Builder");
|
||||
buildConst = builder.getConstructor(String.class, int.class);
|
||||
builderBuild = builder.getMethod("build", (Class [])null);
|
||||
setBlockModes = builder.getMethod("setBlockModes", String[].class);
|
||||
setUserAuthReq = builder.getMethod("setUserAuthenticationRequired", new Class []{boolean.class});
|
||||
setEncPad = builder.getMethod("setEncryptionPaddings", String[].class);
|
||||
|
||||
|
||||
available = true;
|
||||
} catch (Exception e) {
|
||||
available = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static AlgorithmParameterSpec build(String keystoreAlias, int purpose, String blockMode,
|
||||
boolean userAuthReq, String encPadding) {
|
||||
|
||||
if (!available) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Object inst = buildConst.newInstance(keystoreAlias, purpose);
|
||||
inst = setBlockModes.invoke(inst, new Object[] {new String[] {blockMode}});
|
||||
inst = setUserAuthReq.invoke(inst, userAuthReq);
|
||||
inst = setEncPad.invoke(inst, new Object[] {new String[] {encPadding}});
|
||||
|
||||
return (AlgorithmParameterSpec) builderBuild.invoke(inst, null);
|
||||
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin.
|
||||
*
|
||||
* This file is part of KeePassDroid.
|
||||
*
|
||||
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.compat;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class KeyguardManagerCompat {
|
||||
private static Method isKeyguardSecure;
|
||||
|
||||
private static boolean available;
|
||||
|
||||
static {
|
||||
try {
|
||||
isKeyguardSecure = KeyguardManager.class.getMethod("isKeyguardSecure", (Class[]) null);
|
||||
|
||||
available = true;
|
||||
} catch (Exception e) {
|
||||
available = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isKeyguardSecure(KeyguardManager inst) {
|
||||
if (!available) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return (boolean) isKeyguardSecure.invoke(inst, null);
|
||||
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.keepassdroid.database.iterator.EntrySearchStringIterator;
|
||||
|
||||
public abstract class EntrySearchHandler extends EntryHandler<PwEntry> {
|
||||
private List<PwEntry> listStorage;
|
||||
private SearchParameters sp;
|
||||
private Date now;
|
||||
|
||||
public static EntrySearchHandler getInstance(PwGroup group, SearchParameters sp, List<PwEntry> listStorage) {
|
||||
if (group instanceof PwGroupV3) {
|
||||
return new EntrySearchHandlerV4(sp, listStorage);
|
||||
} else if (group instanceof PwGroupV4) {
|
||||
return new EntrySearchHandlerV4(sp, listStorage);
|
||||
} else {
|
||||
throw new RuntimeException("Not implemented.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected EntrySearchHandler(SearchParameters sp, List<PwEntry> listStorage) {
|
||||
this.sp = sp;
|
||||
this.listStorage = listStorage;
|
||||
now = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean operate(PwEntry entry) {
|
||||
if (sp.respectEntrySearchingDisabled && !entry.isSearchingEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sp.excludeExpired && entry.expires() && now.after(entry.getExpiryTime())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String term = sp.searchString;
|
||||
if (sp.ignoreCase) {
|
||||
term = term.toLowerCase();
|
||||
}
|
||||
|
||||
if (searchStrings(entry, term)) {
|
||||
listStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sp.searchInGroupNames) {
|
||||
PwGroup parent = entry.getParent();
|
||||
if (parent != null) {
|
||||
String groupName = parent.getName();
|
||||
if (groupName != null) {
|
||||
if (sp.ignoreCase) {
|
||||
groupName = groupName.toLowerCase();
|
||||
}
|
||||
|
||||
if (groupName.indexOf(term) >= 0) {
|
||||
listStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (searchID(entry)) {
|
||||
listStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean searchID(PwEntry entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean searchStrings(PwEntry entry, String term) {
|
||||
EntrySearchStringIterator iter = EntrySearchStringIterator.getInstance(entry, sp);
|
||||
while (iter.hasNext()) {
|
||||
String str = iter.next();
|
||||
if (str != null & str.length() > 0) {
|
||||
if (sp.ignoreCase) {
|
||||
str = str.toLowerCase();
|
||||
}
|
||||
|
||||
if (str.indexOf(term) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import com.keepassdroid.utils.StrUtil;
|
||||
import com.keepassdroid.utils.UuidUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class EntrySearchHandlerV4 extends EntrySearchHandler {
|
||||
private SearchParametersV4 sp;
|
||||
|
||||
protected EntrySearchHandlerV4(SearchParameters sp, List<PwEntry> listStorage) {
|
||||
super(sp, listStorage);
|
||||
this.sp = (SearchParametersV4) sp;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean searchID(PwEntry e) {
|
||||
PwEntryV4 entry = (PwEntryV4) e;
|
||||
if (sp.searchInUUIDs) {
|
||||
String hex = UuidUtil.toHexString(entry.uuid);
|
||||
return StrUtil.indexOfIgnoreCase(hex, sp.searchString, Locale.ENGLISH) >= 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface ITimeLogger {
|
||||
Date getLastModificationTime();
|
||||
void setLastModificationTime(Date date);
|
||||
|
||||
Date getCreationTime();
|
||||
void setCreationTime(Date date);
|
||||
|
||||
Date getLastAccessTime();
|
||||
void setLastAccessTime(Date date);
|
||||
|
||||
Date getExpiryTime();
|
||||
void setExpiryTime(Date date);
|
||||
|
||||
boolean expires();
|
||||
void setExpires(boolean exp);
|
||||
|
||||
long getUsageCount();
|
||||
void setUsageCount(long count);
|
||||
|
||||
Date getLocationChanged();
|
||||
void setLocationChanged(Date date);
|
||||
|
||||
}
|
||||
@@ -1,533 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import android.webkit.URLUtil;
|
||||
|
||||
import com.keepassdroid.collections.VariantDictionary;
|
||||
import com.keepassdroid.crypto.CryptoUtil;
|
||||
import com.keepassdroid.crypto.engine.AesEngine;
|
||||
import com.keepassdroid.crypto.engine.CipherEngine;
|
||||
import com.keepassdroid.crypto.keyDerivation.AesKdf;
|
||||
import com.keepassdroid.crypto.keyDerivation.KdfEngine;
|
||||
import com.keepassdroid.crypto.keyDerivation.KdfFactory;
|
||||
import com.keepassdroid.crypto.keyDerivation.KdfParameters;
|
||||
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.w3c.dom.Text;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import biz.source_code.base64Coder.Base64Coder;
|
||||
|
||||
|
||||
public class PwDatabaseV4 extends PwDatabase {
|
||||
|
||||
public static final Date DEFAULT_NOW = new Date();
|
||||
public static final UUID UUID_ZERO = new UUID(0,0);
|
||||
public static final int DEFAULT_ROUNDS = 6000;
|
||||
private static final int DEFAULT_HISTORY_MAX_ITEMS = 10; // -1 unlimited
|
||||
private static final long DEFAULT_HISTORY_MAX_SIZE = 6 * 1024 * 1024; // -1 unlimited
|
||||
private static final String RECYCLEBIN_NAME = "RecycleBin";
|
||||
|
||||
public byte[] hmacKey;
|
||||
public UUID dataCipher = AesEngine.CIPHER_UUID;
|
||||
public CipherEngine dataEngine = new AesEngine();
|
||||
public PwCompressionAlgorithm compressionAlgorithm = PwCompressionAlgorithm.Gzip;
|
||||
// TODO: Refactor me away to get directly from kdfParameters
|
||||
public long numKeyEncRounds = 6000;
|
||||
public Date nameChanged = DEFAULT_NOW;
|
||||
public Date settingsChanged = DEFAULT_NOW;
|
||||
public String description = "";
|
||||
public Date descriptionChanged = DEFAULT_NOW;
|
||||
public String defaultUserName = "";
|
||||
public Date defaultUserNameChanged = DEFAULT_NOW;
|
||||
|
||||
public Date keyLastChanged = DEFAULT_NOW;
|
||||
public long keyChangeRecDays = -1;
|
||||
public long keyChangeForceDays = 1;
|
||||
public boolean keyChangeForceOnce = false;
|
||||
|
||||
public long maintenanceHistoryDays = 365;
|
||||
public String color = "";
|
||||
public boolean recycleBinEnabled = true;
|
||||
public UUID recycleBinUUID = UUID_ZERO;
|
||||
public Date recycleBinChanged = DEFAULT_NOW;
|
||||
public UUID entryTemplatesGroup = UUID_ZERO;
|
||||
public Date entryTemplatesGroupChanged = DEFAULT_NOW;
|
||||
public int historyMaxItems = DEFAULT_HISTORY_MAX_ITEMS;
|
||||
public long historyMaxSize = DEFAULT_HISTORY_MAX_SIZE;
|
||||
public UUID lastSelectedGroup = UUID_ZERO;
|
||||
public UUID lastTopVisibleGroup = UUID_ZERO;
|
||||
public MemoryProtectionConfig memoryProtection = new MemoryProtectionConfig();
|
||||
public List<PwDeletedObject> deletedObjects = new ArrayList<PwDeletedObject>();
|
||||
public List<PwIconCustom> customIcons = new ArrayList<PwIconCustom>();
|
||||
public Map<String, String> customData = new HashMap<String, String>();
|
||||
public KdfParameters kdfParameters = KdfFactory.getDefaultParameters();
|
||||
public VariantDictionary publicCustomData = new VariantDictionary();
|
||||
public BinaryPool binPool = new BinaryPool();
|
||||
|
||||
public String localizedAppName = "KeePassDroid";
|
||||
|
||||
public class MemoryProtectionConfig {
|
||||
public boolean protectTitle = false;
|
||||
public boolean protectUserName = false;
|
||||
public boolean protectPassword = false;
|
||||
public boolean protectUrl = false;
|
||||
public boolean protectNotes = false;
|
||||
|
||||
public boolean autoEnableVisualHiding = false;
|
||||
|
||||
public boolean GetProtection(String field) {
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.TITLE_FIELD)) return protectTitle;
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.USERNAME_FIELD)) return protectUserName;
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.PASSWORD_FIELD)) return protectPassword;
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.URL_FIELD)) return protectUrl;
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.NOTES_FIELD)) return protectNotes;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMasterKey(String key, InputStream keyInputStream)
|
||||
throws InvalidKeyFileException, IOException {
|
||||
assert(key != null);
|
||||
|
||||
byte[] fKey = new byte[]{};
|
||||
|
||||
if ( key.length() > 0 && keyInputStream != null) {
|
||||
return getCompositeKey(key, keyInputStream);
|
||||
} else if ( key.length() > 0 ) {
|
||||
fKey = getPasswordKey(key);
|
||||
} else if ( keyInputStream != null) {
|
||||
fKey = getFileKey(keyInputStream);
|
||||
}
|
||||
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException("No SHA-256 implementation");
|
||||
}
|
||||
|
||||
return md.digest(fKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeFinalKey(byte[] masterSeed, byte[] masterSeed2, int numRounds) throws IOException {
|
||||
|
||||
byte[] transformedMasterKey = transformMasterKey(masterSeed2, masterKey, numRounds);
|
||||
|
||||
|
||||
byte[] cmpKey = new byte[65];
|
||||
System.arraycopy(masterSeed, 0, cmpKey, 0, 32);
|
||||
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32);
|
||||
finalKey = CryptoUtil.resizeKey(cmpKey, 0, 64, dataEngine.keyLength());
|
||||
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-512");
|
||||
cmpKey[64] = 1;
|
||||
hmacKey = md.digest(cmpKey);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException("No SHA-512 implementation");
|
||||
} finally {
|
||||
Arrays.fill(cmpKey, (byte)0);
|
||||
}
|
||||
}
|
||||
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP) throws IOException {
|
||||
makeFinalKey(masterSeed, kdfP, 0);
|
||||
}
|
||||
|
||||
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP, long roundsFix)
|
||||
throws IOException {
|
||||
|
||||
KdfEngine kdfEngine = KdfFactory.get(kdfP.kdfUUID);
|
||||
if (kdfEngine == null) {
|
||||
throw new IOException("Unknown key derivation function");
|
||||
}
|
||||
|
||||
// Set to 6000 rounds to open corrupted database
|
||||
if (roundsFix > 0 && kdfP.kdfUUID.equals(AesKdf.CIPHER_UUID)) {
|
||||
kdfP.setUInt32(AesKdf.ParamRounds, roundsFix);
|
||||
numKeyEncRounds = roundsFix;
|
||||
}
|
||||
|
||||
byte[] transformedMasterKey = kdfEngine.transform(masterKey, kdfP);
|
||||
if (transformedMasterKey.length != 32) {
|
||||
transformedMasterKey = CryptoUtil.hashSha256(transformedMasterKey);
|
||||
}
|
||||
|
||||
byte[] cmpKey = new byte[65];
|
||||
System.arraycopy(masterSeed, 0, cmpKey, 0, 32);
|
||||
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32);
|
||||
finalKey = CryptoUtil.resizeKey(cmpKey, 0, 64, dataEngine.keyLength());
|
||||
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-512");
|
||||
cmpKey[64] = 1;
|
||||
hmacKey = md.digest(cmpKey);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException("No SHA-512 implementation");
|
||||
} finally {
|
||||
Arrays.fill(cmpKey, (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPasswordEncoding() {
|
||||
return "UTF-8";
|
||||
}
|
||||
|
||||
private static final String RootElementName = "KeyFile";
|
||||
//private static final String MetaElementName = "Meta";
|
||||
//private static final String VersionElementName = "Version";
|
||||
private static final String KeyElementName = "Key";
|
||||
private static final String KeyDataElementName = "Data";
|
||||
|
||||
@Override
|
||||
protected byte[] loadXmlKeyFile(InputStream keyInputStream) {
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document doc = db.parse(keyInputStream);
|
||||
|
||||
Element el = doc.getDocumentElement();
|
||||
if (el == null || ! el.getNodeName().equalsIgnoreCase(RootElementName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NodeList children = el.getChildNodes();
|
||||
if (children.getLength() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < children.getLength(); i++ ) {
|
||||
Node child = children.item(i);
|
||||
|
||||
if ( child.getNodeName().equalsIgnoreCase(KeyElementName) ) {
|
||||
NodeList keyChildren = child.getChildNodes();
|
||||
for ( int j = 0; j < keyChildren.getLength(); j++ ) {
|
||||
Node keyChild = keyChildren.item(j);
|
||||
if ( keyChild.getNodeName().equalsIgnoreCase(KeyDataElementName) ) {
|
||||
NodeList children2 = keyChild.getChildNodes();
|
||||
for ( int k = 0; k < children2.getLength(); k++) {
|
||||
Node text = children2.item(k);
|
||||
if (text.getNodeType() == Node.TEXT_NODE) {
|
||||
Text txt = (Text) text;
|
||||
return Base64Coder.decode(txt.getNodeValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PwGroup> getGroups() {
|
||||
List<PwGroup> list = new ArrayList<PwGroup>();
|
||||
PwGroupV4 root = (PwGroupV4) rootGroup;
|
||||
root.buildChildGroupsRecursive(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PwGroup> getGrpRoots() {
|
||||
return rootGroup.childGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PwEntry> getEntries() {
|
||||
List<PwEntry> list = new ArrayList<PwEntry>();
|
||||
PwGroupV4 root = (PwGroupV4) rootGroup;
|
||||
root.buildChildEntriesRecursive(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumRounds() {
|
||||
return numKeyEncRounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumRounds(long rounds) throws NumberFormatException {
|
||||
numKeyEncRounds = rounds;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appSettingsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwEncryptionAlgorithm getEncAlgorithm() {
|
||||
return PwEncryptionAlgorithm.Rjindal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupIdV4 newGroupId() {
|
||||
PwGroupIdV4 id = new PwGroupIdV4(UUID_ZERO);
|
||||
|
||||
while (true) {
|
||||
id = new PwGroupIdV4(UUID.randomUUID());
|
||||
|
||||
if (!isGroupIdUsed(id)) break;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroup createGroup() {
|
||||
return new PwGroupV4();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBackup(PwGroup group) {
|
||||
if (!recycleBinEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return group.isContainedIn(getRecycleBin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateGlobals(PwGroup currentGroup) {
|
||||
groups.put(rootGroup.getId(), rootGroup);
|
||||
|
||||
super.populateGlobals(currentGroup);
|
||||
}
|
||||
|
||||
/** Ensure that the recycle bin tree exists, if enabled and create it
|
||||
* if it doesn't exist
|
||||
*
|
||||
*/
|
||||
private void ensureRecycleBin() {
|
||||
if (getRecycleBin() == null) {
|
||||
// Create recycle bin
|
||||
|
||||
PwGroupV4 recycleBin = new PwGroupV4(true, true, RECYCLEBIN_NAME, iconFactory.getIcon(PwIconStandard.TRASH_BIN));
|
||||
recycleBin.enableAutoType = false;
|
||||
recycleBin.enableSearching = false;
|
||||
recycleBin.isExpanded = false;
|
||||
addGroupTo(recycleBin, rootGroup);
|
||||
|
||||
recycleBinUUID = recycleBin.uuid;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRecycle(PwGroup group) {
|
||||
if (!recycleBinEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PwGroup recycle = getRecycleBin();
|
||||
|
||||
return (recycle == null) || (!group.isContainedIn(recycle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRecycle(PwEntry entry) {
|
||||
if (!recycleBinEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PwGroup parent = entry.getParent();
|
||||
return (parent != null) && canRecycle(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle(PwEntry entry) {
|
||||
ensureRecycleBin();
|
||||
|
||||
PwGroup parent = entry.getParent();
|
||||
removeEntryFrom(entry, parent);
|
||||
parent.touch(false, true);
|
||||
|
||||
PwGroup recycleBin = getRecycleBin();
|
||||
addEntryTo(entry, recycleBin);
|
||||
|
||||
entry.touch(false, true);
|
||||
entry.touchLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoRecycle(PwEntry entry, PwGroup origParent) {
|
||||
|
||||
PwGroup recycleBin = getRecycleBin();
|
||||
removeEntryFrom(entry, recycleBin);
|
||||
|
||||
addEntryTo(entry, origParent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteEntry(PwEntry entry) {
|
||||
super.deleteEntry(entry);
|
||||
|
||||
deletedObjects.add(new PwDeletedObject(entry.getUUID()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoDeleteEntry(PwEntry entry, PwGroup origParent) {
|
||||
super.undoDeleteEntry(entry, origParent);
|
||||
|
||||
deletedObjects.remove(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupV4 getRecycleBin() {
|
||||
if (recycleBinUUID == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PwGroupId recycleId = new PwGroupIdV4(recycleBinUUID);
|
||||
return (PwGroupV4) groups.get(recycleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGroupSearchable(PwGroup group, boolean omitBackup) {
|
||||
if (!super.isGroupSearchable(group, omitBackup)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PwGroupV4 g = (PwGroupV4) group;
|
||||
|
||||
return g.isSearchEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validatePasswordEncoding(String key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initNew(String dbPath) {
|
||||
String filename = URLUtil.guessFileName(dbPath, null, null);
|
||||
|
||||
rootGroup = new PwGroupV4(true, true, dbNameFromPath(dbPath), iconFactory.getIcon(PwIconStandard.FOLDER));
|
||||
groups.put(rootGroup.getId(), rootGroup);
|
||||
}
|
||||
|
||||
private String dbNameFromPath(String dbPath) {
|
||||
String filename = URLUtil.guessFileName(dbPath, null, null);
|
||||
|
||||
if (EmptyUtils.isNullOrEmpty(filename)) {
|
||||
return "KeePass Database";
|
||||
}
|
||||
int lastExtDot = filename.lastIndexOf(".");
|
||||
if (lastExtDot == -1) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
return filename.substring(0, lastExtDot);
|
||||
}
|
||||
|
||||
private class GroupHasCustomData extends GroupHandler<PwGroup> {
|
||||
|
||||
public boolean hasCustomData = false;
|
||||
|
||||
@Override
|
||||
public boolean operate(PwGroup group) {
|
||||
if (group == null) {
|
||||
return true;
|
||||
}
|
||||
PwGroupV4 g4 = (PwGroupV4) group;
|
||||
if (g4.customData.size() > 0) {
|
||||
hasCustomData = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryHasCustomData extends EntryHandler<PwEntry> {
|
||||
|
||||
public boolean hasCustomData = false;
|
||||
|
||||
@Override
|
||||
public boolean operate(PwEntry entry) {
|
||||
if (entry == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PwEntryV4 e4 = (PwEntryV4)entry;
|
||||
if (e4.customData.size() > 0) {
|
||||
hasCustomData = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMinKdbxVersion() {
|
||||
if (!AesKdf.CIPHER_UUID.equals(kdfParameters.kdfUUID)) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
if (publicCustomData.size() > 0) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
EntryHasCustomData entryHandler = new EntryHasCustomData();
|
||||
GroupHasCustomData groupHandler = new GroupHasCustomData();
|
||||
|
||||
if (rootGroup == null ) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32_3;
|
||||
}
|
||||
rootGroup.preOrderTraverseTree(groupHandler, entryHandler);
|
||||
if (groupHandler.hasCustomData || entryHandler.hasCustomData) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
return PwDbHeaderV4.FILE_VERSION_32_3;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.keepassdroid.database.iterator.EntrySearchStringIterator;
|
||||
import com.keepassdroid.utils.SprEngine;
|
||||
|
||||
public abstract class PwEntry implements Cloneable {
|
||||
|
||||
protected static final String PMS_TAN_ENTRY = "<TAN>";
|
||||
|
||||
public static class EntryNameComparator implements Comparator<PwEntry> {
|
||||
|
||||
public int compare(PwEntry object1, PwEntry object2) {
|
||||
return object1.getTitle().compareToIgnoreCase(object2.getTitle());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public PwIconStandard icon = PwIconStandard.FIRST;
|
||||
|
||||
public PwEntry() {
|
||||
|
||||
}
|
||||
|
||||
public static PwEntry getInstance(PwGroup parent) {
|
||||
return PwEntry.getInstance(parent, true, true);
|
||||
}
|
||||
|
||||
public static PwEntry getInstance(PwGroup parent, boolean initId, boolean initDates) {
|
||||
if (parent instanceof PwGroupV3) {
|
||||
return new PwEntryV3((PwGroupV3)parent);
|
||||
}
|
||||
else if (parent instanceof PwGroupV4) {
|
||||
return new PwEntryV4((PwGroupV4)parent);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Unknown PwGroup instance.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
PwEntry newEntry;
|
||||
try {
|
||||
newEntry = (PwEntry) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
assert(false);
|
||||
throw new RuntimeException("Clone should be supported");
|
||||
}
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
public PwEntry clone(boolean deepStrings) {
|
||||
return (PwEntry) clone();
|
||||
}
|
||||
|
||||
public void assign(PwEntry source) {
|
||||
icon = source.icon;
|
||||
}
|
||||
|
||||
public abstract UUID getUUID();
|
||||
public abstract void setUUID(UUID u);
|
||||
|
||||
public String getTitle() {
|
||||
return getTitle(false, null);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return getUsername(false, null);
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return getPassword(false, null);
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return getUrl(false, null);
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return getNotes(false, null);
|
||||
}
|
||||
|
||||
public abstract String getTitle(boolean decodeRef, PwDatabase db);
|
||||
public abstract String getUsername(boolean decodeRef, PwDatabase db);
|
||||
public abstract String getPassword(boolean decodeRef, PwDatabase db);
|
||||
public abstract String getUrl(boolean decodeRef, PwDatabase db);
|
||||
public abstract String getNotes(boolean decodeRef, PwDatabase db);
|
||||
public abstract Date getCreationTime();
|
||||
public abstract Date getLastModificationTime();
|
||||
public abstract Date getLastAccessTime();
|
||||
public abstract Date getExpiryTime();
|
||||
public abstract boolean expires();
|
||||
public abstract PwGroup getParent();
|
||||
|
||||
public abstract void setTitle(String title, PwDatabase db);
|
||||
public abstract void setUsername(String user, PwDatabase db);
|
||||
public abstract void setPassword(String pass, PwDatabase db);
|
||||
public abstract void setUrl(String url, PwDatabase db);
|
||||
public abstract void setNotes(String notes, PwDatabase db);
|
||||
public abstract void setCreationTime(Date create);
|
||||
public abstract void setLastModificationTime(Date mod);
|
||||
public abstract void setLastAccessTime(Date access);
|
||||
public abstract void setExpires(boolean exp);
|
||||
public abstract void setExpiryTime(Date expires);
|
||||
|
||||
public PwIcon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(PwIconStandard icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public boolean isTan() {
|
||||
return getTitle().equals(PMS_TAN_ENTRY) && (getUsername().length() > 0);
|
||||
}
|
||||
|
||||
public String getDisplayTitle() {
|
||||
if ( isTan() ) {
|
||||
return PMS_TAN_ENTRY + " " + getUsername();
|
||||
} else {
|
||||
return getTitle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isMetaStream() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EntrySearchStringIterator stringIterator() {
|
||||
return EntrySearchStringIterator.getInstance(this);
|
||||
}
|
||||
|
||||
public void touch(boolean modified, boolean touchParents) {
|
||||
Date now = new Date();
|
||||
|
||||
setLastAccessTime(now);
|
||||
|
||||
if (modified) {
|
||||
setLastModificationTime(now);
|
||||
}
|
||||
|
||||
PwGroup parent = getParent();
|
||||
if (touchParents && parent != null) {
|
||||
parent.touch(modified, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void touchLocation() { }
|
||||
|
||||
public abstract void setParent(PwGroup parent);
|
||||
|
||||
public boolean isSearchingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,530 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
|
||||
This file was derived from
|
||||
|
||||
Copyright 2007 Naomaru Itoi <nao@phoneid.org>
|
||||
|
||||
This file was derived from
|
||||
|
||||
Java clone of KeePass - A KeePass file viewer for Java
|
||||
Copyright 2006 Bill Zwicky <billzwicky@users.sourceforge.net>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package com.keepassdroid.database;
|
||||
|
||||
// PhoneID
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.keepassdroid.utils.Types;
|
||||
|
||||
|
||||
/**
|
||||
* Structure containing information about one entry.
|
||||
*
|
||||
* <PRE>
|
||||
* One entry: [FIELDTYPE(FT)][FIELDSIZE(FS)][FIELDDATA(FD)]
|
||||
* [FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)]...
|
||||
*
|
||||
* [ 2 bytes] FIELDTYPE
|
||||
* [ 4 bytes] FIELDSIZE, size of FIELDDATA in bytes
|
||||
* [ n bytes] FIELDDATA, n = FIELDSIZE
|
||||
*
|
||||
* Notes:
|
||||
* - Strings are stored in UTF-8 encoded form and are null-terminated.
|
||||
* - FIELDTYPE can be one of the FT_ constants.
|
||||
* </PRE>
|
||||
*
|
||||
* @author Naomaru Itoi <nao@phoneid.org>
|
||||
* @author Bill Zwicky <wrzwicky@pobox.com>
|
||||
* @author Dominik Reichl <dominik.reichl@t-online.de>
|
||||
*/
|
||||
public class PwEntryV3 extends PwEntry {
|
||||
|
||||
public static final Date NEVER_EXPIRE = getNeverExpire();
|
||||
public static final Date NEVER_EXPIRE_BUG = getNeverExpireBug();
|
||||
public static final Date DEFAULT_DATE = getDefaultDate();
|
||||
public static final PwDate PW_NEVER_EXPIRE = new PwDate(NEVER_EXPIRE);
|
||||
public static final PwDate PW_NEVER_EXPIRE_BUG = new PwDate(NEVER_EXPIRE_BUG);
|
||||
public static final PwDate DEFAULT_PWDATE = new PwDate(DEFAULT_DATE);
|
||||
|
||||
|
||||
/** Size of byte buffer needed to hold this struct. */
|
||||
public static final String PMS_ID_BINDESC = "bin-stream";
|
||||
public static final String PMS_ID_TITLE = "Meta-Info";
|
||||
public static final String PMS_ID_USER = "SYSTEM";
|
||||
public static final String PMS_ID_URL = "$";
|
||||
|
||||
|
||||
|
||||
public int groupId;
|
||||
public String username;
|
||||
private byte[] password;
|
||||
private byte[] uuid;
|
||||
public String title;
|
||||
public String url;
|
||||
public String additional;
|
||||
|
||||
|
||||
public PwDate tCreation;
|
||||
public PwDate tLastMod;
|
||||
public PwDate tLastAccess;
|
||||
public PwDate tExpire;
|
||||
|
||||
/** A string describing what is in pBinaryData */
|
||||
public String binaryDesc;
|
||||
private byte[] binaryData;
|
||||
|
||||
private static Date getDefaultDate() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(Calendar.YEAR, 2004);
|
||||
cal.set(Calendar.MONTH, Calendar.JANUARY);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 1);
|
||||
cal.set(Calendar.HOUR, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
private static Date getNeverExpire() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(Calendar.YEAR, 2999);
|
||||
cal.set(Calendar.MONTH, 11);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 28);
|
||||
cal.set(Calendar.HOUR, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
/** This date was was accidentally being written
|
||||
* out when an entry was supposed to be marked as
|
||||
* expired. We'll use this to silently correct those
|
||||
* entries.
|
||||
* @return
|
||||
*/
|
||||
private static Date getNeverExpireBug() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(Calendar.YEAR, 2999);
|
||||
cal.set(Calendar.MONTH, 11);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 30);
|
||||
cal.set(Calendar.HOUR, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
public static boolean IsNever(Date date) {
|
||||
return PwDate.IsSameDate(NEVER_EXPIRE, date);
|
||||
}
|
||||
|
||||
// for tree traversing
|
||||
public PwGroupV3 parent = null;
|
||||
|
||||
|
||||
public PwEntryV3() {
|
||||
super();
|
||||
}
|
||||
|
||||
/*
|
||||
public PwEntryV3(PwEntryV3 source) {
|
||||
assign(source);
|
||||
}
|
||||
*/
|
||||
|
||||
public PwEntryV3(PwGroupV3 p) {
|
||||
this(p, true, true);
|
||||
}
|
||||
|
||||
public PwEntryV3(PwGroupV3 p, boolean initId, boolean initDates) {
|
||||
|
||||
parent = p;
|
||||
groupId = ((PwGroupIdV3)parent.getId()).getId();
|
||||
|
||||
if (initId) {
|
||||
Random random = new Random();
|
||||
uuid = new byte[16];
|
||||
random.nextBytes(uuid);
|
||||
}
|
||||
|
||||
if (initDates) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Date now = cal.getTime();
|
||||
tCreation = new PwDate(now);
|
||||
tLastAccess = new PwDate(now);
|
||||
tLastMod = new PwDate(now);
|
||||
tExpire = new PwDate(NEVER_EXPIRE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the actual password byte array.
|
||||
*/
|
||||
@Override
|
||||
public String getPassword(boolean decodeRef, PwDatabase db) {
|
||||
if (password == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return new String(password);
|
||||
}
|
||||
|
||||
public byte[] getPasswordBytes() {
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fill byte array
|
||||
*/
|
||||
private static void fill(byte[] array, byte value)
|
||||
{
|
||||
for (int i=0; i<array.length; i++)
|
||||
array[i] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
/** Securely erase old password before copying new. */
|
||||
public void setPassword( byte[] buf, int offset, int len ) {
|
||||
if( password != null ) {
|
||||
fill( password, (byte)0 );
|
||||
password = null;
|
||||
}
|
||||
password = new byte[len];
|
||||
System.arraycopy( buf, offset, password, 0, len );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setPassword(String pass, PwDatabase db) {
|
||||
byte[] password;
|
||||
try {
|
||||
password = pass.getBytes("UTF-8");
|
||||
setPassword(password, 0, password.length);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
assert false;
|
||||
password = pass.getBytes();
|
||||
setPassword(password, 0, password.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the actual binaryData byte array.
|
||||
*/
|
||||
public byte[] getBinaryData() {
|
||||
return binaryData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Securely erase old data before copying new. */
|
||||
public void setBinaryData( byte[] buf, int offset, int len ) {
|
||||
if( binaryData != null ) {
|
||||
fill( binaryData, (byte)0 );
|
||||
binaryData = null;
|
||||
}
|
||||
binaryData = new byte[len];
|
||||
System.arraycopy( buf, offset, binaryData, 0, len );
|
||||
}
|
||||
|
||||
// Determine if this is a MetaStream entry
|
||||
@Override
|
||||
public boolean isMetaStream() {
|
||||
if ( binaryData == null ) return false;
|
||||
if ( additional == null || additional.length() == 0 ) return false;
|
||||
if ( ! binaryDesc.equals(PMS_ID_BINDESC) ) return false;
|
||||
if ( title == null ) return false;
|
||||
if ( ! title.equals(PMS_ID_TITLE) ) return false;
|
||||
if ( username == null ) return false;
|
||||
if ( ! username.equals(PMS_ID_USER) ) return false;
|
||||
if ( url == null ) return false;
|
||||
if ( ! url.equals(PMS_ID_URL)) return false;
|
||||
if ( !icon.isMetaStreamIcon() ) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void assign(PwEntry source) {
|
||||
|
||||
if ( ! (source instanceof PwEntryV3) ) {
|
||||
throw new RuntimeException("DB version mix");
|
||||
}
|
||||
|
||||
super.assign(source);
|
||||
|
||||
PwEntryV3 src = (PwEntryV3) source;
|
||||
assign(src);
|
||||
|
||||
}
|
||||
|
||||
private void assign(PwEntryV3 source) {
|
||||
title = source.title;
|
||||
url = source.url;
|
||||
groupId = source.groupId;
|
||||
username = source.username;
|
||||
additional = source.additional;
|
||||
uuid = source.uuid;
|
||||
|
||||
int passLen = source.password.length;
|
||||
password = new byte[passLen];
|
||||
System.arraycopy(source.password, 0, password, 0, passLen);
|
||||
|
||||
tCreation = (PwDate) source.tCreation.clone();
|
||||
tLastMod = (PwDate) source.tLastMod.clone();
|
||||
tLastAccess = (PwDate) source.tLastAccess.clone();
|
||||
tExpire = (PwDate) source.tExpire.clone();
|
||||
|
||||
binaryDesc = source.binaryDesc;
|
||||
|
||||
if ( source.binaryData != null ) {
|
||||
int descLen = source.binaryData.length;
|
||||
binaryData = new byte[descLen];
|
||||
System.arraycopy(source.binaryData, 0, binaryData, 0, descLen);
|
||||
}
|
||||
|
||||
parent = source.parent;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
PwEntryV3 newEntry = (PwEntryV3) super.clone();
|
||||
|
||||
if (password != null) {
|
||||
int passLen = password.length;
|
||||
password = new byte[passLen];
|
||||
System.arraycopy(password, 0, newEntry.password, 0, passLen);
|
||||
}
|
||||
|
||||
newEntry.tCreation = (PwDate) tCreation.clone();
|
||||
newEntry.tLastMod = (PwDate) tLastMod.clone();
|
||||
newEntry.tLastAccess = (PwDate) tLastAccess.clone();
|
||||
newEntry.tExpire = (PwDate) tExpire.clone();
|
||||
|
||||
newEntry.binaryDesc = binaryDesc;
|
||||
|
||||
if ( binaryData != null ) {
|
||||
int descLen = binaryData.length;
|
||||
newEntry.binaryData = new byte[descLen];
|
||||
System.arraycopy(binaryData, 0, newEntry.binaryData, 0, descLen);
|
||||
}
|
||||
|
||||
newEntry.parent = parent;
|
||||
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastAccessTime() {
|
||||
return tLastAccess.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreationTime() {
|
||||
return tCreation.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpiryTime() {
|
||||
return tExpire.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastModificationTime() {
|
||||
return tLastMod.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreationTime(Date create) {
|
||||
tCreation = new PwDate(create);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModificationTime(Date mod) {
|
||||
tLastMod = new PwDate(mod);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessTime(Date access) {
|
||||
tLastAccess = new PwDate(access);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpires(boolean expires) {
|
||||
if (!expires) {
|
||||
tExpire = PW_NEVER_EXPIRE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpiryTime(Date expires) {
|
||||
tExpire = new PwDate(expires);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupV3 getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return Types.bytestoUUID(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUUID(UUID u) {
|
||||
uuid = Types.UUIDtoBytes(u);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername(boolean decodeRef, PwDatabase db) {
|
||||
if (username == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String user, PwDatabase db) {
|
||||
username = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle(boolean decodeRef, PwDatabase db) {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title, PwDatabase db) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotes(boolean decodeRef, PwDatabase db) {
|
||||
return additional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotes(String notes, PwDatabase db) {
|
||||
additional = notes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(boolean decodeRef, PwDatabase db) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUrl(String url, PwDatabase db) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expires() {
|
||||
return ! IsNever(tExpire.getJDate());
|
||||
}
|
||||
|
||||
public void populateBlankFields(PwDatabaseV3 db) {
|
||||
if (icon == null) {
|
||||
icon = db.iconFactory.getIcon(1);
|
||||
}
|
||||
|
||||
if (username == null) {
|
||||
username = "";
|
||||
}
|
||||
|
||||
if (password == null) {
|
||||
password = new byte[0];
|
||||
}
|
||||
|
||||
if (uuid == null) {
|
||||
uuid = Types.UUIDtoBytes(UUID.randomUUID());
|
||||
}
|
||||
|
||||
if (title == null) {
|
||||
title = "";
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
url = "";
|
||||
}
|
||||
|
||||
if (additional == null) {
|
||||
additional = "";
|
||||
}
|
||||
|
||||
if (tCreation == null) {
|
||||
tCreation = DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tLastMod == null) {
|
||||
tLastMod = DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tLastAccess == null) {
|
||||
tLastAccess = DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tExpire == null) {
|
||||
tExpire = PW_NEVER_EXPIRE;
|
||||
}
|
||||
|
||||
if (binaryDesc == null) {
|
||||
binaryDesc = "";
|
||||
}
|
||||
|
||||
if (binaryData == null) {
|
||||
binaryData = new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(PwGroup parent) {
|
||||
this.parent = (PwGroupV3) parent;
|
||||
}
|
||||
}
|
||||
@@ -1,491 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.keepassdroid.database.security.ProtectedBinary;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
import com.keepassdroid.utils.SprEngine;
|
||||
|
||||
public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
||||
public static final String STR_TITLE = "Title";
|
||||
public static final String STR_USERNAME = "UserName";
|
||||
public static final String STR_PASSWORD = "Password";
|
||||
public static final String STR_URL = "URL";
|
||||
public static final String STR_NOTES = "Notes";
|
||||
|
||||
public PwGroupV4 parent;
|
||||
public UUID uuid = PwDatabaseV4.UUID_ZERO;
|
||||
public HashMap<String, ProtectedString> strings = new HashMap<String, ProtectedString>();
|
||||
public HashMap<String, ProtectedBinary> binaries = new HashMap<String, ProtectedBinary>();
|
||||
public PwIconCustom customIcon = PwIconCustom.ZERO;
|
||||
public String foregroundColor = "";
|
||||
public String backgroupColor = "";
|
||||
public String overrideURL = "";
|
||||
public AutoType autoType = new AutoType();
|
||||
public ArrayList<PwEntryV4> history = new ArrayList<PwEntryV4>();
|
||||
|
||||
private Date parentGroupLastMod = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date creation = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date lastMod = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date lastAccess = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date expireDate = PwDatabaseV4.DEFAULT_NOW;
|
||||
private boolean expires = false;
|
||||
private long usageCount = 0;
|
||||
public String url = "";
|
||||
public String additional = "";
|
||||
public String tags = "";
|
||||
public Map<String, String> customData = new HashMap<String, String>();
|
||||
|
||||
public class AutoType implements Cloneable {
|
||||
private static final long OBF_OPT_NONE = 0;
|
||||
|
||||
public boolean enabled = true;
|
||||
public long obfuscationOptions = OBF_OPT_NONE;
|
||||
public String defaultSequence = "";
|
||||
|
||||
private HashMap<String, String> windowSeqPairs = new HashMap<String, String>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object clone() {
|
||||
AutoType auto;
|
||||
try {
|
||||
auto = (AutoType) super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
assert(false);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
auto.windowSeqPairs = (HashMap<String, String>) windowSeqPairs.clone();
|
||||
|
||||
return auto;
|
||||
|
||||
}
|
||||
|
||||
public void put(String key, String value) {
|
||||
windowSeqPairs.put(key, value);
|
||||
}
|
||||
|
||||
public Set<Entry<String, String>> entrySet() {
|
||||
return windowSeqPairs.entrySet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public PwEntryV4() {
|
||||
|
||||
}
|
||||
|
||||
public PwEntryV4(PwGroupV4 p) {
|
||||
this(p, true, true);
|
||||
}
|
||||
|
||||
public PwEntryV4(PwGroupV4 p, boolean initId, boolean initDates) {
|
||||
parent = p;
|
||||
|
||||
if (initId) {
|
||||
uuid = UUID.randomUUID();
|
||||
}
|
||||
|
||||
if (initDates) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Date now = cal.getTime();
|
||||
creation = now;
|
||||
lastAccess = now;
|
||||
lastMod = now;
|
||||
expires = false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public PwEntry clone(boolean deepStrings) {
|
||||
PwEntryV4 entry = (PwEntryV4) super.clone(deepStrings);
|
||||
|
||||
if (deepStrings) {
|
||||
entry.strings = (HashMap<String, ProtectedString>) strings.clone();
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PwEntryV4 cloneDeep() {
|
||||
PwEntryV4 entry = (PwEntryV4) clone(true);
|
||||
|
||||
entry.binaries = (HashMap<String, ProtectedBinary>) binaries.clone();
|
||||
entry.history = (ArrayList<PwEntryV4>) history.clone();
|
||||
entry.autoType = (AutoType) autoType.clone();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assign(PwEntry source) {
|
||||
|
||||
if ( ! (source instanceof PwEntryV4) ) {
|
||||
throw new RuntimeException("DB version mix.");
|
||||
}
|
||||
|
||||
super.assign(source);
|
||||
|
||||
PwEntryV4 src = (PwEntryV4) source;
|
||||
assign(src);
|
||||
}
|
||||
|
||||
private void assign(PwEntryV4 source) {
|
||||
parent = source.parent;
|
||||
uuid = source.uuid;
|
||||
strings = source.strings;
|
||||
binaries = source.binaries;
|
||||
customIcon = source.customIcon;
|
||||
foregroundColor = source.foregroundColor;
|
||||
backgroupColor = source.backgroupColor;
|
||||
overrideURL = source.overrideURL;
|
||||
autoType = source.autoType;
|
||||
history = source.history;
|
||||
parentGroupLastMod = source.parentGroupLastMod;
|
||||
creation = source.creation;
|
||||
lastMod = source.lastMod;
|
||||
lastAccess = source.lastAccess;
|
||||
expireDate = source.expireDate;
|
||||
expires = source.expires;
|
||||
usageCount = source.usageCount;
|
||||
url = source.url;
|
||||
additional = source.additional;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
PwEntryV4 newEntry = (PwEntryV4) super.clone();
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
private String decodeRefKey(boolean decodeRef, String key, PwDatabase db) {
|
||||
String text = getString(key);
|
||||
if (decodeRef) {
|
||||
text = decodeRef(text, db);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private String decodeRef(String text, PwDatabase db) {
|
||||
if (db == null) { return text; }
|
||||
|
||||
SprEngine spr = SprEngine.getInstance(db);
|
||||
return spr.compile(text, this, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_USERNAME, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_TITLE, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_PASSWORD, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastAccessTime() {
|
||||
return lastAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreationTime() {
|
||||
return creation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpiryTime() {
|
||||
return expireDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastModificationTime() {
|
||||
return lastMod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectTitle;
|
||||
|
||||
setString(STR_TITLE, title, protect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String user, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectUserName;
|
||||
|
||||
setString(STR_USERNAME, user, protect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassword(String pass, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectPassword;
|
||||
|
||||
setString(STR_PASSWORD, pass, protect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUrl(String url, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectUrl;
|
||||
|
||||
setString(STR_URL, url, protect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotes(String notes, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectNotes;
|
||||
|
||||
setString(STR_NOTES, notes, protect);
|
||||
}
|
||||
|
||||
public void setCreationTime(Date date) {
|
||||
creation = date;
|
||||
}
|
||||
|
||||
public void setExpiryTime(Date date) {
|
||||
expireDate = date;
|
||||
}
|
||||
|
||||
public void setLastAccessTime(Date date) {
|
||||
lastAccess = date;
|
||||
}
|
||||
|
||||
public void setLastModificationTime(Date date) {
|
||||
lastMod = date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupV4 getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setUUID(UUID u) {
|
||||
uuid = u;
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
ProtectedString value = strings.get(key);
|
||||
|
||||
if ( value == null ) return new String("");
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
public void setString(String key, String value, boolean protect) {
|
||||
ProtectedString ps = new ProtectedString(protect, value);
|
||||
strings.put(key, ps);
|
||||
}
|
||||
|
||||
public Date getLocationChanged() {
|
||||
return parentGroupLastMod;
|
||||
}
|
||||
|
||||
public long getUsageCount() {
|
||||
return usageCount;
|
||||
}
|
||||
|
||||
public void setLocationChanged(Date date) {
|
||||
parentGroupLastMod = date;
|
||||
}
|
||||
|
||||
public void setUsageCount(long count) {
|
||||
usageCount = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expires() {
|
||||
return expires;
|
||||
}
|
||||
|
||||
public void setExpires(boolean exp) {
|
||||
expires = exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotes(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_NOTES, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_URL, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwIcon getIcon() {
|
||||
if (customIcon == null || customIcon.uuid.equals(PwDatabaseV4.UUID_ZERO)) {
|
||||
return super.getIcon();
|
||||
} else {
|
||||
return customIcon;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean IsStandardString(String key) {
|
||||
return key.equals(STR_TITLE) || key.equals(STR_USERNAME)
|
||||
|| key.equals(STR_PASSWORD) || key.equals(STR_URL)
|
||||
|| key.equals(STR_NOTES);
|
||||
}
|
||||
|
||||
public void createBackup(PwDatabaseV4 db) {
|
||||
PwEntryV4 copy = cloneDeep();
|
||||
copy.history = new ArrayList<PwEntryV4>();
|
||||
history.add(copy);
|
||||
|
||||
if (db != null) maintainBackups(db);
|
||||
}
|
||||
|
||||
private boolean maintainBackups(PwDatabaseV4 db) {
|
||||
boolean deleted = false;
|
||||
|
||||
int maxItems = db.historyMaxItems;
|
||||
if (maxItems >= 0) {
|
||||
while (history.size() > maxItems) {
|
||||
removeOldestBackup();
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
long maxSize = db.historyMaxSize;
|
||||
if (maxSize >= 0) {
|
||||
while(true) {
|
||||
long histSize = 0;
|
||||
for (PwEntryV4 entry : history) {
|
||||
histSize += entry.getSize();
|
||||
}
|
||||
|
||||
if (histSize > maxSize) {
|
||||
removeOldestBackup();
|
||||
deleted = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
private void removeOldestBackup() {
|
||||
Date min = null;
|
||||
int index = -1;
|
||||
|
||||
for (int i = 0; i < history.size(); i++) {
|
||||
PwEntry entry = history.get(i);
|
||||
Date lastMod = entry.getLastModificationTime();
|
||||
if ((min == null) || lastMod.before(min)) {
|
||||
index = i;
|
||||
min = lastMod;
|
||||
}
|
||||
}
|
||||
|
||||
if (index != -1) {
|
||||
history.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final long FIXED_LENGTH_SIZE = 128; // Approximate fixed length size
|
||||
public long getSize() {
|
||||
long size = FIXED_LENGTH_SIZE;
|
||||
|
||||
for (Entry<String, ProtectedString> pair : strings.entrySet()) {
|
||||
size += pair.getKey().length();
|
||||
size += pair.getValue().length();
|
||||
}
|
||||
|
||||
for (Entry<String, ProtectedBinary> pair : binaries.entrySet()) {
|
||||
size += pair.getKey().length();
|
||||
size += pair.getValue().length();
|
||||
}
|
||||
|
||||
size += autoType.defaultSequence.length();
|
||||
for (Entry<String, String> pair : autoType.entrySet()) {
|
||||
size += pair.getKey().length();
|
||||
size += pair.getValue().length();
|
||||
}
|
||||
|
||||
for (PwEntryV4 entry : history) {
|
||||
size += entry.getSize();
|
||||
}
|
||||
|
||||
size += overrideURL.length();
|
||||
size += tags.length();
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touch(boolean modified, boolean touchParents) {
|
||||
super.touch(modified, touchParents);
|
||||
|
||||
++usageCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchLocation() {
|
||||
parentGroupLastMod = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(PwGroup parent) {
|
||||
this.parent = (PwGroupV4) parent;
|
||||
}
|
||||
|
||||
public boolean isSearchingEnabled() {
|
||||
if (parent != null) {
|
||||
return parent.isSearchEnabled();
|
||||
}
|
||||
|
||||
return PwGroupV4.DEFAULT_SEARCHING_ENABLED;
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.keepassdroid.utils.StrUtil;
|
||||
|
||||
public abstract class PwGroup {
|
||||
public List<PwGroup> childGroups = new ArrayList<PwGroup>();
|
||||
public List<PwEntry> childEntries = new ArrayList<PwEntry>();
|
||||
public String name = "";
|
||||
public PwIconStandard icon;
|
||||
|
||||
public abstract PwGroup getParent();
|
||||
public abstract void setParent(PwGroup parent);
|
||||
|
||||
public abstract PwGroupId getId();
|
||||
public abstract void setId(PwGroupId id);
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public abstract Date getLastMod();
|
||||
|
||||
public PwIcon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void sortGroupsByName() {
|
||||
Collections.sort(childGroups, new GroupNameComparator());
|
||||
}
|
||||
|
||||
public static class GroupNameComparator implements Comparator<PwGroup> {
|
||||
|
||||
public int compare(PwGroup object1, PwGroup object2) {
|
||||
return object1.getName().compareToIgnoreCase(object2.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public abstract void setLastAccessTime(Date date);
|
||||
|
||||
public abstract void setLastModificationTime(Date date);
|
||||
|
||||
public void sortEntriesByName() {
|
||||
Collections.sort(childEntries, new PwEntry.EntryNameComparator());
|
||||
}
|
||||
|
||||
public void initNewGroup(String nm, PwGroupId newId) {
|
||||
setId(newId);
|
||||
name = nm;
|
||||
}
|
||||
|
||||
public boolean isContainedIn(PwGroup container) {
|
||||
PwGroup cur = this;
|
||||
while (cur != null) {
|
||||
if (cur == container) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cur = cur.getParent();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void touch(boolean modified, boolean touchParents) {
|
||||
Date now = new Date();
|
||||
|
||||
setLastAccessTime(now);
|
||||
|
||||
if (modified) {
|
||||
setLastModificationTime(now);
|
||||
}
|
||||
|
||||
PwGroup parent = getParent();
|
||||
if (touchParents && parent != null) {
|
||||
parent.touch(modified, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void searchEntries(SearchParameters sp, List<PwEntry> listStorage) {
|
||||
if (sp == null) { return; }
|
||||
if (listStorage == null) { return; }
|
||||
|
||||
List<String> terms = StrUtil.splitSearchTerms(sp.searchString);
|
||||
if (terms.size() <= 1 || sp.regularExpression) {
|
||||
searchEntriesSingle(sp, listStorage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Search longest term first
|
||||
Comparator<String> stringLengthComparator = new Comparator<String>() {
|
||||
|
||||
@Override
|
||||
public int compare(String lhs, String rhs) {
|
||||
return lhs.length() - rhs.length();
|
||||
}
|
||||
|
||||
};
|
||||
Collections.sort(terms, stringLengthComparator);
|
||||
|
||||
String fullSearch = sp.searchString;
|
||||
List<PwEntry> pg = this.childEntries;
|
||||
for (int i = 0; i < terms.size(); i ++) {
|
||||
List<PwEntry> pgNew = new ArrayList<PwEntry>();
|
||||
|
||||
sp.searchString = terms.get(i);
|
||||
|
||||
boolean negate = false;
|
||||
if (sp.searchString.startsWith("-")) {
|
||||
sp.searchString.substring(1);
|
||||
negate = sp.searchString.length() > 0;
|
||||
}
|
||||
|
||||
if (!searchEntriesSingle(sp, pgNew)) {
|
||||
pg = null;
|
||||
break;
|
||||
}
|
||||
|
||||
List<PwEntry> complement = new ArrayList<PwEntry>();
|
||||
if (negate) {
|
||||
for (PwEntry entry: pg) {
|
||||
if (!pgNew.contains(entry)) {
|
||||
complement.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
pg = complement;
|
||||
}
|
||||
else {
|
||||
pg = pgNew;
|
||||
}
|
||||
}
|
||||
|
||||
if (pg != null) {
|
||||
listStorage.addAll(pg);
|
||||
}
|
||||
sp.searchString = fullSearch;
|
||||
|
||||
}
|
||||
|
||||
private boolean searchEntriesSingle(SearchParameters spIn, List<PwEntry> listStorage) {
|
||||
SearchParameters sp = (SearchParameters) spIn.clone();
|
||||
|
||||
EntryHandler<PwEntry> eh;
|
||||
if (sp.searchString.length() <= 0) {
|
||||
eh = new EntrySearchHandlerAll(sp, listStorage);
|
||||
} else {
|
||||
eh = EntrySearchHandler.getInstance(this, sp, listStorage);
|
||||
}
|
||||
|
||||
if (!preOrderTraverseTree(null, eh)) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean preOrderTraverseTree(GroupHandler<PwGroup> groupHandler, EntryHandler<PwEntry> entryHandler) {
|
||||
if (entryHandler != null) {
|
||||
for (PwEntry entry : childEntries) {
|
||||
if (!entryHandler.operate(entry)) return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (PwGroup group : childGroups) {
|
||||
|
||||
if ((groupHandler != null) && !groupHandler.operate(group)) return false;
|
||||
|
||||
group.preOrderTraverseTree(groupHandler, entryHandler);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.keepassdroid.database;
|
||||
|
||||
public abstract class PwGroupId {
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PwGroupIdV4 extends PwGroupId {
|
||||
private UUID uuid;
|
||||
|
||||
public PwGroupIdV4(UUID u) {
|
||||
uuid = u;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object id) {
|
||||
if ( ! (id instanceof PwGroupIdV4) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PwGroupIdV4 v4 = (PwGroupIdV4) id;
|
||||
|
||||
return uuid.equals(v4.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uuid.hashCode();
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
}
|
||||