2
0

Compare commits

...

27 Commits

Author SHA1 Message Date
Maarten Billemont
b4efe301a4 Fix issues with focus and tips in security questions panel. 2016-04-30 18:06:23 -04:00
Maarten Billemont
9a82e617b3 Fix copy/paste/undo/redo editing hotkeys. 2016-04-30 17:41:51 -04:00
Maarten Billemont
2647279ff6 Only reset the frame size and position if switching window level. 2016-04-30 15:48:10 -04:00
Maarten Billemont
dae84c4a2e Manually fix hardcoded absolute paths, silly Xcode. 2016-04-25 11:13:24 -04:00
Maarten Billemont
3b74543c51 Security answer generation for question keywords. 2016-04-23 23:56:16 -04:00
Maarten Billemont
1918c30bed Wire up some of the security answers UI. Still WIP. 2016-04-23 14:07:08 -04:00
Maarten Billemont
bbeb6bb948 Fix deprecated NSAlert usage. 2016-04-23 12:22:15 -04:00
Maarten Billemont
15b14d67db Improvements to layout, password types and preparations for security answers. 2016-04-23 11:54:13 -04:00
Maarten Billemont
35c443d82d Support for windowed Master Password window on OS X, some UI improvements and modern version of window translucency. 2016-04-17 19:30:06 -04:00
Maarten Billemont
d950d4be3b Improvements to the question UI. 2016-04-17 16:29:37 -04:00
Maarten Billemont
d7aae64b5d Fix appearance of UI on Mac. 2016-04-16 17:22:11 -04:00
Maarten Billemont
f238bb723d WIP - upgrade to tmthrgd latest code. 2016-03-05 16:57:58 -05:00
Maarten Billemont
b4da801bb0 Skip existing files when uploading to s3. 2016-02-21 11:51:32 -05:00
Maarten Billemont
ce7aa46af6 Bump masterpassword-android to 2.4. 2016-02-21 09:08:02 -05:00
Maarten Billemont
83c28692ad [maven-release-plugin] rollback changes from release preparation of 2.4-java 2016-02-20 22:29:27 -05:00
Maarten Billemont
f21d0f7cfc [maven-release-plugin] prepare release 2.4-java 2016-02-20 22:29:27 -05:00
Maarten Billemont
7eb10cb5a6 Fix a bug with storing the default algorithm version. 2016-02-20 22:26:50 -05:00
Maarten Billemont
64829c99d8 Initial support for security questions in Mac app. 2016-02-20 21:56:04 -05:00
Maarten Billemont
0269c2741a Silence warning when releasing about jarsigner version ambiguity. 2016-02-20 21:53:58 -05:00
Maarten Billemont
f5638ea798 A little clean-up. 2016-02-20 21:48:18 -05:00
Maarten Billemont
6a1768a50d Fixed a few bugs. 2016-02-20 21:45:11 -05:00
Maarten Billemont
b346b3be65 Finish up Android UI improvements. 2016-02-20 21:27:59 -05:00
Maarten Billemont
060ec0b5cd Move preferences into a global preferences controller. 2016-02-20 20:30:08 -05:00
Maarten Billemont
7f8a36e32e Added ability to switch from native to java-only KDF. 2016-02-20 18:50:44 -05:00
Maarten Billemont
024899f311 Updated and fixed encoding of a few PNGs. 2016-02-20 18:49:21 -05:00
Maarten Billemont
6ffef78469 Fix some C warnings. 2016-02-19 08:34:06 -05:00
Maarten Billemont
b574158d92 Update logback layout to new format. 2016-02-19 08:25:14 -05:00
107 changed files with 1924 additions and 1263 deletions

View File

@@ -5,7 +5,6 @@
<inspection_tool class="Convert to string" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> <inspection_tool class="Convert to string" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="LossyEncoding" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodIsLaterInTheScope" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="MethodIsLaterInTheScope" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />

2
External/Pearl vendored

View File

@@ -8,6 +8,7 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h" #include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"

View File

@@ -8,6 +8,7 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h" #include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"

View File

@@ -8,6 +8,7 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h" #include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"

View File

@@ -8,6 +8,7 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h" #include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"

View File

