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