diff --git a/.gitmodules b/.gitmodules
index 28f506ee..5e9ec349 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,6 +7,3 @@
[submodule "External/iCloudStoreManager"]
path = External/iCloudStoreManager
url = git://github.com/lhunath/iCloudStoreManager.git
-[submodule "External/apptentive"]
- path = External/apptentive
- url = git://github.com/apptentive/apptentive-ios.git
diff --git a/Apptentive/Apptentive.plist b/Apptentive/Apptentive.plist
deleted file mode 100644
index efa55526..00000000
--- a/Apptentive/Apptentive.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- API Key
-
-
-
diff --git a/External/Pearl b/External/Pearl
index 0a5a01b8..82fb855d 160000
--- a/External/Pearl
+++ b/External/Pearl
@@ -1 +1 @@
-Subproject commit 0a5a01b8862a3e8d986b23513c5d40ebc152b6fc
+Subproject commit 82fb855dd9dd61e8a895852b91885c15d27b7c7d
diff --git a/External/apptentive b/External/apptentive
deleted file mode 160000
index 3b6635e1..00000000
--- a/External/apptentive
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 3b6635e131d19f049fec1fd943afd89855185d39
diff --git a/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword-iOS.xcodeproj/project.pbxproj
index 04901f48..6a02d2cc 100644
--- a/MasterPassword-iOS.xcodeproj/project.pbxproj
+++ b/MasterPassword-iOS.xcodeproj/project.pbxproj
@@ -80,9 +80,6 @@
DA95D5F614DF0B9F008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CC14DF0691008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib */; };
DA95D5F714DF0B9F008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CD14DF0691008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib */; };
DA95D5F814DF0B9F008D1B94 /* IASKSpecifierValuesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CE14DF0691008D1B94 /* IASKSpecifierValuesView.xib */; };
- DAAC35DB156BD62F00C5FD93 /* libApptentiveConnect.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAAC35D2156BD51600C5FD93 /* libApptentiveConnect.a */; };
- DAAC35DE156BD77D00C5FD93 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */; };
- DAAC35E4156BDBA700C5FD93 /* Apptentive.plist in Resources */ = {isa = PBXBuildFile; fileRef = DAAC35E2156BDBA700C5FD93 /* Apptentive.plist */; };
DAB8D45D15036BCF00CED3BC /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */; };
DAB8D45E15036BCF00CED3BC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D43F15036BCF00CED3BC /* InfoPlist.strings */; };
DAB8D45F15036BCF00CED3BC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44115036BCF00CED3BC /* main.m */; };
@@ -676,7 +673,6 @@
DAC632891486D9690075AEA5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC632871486D95D0075AEA5 /* Security.framework */; };
DAC728CA157C247B00889EF2 /* MPPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAC728C9157C247B00889EF2 /* MPPreferencesViewController.m */; };
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
- DACABB8515729E80008BA211 /* ApptentiveResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DAAC35D6156BD51600C5FD93 /* ApptentiveResources.bundle */; };
DACABB861572A2A7008BA211 /* tip_alert_black.png in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D6BA15036BF600CED3BC /* tip_alert_black.png */; };
DACABB871572A2A7008BA211 /* tip_alert_black@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D6BB15036BF600CED3BC /* tip_alert_black@2x.png */; };
DACABB881572A2A7008BA211 /* tip_basic_black.png in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D6CC15036BF600CED3BC /* tip_basic_black.png */; };
@@ -824,6 +820,8 @@
DAFE4A63150399FF003ABA8C /* UIControl+PearlSelect.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA8B /* UIControl+PearlSelect.h */; };
DAFE4A63150399FF003ABA8E /* UIScrollView+PearlFlashingIndicators.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA8D /* UIScrollView+PearlFlashingIndicators.m */; };
DAFE4A63150399FF003ABA90 /* UIScrollView+PearlFlashingIndicators.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA8F /* UIScrollView+PearlFlashingIndicators.h */; };
+ DAFE4A63150399FF003ABA92 /* NSDateFormatter+RFC3339.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */; };
+ DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -855,41 +853,6 @@
remoteGlobalIDString = DA67FE5D14E4834300DB7CC9;
remoteInfo = util;
};
- DAAC35D1156BD51600C5FD93 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 496DC37F1333D35600743F65;
- remoteInfo = ApptentiveConnect;
- };
- DAAC35D3156BD51600C5FD93 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 496DC38C1333D35600743F65;
- remoteInfo = ApptentiveConnectTests;
- };
- DAAC35D5156BD51600C5FD93 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 49D1118C13341A7C00603373;
- remoteInfo = ApptentiveResources;
- };
- DAAC35D7156BD61D00C5FD93 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
- proxyType = 1;
- remoteGlobalIDString = 496DC37E1333D35600743F65;
- remoteInfo = ApptentiveConnect;
- };
- DAAC35D9156BD61D00C5FD93 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
- proxyType = 1;
- remoteGlobalIDString = 49D1118B13341A7C00603373;
- remoteInfo = ApptentiveResources;
- };
DAC63283148681200075AEA5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA5BFA3B147E415C00F98B1E /* Project object */;
@@ -993,11 +956,7 @@
DA95D5CD14DF0691008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IASKPSToggleSwitchSpecifierViewCell.xib; sourceTree = ""; };
DA95D5CE14DF0691008D1B94 /* IASKSpecifierValuesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IASKSpecifierValuesView.xib; sourceTree = ""; };
DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
- DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ApptentiveConnect.xcodeproj; path = External/apptentive/ApptentiveConnect/ApptentiveConnect.xcodeproj; sourceTree = ""; };
DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
- DAAC35E2156BDBA700C5FD93 /* Apptentive.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Apptentive.plist; sourceTree = ""; };
- DAAC35E5156BDBC100C5FD93 /* ATConnect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ATConnect.h; path = ../External/apptentive/ApptentiveConnect/source/ATConnect.h; sourceTree = ""; };
- DAAC35E6156BDF0E00C5FD93 /* ATAppRatingFlow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ATAppRatingFlow.h; path = "../External/apptentive/ApptentiveConnect/source/Rating Flow/ATAppRatingFlow.h"; sourceTree = ""; };
DAB8D43D15036BCF00CED3BC /* MasterPassword.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MasterPassword.xcdatamodel; sourceTree = ""; };
DAB8D44015036BCF00CED3BC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
DAB8D44115036BCF00CED3BC /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
@@ -1817,6 +1776,8 @@
DAFE4A63150399FF003ABA8B /* UIControl+PearlSelect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+PearlSelect.h"; sourceTree = ""; };
DAFE4A63150399FF003ABA8D /* UIScrollView+PearlFlashingIndicators.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlFlashingIndicators.m"; sourceTree = ""; };
DAFE4A63150399FF003ABA8F /* UIScrollView+PearlFlashingIndicators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+PearlFlashingIndicators.h"; sourceTree = ""; };
+ DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+RFC3339.m"; sourceTree = ""; };
+ DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+RFC3339.h"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -1832,8 +1793,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DAAC35DE156BD77D00C5FD93 /* CoreTelephony.framework in Frameworks */,
- DAAC35DB156BD62F00C5FD93 /* libApptentiveConnect.a in Frameworks */,
DA44260A1557D9E40052177D /* libiCloudStoreManager.a in Frameworks */,
DAD312C21552A22700A3F9ED /* libsqlite3.dylib in Frameworks */,
DAD312BF1552A1BD00A3F9ED /* libLocalytics.a in Frameworks */,
@@ -1913,7 +1872,6 @@
DA5BFA39147E415C00F98B1E = {
isa = PBXGroup;
children = (
- DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */,
DA79A9BD1557DDC700BAA07A /* scrypt.xcodeproj */,
DA5BFA50147E415C00F98B1E /* MasterPassword */,
DAB8D46F15036BF600CED3BC /* Resources */,
@@ -1925,7 +1883,6 @@
DAD3125E15528C9C00A3F9ED /* Crashlytics */,
DAD3126115528C9C00A3F9ED /* TestFlight */,
DAD3127315528CD200A3F9ED /* Localytics */,
- DAAC35E0156BDBA700C5FD93 /* Apptentive */,
DA4425D71557BF260052177D /* iCloudStoreManager */,
DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */,
@@ -2128,26 +2085,6 @@
path = External/InAppSettingsKit/InAppSettingsKit/Xibs;
sourceTree = SOURCE_ROOT;
};
- DAAC35C9156BD51500C5FD93 /* Products */ = {
- isa = PBXGroup;
- children = (
- DAAC35D2156BD51600C5FD93 /* libApptentiveConnect.a */,
- DAAC35D4156BD51600C5FD93 /* ApptentiveConnectTests.octest */,
- DAAC35D6156BD51600C5FD93 /* ApptentiveResources.bundle */,
- );
- name = Products;
- sourceTree = "";
- };
- DAAC35E0156BDBA700C5FD93 /* Apptentive */ = {
- isa = PBXGroup;
- children = (
- DAAC35E6156BDF0E00C5FD93 /* ATAppRatingFlow.h */,
- DAAC35E5156BDBC100C5FD93 /* ATConnect.h */,
- DAAC35E2156BDBA700C5FD93 /* Apptentive.plist */,
- );
- path = Apptentive;
- sourceTree = "";
- };
DAB8D43E15036BCF00CED3BC /* iOS */ = {
isa = PBXGroup;
children = (
@@ -3006,6 +2943,8 @@
DAFE45D715039823003ABA7C /* Pearl */ = {
isa = PBXGroup;
children = (
+ DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */,
+ DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */,
DAFE4A63150399FF003ABA87 /* NSObject+PearlKVO.h */,
DAFE4A63150399FF003ABA85 /* NSObject+PearlKVO.m */,
DA30E9CB15722ECA00A68B4C /* NSBundle+PearlMutableInfo.h */,
@@ -3229,6 +3168,7 @@
DAFE4A63150399FF003ABA88 /* NSObject+PearlKVO.h in Headers */,
DAFE4A63150399FF003ABA8C /* UIControl+PearlSelect.h in Headers */,
DAFE4A63150399FF003ABA90 /* UIScrollView+PearlFlashingIndicators.h in Headers */,
+ DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3277,8 +3217,6 @@
buildRules = (
);
dependencies = (
- DAAC35D8156BD61D00C5FD93 /* PBXTargetDependency */,
- DAAC35DA156BD61D00C5FD93 /* PBXTargetDependency */,
);
name = MasterPassword;
productName = MasterPassword;
@@ -3395,10 +3333,6 @@
productRefGroup = DA5BFA45147E415C00F98B1E /* Products */;
projectDirPath = "";
projectReferences = (
- {
- ProductGroup = DAAC35C9156BD51500C5FD93 /* Products */;
- ProjectRef = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
- },
{
ProductGroup = DA79A9BE1557DDC700BAA07A /* Products */;
ProjectRef = DA79A9BD1557DDC700BAA07A /* scrypt.xcodeproj */;
@@ -3439,27 +3373,6 @@
remoteRef = DA79A9D11557DDC800BAA07A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
- DAAC35D2156BD51600C5FD93 /* libApptentiveConnect.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libApptentiveConnect.a;
- remoteRef = DAAC35D1156BD51600C5FD93 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- DAAC35D4156BD51600C5FD93 /* ApptentiveConnectTests.octest */ = {
- isa = PBXReferenceProxy;
- fileType = wrapper.cfbundle;
- path = ApptentiveConnectTests.octest;
- remoteRef = DAAC35D3156BD51600C5FD93 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- DAAC35D6156BD51600C5FD93 /* ApptentiveResources.bundle */ = {
- isa = PBXReferenceProxy;
- fileType = wrapper.cfbundle;
- path = ApptentiveResources.bundle;
- remoteRef = DAAC35D5156BD51600C5FD93 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@@ -4056,8 +3969,6 @@
DA0A1D0615690A9A0092735D /* Default@2x.png in Resources */,
DA0A1D1515690AF40092735D /* Icon-72@2x.png in Resources */,
DA0A1D1615690AF40092735D /* Icon-Small-50@2x.png in Resources */,
- DAAC35E4156BDBA700C5FD93 /* Apptentive.plist in Resources */,
- DACABB8515729E80008BA211 /* ApptentiveResources.bundle in Resources */,
DACABB861572A2A7008BA211 /* tip_alert_black.png in Resources */,
DACABB871572A2A7008BA211 /* tip_alert_black@2x.png in Resources */,
DACABB881572A2A7008BA211 /* tip_basic_black.png in Resources */,
@@ -4263,6 +4174,7 @@
DAFE4A63150399FF003ABA86 /* NSObject+PearlKVO.m in Sources */,
DAFE4A63150399FF003ABA8A /* UIControl+PearlSelect.m in Sources */,
DAFE4A63150399FF003ABA8E /* UIScrollView+PearlFlashingIndicators.m in Sources */,
+ DAFE4A63150399FF003ABA92 /* NSDateFormatter+RFC3339.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4285,16 +4197,6 @@
name = "Makefile-scrypt";
targetProxy = DA4DA1D71564470200F6F596 /* PBXContainerItemProxy */;
};
- DAAC35D8156BD61D00C5FD93 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- name = ApptentiveConnect;
- targetProxy = DAAC35D7156BD61D00C5FD93 /* PBXContainerItemProxy */;
- };
- DAAC35DA156BD61D00C5FD93 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- name = ApptentiveResources;
- targetProxy = DAAC35D9156BD61D00C5FD93 /* PBXContainerItemProxy */;
- };
DAC63284148681200075AEA5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DAC6325C1486805C0075AEA5 /* uicolor-utilities */;
diff --git a/MasterPassword/MPAppDelegate_Key.m b/MasterPassword/MPAppDelegate_Key.m
index 8522bf5b..7631d752 100644
--- a/MasterPassword/MPAppDelegate_Key.m
+++ b/MasterPassword/MPAppDelegate_Key.m
@@ -9,7 +9,6 @@
#import
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
-#import "ATConnect.h"
#import "LocalyticsSession.h"
@implementation MPAppDelegate_Shared (Key)
@@ -28,11 +27,11 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
NSData *key = [PearlKeyChain dataOfItemForQuery:keyQuery(user)];
if (key)
- inf(@"Found key (for: %@) in keychain.", user.name);
+ inf(@"Found key in keychain for: %@", user.userID);
else {
user.saveKey = NO;
- inf(@"No key found (for: %@) in keychain.", user.name);
+ inf(@"No key found in keychain for: %@", user.userID);
}
return key;
@@ -44,7 +43,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
NSData *existingKey = [PearlKeyChain dataOfItemForQuery:keyQuery(user)];
if (![existingKey isEqualToData:self.key]) {
- inf(@"Updating key in keychain.");
+ inf(@"Saving key in keychain for: %@", user.userID);
+
[PearlKeyChain addOrUpdateItemForQuery:keyQuery(user)
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
self.key, (__bridge id)kSecValueData,
@@ -63,7 +63,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
user.saveKey = NO;
if (result == noErr) {
- inf(@"Removed key (for: %@) from keychain.", user.name);
+ inf(@"Removed key from keychain for: %@", user.userID);
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self];
#ifdef TESTFLIGHT_SDK_VERSION
@@ -105,14 +105,10 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
if ((tryKey = [self loadSavedKeyFor:user]))
if (![user.keyID isEqual:keyIDForKey(tryKey)]) {
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
+ inf(@"Saved password doesn't match keyID for: %@", user.userID);
+
tryKey = nil;
[self forgetSavedKeyFor:user];
-
-#ifdef TESTFLIGHT_SDK_VERSION
- [TestFlight passCheckpoint:MPCheckpointSignInFailed];
-#endif
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSignInFailed
- attributes:nil];
}
}
@@ -121,19 +117,24 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
if ([password length])
if ((tryKey = keyForPassword(password, user.name)))
if (![user.keyID isEqual:keyIDForKey(tryKey)]) {
- tryKey = nil;
+ inf(@"Key derived from password doesn't match keyID for: %@", user.userID);
-#ifdef TESTFLIGHT_SDK_VERSION
- [TestFlight passCheckpoint:MPCheckpointSignInFailed];
-#endif
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSignInFailed
- attributes:nil];
+ tryKey = nil;
}
}
// No more methods left, fail if key still not known.
- if (!tryKey)
+ if (!tryKey) {
+ inf(@"Login failed for: %@", user.userID);
+
+#ifdef TESTFLIGHT_SDK_VERSION
+ [TestFlight passCheckpoint:MPCheckpointSignInFailed];
+#endif
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSignInFailed attributes:nil];
+
return NO;
+ }
+ inf(@"Logged in: %@", user.userID);
if (![self.key isEqualToData:tryKey]) {
self.key = tryKey;
@@ -141,10 +142,9 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
}
@try {
- if ([[MPiOSConfig get].sendDebugInfo boolValue]) {
- [TestFlight addCustomEnvironmentInformation:user.name forKey:@"username"];
- [[Crashlytics sharedInstance] setObjectValue:user.name forKey:@"username"];
- [[ATConnect sharedConnection] addAdditionalInfoToFeedback:user.name withKey:@"username"];
+ if ([[MPiOSConfig get].sendInfo boolValue]) {
+ [TestFlight addCustomEnvironmentInformation:user.userID forKey:@"username"];
+ [[Crashlytics sharedInstance] setObjectValue:user.userID forKey:@"username"];
}
}
@catch (id exception) {
diff --git a/MasterPassword/MPAppDelegate_Store.m b/MasterPassword/MPAppDelegate_Store.m
index 428cbb53..0fa3ff91 100644
--- a/MasterPassword/MPAppDelegate_Store.m
+++ b/MasterPassword/MPAppDelegate_Store.m
@@ -11,8 +11,6 @@
@implementation MPAppDelegate_Shared (Store)
-static NSDateFormatter *rfc3339DateFormatter = nil;
-
#pragma mark - Core Data setup
+ (NSManagedObjectContext *)managedObjectContext {
@@ -136,26 +134,27 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)iCloudEnabled {
- // manager.iCloudEnabled is more reliable (eg. iOS tampers with didSwitch a bit)
+ // manager.iCloudEnabled is more reliable (eg. iOS' MPAppDelegate tampers with didSwitch a bit)
iCloudEnabled = manager.iCloudEnabled;
+ inf(@"Using iCloud? %@", iCloudEnabled? @"YES": @"NO");
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:iCloudEnabled? MPCheckpointCloudEnabled: MPCheckpointCloudDisabled];
#endif
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:iCloudEnabled? MPCheckpointCloudEnabled: MPCheckpointCloudDisabled
- attributes:nil];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloud
+ attributes:[NSDictionary dictionaryWithObject:iCloudEnabled? @"YES": @"NO" forKey:@"enabled"]];
- inf(@"Using iCloud? %@", iCloudEnabled? @"YES": @"NO");
[MPConfig get].iCloud = [NSNumber numberWithBool:iCloudEnabled];
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreManagerErrorCause)cause
context:(id)context {
+ err(@"StoreManager: cause=%d, context=%@, error=%@", cause, context, error);
+
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:PearlString(@"MPCheckpointMPErrorUbiquity_%d", cause)];
#endif
- err(@"StoreManager: cause=%d, context=%@, error=%@", cause, context, error);
switch (cause) {
case UbiquityStoreManagerErrorCauseDeleteStore:
@@ -164,12 +163,13 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
case UbiquityStoreManagerErrorCauseClearStore:
break;
case UbiquityStoreManagerErrorCauseOpenLocalStore: {
+ wrn(@"Local store could not be opened, resetting it.");
+
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPCheckpointLocalStoreIncompatible];
#endif
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointLocalStoreIncompatible
attributes:nil];
- wrn(@"Local store could not be opened, resetting it.");
manager.hardResetEnabled = YES;
[manager hardResetLocalStorage];
@@ -177,12 +177,13 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
return;
}
case UbiquityStoreManagerErrorCauseOpenCloudStore: {
+ wrn(@"iCloud store could not be opened, resetting it.");
+
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPCheckpointCloudStoreIncompatible];
#endif
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloudStoreIncompatible
attributes:nil];
- wrn(@"iCloud store could not be opened, resetting it.");
manager.hardResetEnabled = YES;
[manager hardResetCloudStorage];
break;
@@ -192,22 +193,10 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
#pragma mark - Import / Export
-- (void)loadRFC3339DateFormatter {
-
- if (rfc3339DateFormatter)
- return;
-
- rfc3339DateFormatter = [NSDateFormatter new];
- NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- [rfc3339DateFormatter setLocale:enUSPOSIXLocale];
- [rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
- [rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
-}
-
- (MPImportResult)importSites:(NSString *)importedSitesString withPassword:(NSString *)password
askConfirmation:(BOOL(^)(NSUInteger importCount, NSUInteger deleteCount))confirmation {
- [self loadRFC3339DateFormatter];
+ inf(@"Importing sites.");
static NSRegularExpression *headerPattern, *sitePattern;
__autoreleasing NSError *error;
@@ -299,17 +288,24 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
NSArray *existingSites = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error)
err(@"Couldn't search existing sites: %@", error);
- if (!existingSites)
+ if (!existingSites) {
+ err(@"Lookup of existing sites failed for site: %@, user: %@", name, user.userID);
return MPImportResultInternalError;
+ }
[elementsToDelete addObjectsFromArray:existingSites];
[importedSiteElements addObject:[NSArray arrayWithObjects:lastUsed, uses, type, name, exportContent, nil]];
}
}
+ inf(@"Importing %u sites, deleting %u sites, for user: %@",
+ [importedSiteElements count], [elementsToDelete count], [MPUserEntity idFor:userName]);
+
// Ask for confirmation to import these sites.
- if (!confirmation([importedSiteElements count], [elementsToDelete count]))
+ if (!confirmation([importedSiteElements count], [elementsToDelete count])) {
+ inf(@"Import cancelled.");
return MPImportResultCancelled;
+ }
// Delete existing sites.
[elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
@@ -326,7 +322,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
user.keyID = [keyIDHex decodeHex];
}
for (NSArray *siteElements in importedSiteElements) {
- NSDate *lastUsed = [rfc3339DateFormatter dateFromString:[siteElements objectAtIndex:0]];
+ NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:[siteElements objectAtIndex:0]];
NSUInteger uses = (unsigned)[[siteElements objectAtIndex:1] integerValue];
MPElementType type = (MPElementType)[[siteElements objectAtIndex:2] integerValue];
NSString *name = [siteElements objectAtIndex:3];
@@ -345,6 +341,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
}
[self saveContext];
+ inf(@"Import completed successfully.");
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPCheckpointSitesImported];
#endif
@@ -356,7 +353,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
- (NSString *)exportSitesShowingPasswords:(BOOL)showPasswords {
- [self loadRFC3339DateFormatter];
+ inf(@"Exporting sites, %@, for: %@", showPasswords? @"showing passwords": @"omitting passwords", self.activeUser.userID);
// Header.
NSMutableString *export = [NSMutableString new];
@@ -370,7 +367,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion];
[export appendFormat:@"# User Name: %@\n", self.activeUser.name];
[export appendFormat:@"# Key ID: %@\n", [self.activeUser.keyID encodeHex]];
- [export appendFormat:@"# Date: %@\n", [rfc3339DateFormatter stringFromDate:[NSDate date]]];
+ [export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
if (showPasswords)
[export appendFormat:@"# Passwords: VISIBLE\n"];
else
@@ -398,15 +395,14 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
}
[export appendFormat:@"%@ %8d %8d %20s\t%@\n",
- [rfc3339DateFormatter stringFromDate:lastUsed], uses, type, [name cStringUsingEncoding:NSUTF8StringEncoding], content
+ [[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], uses, type, [name cStringUsingEncoding:NSUTF8StringEncoding], content
? content: @""];
}
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPCheckpointSitesExported];
#endif
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSitesExported
- attributes:nil];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSitesExported attributes:nil];
return export;
}
diff --git a/MasterPassword/MPEntities.h b/MasterPassword/MPEntities.h
index 4591cd22..f3a32dad 100644
--- a/MasterPassword/MPEntities.h
+++ b/MasterPassword/MPEntities.h
@@ -36,5 +36,8 @@
@property (assign) NSUInteger avatar;
@property (assign) BOOL saveKey;
@property (assign) MPElementType defaultType;
+@property (readonly) NSString *userID;
+
++ (NSString *)idFor:(NSString *)userName;
@end
diff --git a/MasterPassword/MPEntities.m b/MasterPassword/MPEntities.m
index a66287fe..cc964c69 100644
--- a/MasterPassword/MPEntities.m
+++ b/MasterPassword/MPEntities.m
@@ -178,9 +178,20 @@
return (MPElementType)[self.defaultType_ unsignedIntegerValue];
}
+- (NSString *)userID {
+
+ return [MPUserEntity idFor:self.name];
+}
+
+
- (void)setDefaultType:(MPElementType)aDefaultType {
self.defaultType_ = PearlUnsignedInteger(aDefaultType);
}
++ (NSString *)idFor:(NSString *)userName {
+
+ return [[userName hashWith:PearlHashSHA1] encodeHex];
+}
+
@end
diff --git a/MasterPassword/MPTypes.h b/MasterPassword/MPTypes.h
index e08e4692..6ebf79e5 100644
--- a/MasterPassword/MPTypes.h
+++ b/MasterPassword/MPTypes.h
@@ -64,6 +64,8 @@ typedef enum {
#define MPCheckpointCloudStoreIncompatible @"MPCheckpointCloudStoreIncompatible"
#define MPCheckpointSignInFailed @"MPCheckpointSignInFailed"
#define MPCheckpointSignedIn @"MPCheckpointSignedIn"
+#define MPCheckpointConfig @"MPCheckpointConfig"
+#define MPCheckpointCloud @"MPCheckpointCloud"
#define MPCheckpointCloudEnabled @"MPCheckpointCloudEnabled"
#define MPCheckpointCloudDisabled @"MPCheckpointCloudDisabled"
#define MPCheckpointSitesImported @"MPCheckpointSitesImported"
diff --git a/MasterPassword/iOS/MPAppDelegate.m b/MasterPassword/iOS/MPAppDelegate.m
index 24f93fb1..e50837c9 100644
--- a/MasterPassword/iOS/MPAppDelegate.m
+++ b/MasterPassword/iOS/MPAppDelegate.m
@@ -13,7 +13,6 @@
#import "IASKSettingsReader.h"
#import "LocalyticsSession.h"
-#import "ATConnect.h"
@interface MPAppDelegate ()
@@ -23,9 +22,6 @@
- (NSDictionary *)crashlyticsInfo;
- (NSString *)crashlyticsAPIKey;
-- (NSDictionary *)apptentiveInfo;
-- (NSString *)apptentiveAPIKey;
-
- (NSDictionary *)localyticsInfo;
- (NSString *)localyticsKey;
@@ -49,214 +45,85 @@
return (MPAppDelegate *)[super get];
}
-- (void)checkConfig {
-
- if ([[MPConfig get].iCloud boolValue] != [self.storeManager iCloudEnabled])
- [self.storeManager useiCloudStore:[[MPConfig get].iCloud boolValue] alertUser:YES];
- if ([[MPiOSConfig get].sendDebugInfo boolValue]) {
- [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"];
- [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloud boolValue] forKey:@"iCloud"];
- [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloudDecided boolValue] forKey:@"iCloudDecided"];
- [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendDebugInfo boolValue] forKey:@"sendDebugInfo"];
- [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"];
- [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showQuickStart boolValue] forKey:@"showQuickStart"];
- [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"];
- [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"];
- [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"];
- [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
- [[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
- }
-}
-
-- (void)showGuide {
-
- [self.navigationController performSegueWithIdentifier:@"MP_Guide" sender:self];
-
- [TestFlight passCheckpoint:MPCheckpointShowGuide];
-}
-
-- (void)export {
-
- [PearlAlert showNotice:
- @"This will export all your site names.\n\n"
- @"You can open the export with a text editor to get an overview of all your sites.\n\n"
- @"The file also acts as a personal backup of your site list in case you don't sync with iCloud/iTunes."
- tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
- [PearlAlert showAlertWithTitle:@"Reveal Passwords?" message:
- @"Would you like to make all your passwords visible in the export?\n\n"
- @"A safe export will only include your stored passwords, in an encrypted manner, "
- @"making the result safe from falling in the wrong hands.\n\n"
- @"If all your passwords are shown and somebody else finds the export, "
- @"they could gain access to all your sites!"
- viewStyle:UIAlertViewStyleDefault initAlert:nil
- tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
- if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 0)
- // Safe Export
- [self exportShowPasswords:NO];
- if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1)
- // Safe Export
- [self exportShowPasswords:YES];
- } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Safe Export", @"Show Passwords", nil];
- } otherTitles:nil];
-}
-
-- (void)exportShowPasswords:(BOOL)showPasswords {
-
- NSString *exportedSites = [self exportSitesShowingPasswords:showPasswords];
- NSString *message;
- if (showPasswords)
- message = @"Export of my Master Password sites with passwords visible.\n\nREMINDER: Make sure nobody else sees this file!\n";
- else
- message = @"Backup of my Master Password sites.\n";
-
- NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
- [exportDateFormatter setDateFormat:@"'Master Password sites ('yyyy'-'MM'-'DD').mpsites'"];
-
- MFMailComposeViewController *composer = [[MFMailComposeViewController alloc] init];
- [composer setMailComposeDelegate:self];
- [composer setSubject:@"Master Password site export"];
- [composer setMessageBody:message isHTML:NO];
- [composer addAttachmentData:[exportedSites dataUsingEncoding:NSUTF8StringEncoding] mimeType:@"text/plain"
- fileName:[exportDateFormatter stringFromDate:[NSDate date]]];
- [self.window.rootViewController presentModalViewController:composer animated:YES];
-}
-
-- (void)changeMP {
-
- [PearlAlert showAlertWithTitle:@"Changing Master Password"
- message:
- @"This will allow you to log in with a different master password.\n\n"
- @"Note that you will only see the sites and passwords for the master password you log in with.\n"
- @"If you log in with a different master password, your current sites will be unavailable.\n\n"
- @"You can always change back to your current master password later.\n"
- @"Your current sites and passwords will then become available again."
- viewStyle:UIAlertViewStyleDefault
- initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
- if (buttonIndex == [alert cancelButtonIndex])
- return;
-
- self.activeUser.keyID = nil;
- [self signOut];
-
- [TestFlight passCheckpoint:MPCheckpointChangeMP];
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointChangeMP
- attributes:nil];
- }
- cancelTitle:[PearlStrings get].commonButtonAbort
- otherTitles:[PearlStrings get].commonButtonContinue, nil];
-}
-
-#pragma mark - PearlConfigDelegate
-
-- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value {
-
- [self checkConfig];
-}
-
-#pragma mark - UIApplicationDelegate
-
-
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[[NSBundle mainBundle] mutableInfoDictionary] setObject:@"Master Password" forKey:@"CFBundleDisplayName"];
[[[NSBundle mainBundle] mutableLocalizedInfoDictionary] setObject:@"Master Password" forKey:@"CFBundleDisplayName"];
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
-//#ifndef DEBUG
- @try {
- NSString *testFlightToken = [self testFlightToken];
- if ([testFlightToken length]) {
- dbg(@"Initializing TestFlight");
- [TestFlight addCustomEnvironmentInformation:@"Anonymous" forKey:@"username"];
-#ifdef ADHOC
- [TestFlight setDeviceIdentifier:[(id)[UIDevice currentDevice] uniqueIdentifier]];
-#else
- [TestFlight setDeviceIdentifier:[PearlKeyChain deviceIdentifier]];
-#endif
- [TestFlight setOptions:[NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithBool:NO], @"logToConsole",
- [NSNumber numberWithBool:NO], @"logToSTDERR",
- nil]];
- [TestFlight takeOff:testFlightToken];
- [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
- PearlLogLevel level = PearlLogLevelInfo;
- if ([[MPiOSConfig get].sendDebugInfo boolValue])
- level = PearlLogLevelDebug;
-
- if (message.level >= level)
- TFLog(@"%@", message);
-
- return YES;
- }];
- [TestFlight passCheckpoint:MPCheckpointLaunched];
- }
- }
- @catch (id exception) {
- err(@"TestFlight: %@", exception);
- }
- @try {
- NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
- if ([crashlyticsAPIKey length]) {
- dbg(@"Initializing Crashlytics");
- //[Crashlytics sharedInstance].debugMode = YES;
- [[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
- [[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
- [Crashlytics startWithAPIKey:crashlyticsAPIKey afterDelay:0];
- [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
- PearlLogLevel level = PearlLogLevelInfo;
- if ([[MPiOSConfig get].sendDebugInfo boolValue])
- level = PearlLogLevelDebug;
-
- if (message.level >= level)
- CLSLog(@"%@", message);
-
- return YES;
- }];
- }
- }
- @catch (id exception) {
- err(@"Crashlytics: %@", exception);
- }
- @try {
- NSString *localyticsKey = [self localyticsKey];
- if ([localyticsKey length]) {
- dbg(@"Initializing Localytics");
- [[LocalyticsSession sharedLocalyticsSession] startSession:localyticsKey];
- [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
- if (message.level >= PearlLogLevelError)
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:@"Problem" attributes:
- [NSDictionary dictionaryWithObjectsAndKeys:
- [message levelDescription],
- @"level",
- message.message,
- @"message",
- nil]];
-
- return YES;
- }];
- }
- }
- @catch (id exception) {
- err(@"Localytics exception: %@", exception);
- }
-//#endif
- });
-
@try {
- NSString *apptentiveAPIKey = [self apptentiveAPIKey];
- if ([apptentiveAPIKey length]) {
- dbg(@"Initializing Apptentive");
+ NSString *testFlightToken = [self testFlightToken];
+ if ([testFlightToken length]) {
+ inf(@"Initializing TestFlight");
+ [TestFlight addCustomEnvironmentInformation:@"Anonymous" forKey:@"username"];
+#ifdef ADHOC
+ [TestFlight setDeviceIdentifier:[(id)[UIDevice currentDevice] uniqueIdentifier]];
+#else
+ [TestFlight setDeviceIdentifier:[PearlKeyChain deviceIdentifier]];
+#endif
+ [TestFlight setOptions:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:NO], @"logToConsole",
+ [NSNumber numberWithBool:NO], @"logToSTDERR",
+ nil]];
+ [TestFlight takeOff:testFlightToken];
+ [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
+ PearlLogLevel level = PearlLogLevelWarn;
+ if ([[MPiOSConfig get].sendInfo boolValue])
+ level = PearlLogLevelInfo;
- ATConnect *connection = [ATConnect sharedConnection];
- [connection setApiKey:apptentiveAPIKey];
- [connection setShouldTakeScreenshot:NO];
- [connection addAdditionalInfoToFeedback:[PearlInfoPlist get].CFBundleVersion withKey:@"CFBundleVersion"];
- [connection addAdditionalInfoToFeedback:[PearlKeyChain deviceIdentifier] withKey:@"deviceIdentifier"];
- [connection addAdditionalInfoToFeedback:@"Anonymous" withKey:@"username"];
+ if (message.level >= level)
+ TFLog(@"%@", message);
+
+ return YES;
+ }];
}
}
- @catch (NSException *exception) {
- err(@"Apptentive: %@", exception);
+ @catch (id exception) {
+ err(@"TestFlight: %@", exception);
+ }
+ @try {
+ NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
+ if ([crashlyticsAPIKey length]) {
+ inf(@"Initializing Crashlytics");
+ [Crashlytics sharedInstance].debugMode = YES;
+ [[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
+ [[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
+ [Crashlytics startWithAPIKey:crashlyticsAPIKey afterDelay:0];
+ [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
+ PearlLogLevel level = PearlLogLevelWarn;
+ if ([[MPiOSConfig get].sendInfo boolValue])
+ level = PearlLogLevelInfo;
+
+ if (message.level >= level)
+ CLSLog(@"%@", message);
+
+ return YES;
+ }];
+ }
+ }
+ @catch (id exception) {
+ err(@"Crashlytics: %@", exception);
+ }
+ @try {
+ NSString *localyticsKey = [self localyticsKey];
+ if ([localyticsKey length]) {
+ inf(@"Initializing Localytics");
+ [[LocalyticsSession sharedLocalyticsSession] startSession:localyticsKey];
+ [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
+ if (message.level >= PearlLogLevelWarn)
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:@"Problem" attributes:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [message levelDescription],
+ @"level",
+ message.message,
+ @"message",
+ nil]];
+
+ return YES;
+ }];
+ }
+ }
+ @catch (id exception) {
+ err(@"Localytics exception: %@", exception);
}
UIImage *navBarImage = [[UIImage imageNamed:@"ui_navbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
@@ -290,26 +157,27 @@
[[UIToolbar appearance] setBackgroundImage:toolBarImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
/*
-UIImage *minImage = [[UIImage imageNamed:@"slider-minimum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
-UIImage *maxImage = [[UIImage imageNamed:@"slider-maximum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
-UIImage *thumbImage = [UIImage imageNamed:@"slider-handle.png"];
+ UIImage *minImage = [[UIImage imageNamed:@"slider-minimum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
+ UIImage *maxImage = [[UIImage imageNamed:@"slider-maximum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
+ UIImage *thumbImage = [UIImage imageNamed:@"slider-handle.png"];
-[[UISlider appearance] setMaximumTrackImage:maxImage forState:UIControlStateNormal];
-[[UISlider appearance] setMinimumTrackImage:minImage forState:UIControlStateNormal];
-[[UISlider appearance] setThumbImage:thumbImage forState:UIControlStateNormal];
+ [[UISlider appearance] setMaximumTrackImage:maxImage forState:UIControlStateNormal];
+ [[UISlider appearance] setMinimumTrackImage:minImage forState:UIControlStateNormal];
+ [[UISlider appearance] setThumbImage:thumbImage forState:UIControlStateNormal];
-UIImage *segmentSelected = [[UIImage imageNamed:@"segcontrol_sel.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 4)];
-UIImage *segmentUnselected = [[UIImage imageNamed:@"segcontrol_uns.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
-UIImage *segmentSelectedUnselected = [UIImage imageNamed:@"segcontrol_sel-uns.png"];
-UIImage *segUnselectedSelected = [UIImage imageNamed:@"segcontrol_uns-sel.png"];
-UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.png"];
+ UIImage *segmentSelected = [[UIImage imageNamed:@"segcontrol_sel.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 4)];
+ UIImage *segmentUnselected = [[UIImage imageNamed:@"segcontrol_uns.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
+ UIImage *segmentSelectedUnselected = [UIImage imageNamed:@"segcontrol_sel-uns.png"];
+ UIImage *segUnselectedSelected = [UIImage imageNamed:@"segcontrol_uns-sel.png"];
+ UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.png"];
-[[UISegmentedControl appearance] setBackgroundImage:segmentUnselected forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
-[[UISegmentedControl appearance] setBackgroundImage:segmentSelected forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
+ [[UISegmentedControl appearance] setBackgroundImage:segmentUnselected forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
+ [[UISegmentedControl appearance] setBackgroundImage:segmentSelected forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
-[[UISegmentedControl appearance] setDividerImage:segmentUnselectedUnselected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
-[[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected forLeftSegmentState:UIControlStateSelected rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
-[[UISegmentedControl appearance] setDividerImage:segUnselectedSelected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateSelected barMetrics:UIBarMetricsDefault];*/
+ [[UISegmentedControl appearance] setDividerImage:segmentUnselectedUnselected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
+ [[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected forLeftSegmentState:UIControlStateSelected rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
+ [[UISegmentedControl appearance] setDividerImage:segUnselectedSelected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
+ */
[[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationSignedOut object:nil queue:nil
usingBlock:^(NSNotification *note) {
@@ -334,7 +202,13 @@ UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
- return [super application:application didFinishLaunchingWithOptions:launchOptions];
+ [super application:application didFinishLaunchingWithOptions:launchOptions];
+
+ inf(@"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier]);
+ [TestFlight passCheckpoint:MPCheckpointLaunched];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointLaunched];
+
+ return YES;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
@@ -407,6 +281,7 @@ UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.
- (void)applicationDidBecomeActive:(UIApplication *)application {
+ inf(@"Re-activated");
[[MPAppDelegate get] checkConfig];
if ([[MPiOSConfig get].showQuickStart boolValue])
@@ -447,6 +322,7 @@ UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.
- (void)applicationWillResignActive:(UIApplication *)application {
+ inf(@"Will deactivate");
[self saveContext];
if (![[MPiOSConfig get].rememberLogin boolValue])
@@ -455,6 +331,159 @@ UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.
[TestFlight passCheckpoint:MPCheckpointDeactivated];
}
+#pragma - mark Behavior
+
+- (void)checkConfig {
+
+ if ([[MPConfig get].iCloud boolValue] != [self.storeManager iCloudEnabled])
+ [self.storeManager useiCloudStore:[[MPConfig get].iCloud boolValue] alertUser:YES];
+ if ([[MPiOSConfig get].sendInfo boolValue]) {
+ if ([PearlLogger get].autoprintLevel > PearlLogLevelInfo)
+ [PearlLogger get].autoprintLevel = PearlLogLevelInfo;
+
+ [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"];
+ [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloud boolValue] forKey:@"iCloud"];
+ [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloudDecided boolValue] forKey:@"iCloudDecided"];
+ [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendInfo boolValue] forKey:@"sendInfo"];
+ [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"];
+ [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showQuickStart boolValue] forKey:@"showQuickStart"];
+ [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"];
+ [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"];
+ [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"];
+ [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
+ [[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
+
+ [TestFlight addCustomEnvironmentInformation:[[MPConfig get].rememberLogin boolValue]? @"YES": @"NO" forKey:@"rememberLogin"];
+ [TestFlight addCustomEnvironmentInformation:[[MPConfig get].iCloud boolValue]? @"YES": @"NO" forKey:@"iCloud"];
+ [TestFlight addCustomEnvironmentInformation:[[MPConfig get].iCloudDecided boolValue]? @"YES": @"NO" forKey:@"iCloudDecided"];
+ [TestFlight addCustomEnvironmentInformation:[[MPiOSConfig get].sendInfo boolValue]? @"YES": @"NO" forKey:@"sendInfo"];
+ [TestFlight addCustomEnvironmentInformation:[[MPiOSConfig get].helpHidden boolValue]? @"YES": @"NO" forKey:@"helpHidden"];
+ [TestFlight addCustomEnvironmentInformation:[[MPiOSConfig get].showQuickStart boolValue]? @"YES": @"NO" forKey:@"showQuickStart"];
+ [TestFlight addCustomEnvironmentInformation:[[PearlConfig get].firstRun boolValue]? @"YES": @"NO" forKey:@"firstRun"];
+ [TestFlight addCustomEnvironmentInformation:[[PearlConfig get].launchCount description] forKey:@"launchCount"];
+ [TestFlight addCustomEnvironmentInformation:[[PearlConfig get].askForReviews boolValue]? @"YES": @"NO" forKey:@"askForReviews"];
+ [TestFlight addCustomEnvironmentInformation:[[PearlConfig get].reviewAfterLaunches description] forKey:@"reviewAfterLaunches"];
+ [TestFlight addCustomEnvironmentInformation:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
+
+ [TestFlight passCheckpoint:MPCheckpointConfig];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointConfig attributes:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [[MPConfig get].rememberLogin boolValue]
+ ? @"YES": @"NO", @"rememberLogin",
+ [[MPConfig get].iCloud boolValue]? @"YES"
+ : @"NO", @"iCloud",
+ [[MPConfig get].iCloudDecided boolValue]
+ ? @"YES": @"NO", @"iCloudDecided",
+ [[MPiOSConfig get].sendInfo boolValue]
+ ? @"YES": @"NO", @"sendInfo",
+ [[MPiOSConfig get].helpHidden boolValue]
+ ? @"YES": @"NO", @"helpHidden",
+ [[MPiOSConfig get].showQuickStart boolValue]
+ ? @"YES": @"NO", @"showQuickStart",
+ [[PearlConfig get].firstRun boolValue]
+ ? @"YES": @"NO", @"firstRun",
+ [[PearlConfig get].launchCount description], @"launchCount",
+ [[PearlConfig get].askForReviews boolValue]
+ ? @"YES": @"NO", @"askForReviews",
+ [[PearlConfig get].reviewAfterLaunches description], @"reviewAfterLaunches",
+ [PearlConfig get].reviewedVersion, @"reviewedVersion",
+ nil]];
+ }
+}
+
+- (void)showGuide {
+
+ [self.navigationController performSegueWithIdentifier:@"MP_Guide" sender:self];
+
+ [TestFlight passCheckpoint:MPCheckpointShowGuide];
+}
+
+- (void)export {
+
+ [PearlAlert showNotice:
+ @"This will export all your site names.\n\n"
+ @"You can open the export with a text editor to get an overview of all your sites.\n\n"
+ @"The file also acts as a personal backup of your site list in case you don't sync with iCloud/iTunes."
+ tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
+ [PearlAlert showAlertWithTitle:@"Reveal Passwords?" message:
+ @"Would you like to make all your passwords visible in the export?\n\n"
+ @"A safe export will only include your stored passwords, in an encrypted manner, "
+ @"making the result safe from falling in the wrong hands.\n\n"
+ @"If all your passwords are shown and somebody else finds the export, "
+ @"they could gain access to all your sites!"
+ viewStyle:UIAlertViewStyleDefault initAlert:nil
+ tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
+ if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 0)
+ // Safe Export
+ [self exportShowPasswords:NO];
+ if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1)
+ // Safe Export
+ [self exportShowPasswords:YES];
+ } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Safe Export", @"Show Passwords", nil];
+ } otherTitles:nil];
+}
+
+- (void)exportShowPasswords:(BOOL)showPasswords {
+
+ NSString *exportedSites = [self exportSitesShowingPasswords:showPasswords];
+ NSString *message;
+ if (showPasswords)
+ message = PearlString(
+ @"Export of %@'s Master Password sites with passwords visible.\n"
+ @"REMINDER: Make sure nobody else sees this file! All passwords are visible!\n",
+ self.activeUser.name);
+ else
+ message = PearlString(
+ @"Backup of %@'s Master Password sites.\n",
+ self.activeUser.name);
+
+ NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
+ [exportDateFormatter setDateFormat:@"yyyy'-'MM'-'DD"];
+
+ MFMailComposeViewController *composer = [MFMailComposeViewController new];
+ [composer setMailComposeDelegate:self];
+ [composer setSubject:@"Master Password Export"];
+ [composer setMessageBody:message isHTML:NO];
+ [composer addAttachmentData:
+ [exportedSites dataUsingEncoding:NSUTF8StringEncoding] mimeType:@"text/plain"
+ fileName:PearlString(@"%@ (%@).mpsites",
+ self.activeUser.name,
+ [exportDateFormatter stringFromDate:[NSDate date]])];
+ [self.window.rootViewController presentModalViewController:composer animated:YES];
+}
+
+- (void)changeMP {
+
+ [PearlAlert showAlertWithTitle:@"Changing Master Password"
+ message:
+ @"If you continue, you'll be able to set a new master password.\n\n"
+ @"Changing your master password will cause all your generated passwords to change!\n"
+ @"Changing the master password back to the old one will cause your passwords to revert as well."
+ viewStyle:UIAlertViewStyleDefault
+ initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
+ if (buttonIndex == [alert cancelButtonIndex])
+ return;
+
+ inf(@"Unsetting master password for: %@.", self.activeUser.userID);
+ self.activeUser.keyID = nil;
+ [self forgetSavedKeyFor:self.activeUser];
+ [self signOut];
+
+ [TestFlight passCheckpoint:MPCheckpointChangeMP];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointChangeMP
+ attributes:nil];
+ }
+ cancelTitle:[PearlStrings get].commonButtonAbort
+ otherTitles:[PearlStrings get].commonButtonContinue, nil];
+}
+
+#pragma mark - PearlConfigDelegate
+
+- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value {
+
+ [self checkConfig];
+}
+
#pragma mark - MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller
@@ -566,25 +595,6 @@ UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.
}
-#pragma mark - Apptentive
-
-
-- (NSDictionary *)apptentiveInfo {
-
- static NSDictionary *apptentiveInfo = nil;
- if (apptentiveInfo == nil)
- apptentiveInfo = [[NSDictionary alloc] initWithContentsOfURL:
- [[NSBundle mainBundle] URLForResource:@"Apptentive" withExtension:@"plist"]];
-
- return apptentiveInfo;
-}
-
-- (NSString *)apptentiveAPIKey {
-
- return NSNullToNil([[self apptentiveInfo] valueForKeyPath:@"API Key"]);
-}
-
-
#pragma mark - Localytics
diff --git a/MasterPassword/iOS/MPGuideViewController.m b/MasterPassword/iOS/MPGuideViewController.m
index 653d6e85..6308ce6f 100644
--- a/MasterPassword/iOS/MPGuideViewController.m
+++ b/MasterPassword/iOS/MPGuideViewController.m
@@ -24,15 +24,22 @@
[self.scrollView autoSizeContent];
}
+- (void)viewWillAppear:(BOOL)animated {
+
+ inf(@"Guide will appear.");
+ [super viewWillAppear:animated];
+}
+
- (void)viewDidAppear:(BOOL)animated {
-
+
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:animated? UIStatusBarAnimationSlide: UIStatusBarAnimationNone];
-
+
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
+ inf(@"Guide will disappear.");
[super viewWillDisappear:animated];
[MPiOSConfig get].showQuickStart = [NSNumber numberWithBool:NO];
diff --git a/MasterPassword/iOS/MPMainViewController.h b/MasterPassword/iOS/MPMainViewController.h
index be690637..41c70f24 100644
--- a/MasterPassword/iOS/MPMainViewController.h
+++ b/MasterPassword/iOS/MPMainViewController.h
@@ -6,11 +6,12 @@
// Copyright (c) 2011 Lyndir. All rights reserved.
//
+#import
#import "MPTypeViewController.h"
#import "MPElementEntity.h"
#import "MPSearchDelegate.h"
-@interface MPMainViewController : UIViewController
+@interface MPMainViewController : UIViewController
@property (strong, nonatomic) MPElementEntity *activeElement;
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchResultsController;
diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m
index 72522cda..75b29ccf 100644
--- a/MasterPassword/iOS/MPMainViewController.m
+++ b/MasterPassword/iOS/MPMainViewController.m
@@ -10,7 +10,6 @@
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
-#import "ATConnect.h"
#import "LocalyticsSession.h"
@@ -66,8 +65,21 @@
((MPTypeViewController *)[segue destinationViewController]).delegate = self;
}
+- (void)viewDidLoad {
+
+ self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
+
+ self.contentField.font = [UIFont fontWithName:@"Exo-Black" size:self.contentField.font.pointSize];
+
+ self.alertBody.text = nil;
+ self.contentTipEditIcon.hidden = YES;
+
+ [super viewDidLoad];
+}
+
- (void)viewWillAppear:(BOOL)animated {
-
+
+ inf(@"Main will appear.");
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:animated? UIStatusBarAnimationSlide: UIStatusBarAnimationNone];
if (![MPAppDelegate get].activeUser)
@@ -83,7 +95,7 @@
[self setHelpHidden:[[MPiOSConfig get].helpHidden boolValue] animated:animated];
[self updateAnimated:animated];
-
+
[super viewWillAppear:animated];
}
@@ -110,16 +122,10 @@
[super viewDidAppear:animated];
}
-- (void)viewDidLoad {
+- (void)viewWillDisappear:(BOOL)animated {
- self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
-
- self.contentField.font = [UIFont fontWithName:@"Exo-Black" size:self.contentField.font.pointSize];
-
- self.alertBody.text = nil;
- self.contentTipEditIcon.hidden = YES;
-
- [super viewDidLoad];
+ inf(@"Main will disappear.");
+ [super viewWillDisappear:animated];
}
- (void)viewDidUnload {
@@ -280,6 +286,7 @@
if (!self.activeElement)
return;
+ inf(@"Copying password for: %@", self.activeElement.name);
[UIPasteboard generalPasteboard].string = [self.activeElement.content description];
[self showContentTip:@"Copied!" withIcon:nil];
@@ -303,14 +310,15 @@
@"You will then need to update your account's old password to this newly generated password.\n\n"
@"You can reset the counter by holding down on this button."
do:^{
+ inf(@"Incrementing password counter for: %@", self.activeElement.name);
++((MPElementGeneratedEntity *)self.activeElement).counter;
- }];
- [TestFlight passCheckpoint:MPCheckpointIncrementPasswordCounter];
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointIncrementPasswordCounter
- attributes:[NSDictionary dictionaryWithObjectsAndKeys:
- NSStringFromMPElementType(self.activeElement.type), @"type",
- nil]];
+ [TestFlight passCheckpoint:MPCheckpointIncrementPasswordCounter];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointIncrementPasswordCounter
+ attributes:[NSDictionary dictionaryWithObjectsAndKeys:
+ NSStringFromMPElementType(self.activeElement.type), @"type",
+ nil]];
+ }];
}
- (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender {
@@ -330,14 +338,15 @@
@"If you continue, the site's password will change back to its original value. "
@"You will then need to update your account's password back to this original value."
do:^{
+ inf(@"Resetting password counter for: %@", self.activeElement.name);
((MPElementGeneratedEntity *)self.activeElement).counter = 1;
- }];
- [TestFlight passCheckpoint:MPCheckpointResetPasswordCounter];
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointResetPasswordCounter
- attributes:[NSDictionary dictionaryWithObjectsAndKeys:
- NSStringFromMPElementType(self.activeElement.type), @"type",
- nil]];
+ [TestFlight passCheckpoint:MPCheckpointResetPasswordCounter];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointResetPasswordCounter
+ attributes:[NSDictionary dictionaryWithObjectsAndKeys:
+ NSStringFromMPElementType(self.activeElement.type), @"type",
+ nil]];
+ }];
}
- (void)changeElementWithWarning:(NSString *)warning do:(void (^)(void))task; {
@@ -376,13 +385,14 @@
if (self.activeElement.type & MPElementTypeClassStored) {
self.contentField.enabled = YES;
[self.contentField becomeFirstResponder];
- }
- [TestFlight passCheckpoint:MPCheckpointEditPassword];
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditPassword
- attributes:[NSDictionary dictionaryWithObjectsAndKeys:
- NSStringFromMPElementType(self.activeElement.type), @"type",
- nil]];
+ [TestFlight passCheckpoint:MPCheckpointEditPassword];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditPassword
+ attributes:[NSDictionary dictionaryWithObjectsAndKeys:
+ NSStringFromMPElementType(
+ self.activeElement.type), @"type",
+ nil]];
+ }
}
- (IBAction)closeAlert {
@@ -406,37 +416,88 @@
switch (buttonIndex - [sheet firstOtherButtonIndex]) {
case 0: {
+ inf(@"Action: Toggle Help");
[self toggleHelpAnimated:YES];
break;
}
case 1: {
+ inf(@"Action: FAQ");
[self setHelpChapter:@"faq"];
[self setHelpHidden:NO animated:YES];
break;
}
case 2: {
+ inf(@"Action: Guide");
[[MPAppDelegate get] showGuide];
break;
}
case 3: {
+ inf(@"Action: Preferences");
[self performSegueWithIdentifier:@"UserProfile" sender:self];
break;
}
#ifdef ADHOC
case 4: {
+ inf(@"Action: Feedback via TestFlight");
[TestFlight openFeedbackView];
break;
}
case 5:
#else
case 4: {
- ATConnect *connection = [ATConnect sharedConnection];
- [connection presentFeedbackControllerFromViewController:self];
+ inf(@"Action: Feedback via Mail");
+ if (![MFMailComposeViewController canSendMail])
+ [PearlAlert showAlertWithTitle:@"Feedback"
+ message:
+ @"We'd love to hear what you think!\n\n"
+ @"Please send any comments or reports to:\n"
+ @"masterpassword@lyndir.com"
+ viewStyle:UIAlertViewStyleDefault
+ initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay
+ otherTitles:nil];
+
+ else {
+ [PearlAlert showAlertWithTitle:@"Feedback"
+ message:
+ @"We'd love to hear what you think!\n\n"
+ @"If you're having trouble, it may help us if you can first reproduce the problem "
+ @"and then include log files in your message."
+ viewStyle:UIAlertViewStyleDefault
+ initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
+ MFMailComposeViewController *composer = [MFMailComposeViewController new];
+ [composer setMailComposeDelegate:self];
+ [composer setToRecipients:[NSArray arrayWithObject:@"Master Password Development "]];
+ [composer setSubject:PearlString(@"Feedback for Master Password [%@]", [[PearlKeyChain deviceIdentifier] stringByDeletingMatchesOf:@"-.*"])];
+ [composer setMessageBody:
+ PearlString(
+ @"\n\n\n"
+ @"--\n"
+ @"%@\n"
+ @"Master Password %@, build %@",
+ [MPAppDelegate get].activeUser.name,
+ [PearlInfoPlist get].CFBundleShortVersionString,
+ [PearlInfoPlist get].CFBundleVersion)
+ isHTML:NO];
+
+ if (buttonIndex_ == [alert_ firstOtherButtonIndex]) {
+ PearlLogLevel logLevel = [[MPiOSConfig get].sendInfo boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
+ [composer addAttachmentData:[[[PearlLogger get] formatMessagesWithLevel:logLevel] dataUsingEncoding:NSUTF8StringEncoding]
+ mimeType:@"text/plain"
+ fileName:PearlString(@"%@-%@.log",
+ [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]],
+ [PearlKeyChain deviceIdentifier])];
+ }
+
+ [self presentModalViewController:composer animated:YES];
+ }
+ cancelTitle:nil otherTitles:@"Include Logs", @"No Logs", nil];
+ }
break;
}
case 5:
#endif
{
+ inf(@"Action: Sign out");
[[MPAppDelegate get] signOut];
break;
}
@@ -448,6 +509,17 @@
[self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Preferences", @"Feedback", @"Sign Out", nil];
}
+- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result
+ error:(NSError *)error {
+
+ if (error)
+ err(@"Feedback composer error: %@, result: %d", error, result);
+ else
+ inf(@"Feedback composer result: %d", result);
+
+ [controller dismissViewControllerAnimated:YES completion:nil];
+}
+
- (MPElementType)selectedType {
return self.activeElement.type;
@@ -485,6 +557,8 @@
- (void)didSelectElement:(MPElementEntity *)element {
+ inf(@"Selected: %@", element.name);
+
[self closeAlert];
if (element) {
@@ -516,10 +590,10 @@
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationElementUsed object:self.activeElement];
[TestFlight passCheckpoint:PearlString(MPCheckpointUseType @"_%@", NSStringFromMPElementType(self.activeElement.type))];
- [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointUseType
- attributes:[NSDictionary dictionaryWithObjectsAndKeys:
- NSStringFromMPElementType(self.activeElement.type), @"type",
- nil]];
+ [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointUseType attributes:[NSDictionary dictionaryWithObjectsAndKeys:
+ NSStringFromMPElementType(
+ self.activeElement.type), @"type",
+ nil]];
}
[self updateAnimated:YES];
@@ -555,6 +629,7 @@
navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
+ inf(@"External link: %@", [request URL]);
[TestFlight passCheckpoint:MPCheckpointExternalLink];
[[UIApplication sharedApplication] openURL:[request URL]];
diff --git a/MasterPassword/iOS/MPPreferencesViewController.m b/MasterPassword/iOS/MPPreferencesViewController.m
index af906138..59b36e04 100644
--- a/MasterPassword/iOS/MPPreferencesViewController.m
+++ b/MasterPassword/iOS/MPPreferencesViewController.m
@@ -66,6 +66,7 @@
- (void)viewWillAppear:(BOOL)animated {
+ inf(@"Preferences will appear");
[self.avatarsView autoSizeContent];
[self.avatarsView enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
if (subview.tag && ((UIControl *)subview).selected) {
@@ -79,6 +80,12 @@
[super viewWillAppear:animated];
}
+- (void)viewWillDisappear:(BOOL)animated {
+
+ inf(@"Preferences will disappear");
+ [super viewWillDisappear:animated];
+}
+
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
diff --git a/MasterPassword/iOS/MPSearchDelegate.m b/MasterPassword/iOS/MPSearchDelegate.m
index ec87cd42..9bfe727b 100644
--- a/MasterPassword/iOS/MPSearchDelegate.m
+++ b/MasterPassword/iOS/MPSearchDelegate.m
@@ -110,6 +110,7 @@
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
+ dbg(@"Search ended with: %@", controller.searchBar.text);
controller.searchBar.prompt = nil;
controller.searchBar.searchResultsButtonSelected = NO;
}
@@ -345,6 +346,8 @@ forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete)
[self.fetchedResultsController.managedObjectContext performBlock:^{
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
+
+ inf(@"Deleting element: %@", element.name);
[self.fetchedResultsController.managedObjectContext deleteObject:element];
[TestFlight passCheckpoint:MPCheckpointDeleteElement];
diff --git a/MasterPassword/iOS/MPTypeViewController.m b/MasterPassword/iOS/MPTypeViewController.m
index 050a06e9..9c23831d 100644
--- a/MasterPassword/iOS/MPTypeViewController.m
+++ b/MasterPassword/iOS/MPTypeViewController.m
@@ -23,7 +23,10 @@
- (void)viewWillAppear:(BOOL)animated {
+ inf(@"Type selection will appear");
self.recommendedTipContainer.alpha = 0;
+
+ [super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
@@ -51,6 +54,12 @@
[super viewDidLoad];
}
+- (void)viewWillDisappear:(BOOL)animated {
+
+ inf(@"Type selection will disappear");
+ [super viewWillDisappear:animated];
+}
+
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
diff --git a/MasterPassword/iOS/MPUnlockViewController.m b/MasterPassword/iOS/MPUnlockViewController.m
index ce71b903..a3dfb33b 100644
--- a/MasterPassword/iOS/MPUnlockViewController.m
+++ b/MasterPassword/iOS/MPUnlockViewController.m
@@ -142,6 +142,7 @@
- (void)viewWillAppear:(BOOL)animated {
+ inf(@"Lock screen will appear");
self.selectedUser = nil;
[self updateUsers];
@@ -157,6 +158,7 @@
- (void)viewWillDisappear:(BOOL)animated {
+ inf(@"Lock screen will disappear");
[super viewWillDisappear:animated];
}
diff --git a/MasterPassword/iOS/MPiOSConfig.h b/MasterPassword/iOS/MPiOSConfig.h
index d087b2f1..38cf8f87 100644
--- a/MasterPassword/iOS/MPiOSConfig.h
+++ b/MasterPassword/iOS/MPiOSConfig.h
@@ -10,7 +10,7 @@
@interface MPiOSConfig : MPConfig
-@property (nonatomic, retain) NSNumber *sendDebugInfo;
+@property (nonatomic, retain) NSNumber *sendInfo;
@property (nonatomic, retain) NSNumber *helpHidden;
@property (nonatomic, retain) NSNumber *showQuickStart;
diff --git a/MasterPassword/iOS/MPiOSConfig.m b/MasterPassword/iOS/MPiOSConfig.m
index 1b95d59b..41093dbc 100644
--- a/MasterPassword/iOS/MPiOSConfig.m
+++ b/MasterPassword/iOS/MPiOSConfig.m
@@ -7,7 +7,7 @@
//
@implementation MPiOSConfig
-@dynamic sendDebugInfo, helpHidden, showQuickStart;
+@dynamic sendInfo, helpHidden, showQuickStart;
- (id)init {
@@ -15,7 +15,7 @@
return self;
[self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(sendDebugInfo)),
+ [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(sendInfo)),
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)),
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)),
@"510296984", NSStringFromSelector(@selector(iTunesID)),
diff --git a/MasterPassword/iOS/Settings.bundle/Root.plist b/MasterPassword/iOS/Settings.bundle/Root.plist
index 8cf3410a..d59f7c04 100644
--- a/MasterPassword/iOS/Settings.bundle/Root.plist
+++ b/MasterPassword/iOS/Settings.bundle/Root.plist
@@ -48,7 +48,7 @@
Title
Send Diagnostic Info
Key
- sendDebugInfo
+ sendInfo
DefaultValue