@@ -16,12 +16,12 @@ import org.jetbrains.annotations.Contract;
*/ */
public enum MPSiteType { public enum MPSiteType {
GeneratedMaximum( "20 characters, contains symbols.", // GeneratedMaximum( "Max", "20 characters, contains symbols.", //
ImmutableList.of( "x", "max", "maximum" ), // ImmutableList.of( "x", "max", "maximum" ), //
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), // ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
MPSiteTypeClass.Generated, 0x0 ), MPSiteTypeClass.Generated, 0x0 ),
GeneratedLong( "Copy-friendly, 14 characters, contains symbols.", // GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
ImmutableList.of( "l", "long" ), // ImmutableList.of( "l", "long" ), //
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ), ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
@@ -36,49 +36,50 @@ public enum MPSiteType {
new MPTemplate( "CvccCvcvCvccno" ) ), // new MPTemplate( "CvccCvcvCvccno" ) ), //
MPSiteTypeClass.Generated, 0x1 ), MPSiteTypeClass.Generated, 0x1 ),
GeneratedMedium( "Copy-friendly, 8 characters, contains symbols.", // GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
ImmutableList.of( "m", "med", "medium" ), // ImmutableList.of( "m", "med", "medium" ), //
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), // ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
MPSiteTypeClass.Generated, 0x2 ), MPSiteTypeClass.Generated, 0x2 ),
GeneratedBasic( "8 characters, no symbols.", // GeneratedBasic( "Basic", "8 characters, no symbols.", //
ImmutableList.of( "b", "basic" ), // ImmutableList.of( "b", "basic" ), //
ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), // ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), //
MPSiteTypeClass.Generated, 0x3 ), MPSiteTypeClass.Generated, 0x3 ),
GeneratedShort( "Copy-friendly, 4 characters, no symbols.", // GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", //
ImmutableList.of( "s", "short" ), // ImmutableList.of( "s", "short" ), //
ImmutableList.of( new MPTemplate( "Cvcn" ) ), // ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
MPSiteTypeClass.Generated, 0x4 ), MPSiteTypeClass.Generated, 0x4 ),
GeneratedPIN( "4 numbers.", // GeneratedPIN( "PIN", "4 numbers.", //
ImmutableList.of( "i", "pin" ), // ImmutableList.of( "i", "pin" ), //
ImmutableList.of( new MPTemplate( "nnnn" ) ), // ImmutableList.of( new MPTemplate( "nnnn" ) ), //
MPSiteTypeClass.Generated, 0x5 ), MPSiteTypeClass.Generated, 0x5 ),
GeneratedName( "9 letter name.", // GeneratedName( "Name", "9 letter name.", //
ImmutableList.of( "n", "name" ), // ImmutableList.of( "n", "name" ), //
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), // ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
MPSiteTypeClass.Generated, 0xE ), MPSiteTypeClass.Generated, 0xE ),
GeneratedPhrase( "20 character sentence.", // GeneratedPhrase( "Phrase", "20 character sentence.", //
ImmutableList.of( "p", "phrase" ), // ImmutableList.of( "p", "phrase" ), //
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ), ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), // new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
MPSiteTypeClass.Generated, 0xF ), MPSiteTypeClass.Generated, 0xF ),
StoredPersonal( "AES-encrypted, exportable.", // StoredPersonal( "Personal", "AES-encrypted, exportable.", //
ImmutableList.of( "personal" ), // ImmutableList.of( "personal" ), //
ImmutableList.<MPTemplate>of(), // ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ), MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
StoredDevicePrivate( "AES-encrypted, not exported.", // StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
ImmutableList.of( "device" ), // ImmutableList.of( "device" ), //
ImmutableList.<MPTemplate>of(), // ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate ); MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
static final Logger logger = Logger.get( MPSiteType.class ); static final Logger logger = Logger.get( MPSiteType.class );
private final String shortName;
private final String description; private final String description;
private final List<String> options; private final List<String> options;
private final List<MPTemplate> templates; private final List<MPTemplate> templates;
@@ -86,9 +87,10 @@ public enum MPSiteType {
private final int typeIndex; private final int typeIndex;
private final Set<MPSiteFeature> typeFeatures; private final Set<MPSiteFeature> typeFeatures;
MPSiteType(final String description, final List<String> options, final List<MPTemplate> templates, final MPSiteTypeClass typeClass, MPSiteType(final String shortName, final String description, final List<String> options, final List<MPTemplate> templates,
final int typeIndex, final MPSiteFeature... typeFeatures) { final MPSiteTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
this.shortName = shortName;
this.description = description; this.description = description;
this.options = options; this.options = options;
this.templates = templates; this.templates = templates;
@@ -102,6 +104,10 @@ public enum MPSiteType {
this.typeFeatures = typeFeaturesBuilder.build(); this.typeFeatures = typeFeaturesBuilder.build();
} }
public String getShortName() {
return shortName;
}
public String getDescription() { public String getDescription() {
return description; return description;

View File

@@ -16,10 +16,12 @@ import org.jetbrains.annotations.NotNull;
public abstract class MasterKey { public abstract class MasterKey {
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKey.class ); private static final Logger logger = Logger.get( MasterKey.class );
private static boolean allowNativeByDefault = true;
@Nonnull @Nonnull
private final String fullName; private final String fullName;
private boolean allowNative = allowNativeByDefault;
@Nullable @Nullable
private byte[] masterKey; private byte[] masterKey;
@@ -46,6 +48,23 @@ public abstract class MasterKey {
throw new UnsupportedOperationException( "Unsupported version: " + version ); throw new UnsupportedOperationException( "Unsupported version: " + version );
} }
public static boolean isAllowNativeByDefault() {
return allowNativeByDefault;
}
/**
* Native libraries are useful for speeding up the performance of cryptographical functions.
* Sometimes, however, we may prefer to use Java-only code.
* For instance, for auditability / trust or because the native code doesn't work on our CPU/platform.
* <p/>
* This setter affects the default setting for any newly created {@link MasterKey}s.
*
* @param allowNative false to disallow the use of native libraries.
*/
public static void setAllowNativeByDefault(final boolean allowNative) {
allowNativeByDefault = allowNative;
}
protected MasterKey(@NotNull final String fullName) { protected MasterKey(@NotNull final String fullName) {
this.fullName = fullName; this.fullName = fullName;
@@ -63,6 +82,15 @@ public abstract class MasterKey {
return fullName; return fullName;
} }
public boolean isAllowNative() {
return allowNative;
}
public MasterKey setAllowNative(final boolean allowNative) {
this.allowNative = allowNative;
return this;
}
@Nonnull @Nonnull
protected byte[] getKey() { protected byte[] getKey() {

View File

@@ -65,8 +65,15 @@ public class MasterKeyV0 extends MasterKey {
mpBytesBuf.get( mpBytes, 0, mpBytes.length ); mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 ); Arrays.fill( mpBytesBuf.array(), (byte) 0 );
return scrypt( masterKeySalt, mpBytes );
}
protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
try { try {
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen ); if (isAllowNative())
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
else
return SCrypt.scryptJ( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
} }
catch (GeneralSecurityException e) { catch (GeneralSecurityException e) {
logger.bug( e ); logger.bug( e );

View File

@@ -14,7 +14,7 @@ import javax.annotation.Nullable;
/** /**
* bugs: * bugs:
* - no known issues. * - no known issues.
* *
* @author lhunath, 2014-08-30 * @author lhunath, 2014-08-30
*/ */
public class MasterKeyV3 extends MasterKeyV2 { public class MasterKeyV3 extends MasterKeyV2 {
@@ -46,17 +46,8 @@ public class MasterKeyV3 extends MasterKeyV2 {
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) ); ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
byte[] mpBytes = new byte[mpBytesBuf.remaining()]; byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length ); mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte)0 ); Arrays.fill( mpBytesBuf.array(), (byte) 0 );
try { return scrypt( masterKeySalt, mpBytes );
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
}
catch (GeneralSecurityException e) {
logger.bug( e );
return null;
}
finally {
Arrays.fill( mpBytes, (byte) 0 );
}
} }
} }

View File

@@ -11,14 +11,15 @@
<application <application
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:label="@string/app_name" android:label="@string/app_name"
android:allowBackup="true"> android:allowBackup="true"
<activity android:name=".TestActivity" android:theme="@style/MPTheme"> android:debuggable="true">
<activity android:name=".EmergencyActivity" android:theme="@style/MPTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".EmergencyActivity" android:theme="@style/MPTheme" /> <activity android:name=".TestActivity" android:theme="@style/MPTheme" />
</application> </application>
</manifest> </manifest>

View File

@@ -67,6 +67,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jarsigner-plugin</artifactId> <artifactId>maven-jarsigner-plugin</artifactId>
<version>1.4</version>
<executions> <executions>
<execution> <execution>
<id>signing</id> <id>signing</id>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true" android:fillViewport="true"
@@ -17,6 +18,13 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
android:src="@drawable/img_identity" />
<EditText <EditText
android:id="@+id/fullNameField" android:id="@+id/fullNameField"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -26,14 +34,14 @@
android:hint="@string/fullName_hint" android:hint="@string/fullName_hint"
android:gravity="center" android:gravity="center"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="26sp" /> android:textSize="16sp" />
<CheckBox <CheckBox
android:id="@+id/rememberFullNameField" android:id="@+id/rememberFullNameField"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusForward="@+id/rememberPasswordField" android:nextFocusForward="@+id/rememberPasswordField"
android:textSize="14sp" android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark" android:textColor="@android:color/tertiary_text_dark"
android:text="@string/remember" /> android:text="@string/remember" />
@@ -46,16 +54,24 @@
android:hint="@string/masterPassword_hint" android:hint="@string/masterPassword_hint"
android:gravity="center" android:gravity="center"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="18sp" /> android:textSize="16sp" />
<CheckBox <CheckBox
android:id="@id/rememberPasswordField" android:id="@id/rememberPasswordField"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="14sp" android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark" android:textColor="@android:color/tertiary_text_dark"
android:text="@string/forgetOnClose" /> android:text="@string/forgetOnClose" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
android:src="@drawable/img_key" />
<EditText <EditText
android:id="@id/siteNameField" android:id="@id/siteNameField"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -65,7 +81,7 @@
android:hint="@string/siteName_hint" android:hint="@string/siteName_hint"
android:gravity="center" android:gravity="center"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="18sp" /> android:textSize="16sp" />
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -76,8 +92,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_margin="20dp" android:visibility="invisible"
android:indeterminate="true" /> android:indeterminate="true"
tools:visibility="visible" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -89,12 +106,12 @@
android:id="@id/sitePasswordField" android:id="@id/sitePasswordField"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteTypeField" android:nextFocusForward="@+id/siteTypeButton"
android:gravity="center" android:gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="32sp" android:textSize="28sp"
android:text="LuxdZozvDuma4[" tools:text="LuxdZozvDuma4["
android:onClick="copySitePassword" /> android:onClick="copySitePassword" />
<TextView <TextView
@@ -104,7 +121,7 @@
android:labelFor="@id/sitePasswordField" android:labelFor="@id/sitePasswordField"
android:gravity="center" android:gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textSize="14sp" android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark" android:textColor="@android:color/tertiary_text_dark"
android:text="@string/sitePassword_hint" /> android:text="@string/sitePassword_hint" />
@@ -115,54 +132,136 @@
android:id="@+id/maskPasswordField" android:id="@+id/maskPasswordField"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="14sp" android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark" android:textColor="@android:color/tertiary_text_dark"
android:text="@string/maskPassword" /> android:text="@string/maskPassword" />
<Spinner <ImageView
android:id="@id/siteTypeField"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusForward="@+id/counterField" android:layout_marginTop="20dp"
android:gravity="center" /> android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
android:src="@drawable/divider" />
<EditText <LinearLayout
android:id="@id/counterField"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteVersionField" style="?android:buttonBarStyle"
android:gravity="center" android:orientation="horizontal"
android:inputType="text|textNoSuggestions" android:gravity="center">
android:textColor="#FFFFFF"
android:textSize="18sp"
android:text="1" />
<TextView <LinearLayout
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:labelFor="@id/counterField" android:orientation="vertical"
android:gravity="center" android:gravity="center">
android:background="@android:color/transparent"
android:textSize="14sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteCounter_hint" />
<Spinner <Button
android:id="@id/siteVersionField" android:id="@id/siteTypeButton"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusForward="@id/rememberFullNameField" android:layout_marginStart="8dp"
android:gravity="center" /> android:layout_marginEnd="8dp"
style="?android:buttonBarButtonStyle"
android:nextFocusForward="@+id/counterField"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:drawableStart="@drawable/icon_key"
android:drawablePadding="8dp"
android:background="@android:color/transparent"
tools:text="Long" />
<TextView <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteTypeButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteType_hint" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@id/counterField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
style="?android:buttonBarButtonStyle"
android:nextFocusForward="@+id/siteVersionButton"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:drawableStart="@drawable/icon_plus"
android:drawablePadding="8dp"
android:background="@android:color/transparent"
tools:text="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteVersionButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteCounter_hint" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@id/siteVersionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
style="?android:buttonBarButtonStyle"
android:nextFocusForward="@+id/rememberFullNameField"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:drawableStart="@drawable/icon_gears"
android:drawablePadding="8dp"
android:background="@android:color/transparent"
tools:text="3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteVersionButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteVersion_hint" />
</LinearLayout>
</LinearLayout>
<Button
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:labelFor="@id/siteVersionField" android:layout_marginTop="8dp"
android:gravity="center" android:textSize="16sp"
android:background="@android:color/transparent" android:text="@string/btn_tests"
android:textSize="14sp" android:onClick="integrityTests"
android:textColor="@android:color/tertiary_text_dark" android:background="@android:color/transparent" />
android:text="@string/siteVersion_hint" />
<View <View
android:layout_width="1dp" android:layout_width="1dp"

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true" android:fillViewport="true"
@@ -17,22 +18,31 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:src="@drawable/img_stats" />
<ProgressBar <ProgressBar
android:id="@+id/progressView" android:id="@+id/progressView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_margin="8dp" android:layout_margin="8dp"
style="@android:style/Widget.ProgressBar.Horizontal" /> tools:max="100"
tools:progress="80"
style="?android:progressBarStyleHorizontal" />
<TextView <TextView
android:id="@+id/statusView" android:id="@+id/statusView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:labelFor="@id/sitePasswordField"
android:gravity="center" android:gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textSize="14sp" android:textSize="12sp"
android:textColor="@android:color/secondary_text_dark" android:textColor="@android:color/tertiary_text_dark"
android:text="@string/tests_testing" /> android:text="@string/tests_testing" />
<TextView <TextView
@@ -53,8 +63,15 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:enabled="false" android:enabled="false"
android:text="@string/tests_btn_testing" android:text="@string/tests_btn_testing"
android:onClick="onAction"/> android:onClick="onAction" />
<CheckBox
android:id="@+id/nativeKDFField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/nativeKDF" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View File

@@ -8,15 +8,18 @@
<string name="masterPassword_hint">Your master password</string> <string name="masterPassword_hint">Your master password</string>
<string name="siteName_hint">eg. google.com</string> <string name="siteName_hint">eg. google.com</string>
<string name="sitePassword_hint">Tap to copy</string> <string name="sitePassword_hint">Tap to copy</string>
<string name="siteCounter_hint">Password #</string> <string name="siteType_hint">Type</string>
<string name="siteCounter_hint">Counter</string>
<string name="siteVersion_hint">Algorithm</string> <string name="siteVersion_hint">Algorithm</string>
<string name="empty" /> <string name="empty" />
<string name="btn_tests">Integrity Tests </string>
<string name="tests_unavailable">Test suite unavailable.</string> <string name="tests_unavailable">Test suite unavailable.</string>
<string name="tests_btn_unavailable">Exit</string> <string name="tests_btn_unavailable">Retest</string>
<string name="tests_testing">Testing device\'s password generation integrity…</string> <string name="tests_testing">Testing device\'s password generation integrity…</string>
<string name="tests_btn_testing">Please Stand By…</string> <string name="tests_btn_testing">Please Stand By…</string>
<string name="tests_failed">Incompatible device or OS.</string> <string name="tests_failed">Incompatible device or OS.</string>
<string name="tests_btn_failed">Exit</string> <string name="tests_btn_failed">Retest</string>
<string name="tests_passed">Integrity checks passed!</string> <string name="tests_passed">Integrity checks passed!</string>
<string name="tests_btn_passed">Continue</string> <string name="tests_btn_passed">Close</string>
<string name="nativeKDF">Use native key derivation</string>
</resources> </resources>

View File

@@ -1,6 +1,5 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import android.app.*; import android.app.*;
@@ -17,9 +16,11 @@ import android.widget.*;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.InjectView; import butterknife.InjectView;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -32,19 +33,10 @@ public class EmergencyActivity extends Activity {
private static final ClipData EMPTY_CLIP = new ClipData( new ClipDescription( "", new String[0] ), new ClipData.Item( "" ) ); private static final ClipData EMPTY_CLIP = new ClipData( new ClipDescription( "", new String[0] ), new ClipData.Item( "" ) );
private static final int PASSWORD_NOTIFICATION = 0; private static final int PASSWORD_NOTIFICATION = 0;
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() ); private final Preferences preferences = Preferences.get( this );
private final ValueChangedListener updateMasterKey = new ValueChangedListener() { private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
@Override private final ImmutableList<MPSiteType> allSiteTypes = ImmutableList.copyOf( MPSiteType.forClass( MPSiteTypeClass.Generated ) );
void update() { private final ImmutableList<MasterKey.Version> allVersions = ImmutableList.copyOf( MasterKey.Version.values() );
updateMasterKey();
}
};
private final ValueChangedListener updateSitePassword = new ValueChangedListener() {
@Override
void update() {
updateSitePassword();
}
};
private ListenableFuture<MasterKey> masterKeyFuture; private ListenableFuture<MasterKey> masterKeyFuture;
@@ -60,14 +52,14 @@ public class EmergencyActivity extends Activity {
@InjectView(R.id.siteNameField) @InjectView(R.id.siteNameField)
EditText siteNameField; EditText siteNameField;
@InjectView(R.id.siteTypeField) @InjectView(R.id.siteTypeButton)
Spinner siteTypeField; Button siteTypeButton;
@InjectView(R.id.counterField) @InjectView(R.id.counterField)
EditText counterField; Button siteCounterButton;
@InjectView(R.id.siteVersionField) @InjectView(R.id.siteVersionButton)
Spinner siteVersionField; Button siteVersionButton;
@InjectView(R.id.sitePasswordField) @InjectView(R.id.sitePasswordField)
Button sitePasswordField; Button sitePasswordField;
@@ -84,8 +76,9 @@ public class EmergencyActivity extends Activity {
@InjectView(R.id.maskPasswordField) @InjectView(R.id.maskPasswordField)
CheckBox maskPasswordField; CheckBox maskPasswordField;
private int hc_userName; private int id_userName;
private int hc_masterPassword; private int id_masterPassword;
private int id_version;
private String sitePassword; private String sitePassword;
public static void start(Context context) { public static void start(Context context) {
@@ -101,12 +94,58 @@ public class EmergencyActivity extends Activity {
setContentView( R.layout.activity_emergency ); setContentView( R.layout.activity_emergency );
ButterKnife.inject( this ); ButterKnife.inject( this );
fullNameField.setOnFocusChangeListener( updateMasterKey ); fullNameField.setOnFocusChangeListener( new ValueChangedListener() {
masterPasswordField.setOnFocusChangeListener( updateMasterKey ); @Override
siteNameField.addTextChangedListener( updateSitePassword ); void update() {
siteTypeField.setOnItemSelectedListener( updateSitePassword ); updateMasterKey();
counterField.addTextChangedListener( updateSitePassword ); }
siteVersionField.setOnItemSelectedListener( updateMasterKey ); } );
masterPasswordField.setOnFocusChangeListener( new ValueChangedListener() {
@Override
void update() {
updateMasterKey();
}
} );
siteNameField.addTextChangedListener( new ValueChangedListener() {
@Override
void update() {
siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
updateSitePassword();
}
} );
siteTypeButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(final View v) {
@SuppressWarnings("SuspiciousMethodCalls")
MPSiteType siteType =
allSiteTypes.get( (allSiteTypes.indexOf( siteTypeButton.getTag() ) + 1) % allSiteTypes.size() );
preferences.setDefaultSiteType( siteType );
siteTypeButton.setTag( siteType );
siteTypeButton.setText( siteType.getShortName() );
updateSitePassword();
}
} );
siteCounterButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(final View v) {
UnsignedInteger counter =
UnsignedInteger.valueOf( siteCounterButton.getText().toString() ).plus( UnsignedInteger.ONE );
siteCounterButton.setText( MessageFormat.format( "{0}", counter ) );
updateSitePassword();
}
} );
siteVersionButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(final View v) {
@SuppressWarnings("SuspiciousMethodCalls")
MasterKey.Version siteVersion =
allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() );
preferences.setDefaultVersion( siteVersion );
siteVersionButton.setTag( siteVersion );
siteVersionButton.setText( siteVersion.name() );
updateMasterKey();
}
} );
sitePasswordField.addTextChangedListener( new ValueChangedListener() { sitePasswordField.addTextChangedListener( new ValueChangedListener() {
@Override @Override
void update() { void update() {
@@ -127,32 +166,26 @@ public class EmergencyActivity extends Activity {
sitePasswordField.setTypeface( Res.sourceCodePro_Black ); sitePasswordField.setTypeface( Res.sourceCodePro_Black );
sitePasswordField.setPaintFlags( sitePasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); sitePasswordField.setPaintFlags( sitePasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
siteTypeField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) );
siteTypeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
siteVersionField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MasterKey.Version.values() ) );
siteVersionField.setSelection( MasterKey.Version.CURRENT.ordinal() );
rememberFullNameField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() { rememberFullNameField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
getPreferences( MODE_PRIVATE ).edit().putBoolean( "rememberFullName", isChecked ).apply(); preferences.setRememberFullName( isChecked );
if (isChecked) if (isChecked)
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", fullNameField.getText().toString() ).apply(); preferences.setFullName( fullNameField.getText().toString() );
else else
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", "" ).apply(); preferences.setFullName( null );
} }
} ); } );
forgetPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() { forgetPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
getPreferences( MODE_PRIVATE ).edit().putBoolean( "forgetPassword", isChecked ).apply(); preferences.setForgetPassword( isChecked );
} }
} ); } );
maskPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() { maskPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
getPreferences( MODE_PRIVATE ).edit().putBoolean( "maskPassword", isChecked ).apply(); preferences.setMaskPassword( isChecked );
sitePasswordField.setTransformationMethod( isChecked? new PasswordTransformationMethod(): null ); sitePasswordField.setTransformationMethod( isChecked? new PasswordTransformationMethod(): null );
} }
} ); } );
@@ -162,13 +195,24 @@ public class EmergencyActivity extends Activity {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
fullNameField.setText( getPreferences( MODE_PRIVATE ).getString( "fullName", "" ) ); MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
rememberFullNameField.setChecked( isRememberFullNameEnabled() );
forgetPasswordField.setChecked( isForgetPasswordEnabled() );
maskPasswordField.setChecked( isMaskPasswordEnabled() );
sitePasswordField.setTransformationMethod( isMaskPasswordEnabled()? new PasswordTransformationMethod(): null );
if (TextUtils.isEmpty( masterPasswordField.getText() )) fullNameField.setText( preferences.getFullName() );
rememberFullNameField.setChecked( preferences.isRememberFullName() );
forgetPasswordField.setChecked( preferences.isForgetPassword() );
maskPasswordField.setChecked( preferences.isMaskPassword() );
sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null );
MPSiteType defaultSiteType = preferences.getDefaultSiteType();
siteTypeButton.setTag( defaultSiteType );
siteTypeButton.setText( defaultSiteType.getShortName() );
MasterKey.Version defaultVersion = preferences.getDefaultVersion();
siteVersionButton.setTag( defaultVersion );
siteVersionButton.setText( defaultVersion.name() );
siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
if (TextUtils.isEmpty( fullNameField.getText() ))
fullNameField.requestFocus();
else if (TextUtils.isEmpty( masterPasswordField.getText() ))
masterPasswordField.requestFocus(); masterPasswordField.requestFocus();
else else
siteNameField.requestFocus(); siteNameField.requestFocus();
@@ -176,9 +220,9 @@ public class EmergencyActivity extends Activity {
@Override @Override
protected void onPause() { protected void onPause() {
if (isForgetPasswordEnabled()) { if (preferences.isForgetPassword()) {
synchronized (this) { synchronized (this) {
hc_userName = hc_masterPassword = 0; id_userName = id_masterPassword = 0;
if (masterKeyFuture != null) { if (masterKeyFuture != null) {
masterKeyFuture.cancel( true ); masterKeyFuture.cancel( true );
masterKeyFuture = null; masterKeyFuture = null;
@@ -195,35 +239,20 @@ public class EmergencyActivity extends Activity {
super.onPause(); super.onPause();
} }
private boolean isRememberFullNameEnabled() {
return getPreferences( MODE_PRIVATE ).getBoolean( "rememberFullName", false );
}
private boolean isForgetPasswordEnabled() {
return getPreferences( MODE_PRIVATE ).getBoolean( "forgetPassword", false );
}
private boolean isMaskPasswordEnabled() {
return getPreferences( MODE_PRIVATE ).getBoolean( "maskPassword", false );
}
private synchronized void updateMasterKey() { private synchronized void updateMasterKey() {
final String fullName = fullNameField.getText().toString(); final String fullName = fullNameField.getText().toString();
final char[] masterPassword = masterPasswordField.getText().toString().toCharArray(); final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
final MasterKey.Version version = (MasterKey.Version) siteVersionField.getSelectedItem(); final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
try { if (fullName.hashCode() == id_userName && Arrays.hashCode( masterPassword ) == id_masterPassword &&
if (fullName.hashCode() == hc_userName && Arrays.hashCode( masterPassword ) == hc_masterPassword && version.ordinal() == id_version && masterKeyFuture != null && !masterKeyFuture.isCancelled())
masterKeyFuture != null && masterKeyFuture.get().getAlgorithmVersion() == version)
return;
}
catch (InterruptedException | ExecutionException e) {
return; return;
}
hc_userName = fullName.hashCode();
hc_masterPassword = Arrays.hashCode( masterPassword );
if (isRememberFullNameEnabled()) id_userName = fullName.hashCode();
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", fullName ).apply(); id_masterPassword = Arrays.hashCode( masterPassword );
id_version = version.ordinal();
if (preferences.isRememberFullName())
preferences.setFullName( fullName );
if (masterKeyFuture != null) if (masterKeyFuture != null)
masterKeyFuture.cancel( true ); masterKeyFuture.cancel( true );
@@ -243,7 +272,7 @@ public class EmergencyActivity extends Activity {
try { try {
return MasterKey.create( version, fullName, masterPassword ); return MasterKey.create( version, fullName, masterPassword );
} }
catch (RuntimeException e) { catch (Exception e) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE ); progressView.setVisibility( View.INVISIBLE );
logger.err( e, "While generating master key." ); logger.err( e, "While generating master key." );
@@ -265,8 +294,8 @@ public class EmergencyActivity extends Activity {
private void updateSitePassword() { private void updateSitePassword() {
final String siteName = siteNameField.getText().toString(); final String siteName = siteNameField.getText().toString();
final MPSiteType type = (MPSiteType) siteTypeField.getSelectedItem(); final MPSiteType type = (MPSiteType) siteTypeButton.getTag();
final UnsignedInteger counter = UnsignedInteger.valueOf( ifNotNullElse( counterField.getText(), "1" ).toString() ); final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
if (masterKeyFuture == null || siteName.isEmpty() || type == null) { if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
@@ -313,6 +342,14 @@ public class EmergencyActivity extends Activity {
} ); } );
} }
public void integrityTests(View view) {
if (masterKeyFuture != null) {
masterKeyFuture.cancel( true );
masterKeyFuture = null;
}
TestActivity.startNoSkip( this );
}
public void copySitePassword(View view) { public void copySitePassword(View view) {
final String currentSitePassword = this.sitePassword; final String currentSitePassword = this.sitePassword;
if (TextUtils.isEmpty( currentSitePassword )) if (TextUtils.isEmpty( currentSitePassword ))

View File

@@ -0,0 +1,148 @@
package com.lyndir.masterpassword;
import android.content.Context;
import android.content.SharedPreferences;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2016-02-20
*/
public class Preferences {
private static final String PREF_TESTS_PASSED = "integrityTestsPassed";
private static final String PREF_NATIVE_KDF = "nativeKDF";
private static final String PREF_REMEMBER_FULL_NAME = "rememberFullName";
private static final String PREF_FORGET_PASSWORD = "forgetPassword";
private static final String PREF_MASK_PASSWORD = "maskPassword";
private static final String PREF_FULL_NAME = "fullName";
private static final String PREF_SITE_TYPE = "siteType";
private static final String PREF_ALGORITHM_VERSION = "algorithmVersion";
private static Preferences instance;
private Context context;
@Nullable
private SharedPreferences prefs;
public static synchronized Preferences get(final Context context) {
if (instance == null)
instance = new Preferences( context );
return instance;
}
private Preferences(Context context) {
this.context = context;
}
@Nonnull
private SharedPreferences prefs() {
if (prefs == null)
prefs = (context = context.getApplicationContext()).getSharedPreferences( getClass().getCanonicalName(), Context.MODE_PRIVATE );
return prefs;
}
public boolean setNativeKDFEnabled(boolean enabled) {
if (isAllowNativeKDF() == enabled)
return false;
prefs().edit().putBoolean( PREF_NATIVE_KDF, enabled ).apply();
return true;
}
public boolean isAllowNativeKDF() {
return prefs().getBoolean( PREF_NATIVE_KDF, MasterKey.isAllowNativeByDefault() );
}
public boolean setTestsPassed(final Set<String> value) {
if (Sets.symmetricDifference( getTestsPassed(), value ).isEmpty())
return false;
prefs().edit().putStringSet( PREF_TESTS_PASSED, value ).apply();
return true;
}
public Set<String> getTestsPassed() {
return prefs().getStringSet( PREF_TESTS_PASSED, ImmutableSet.<String>of() );
}
public boolean setRememberFullName(boolean enabled) {
if (isRememberFullName() == enabled)
return false;
prefs().edit().putBoolean( PREF_REMEMBER_FULL_NAME, enabled ).apply();
return true;
}
public boolean isRememberFullName() {
return prefs().getBoolean( PREF_REMEMBER_FULL_NAME, false );
}
public boolean setForgetPassword(boolean enabled) {
if (isForgetPassword() == enabled)
return false;
prefs().edit().putBoolean( PREF_FORGET_PASSWORD, enabled ).apply();
return true;
}
public boolean isForgetPassword() {
return prefs().getBoolean( PREF_FORGET_PASSWORD, false );
}
public boolean setMaskPassword(boolean enabled) {
if (isMaskPassword() == enabled)
return false;
prefs().edit().putBoolean( PREF_MASK_PASSWORD, enabled ).apply();
return true;
}
public boolean isMaskPassword() {
return prefs().getBoolean( PREF_MASK_PASSWORD, false );
}
public boolean setFullName(@Nullable String value) {
if (getFullName().equals( value ))
return false;
prefs().edit().putString( PREF_FULL_NAME, value ).apply();
return true;
}
@Nonnull
public String getFullName() {
return prefs().getString( PREF_FULL_NAME, "" );
}
public boolean setDefaultSiteType(@Nonnull MPSiteType value) {
if (getDefaultSiteType().equals( value ))
return false;
prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply();
return true;
}
@Nonnull
public MPSiteType getDefaultSiteType() {
return MPSiteType.values()[prefs().getInt( PREF_SITE_TYPE, MPSiteType.GeneratedLong.ordinal() )];
}
public boolean setDefaultVersion(@Nonnull MasterKey.Version value) {
if (getDefaultVersion().equals( value ))
return false;
prefs().edit().putInt( PREF_ALGORITHM_VERSION, value.ordinal() ).apply();
return true;
}
@Nonnull
public MasterKey.Version getDefaultVersion() {
return MasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MasterKey.Version.CURRENT.ordinal() )];
}
}

View File

@@ -3,9 +3,10 @@ package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import android.app.*; import android.app.*;
import android.content.Context;
import android.content.Intent;
import android.os.*; import android.os.*;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import android.widget.*; import android.widget.*;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.InjectView; import butterknife.InjectView;
@@ -13,7 +14,6 @@ import com.google.common.base.*;
import com.google.common.collect.*; import com.google.common.collect.*;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Set;
import java.util.concurrent.*; import java.util.concurrent.*;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -23,6 +23,7 @@ public class TestActivity extends Activity implements MPTestSuite.Listener {
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( TestActivity.class ); private static final Logger logger = Logger.get( TestActivity.class );
private final Preferences preferences = Preferences.get( this );
private final ListeningExecutorService backgroundExecutor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() ); private final ListeningExecutorService backgroundExecutor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
private final ListeningExecutorService mainExecutor = MoreExecutors.listeningDecorator( new MainThreadExecutor() ); private final ListeningExecutorService mainExecutor = MoreExecutors.listeningDecorator( new MainThreadExecutor() );
@@ -38,20 +39,34 @@ public class TestActivity extends Activity implements MPTestSuite.Listener {
@InjectView(R.id.actionButton) @InjectView(R.id.actionButton)
Button actionButton; Button actionButton;
@InjectView(R.id.nativeKDFField)
CheckBox nativeKDFField;
private MPTestSuite testSuite; private MPTestSuite testSuite;
private ListenableFuture<Boolean> testFuture; private ListenableFuture<Boolean> testFuture;
private Runnable action; private Runnable action;
private ImmutableSet<String> testNames; private ImmutableSet<String> testNames;
public static void startNoSkip(Context context) {
context.startActivity( new Intent( context, TestActivity.class ) );
}
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate( savedInstanceState ); super.onCreate( savedInstanceState );
Res.init( getResources() ); Res.init( getResources() );
getWindow().setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE );
setContentView( R.layout.activity_test ); setContentView( R.layout.activity_test );
ButterKnife.inject( this ); ButterKnife.inject( this );
nativeKDFField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
preferences.setNativeKDFEnabled( isChecked );
MasterKey.setAllowNativeByDefault( isChecked );
}
} );
try { try {
setStatus( 0, 0, null ); setStatus( 0, 0, null );
testSuite = new MPTestSuite(); testSuite = new MPTestSuite();
@@ -76,63 +91,54 @@ public class TestActivity extends Activity implements MPTestSuite.Listener {
} }
} }
@Override
protected void onStart() {
super.onStart();
final Set<String> integrityTestsPassed = getPreferences( MODE_PRIVATE ).getStringSet( "integrityTestsPassed",
ImmutableSet.<String>of() );
if (!FluentIterable.from( testNames ).anyMatch( new Predicate<String>() {
@Override
public boolean apply(@Nullable final String testName) {
return !integrityTestsPassed.contains( testName );
}
} )) {
// None of the tests we need to perform were missing from the tests that have already been passed on this device.
finish();
EmergencyActivity.start( TestActivity.this );
}
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
if (testFuture == null) { nativeKDFField.setChecked( preferences.isAllowNativeKDF() );
setStatus( R.string.tests_testing, R.string.tests_btn_testing, null );
Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable final Boolean result) {
if (result != null && result)
setStatus( R.string.tests_passed, R.string.tests_btn_passed, new Runnable() {
@Override
public void run() {
getPreferences( MODE_PRIVATE ).edit().putStringSet( "integrityTestsPassed", testNames ).apply();
finish();
EmergencyActivity.start( TestActivity.this );
}
} );
else
setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
@Override
public void run() {
finish();
}
} );
}
@Override if (testFuture == null)
public void onFailure(final Throwable t) { startTestSuite();
logger.err( t, "While running test suite" ); }
setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
private void startTestSuite() {
if (testFuture != null)
testFuture.cancel( true );
MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
setStatus( R.string.tests_testing, R.string.tests_btn_testing, null );
Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable final Boolean result) {
if (result != null && result)
setStatus( R.string.tests_passed, R.string.tests_btn_passed, new Runnable() {
@Override @Override
public void run() { public void run() {
preferences.setTestsPassed( testNames );
finish(); finish();
} }
} ); } );
} else
}, mainExecutor ); setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
} @Override
public void run() {
startTestSuite();
}
} );
}
@Override
public void onFailure(final Throwable t) {
logger.err( t, "While running test suite" );
setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
@Override
public void run() {
finish();
}
} );
}
}, mainExecutor );
} }
public void onAction(View v) { public void onAction(View v) {

View File

@@ -1,15 +1,15 @@
<configuration scan="false"> <configuration scan="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout"> <encoder>
<Pattern>%-8relative %22c{0} [%-5level] %msg%n</Pattern> <pattern>%-8relative %22c{0} [%-5level] %msg%n</pattern>
</layout> </encoder>
</appender> </appender>
<logger name="com.lyndir" level="${mp.log.level:-INFO}" /> <logger name="com.lyndir" level="${mp.log.level:-INFO}" />
<root level="INFO"> <root level="INFO">
<appender-ref ref="STDOUT" /> <appender-ref ref="stdout" />
</root> </root>
</configuration> </configuration>

View File

@@ -97,6 +97,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jarsigner-plugin</artifactId> <artifactId>maven-jarsigner-plugin</artifactId>
<version>1.4</version>
<executions> <executions>
<execution> <execution>
<id>signing</id> <id>signing</id>

View File

@@ -1,15 +1,15 @@
<configuration scan="false"> <configuration scan="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout"> <encoder>
<Pattern>%-8relative %22c{0} [%-5level] %msg%n</Pattern> <pattern>%-8relative %22c{0} [%-5level] %msg%n</pattern>
</layout> </encoder>
</appender> </appender>
<logger name="com.lyndir" level="${mp.log.level:-INFO}" /> <logger name="com.lyndir" level="${mp.log.level:-INFO}" />
<root level="INFO"> <root level="INFO">
<appender-ref ref="STDOUT" /> <appender-ref ref="stdout" />
</root> </root>
</configuration> </configuration>

View File

@@ -1,15 +1,15 @@
<configuration scan="false"> <configuration scan="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout"> <encoder>
<Pattern>%-8relative %22c{0} [%-5level] %msg%n</Pattern> <pattern>%-8relative %22c{0} [%-5level] %msg%n</pattern>
</layout> </encoder>
</appender> </appender>
<logger name="com.lyndir.masterpassword" level="${mp.log.level:-TRACE}" /> <logger name="com.lyndir" level="${mp.log.level:-INFO}" />
<root level="INFO"> <root level="INFO">
<appender-ref ref="STDOUT" /> <appender-ref ref="stdout" />
</root> </root>
</configuration> </configuration>

View File

@@ -179,7 +179,7 @@ NSOperationQueue *_mpwQueue = nil;
return @"PIN"; return @"PIN";
case MPSiteTypeGeneratedName: case MPSiteTypeGeneratedName:
return @"Login Name"; return @"Name";
case MPSiteTypeGeneratedPhrase: case MPSiteTypeGeneratedPhrase:
return @"Phrase"; return @"Phrase";
@@ -281,7 +281,7 @@ NSOperationQueue *_mpwQueue = nil;
- (NSArray *)allTypes { - (NSArray *)allTypes {
return [self allTypesStartingWith:MPSiteTypeGeneratedMaximum]; return [self allTypesStartingWith:MPSiteTypeGeneratedPhrase];
} }
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType { - (NSArray *)allTypesStartingWith:(MPSiteType)startingType {
@@ -298,6 +298,10 @@ NSOperationQueue *_mpwQueue = nil;
- (MPSiteType)nextType:(MPSiteType)type { - (MPSiteType)nextType:(MPSiteType)type {
switch (type) { switch (type) {
case MPSiteTypeGeneratedPhrase:
return MPSiteTypeGeneratedName;
case MPSiteTypeGeneratedName:
return MPSiteTypeGeneratedMaximum;
case MPSiteTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
return MPSiteTypeGeneratedLong; return MPSiteTypeGeneratedLong;
case MPSiteTypeGeneratedLong: case MPSiteTypeGeneratedLong:
@@ -313,7 +317,7 @@ NSOperationQueue *_mpwQueue = nil;
case MPSiteTypeStoredPersonal: case MPSiteTypeStoredPersonal:
return MPSiteTypeStoredDevicePrivate; return MPSiteTypeStoredDevicePrivate;
case MPSiteTypeStoredDevicePrivate: case MPSiteTypeStoredDevicePrivate:
return MPSiteTypeGeneratedMaximum; return MPSiteTypeGeneratedPhrase;
default: default:
return MPSiteTypeGeneratedLong; return MPSiteTypeGeneratedLong;
} }

View File

@@ -22,6 +22,13 @@
@end @end
@interface MPSiteQuestionEntity(MP)
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key;
- (void)resolveQuestionAnswerUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
@end
@interface MPSiteEntity(MP)<MPFixable> @interface MPSiteEntity(MP)<MPFixable>
@property(assign) BOOL loginGenerated; @property(assign) BOOL loginGenerated;
@@ -38,8 +45,11 @@
- (BOOL)tryMigrateExplicitly:(BOOL)explicit; - (BOOL)tryMigrateExplicitly:(BOOL)explicit;
- (NSString *)resolveLoginUsingKey:(MPKey *)key; - (NSString *)resolveLoginUsingKey:(MPKey *)key;
- (NSString *)resolvePasswordUsingKey:(MPKey *)key; - (NSString *)resolvePasswordUsingKey:(MPKey *)key;
- (NSString *)resolveSiteAnswerUsingKey:(MPKey *)key;
- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result; - (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result; - (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
- (void)resolveSiteAnswerUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
- (void)resolveAnswerUsingKey:(MPKey *)key forQuestion:(NSString *)question result:(void ( ^ )(NSString *))result;
@end @end

View File

@@ -35,6 +35,20 @@
@end @end
@implementation MPSiteQuestionEntity(MP)
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key {
return [self.site.algorithm resolveAnswerForQuestion:self usingKey:key];
}
- (void)resolveQuestionAnswerUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
[self.site.algorithm resolveAnswerForQuestion:self usingKey:key result:result];
}
@end
@implementation MPSiteEntity(MP) @implementation MPSiteEntity(MP)
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { - (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
@@ -175,6 +189,11 @@
return [self.algorithm resolvePasswordForSite:self usingKey:key]; return [self.algorithm resolvePasswordForSite:self usingKey:key];
} }
- (NSString *)resolveSiteAnswerUsingKey:(MPKey *)key {
return [self.algorithm resolveAnswerForSite:self usingKey:key];
}
- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result { - (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
[self.algorithm resolveLoginForSite:self usingKey:key result:result]; [self.algorithm resolveLoginForSite:self usingKey:key result:result];
@@ -185,6 +204,19 @@
[self.algorithm resolvePasswordForSite:self usingKey:key result:result]; [self.algorithm resolvePasswordForSite:self usingKey:key result:result];
} }
- (void)resolveSiteAnswerUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
[self.algorithm resolveAnswerForSite:self usingKey:key result:result];
}
- (void)resolveAnswerUsingKey:(MPKey *)key forQuestion:(NSString *)question result:(void ( ^ )(NSString *))result {
MPSiteQuestionEntity *questionEntity = [MPSiteQuestionEntity new];
questionEntity.site = self;
questionEntity.keyword = question;
[questionEntity resolveQuestionAnswerUsingKey:key result:result];
}
@end @end
@implementation MPGeneratedSiteEntity(MP) @implementation MPGeneratedSiteEntity(MP)

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/> <capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
</dependencies> </dependencies>
<objects> <objects>
@@ -102,8 +102,8 @@ from anywhere.</string>
<constraint firstItem="C4d-ih-ARL" firstAttribute="top" secondItem="283" secondAttribute="bottom" constant="8" symbolic="YES" id="tqS-gV-jpd"/> <constraint firstItem="C4d-ih-ARL" firstAttribute="top" secondItem="283" secondAttribute="bottom" constant="8" symbolic="YES" id="tqS-gV-jpd"/>
</constraints> </constraints>
</customView> </customView>
<button verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="508"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="508">
<rect key="frame" x="766" y="8" width="214" height="25"/> <rect key="frame" x="858" y="8" width="122" height="25"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="122" id="509"/> <constraint firstAttribute="width" constant="122" id="509"/>
</constraints> </constraints>
@@ -115,8 +115,8 @@ from anywhere.</string>
<action selector="togglePreference:" target="-2" id="574"/> <action selector="togglePreference:" target="-2" id="574"/>
</connections> </connections>
</button> </button>
<textField verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="562"> <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="562">
<rect key="frame" x="21" y="13" width="739" height="17"/> <rect key="frame" x="21" y="13" width="831" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="To begin: hold command, control and P (⌘⌃P) or access the ●●●| menu icon next to your clock." id="563"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="To begin: hold command, control and P (⌘⌃P) or access the ●●●| menu icon next to your clock." id="563">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>

View File

@@ -22,6 +22,7 @@
@property(nonatomic, weak) IBOutlet NSMenuItem *hidePasswordsItem; @property(nonatomic, weak) IBOutlet NSMenuItem *hidePasswordsItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *rememberPasswordItem; @property(nonatomic, weak) IBOutlet NSMenuItem *rememberPasswordItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *openAtLoginItem; @property(nonatomic, weak) IBOutlet NSMenuItem *openAtLoginItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *showFullScreenItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *savePasswordItem; @property(nonatomic, weak) IBOutlet NSMenuItem *savePasswordItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *createUserItem; @property(nonatomic, weak) IBOutlet NSMenuItem *createUserItem;
@property(nonatomic, weak) IBOutlet NSMenuItem *deleteUserItem; @property(nonatomic, weak) IBOutlet NSMenuItem *deleteUserItem;

View File

@@ -335,11 +335,15 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)togglePreference:(id)sender { - (IBAction)togglePreference:(id)sender {
if (sender == self.hidePasswordsItem) if (sender == self.hidePasswordsItem)
[MPConfig get].hidePasswords = @(![[MPConfig get].hidePasswords boolValue]); [MPConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
if (sender == self.rememberPasswordItem) if (sender == self.rememberPasswordItem)
[MPConfig get].rememberLogin = @(![[MPConfig get].rememberLogin boolValue]); [MPConfig get].rememberLogin = @(self.rememberPasswordItem.state != NSOnState);
if (sender == self.openAtLoginItem) if (sender == self.openAtLoginItem)
[self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState]; [self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState];
if (sender == self.showFullScreenItem) {
[MPMacConfig get].fullScreen = @(self.showFullScreenItem.state != NSOnState);
[NSApp updateWindows];
}
if (sender == self.savePasswordItem) { if (sender == self.savePasswordItem) {
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context]; MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
@@ -610,6 +614,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
BOOL loginItemEnabled = [self loginItemEnabled]; BOOL loginItemEnabled = [self loginItemEnabled];
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState; self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
self.showFullScreenItem.state = [[MPMacConfig get].fullScreen boolValue]? NSOnState: NSOffState;
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState; self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState; self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;

View File

@@ -0,0 +1,9 @@
//
// Created by Maarten Billemont on 2016-04-30.
// Copyright (c) 2016 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MPMacApplication : NSApplication
@end

View File

@@ -0,0 +1,54 @@
//
// Created by Maarten Billemont on 2016-04-30.
// Copyright (c) 2016 Lyndir. All rights reserved.
//
#import "MPMacApplication.h"
@interface NSResponder (Editing)
- (void)undo:(id)sender;
- (void)redo:(id)sender;
@end
@implementation MPMacApplication {
}
- (void)sendEvent:(NSEvent *)event {
if ([event type] == NSKeyDown) {
if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) {
if ([[event charactersIgnoringModifiers] isEqualToString:@"x"]) {
if ([self sendAction:@selector( cut: ) to:nil from:self])
return;
}
else if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) {
if ([self sendAction:@selector( copy: ) to:nil from:self])
return;
}
else if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) {
if ([self sendAction:@selector( paste: ) to:nil from:self])
return;
}
else if ([[event charactersIgnoringModifiers] isEqualToString:@"z"]) {
if ([self sendAction:@selector( undo: ) to:nil from:self])
return;
}
else if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) {
if ([self sendAction:@selector( selectAll: ) to:nil from:self])
return;
}
}
else if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask)) {
if ([[event charactersIgnoringModifiers] isEqualToString:@"Z"]) {
if ([self sendAction:@selector( redo: ) to:nil from:self])
return;
}
}
}
[super sendEvent:event];
}
@end

View File

@@ -11,5 +11,6 @@
@interface MPMacConfig : MPConfig @interface MPMacConfig : MPConfig
@property(nonatomic, retain) NSString *usedUserName; @property(nonatomic, retain) NSString *usedUserName;
@property(nonatomic, retain) NSNumber *fullScreen;
@end @end

View File

@@ -9,6 +9,7 @@
@implementation MPMacConfig @implementation MPMacConfig
@dynamic usedUserName; @dynamic usedUserName;
@dynamic fullScreen;
- (id)init { - (id)init {
@@ -16,7 +17,8 @@
return self; return self;
[self.defaults registerDefaults:@{ [self.defaults registerDefaults:@{
NSStringFromSelector( @selector(appleID) ) : @"510296984", NSStringFromSelector( @selector( appleID ) ) : @"510296984",
NSStringFromSelector( @selector( fullScreen ) ) : @YES,
}]; }];
return self; return self;

View File

@@ -20,4 +20,5 @@
@interface MPPasswordWindow : NSWindow @interface MPPasswordWindow : NSWindow
@end @end

View File

@@ -18,21 +18,10 @@
#import "MPPasswordWindow.h" #import "MPPasswordWindow.h"
@implementation MPPasswordWindow @implementation MPPasswordWindow
#pragma mark - Life #pragma mark - Life
- (void)awakeFromNib {
[super awakeFromNib];
self.opaque = NO;
self.backgroundColor = [NSColor clearColor];
self.level = NSScreenSaverWindowLevel;
self.alphaValue = 0;
}
- (BOOL)canBecomeKeyWindow { - (BOOL)canBecomeKeyWindow {
return YES; return YES;
@@ -40,6 +29,21 @@
#pragma mark - State #pragma mark - State
- (void)update {
if ([[MPMacConfig get].fullScreen boolValue]) {
[self setLevel:NSScreenSaverWindowLevel];
[self setFrame:self.screen.frame display:YES];
}
else if (self.level != NSNormalWindowLevel) {
[self setLevel:NSNormalWindowLevel];
[self setFrame:NSMakeRect( 0, 0, 640, 600) display:NO];
[self center];
}
[super update];
}
#pragma mark - Private #pragma mark - Private
@end @end

View File

@@ -19,6 +19,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "MPSiteModel.h" #import "MPSiteModel.h"
#import "MPSitesTableView.h" #import "MPSitesTableView.h"
#import "MPPasswordWindow.h"
@class MPMacAppDelegate; @class MPMacAppDelegate;
@@ -28,11 +29,11 @@
@property(nonatomic) NSString *masterPassword; @property(nonatomic) NSString *masterPassword;
@property(nonatomic) BOOL showVersionContainer; @property(nonatomic) BOOL showVersionContainer;
@property(nonatomic) BOOL alternatePressed; @property(nonatomic) BOOL alternatePressed;
@property(nonatomic) BOOL shiftPressed;
@property(nonatomic) BOOL locked; @property(nonatomic) BOOL locked;
@property(nonatomic) BOOL newUser; @property(nonatomic) BOOL newUser;
@property(nonatomic, weak) IBOutlet NSArrayController *sitesController; @property(nonatomic, weak) IBOutlet NSArrayController *sitesController;
@property(nonatomic, weak) IBOutlet NSImageView *blurView;
@property(nonatomic, weak) IBOutlet NSTextField *inputLabel; @property(nonatomic, weak) IBOutlet NSTextField *inputLabel;
@property(nonatomic, weak) IBOutlet NSTextField *securePasswordField; @property(nonatomic, weak) IBOutlet NSTextField *securePasswordField;
@property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField; @property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField;
@@ -44,6 +45,10 @@
@property(nonatomic, strong) IBOutlet NSBox *passwordTypesBox; @property(nonatomic, strong) IBOutlet NSBox *passwordTypesBox;
@property(nonatomic, weak) IBOutlet NSMatrix *passwordTypesMatrix; @property(nonatomic, weak) IBOutlet NSMatrix *passwordTypesMatrix;
@property(nonatomic, strong) IBOutlet NSBox *securityQuestionsBox;
@property(nonatomic, weak) IBOutlet NSTextField *securityQuestionField;
@property(nonatomic, weak) IBOutlet NSTextField *securityAnswerField;
- (BOOL)handleCommand:(SEL)commandSelector; - (BOOL)handleCommand:(SEL)commandSelector;
@end @end

View File

@@ -23,14 +23,6 @@
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#define MPAlertIncorrectMP @"MPAlertIncorrectMP"
#define MPAlertChangeMP @"MPAlertChangeMP"
#define MPAlertCreateSite @"MPAlertCreateSite"
#define MPAlertChangeType @"MPAlertChangeType"
#define MPAlertChangeLogin @"MPAlertChangeLogin"
#define MPAlertChangeContent @"MPAlertChangeContent"
#define MPAlertDeleteSite @"MPAlertDeleteSite"
@interface MPPasswordWindowController() @interface MPPasswordWindowController()
@property(nonatomic, strong) CAGradientLayer *siteGradient; @property(nonatomic, strong) CAGradientLayer *siteGradient;
@@ -61,7 +53,7 @@
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
prof_new( @"didBecomeKey" ); prof_new( @"didBecomeKey" );
[self fadeIn]; [self.window makeKeyAndOrderFront:nil];
prof_rewind( @"fadeIn" ); prof_rewind( @"fadeIn" );
[self updateUser]; [self updateUser];
prof_finish( @"updateUser" ); prof_finish( @"updateUser" );
@@ -75,7 +67,7 @@
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
#ifndef DEBUG #ifndef DEBUG
[self fadeOut]; [self.window fadeOut];
#endif #endif
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil
@@ -124,6 +116,10 @@
- (void)flagsChanged:(NSEvent *)theEvent { - (void)flagsChanged:(NSEvent *)theEvent {
BOOL shiftPressed = (theEvent.modifierFlags & NSShiftKeyMask) != 0;
if (shiftPressed != self.shiftPressed)
self.shiftPressed = shiftPressed;
BOOL alternatePressed = (theEvent.modifierFlags & NSAlternateKeyMask) != 0; BOOL alternatePressed = (theEvent.modifierFlags & NSAlternateKeyMask) != 0;
if (alternatePressed != self.alternatePressed) { if (alternatePressed != self.alternatePressed) {
self.alternatePressed = alternatePressed; self.alternatePressed = alternatePressed;
@@ -189,8 +185,7 @@
if (!success) if (!success)
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{ [[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
NSLocalizedDescriptionKey : strf( @"Incorrect master password for user %@", userName ) NSLocalizedDescriptionKey : strf( @"Incorrect master password for user %@", userName )
}]] beginSheetModalForWindow:self.window modalDelegate:self }]] beginSheetModalForWindow:self.window completionHandler:nil];
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertIncorrectMP];
} ); } );
}]; }];
} }
@@ -221,13 +216,96 @@
[self replaceFonts:rowView]; [self replaceFonts:rowView];
} }
#pragma mark - NSAlert #pragma mark - State
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - (void)insertObject:(MPSiteModel *)model inSitesAtIndex:(NSUInteger)index {
if (contextInfo == MPAlertIncorrectMP) [self.sites insertObject:model atIndex:index];
return; }
if (contextInfo == MPAlertChangeMP) {
- (void)removeObjectFromSitesAtIndex:(NSUInteger)index {
[self.sites removeObjectAtIndex:index];
}
- (MPSiteModel *)selectedSite {
return [self.sitesController.selectedObjects firstObject];
}
#pragma mark - Actions
- (IBAction)settings:(id)sender {
[self.window close];
[[MPMacAppDelegate get] showPopup:sender];
}
- (IBAction)deleteSite:(id)sender {
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Delete"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Delete Site?"];
[alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedSite.name )];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Delete" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[context deleteObject:[self.selectedSite entityInContext:context]];
[context saveToStore];
}];
break;
}
default:
break;
}
}];
}
- (IBAction)changeLogin:(id)sender {
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Login Name"];
[alert setInformativeText:strf( @"Enter the login name for: %@", self.selectedSite.name )];
NSTextField *loginField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
loginField.stringValue = self.selectedSite.loginName?: @"";
[loginField selectText:self];
[alert setAccessoryView:loginField];
[alert layout];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Save" button.
NSString *loginName = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *entity = [self.selectedSite entityInContext:context];
entity.loginName = loginName;
[context saveToStore];
}];
break;
}
default:
break;
}
}];
}
- (IBAction)resetMasterPassword:(id)sender {
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Reset"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Reset My Master Password"];
[alert setInformativeText:strf( @"This will allow you to change %@'s master password.\n\n"
@"WARNING: All your site passwords will change. Do this only if you've forgotten your "
@"master password and are fully prepared to change all your sites' passwords to the new ones.",
[MPMacAppDelegate get].activeUserForMainThread.name )];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
switch (returnCode) { switch (returnCode) {
case NSAlertFirstButtonReturn: { case NSAlertFirstButtonReturn: {
// "Reset" button. // "Reset" button.
@@ -253,24 +331,66 @@
default: default:
break; break;
} }
}];
}
- (IBAction)changePassword:(id)sender {
if (!self.selectedSite.stored)
return; return;
}
if (contextInfo == MPAlertCreateSite) { NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Password"];
[alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedSite.name )];
[alert setAccessoryView:[[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]];
[alert layout];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
switch (returnCode) { switch (returnCode) {
case NSAlertFirstButtonReturn: { case NSAlertFirstButtonReturn: {
// "Create" button. // "Save" button.
[[MPMacAppDelegate get] addSiteNamed:[self.siteField stringValue] completion: NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
^(MPSiteEntity *site, NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
if (site) MPSiteEntity *entity = [self.selectedSite entityInContext:context];
PearlMainQueue( ^{ [self updateSites]; } ); [entity.algorithm savePassword:password toSite:entity usingKey:[MPMacAppDelegate get].key];
}]; [context saveToStore];
}];
break; break;
} }
default: default:
break; break;
} }
}];
}
- (IBAction)changeType:(id)sender {
MPSiteModel *site = self.selectedSite;
NSArray *types = [site.algorithm allTypes];
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
for (NSUInteger t = 0; t < [types count]; ++t) {
MPSiteType type = (MPSiteType)[types[t] unsignedIntegerValue];
NSString *title = [site.algorithm nameOfType:type];
if (type & MPSiteTypeClassGenerated)
title = strf( @"%@ %@", [site.algorithm generatePasswordForSiteNamed:site.name ofType:type withCounter:site.counter
usingKey:[MPMacAppDelegate get].key], title );
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
cell.tag = type;
cell.state = type == site.type? NSOnState: NSOffState;
cell.title = title;
} }
if (contextInfo == MPAlertChangeType) {
self.passwordTypesBox.title = strf( @"Choose a password type for %@:", site.name );
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Password Type"];
[alert setAccessoryView:self.passwordTypesBox];
[alert layout];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
switch (returnCode) { switch (returnCode) {
case NSAlertFirstButtonReturn: { case NSAlertFirstButtonReturn: {
// "Save" button. // "Save" button.
@@ -288,166 +408,32 @@
default: default:
break; break;
} }
} }];
if (contextInfo == MPAlertChangeLogin) {
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Save" button.
NSString *loginName = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *entity = [self.selectedSite entityInContext:context];
entity.loginName = loginName;
[context saveToStore];
}];
break;
}
default:
break;
}
}
if (contextInfo == MPAlertChangeContent) {
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Save" button.
NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *entity = [self.selectedSite entityInContext:context];
[entity.algorithm savePassword:password toSite:entity usingKey:[MPMacAppDelegate get].key];
[context saveToStore];
}];
break;
}
default:
break;
}
}
if (contextInfo == MPAlertDeleteSite) {
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Delete" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[context deleteObject:[self.selectedSite entityInContext:context]];
[context saveToStore];
}];
break;
}
default:
break;
}
}
} }
#pragma mark - State - (IBAction)securityQuestions:(id)sender {
- (void)insertObject:(MPSiteModel *)model inSitesAtIndex:(NSUInteger)index {
[self.sites insertObject:model atIndex:index];
}
- (void)removeObjectFromSitesAtIndex:(NSUInteger)index {
[self.sites removeObjectAtIndex:index];
}
- (MPSiteModel *)selectedSite {
return [self.sitesController.selectedObjects firstObject];
}
#pragma mark - Actions
- (IBAction)settings:(id)sender {
[self fadeOut:NO];
[[MPMacAppDelegate get] showPopup:sender];
}
- (IBAction)deleteSite:(id)sender {
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Delete"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Delete Site?"];
[alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedSite.name )];
[alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertDeleteSite];
}
- (IBAction)changeLogin:(id)sender {
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Login Name"];
[alert setInformativeText:strf( @"Enter the login name for: %@", self.selectedSite.name )];
NSTextField *loginField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
loginField.stringValue = self.selectedSite.loginName?: @"";
[loginField selectText:self];
[alert setAccessoryView:loginField];
[alert layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertChangeLogin];
}
- (IBAction)resetMasterPassword:(id)sender {
MPUserEntity *activeUser = [MPMacAppDelegate get].activeUserForMainThread;
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Reset"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Reset My Master Password"];
[alert setInformativeText:strf( @"This will allow you to change %@'s master password.\n\n"
@"WARNING: All your site passwords will change. Do this only if you've forgotten your "
@"master password and are fully prepared to change all your sites' passwords to the new ones.", activeUser.name )];
[alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertChangeMP];
}
- (IBAction)changePassword:(id)sender {
if (!self.selectedSite.stored)
return;
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Password"];
[alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedSite.name )];
[alert setAccessoryView:[[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]];
[alert layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertChangeContent];
}
- (IBAction)changeType:(id)sender {
MPSiteModel *site = self.selectedSite; MPSiteModel *site = self.selectedSite;
NSArray *types = [site.algorithm allTypesStartingWith:MPSiteTypeGeneratedPIN]; self.securityQuestionsBox.title = strf( @"Answer to security questions for %@:", site.name );
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
for (NSUInteger t = 0; t < [types count]; ++t) {
MPSiteType type = (MPSiteType)[types[t] unsignedIntegerValue];
NSString *title = [site.algorithm nameOfType:type];
if (type & MPSiteTypeClassGenerated)
title = [site.algorithm generatePasswordForSiteNamed:site.name ofType:type
withCounter:site.counter
usingKey:[MPMacAppDelegate get].key];
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
cell.tag = type;
cell.state = type == site.type? NSOnState: NSOffState;
cell.title = title;
}
NSAlert *alert = [NSAlert new]; NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"]; [alert addButtonWithTitle:@"Copy Answer"];
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Close"];
[alert setMessageText:@"Change Password Type"]; [alert setMessageText:@"Security Questions"];
[alert setInformativeText:strf( @"Choose a new password type for: %@", site.name )]; [alert setAccessoryView:self.securityQuestionsBox];
[alert setAccessoryView:self.passwordTypesBox];
[alert layout]; [alert layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self [alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertChangeType]; switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Copy Answer" button.
[self copyContent:self.securityAnswerField.stringValue];
[self.window close];
break;
}
default:
break;
}
}];
} }
#pragma mark - Private #pragma mark - Private
@@ -467,7 +453,7 @@
return YES; return YES;
} }
if (commandSelector == @selector( cancel: ) || commandSelector == @selector( cancelOperation: )) { if (commandSelector == @selector( cancel: ) || commandSelector == @selector( cancelOperation: )) {
[self fadeOut]; [self.window close];
return YES; return YES;
} }
@@ -486,9 +472,8 @@
} }
// Performing action while content is available. Copy it. // Performing action while content is available. Copy it.
[self copyContent:selectedSite.content]; [self copyContent:self.shiftPressed? selectedSite.answer: selectedSite.content];
[self.window close];
[self fadeOut];
NSUserNotification *notification = [NSUserNotification new]; NSUserNotification *notification = [NSUserNotification new];
notification.title = @"Password Copied"; notification.title = @"Password Copied";
@@ -598,6 +583,7 @@
CGFloat gradientOpacity = selectedOffset / siteScrollView.bounds.size.height; CGFloat gradientOpacity = selectedOffset / siteScrollView.bounds.size.height;
self.siteGradient.colors = @[ self.siteGradient.colors = @[
(__bridge id)[NSColor whiteColor].CGColor, (__bridge id)[NSColor whiteColor].CGColor,
(__bridge id)[NSColor colorWithDeviceWhite:1 alpha:1 - (1 - gradientOpacity) * 4 / 5].CGColor,
(__bridge id)[NSColor colorWithDeviceWhite:1 alpha:gradientOpacity].CGColor (__bridge id)[NSColor colorWithDeviceWhite:1 alpha:gradientOpacity].CGColor
]; ];
@@ -613,8 +599,21 @@
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Create site?"]; [alert setMessageText:@"Create site?"];
[alert setInformativeText:strf( @"Do you want to create a new site named:\n\n%@", siteName )]; [alert setInformativeText:strf( @"Do you want to create a new site named:\n\n%@", siteName )];
[alert beginSheetModalForWindow:self.window modalDelegate:self [alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertCreateSite]; switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Create" button.
[[MPMacAppDelegate get] addSiteNamed:[self.siteField stringValue] completion:
^(MPSiteEntity *site, NSManagedObjectContext *context) {
if (site)
PearlMainQueue( ^{ [self updateSites]; } );
}];
break;
}
default:
break;
}
}];
} ); } );
} }
@@ -632,70 +631,4 @@
}]; }];
} }
- (void)fadeIn {
prof_new( @"fadeIn" );
if ([self.window isOnActiveSpace] && self.window.alphaValue > FLT_EPSILON) {
prof_finish( @"showing" );
return;
}
CGDirectDisplayID displayID = [self.window.screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
CGImageRef capturedImage = CGDisplayCreateImage( displayID );
if (!capturedImage || CGImageGetWidth( capturedImage ) <= 1) {
if (capturedImage)
CFRelease( capturedImage );
wrn( @"Failed to capture screen image for display: %d", displayID );
prof_finish( @"capture failed" );
return;
}
prof_rewind( @"capture" );
NSImage *screenImage = [[NSImage alloc] initWithCGImage:capturedImage size:NSMakeSize(
CGImageGetWidth( capturedImage ) / self.window.backingScaleFactor,
CGImageGetHeight( capturedImage ) / self.window.backingScaleFactor )];
prof_rewind( @"screenImage" );
NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize(
CGImageGetWidth( capturedImage ) / 20,
CGImageGetHeight( capturedImage ) / 20 )];
prof_rewind( @"smallImage" );
CFRelease( capturedImage );
[smallImage lockFocus];
[screenImage drawInRect:(NSRect){ .origin = CGPointZero, .size = smallImage.size, }
fromRect:NSZeroRect
operation:NSCompositeSourceOver
fraction:1.0];
[smallImage unlockFocus];
prof_rewind( @"scale" );
self.blurView.image = smallImage;
prof_rewind( @"blurView" );
[self.window setFrame:self.window.screen.frame display:YES];
[NSAnimationContext currentContext].timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
self.window.animator.alphaValue = 1.0;
prof_finish( @"window setup" );
}
- (void)fadeOut {
[self fadeOut:YES];
}
- (void)fadeOut:(BOOL)hide {
if (![NSApp isActive] && self.window.alphaValue <= FLT_EPSILON)
return;
[[NSAnimationContext currentContext] setCompletionHandler:^{
[self close];
if (hide)
[NSApp hide:self];
}];
[NSAnimationContext currentContext].timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[self.window animator].alphaValue = 0.0;
}
@end @end

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,8 @@
@property (nonatomic) NSString *typeName; @property (nonatomic) NSString *typeName;
@property (nonatomic) NSString *content; @property (nonatomic) NSString *content;
@property (nonatomic) NSString *displayedContent; @property (nonatomic) NSString *displayedContent;
@property (nonatomic) NSString *question;
@property (nonatomic) NSString *answer;
@property (nonatomic) NSString *loginName; @property (nonatomic) NSString *loginName;
@property (nonatomic) NSNumber *uses; @property (nonatomic) NSNumber *uses;
@property (nonatomic) NSUInteger counter; @property (nonatomic) NSUInteger counter;

View File

@@ -168,6 +168,15 @@
[self updateContent]; [self updateContent];
} }
- (void)setQuestion:(NSString *)question {
if ([question isEqualToString:_question])
return;
_question = question;
[self updateContent];
}
- (BOOL)outdated { - (BOOL)outdated {
return self.algorithmVersion < MPAlgorithmVersionCurrent; return self.algorithmVersion < MPAlgorithmVersionCurrent;
@@ -194,13 +203,18 @@
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[self updateContent:[MPSiteEntity existingObjectWithID:_entityOID inContext:context]]; [self updateContent:[MPSiteEntity existingObjectWithID:_entityOID inContext:context]];
}]; }];
else else
PearlNotMainQueue( ^{ PearlNotMainQueue( ^{
NSString *password = [self.algorithm generatePasswordForSiteNamed:self.name ofType:self.type withCounter:self.counter [self updatePasswordWithResult:
usingKey:[MPAppDelegate_Shared get].key]; [self.algorithm generatePasswordForSiteNamed:self.name ofType:self.type withCounter:self.counter
NSString *loginName = [self.algorithm generateLoginForSiteNamed:self.name usingKey:[MPAppDelegate_Shared get].key]; usingKey:[MPAppDelegate_Shared get].key]];
[self updatePasswordWithResult:password]; [self updateLoginNameWithResult:
[self updateLoginNameWithResult:loginName]; [self.algorithm generateLoginForSiteNamed:self.name
usingKey:[MPAppDelegate_Shared get].key]];
[self updateAnswerWithResult:
[self.algorithm generateAnswerForSiteNamed:self.name onQuestion:self.question
usingKey:[MPAppDelegate_Shared get].key]];
} ); } );
} }
@@ -212,6 +226,8 @@
[entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) { [entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
[self updateLoginNameWithResult:result]; [self updateLoginNameWithResult:result];
}]; }];
[self updateAnswerWithResult:[self.algorithm generateAnswerForSiteNamed:self.name onQuestion:self.question
usingKey:[MPAppDelegate_Shared get].key]];
} }
- (void)updatePasswordWithResult:(NSString *)result { - (void)updatePasswordWithResult:(NSString *)result {
@@ -239,4 +255,11 @@
} ); } );
} }
- (void)updateAnswerWithResult:(NSString *)answer {
PearlMainQueue( ^{
self.answer = answer;
} );
}
@end @end

View File

@@ -78,7 +78,7 @@
<key>NSMainNibFile</key> <key>NSMainNibFile</key>
<string>MainMenu</string> <string>MainMenu</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>MPMacApplication</string>
<key>UTExportedTypeDeclarations</key> <key>UTExportedTypeDeclarations</key>
<array> <array>
<dict> <dict>

View File

@@ -10,6 +10,7 @@
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; }; 93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; };
93D391E61DC23E128DA4446C /* NSView+Traversing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393EE88DE554BCCBC1C2D /* NSView+Traversing.h */; }; 93D391E61DC23E128DA4446C /* NSView+Traversing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393EE88DE554BCCBC1C2D /* NSView+Traversing.h */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; }; 93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D393A1646430FAAC73E7FE /* MPMacApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F83DD151985F2C7345A /* MPMacApplication.m */; };
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */; }; 93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */; };
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39538C4CEFF46DF379254 /* MPNoStateButton.m */; }; 93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39538C4CEFF46DF379254 /* MPNoStateButton.m */; };
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; }; 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
@@ -273,6 +274,7 @@
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = "<group>"; }; 93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D393EE88DE554BCCBC1C2D /* NSView+Traversing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+Traversing.h"; sourceTree = "<group>"; }; 93D393EE88DE554BCCBC1C2D /* NSView+Traversing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+Traversing.h"; sourceTree = "<group>"; };
93D3942099C9AD0374B5777D /* MPMacApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacApplication.h; sourceTree = "<group>"; };
93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesTableView.m; sourceTree = "<group>"; }; 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesTableView.m; sourceTree = "<group>"; };
93D39538C4CEFF46DF379254 /* MPNoStateButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNoStateButton.m; sourceTree = "<group>"; }; 93D39538C4CEFF46DF379254 /* MPNoStateButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNoStateButton.m; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
@@ -283,6 +285,7 @@
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = "<group>"; }; 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = "<group>"; };
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = "<group>"; }; 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = "<group>"; };
93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteModel.m; sourceTree = "<group>"; }; 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteModel.m; sourceTree = "<group>"; };
93D39F83DD151985F2C7345A /* MPMacApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacApplication.m; sourceTree = "<group>"; };
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = "<group>"; }; DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = "<group>"; };
DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = "<group>"; }; DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = "<group>"; };
DA0933CF1747B91B00DE1CEF /* appstore.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = appstore.png; sourceTree = "<group>"; }; DA0933CF1747B91B00DE1CEF /* appstore.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = appstore.png; sourceTree = "<group>"; };
@@ -1140,6 +1143,8 @@
93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */, 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */,
93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */, 93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */,
93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */, 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */,
93D39F83DD151985F2C7345A /* MPMacApplication.m */,
93D3942099C9AD0374B5777D /* MPMacApplication.h */,
); );
path = Mac; path = Mac;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2263,6 +2268,7 @@
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */, 93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */,
DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */, DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */,
DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */, DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */,
93D393A1646430FAAC73E7FE /* MPMacApplication.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -2456,7 +2462,7 @@
"$(inherit)", "$(inherit)",
"\"$(SRCROOT)/../../../External\"/**", "\"$(SRCROOT)/../../../External\"/**",
); );
MACOSX_DEPLOYMENT_TARGET = 10.9; MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
@@ -2546,7 +2552,7 @@
"$(inherit)", "$(inherit)",
"\"$(SRCROOT)/../../../External\"/**", "\"$(SRCROOT)/../../../External\"/**",
); );
MACOSX_DEPLOYMENT_TARGET = 10.9; MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "${TARGET_NAME}"; PRODUCT_NAME = "${TARGET_NAME}";
@@ -2569,14 +2575,15 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Mac, "\"$(SRCROOT)/../../../External/Mac\"",
); );
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac; PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
@@ -2597,14 +2604,15 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Mac, "\"$(SRCROOT)/../../../External/Mac\"",
); );
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac; PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
@@ -2728,7 +2736,7 @@
"$(inherit)", "$(inherit)",
"\"$(SRCROOT)/../../../External\"/**", "\"$(SRCROOT)/../../../External\"/**",
); );
MACOSX_DEPLOYMENT_TARGET = 10.9; MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "${TARGET_NAME}"; PRODUCT_NAME = "${TARGET_NAME}";
@@ -2751,14 +2759,15 @@
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Mac, "\"$(SRCROOT)/../../../External/Mac\"",
); );
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac; PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword.Mac;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
@@ -2776,9 +2785,7 @@
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch"; GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto\"",
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
); );
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -2858,9 +2865,7 @@
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch"; GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto\"",
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
); );
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -2877,9 +2882,7 @@
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch"; GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto\"",
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
); );
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13E28" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment version="1070" defaultVersion="1080" identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
</dependencies> </dependencies>
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"> <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -11,7 +11,7 @@
</connections> </connections>
</customObject> </customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<menu title="AMainMenu" systemMenu="main" id="29"/> <menu title="AMainMenu" systemMenu="main" id="29"/>
<customObject id="494" customClass="MPMacAppDelegate"> <customObject id="494" customClass="MPMacAppDelegate">
<connections> <connections>
@@ -22,6 +22,7 @@
<outlet property="openAtLoginItem" destination="785" id="788"/> <outlet property="openAtLoginItem" destination="785" id="788"/>
<outlet property="rememberPasswordItem" destination="744" id="750"/> <outlet property="rememberPasswordItem" destination="744" id="750"/>
<outlet property="savePasswordItem" destination="747" id="751"/> <outlet property="savePasswordItem" destination="747" id="751"/>
<outlet property="showFullScreenItem" destination="Rvo-MK-GgF" id="NYi-85-Yuc"/>
<outlet property="showItem" destination="719" id="783"/> <outlet property="showItem" destination="719" id="783"/>
<outlet property="statusMenu" destination="716" id="731"/> <outlet property="statusMenu" destination="716" id="731"/>
<outlet property="usersItem" destination="755" id="762"/> <outlet property="usersItem" destination="755" id="762"/>
@@ -71,6 +72,23 @@
</attributedString> </attributedString>
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
</menuItem> </menuItem>
<menuItem title="Show Full Screen" id="Rvo-MK-GgF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="togglePreference:" target="494" id="wfI-yQ-Kla"/>
</connections>
</menuItem>
<menuItem title="Claim the entire screen while looking up passwords." enabled="NO" id="3k1-4V-IZ7">
<attributedString key="attributedTitle">
<fragment content="Always open Master Password at start-up.">
<attributes>
<font key="NSFont" size="12" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
</attributedString>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Hide Passwords" id="9G7-17-PzY"> <menuItem title="Hide Passwords" id="9G7-17-PzY">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>

View File

@@ -178,7 +178,7 @@
else if ([cell isKindOfClass:[MPSendAnswersCell class]]) { else if ([cell isKindOfClass:[MPSendAnswersCell class]]) {
NSString *body; NSString *body;
if (!_multiple) { if (!_multiple) {
NSObject *answer = [site.algorithm resolveAnswerForSite:site usingKey:[MPiOSAppDelegate get].key]; NSObject *answer = [site resolveSiteAnswerUsingKey:[MPiOSAppDelegate get].key];
body = strf( @"Master Password generated the following security answer for your site: %@\n\n" body = strf( @"Master Password generated the following security answer for your site: %@\n\n"
@"%@\n" @"%@\n"
@"\n\nYou should use this as the answer to each security question the site asks you.\n" @"\n\nYou should use this as the answer to each security question the site asks you.\n"
@@ -188,7 +188,7 @@
NSMutableString *bodyBuilder = [NSMutableString string]; NSMutableString *bodyBuilder = [NSMutableString string];
[bodyBuilder appendFormat:@"Master Password generated the following security answers for your site: %@\n\n", site.name]; [bodyBuilder appendFormat:@"Master Password generated the following security answers for your site: %@\n\n", site.name];
for (MPSiteQuestionEntity *question in site.questions) { for (MPSiteQuestionEntity *question in site.questions) {
NSObject *answer = [site.algorithm resolveAnswerForQuestion:question usingKey:[MPiOSAppDelegate get].key]; NSObject *answer = [question resolveQuestionAnswerUsingKey:[MPiOSAppDelegate get].key];
[bodyBuilder appendFormat:@"For question: '%@', use answer: %@\n", question.keyword, answer]; [bodyBuilder appendFormat:@"For question: '%@', use answer: %@\n", question.keyword, answer];
} }
[bodyBuilder appendFormat:@"\n\nUse the answer for the matching security question.\n" [bodyBuilder appendFormat:@"\n\nUse the answer for the matching security question.\n"
@@ -241,7 +241,7 @@
self.titleLabel.text = strl( @"Answer for %@:", site.name ); self.titleLabel.text = strl( @"Answer for %@:", site.name );
self.answerField.text = @"..."; self.answerField.text = @"...";
[site.algorithm resolveAnswerForSite:site usingKey:[MPiOSAppDelegate get].key result:^(NSString *result) { [site resolveSiteAnswerUsingKey:[MPiOSAppDelegate get].key result:^(NSString *result) {
PearlMainQueue( ^{ PearlMainQueue( ^{
self.answerField.text = result; self.answerField.text = result;
} ); } );
@@ -330,7 +330,7 @@
PearlMainQueue( ^{ PearlMainQueue( ^{
self.answerField.text = @"..."; self.answerField.text = @"...";
} ); } );
[site.algorithm resolveAnswerForQuestion:question usingKey:[MPiOSAppDelegate get].key result:^(NSString *result) { [question resolveQuestionAnswerUsingKey:[MPiOSAppDelegate get].key result:^(NSString *result) {
PearlMainQueue( ^{ PearlMainQueue( ^{
self.questionField.text = keyword; self.questionField.text = keyword;
self.answerField.text = result; self.answerField.text = result;

View File

@@ -4225,7 +4225,6 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"$(inherited)", "$(inherited)",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
); );
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -4325,7 +4324,6 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"$(inherited)", "$(inherited)",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
); );
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -4341,7 +4339,6 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"", "\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"$(inherited)", "$(inherited)",
"/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
); );
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 585 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 764 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 906 B

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Some files were not shown because too many files have changed in this diff Show More