2
0

Compare commits

...

31 Commits

Author SHA1 Message Date
Maarten Billemont
52c87eaeca Keep sites sorted by name on export to ensure consistency. 2020-05-16 16:03:42 -04:00
Maarten Billemont
1dccdd0a3c Improvements to algorithm and counter steppers. 2020-05-16 15:40:59 -04:00
Maarten Billemont
eb8d10ed05 Use new UN notification API. 2020-05-14 10:32:04 -04:00
Maarten Billemont
d9e5f77bee Resolve export state generation. 2020-05-14 00:04:29 -04:00
Maarten Billemont
60f60d087e Embed views using embed segue instead of MPRootSegue. 2020-05-12 23:27:56 -04:00
Maarten Billemont
df97dec2fe Sentry user login & MPError arguments. 2020-05-12 10:54:15 -04:00
Maarten Billemont
3bac8d9e0a iPad fixes & log initial start-up crashes. 2020-05-11 21:28:27 -04:00
Maarten Billemont
3fa7e1e8a1 Ensure Countly does not use the IDFA. 2020-05-02 12:31:54 -04:00
Maarten Billemont
d1104e4028 Need icon 1x icon for iPad. 2020-04-30 09:32:17 -04:00
Maarten Billemont
e9f2a25c9c Project icons update. 2020-04-28 13:49:34 -04:00
Maarten Billemont
171a3f0978 Icon update. 2020-04-28 12:24:41 -04:00
Maarten Billemont
8cfb9a83c5 mpjson support on iOS. 2020-04-27 16:33:10 -04:00
Maarten Billemont
5717375e75 Improve language. 2020-04-27 16:33:01 -04:00
Maarten Billemont
cc2dca3bd0 Report missing support when trying to parse unknown format. 2020-04-27 16:32:30 -04:00
Maarten Billemont
7575924d80 Bump site links to macOS app. 2020-04-26 00:39:08 -04:00
Maarten Billemont
8bedcedfaf Enable support for internal actions from URLs. 2020-04-25 17:13:48 -04:00
Maarten Billemont
10b205c541 Open URLs in external browser. 2020-04-25 11:44:27 -04:00
Maarten Billemont
774f183ac0 Improved web support & trigger notification URLs. 2020-04-25 10:44:59 -04:00
Maarten Billemont
2279aacb5a Change the user's default type from the Mac UI. 2020-04-21 13:27:06 -04:00
Maarten Billemont
1bd654621c Fix persistence of sendInfoDecided.
Don't log it as an event, event logging is probably disabled at this
point anyway.
2020-04-21 13:26:12 -04:00
Maarten Billemont
c4f60e325d Harmonize consent flow on both platforms. 2020-04-21 11:33:31 -04:00
Maarten Billemont
d4de3afb72 Show internal reason for why import fails. 2020-04-20 17:09:38 -04:00
Maarten Billemont
694b5ea227 Make marshal error messages owned by the file.
Error message lifecycle was limited to the static mpw_str buffer, which
is far too limited and also dangerous.  Own the message by the
MPMarshalFile object, freed in mpw_marshal_file_free.
2020-04-20 17:07:35 -04:00
Maarten Billemont
66dd78797b We use bounds-checked interfaces, so need ext1. 2020-04-20 17:05:37 -04:00
Maarten Billemont
61d1660560 Consent tweaks.
Don't conditional Countly events on sendInfo, it already turns Countly
on/off.

Keep Sentry enabled for longer while turning on/off for sendInfo to
cache more errors.

Use sendInfoDecided event only for initial sendInfo prompt, can use
Countly's censent tools to view overall opt-in/opt-outs.
2020-04-19 17:40:28 -04:00
Maarten Billemont
c3568e4744 Enable minor & build numbers > 9. 2020-04-18 22:13:36 -04:00
Maarten Billemont
0c921d4318 Update fonts to stay consistent with system UIs. 2020-04-18 21:50:06 -04:00
Maarten Billemont
0178efaaf7 Ask for consent to sendInfo. 2020-04-18 21:48:55 -04:00
Maarten Billemont
14f919584b Project update. 2020-04-16 17:46:39 -04:00
Maarten Billemont
16f6c3c593 Update preference menu item state. 2020-04-16 17:13:23 -04:00
Maarten Billemont
63ca2ae83e Can lower deployment target to 10.10 2020-04-16 17:13:08 -04:00
67 changed files with 1149 additions and 747 deletions

3
.gitmodules vendored
View File

@@ -15,7 +15,6 @@
url = https://github.com/json-c/json-c.git
[submodule "public/site"]
path = public/site
url = https://github.com/Lyndir/MasterPassword.git
url = https://gitlab.com/MasterPassword/MasterPassword.git
branch = gh-pages
shallow = true
update = none

View File

@@ -49,7 +49,6 @@
93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPSiteCell.m */; };
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; };
93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */; };
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */; };
93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */; };
93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; };
93D39B8F90F58A5D158DDBA3 /* MPSitesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPSitesViewController.m */; };
@@ -77,11 +76,6 @@
DA0CC5951EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */; };
DA24EBAE19DAD08900FF010B /* tip_basic_black_top.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38941711E29700CF925C /* tip_basic_black_top.png */; };
DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38951711E29700CF925C /* tip_basic_black_top@2x.png */; };
DA24EBE819DAD6DE00FF010B /* Icon-320.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBE619DAD6DE00FF010B /* Icon-320.png */; };
DA24EBE919DAD6DE00FF010B /* Icon-64.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBE719DAD6DE00FF010B /* Icon-64.png */; };
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBA19DAD4D000FF010B /* Icon-Small.png */; };
DA24EBEB19DAD6EE00FF010B /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */; };
DA24EBEC19DAD6EE00FF010B /* Icon-Small@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */; };
DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */; };
DA250A18195665A100AC23F1 /* UITableView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */; };
DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */; };
@@ -155,6 +149,21 @@
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */; };
DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4E147E415C00F98B1E /* CoreData.framework */; };
DA5E0E5524589C9B0007FBA7 /* Icon-Small@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4624589C9A0007FBA7 /* Icon-Small@3x.png */; };
DA5E0E5624589C9B0007FBA7 /* Icon-Small-40.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4724589C9A0007FBA7 /* Icon-Small-40.png */; };
DA5E0E5724589C9B0007FBA7 /* Icon-Small-20.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4824589C9A0007FBA7 /* Icon-Small-20.png */; };
DA5E0E5824589C9B0007FBA7 /* Icon-76.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4924589C9A0007FBA7 /* Icon-76.png */; };
DA5E0E5924589C9B0007FBA7 /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4A24589C9A0007FBA7 /* Icon-Small@2x.png */; };
DA5E0E5A24589C9B0007FBA7 /* Icon-Small-40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4B24589C9A0007FBA7 /* Icon-Small-40@2x.png */; };
DA5E0E5B24589C9B0007FBA7 /* Icon-76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4C24589C9A0007FBA7 /* Icon-76@2x.png */; };
DA5E0E5C24589C9B0007FBA7 /* Icon-Small-20@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4D24589C9A0007FBA7 /* Icon-Small-20@3x.png */; };
DA5E0E5D24589C9B0007FBA7 /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4E24589C9A0007FBA7 /* Icon-Small.png */; };
DA5E0E5E24589C9B0007FBA7 /* Icon-83@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4F24589C9A0007FBA7 /* Icon-83@2x.png */; };
DA5E0E5F24589C9B0007FBA7 /* Icon-Small-40@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5024589C9A0007FBA7 /* Icon-Small-40@3x.png */; };
DA5E0E6024589C9B0007FBA7 /* Icon-60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5124589C9A0007FBA7 /* Icon-60@3x.png */; };
DA5E0E6124589C9B0007FBA7 /* iTunesArtwork@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5224589C9B0007FBA7 /* iTunesArtwork@2x.png */; };
DA5E0E6224589C9B0007FBA7 /* Icon-Small-20@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5324589C9B0007FBA7 /* Icon-Small-20@2x.png */; };
DA5E0E6324589C9B0007FBA7 /* Icon-60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5424589C9B0007FBA7 /* Icon-60@2x.png */; };
DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701B716406A4100B61001 /* Accounts.framework */; settings = {ATTRIBUTES = (Required, ); }; };
DA6701DE16406B7300B61001 /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701DD16406B7300B61001 /* Social.framework */; settings = {ATTRIBUTES = (Required, ); }; };
DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701DF16406BB400B61001 /* AdSupport.framework */; settings = {ATTRIBUTES = (Required, ); }; };
@@ -166,6 +175,7 @@
DA69540617D975D900BF294E /* icon_gears.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37841711E29500CF925C /* icon_gears.png */; };
DA69540717D975D900BF294E /* icon_gears@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37851711E29500CF925C /* icon_gears@2x.png */; };
DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */ = {isa = PBXBuildFile; fileRef = DACE2F6719BA6A2A0010F92E /* UIView+FontScale.m */; };
DA72E2302453B91700676D4F /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA72E22F2453B91700676D4F /* WebKit.framework */; };
DA73049D194E022700E72520 /* ui_spinner.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36511711E29400CF925C /* ui_spinner.png */; };
DA73049E194E022700E72520 /* ui_spinner@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36521711E29400CF925C /* ui_spinner@2x.png */; };
DA73049F194E022B00E72520 /* ui_textfield.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD365B1711E29400CF925C /* ui_textfield.png */; };
@@ -465,7 +475,6 @@
93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; };
93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadItems.h"; sourceTree = "<group>"; };
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = "<group>"; };
93D3924EE15017F8A12CB436 /* MPSitesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesViewController.m; sourceTree = "<group>"; };
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; };
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-types.c"; sourceTree = "<group>"; };
@@ -494,7 +503,6 @@
93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-algorithm.h"; sourceTree = "<group>"; };
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; };
93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRootSegue.m; sourceTree = "<group>"; };
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; };
93D39975CE5AEC99E3F086C7 /* MPSiteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteCell.h; sourceTree = "<group>"; };
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; };
@@ -573,19 +581,6 @@
DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; };
DA0CC58A1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
DA24EBB219DAD4D000FF010B /* Icon-60.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60.png"; sourceTree = "<group>"; };
DA24EBB319DAD4D000FF010B /* Icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@2x.png"; sourceTree = "<group>"; };
DA24EBB419DAD4D000FF010B /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = "<group>"; };
DA24EBB519DAD4D000FF010B /* Icon-76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-76.png"; sourceTree = "<group>"; };
DA24EBB619DAD4D000FF010B /* Icon-76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-76@2x.png"; sourceTree = "<group>"; };
DA24EBB719DAD4D000FF010B /* Icon-Small-40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40.png"; sourceTree = "<group>"; };
DA24EBB819DAD4D000FF010B /* Icon-Small-40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40@2x.png"; sourceTree = "<group>"; };
DA24EBB919DAD4D000FF010B /* Icon-Small-40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40@3x.png"; sourceTree = "<group>"; };
DA24EBBA19DAD4D000FF010B /* Icon-Small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small.png"; sourceTree = "<group>"; };
DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@2x.png"; sourceTree = "<group>"; };
DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@3x.png"; sourceTree = "<group>"; };
DA24EBBD19DAD4D000FF010B /* iTunesArtwork.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = iTunesArtwork.png; sourceTree = "<group>"; };
DA24EBBE19DAD4D000FF010B /* iTunesArtwork@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "iTunesArtwork@2x.png"; sourceTree = "<group>"; };
DA24EBBF19DAD4D000FF010B /* icon.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = icon.sketch; sourceTree = "<group>"; };
DA24EBC119DAD4D000FF010B /* 32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 32.png; sourceTree = "<group>"; };
DA24EBC219DAD4D000FF010B /* 320-480.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "320-480.png"; sourceTree = "<group>"; };
@@ -597,8 +592,6 @@
DA24EBC819DAD4D000FF010B /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@2x.png"; sourceTree = "<group>"; };
DA24EBC919DAD4D000FF010B /* background@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@3x.png"; sourceTree = "<group>"; };
DA24EBCA19DAD4D000FF010B /* launch.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = launch.sketch; sourceTree = "<group>"; };
DA24EBE619DAD6DE00FF010B /* Icon-320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-320.png"; sourceTree = "<group>"; };
DA24EBE719DAD6DE00FF010B /* Icon-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-64.png"; sourceTree = "<group>"; };
DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+PearlReloadItems.m"; sourceTree = "<group>"; };
DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadItems.h"; sourceTree = "<group>"; };
DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+PearlDequeue.m"; sourceTree = "<group>"; };
@@ -653,6 +646,21 @@
DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
DA5BFA4E147E415C00F98B1E /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
DA5E0E4624589C9A0007FBA7 /* Icon-Small@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@3x.png"; sourceTree = "<group>"; };
DA5E0E4724589C9A0007FBA7 /* Icon-Small-40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40.png"; sourceTree = "<group>"; };
DA5E0E4824589C9A0007FBA7 /* Icon-Small-20.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-20.png"; sourceTree = "<group>"; };
DA5E0E4924589C9A0007FBA7 /* Icon-76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-76.png"; sourceTree = "<group>"; };
DA5E0E4A24589C9A0007FBA7 /* Icon-Small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@2x.png"; sourceTree = "<group>"; };
DA5E0E4B24589C9A0007FBA7 /* Icon-Small-40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40@2x.png"; sourceTree = "<group>"; };
DA5E0E4C24589C9A0007FBA7 /* Icon-76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-76@2x.png"; sourceTree = "<group>"; };
DA5E0E4D24589C9A0007FBA7 /* Icon-Small-20@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-20@3x.png"; sourceTree = "<group>"; };
DA5E0E4E24589C9A0007FBA7 /* Icon-Small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small.png"; sourceTree = "<group>"; };
DA5E0E4F24589C9A0007FBA7 /* Icon-83@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-83@2x.png"; sourceTree = "<group>"; };
DA5E0E5024589C9A0007FBA7 /* Icon-Small-40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40@3x.png"; sourceTree = "<group>"; };
DA5E0E5124589C9A0007FBA7 /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = "<group>"; };
DA5E0E5224589C9B0007FBA7 /* iTunesArtwork@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "iTunesArtwork@2x.png"; sourceTree = "<group>"; };
DA5E0E5324589C9B0007FBA7 /* Icon-Small-20@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-20@2x.png"; sourceTree = "<group>"; };
DA5E0E5424589C9B0007FBA7 /* Icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@2x.png"; sourceTree = "<group>"; };
DA5E5C3C1723681B003798D8 /* Square-bottom.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Square-bottom.png"; path = "Dividers/Square-bottom.png"; sourceTree = "<group>"; };
DA6701B716406A4100B61001 /* Accounts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accounts.framework; path = System/Library/Frameworks/Accounts.framework; sourceTree = SDKROOT; };
DA6701DD16406B7300B61001 /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; };
@@ -662,6 +670,7 @@
DA67460B18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-ExtraBold.otf"; sourceTree = "<group>"; };
DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Bold.otf"; sourceTree = "<group>"; };
DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
DA72E22F2453B91700676D4F /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MasterPassword-Prefix.pch"; sourceTree = "<group>"; };
DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = "<group>"; };
DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = "<group>"; };
@@ -1597,6 +1606,7 @@
DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */,
DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */,
DA6701DE16406B7300B61001 /* Social.framework in Frameworks */,
DA72E2302453B91700676D4F /* WebKit.framework in Frameworks */,
DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */,
DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */,
DA672D3014F9413D004A189C /* libPearl.a in Frameworks */,
@@ -1685,21 +1695,21 @@
DA24EBB119DAD4D000FF010B /* icon */ = {
isa = PBXGroup;
children = (
DA24EBB219DAD4D000FF010B /* Icon-60.png */,
DA24EBB319DAD4D000FF010B /* Icon-60@2x.png */,
DA24EBB419DAD4D000FF010B /* Icon-60@3x.png */,
DA24EBE719DAD6DE00FF010B /* Icon-64.png */,
DA24EBB519DAD4D000FF010B /* Icon-76.png */,
DA24EBB619DAD4D000FF010B /* Icon-76@2x.png */,
DA24EBE619DAD6DE00FF010B /* Icon-320.png */,
DA24EBB719DAD4D000FF010B /* Icon-Small-40.png */,
DA24EBB819DAD4D000FF010B /* Icon-Small-40@2x.png */,
DA24EBB919DAD4D000FF010B /* Icon-Small-40@3x.png */,
DA24EBBA19DAD4D000FF010B /* Icon-Small.png */,
DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */,
DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */,
DA24EBBD19DAD4D000FF010B /* iTunesArtwork.png */,
DA24EBBE19DAD4D000FF010B /* iTunesArtwork@2x.png */,
DA5E0E5424589C9B0007FBA7 /* Icon-60@2x.png */,
DA5E0E5124589C9A0007FBA7 /* Icon-60@3x.png */,
DA5E0E4924589C9A0007FBA7 /* Icon-76.png */,
DA5E0E4C24589C9A0007FBA7 /* Icon-76@2x.png */,
DA5E0E4F24589C9A0007FBA7 /* Icon-83@2x.png */,
DA5E0E4824589C9A0007FBA7 /* Icon-Small-20.png */,
DA5E0E5324589C9B0007FBA7 /* Icon-Small-20@2x.png */,
DA5E0E4D24589C9A0007FBA7 /* Icon-Small-20@3x.png */,
DA5E0E4724589C9A0007FBA7 /* Icon-Small-40.png */,
DA5E0E4B24589C9A0007FBA7 /* Icon-Small-40@2x.png */,
DA5E0E5024589C9A0007FBA7 /* Icon-Small-40@3x.png */,
DA5E0E4E24589C9A0007FBA7 /* Icon-Small.png */,
DA5E0E4A24589C9A0007FBA7 /* Icon-Small@2x.png */,
DA5E0E4624589C9A0007FBA7 /* Icon-Small@3x.png */,
DA5E0E5224589C9B0007FBA7 /* iTunesArtwork@2x.png */,
);
path = icon;
sourceTree = "<group>";
@@ -1775,6 +1785,7 @@
DA5BFA47147E415C00F98B1E /* Frameworks */ = {
isa = PBXGroup;
children = (
DA72E22F2453B91700676D4F /* WebKit.framework */,
DAFC1F6F2439795000D1CD66 /* CoreServices.framework */,
DAB7AE5C1F3D752900C856B1 /* libjson-c.a */,
DA6701B716406A4100B61001 /* Accounts.framework */,
@@ -2770,8 +2781,6 @@
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */,
DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */,
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */,
93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */,
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */,
93D39A28369954D147E239BA /* MPSetupViewController.m */,
93D39975CE5AEC99E3F086C7 /* MPSiteCell.h */,
@@ -3286,6 +3295,7 @@
DA69540617D975D900BF294E /* icon_gears.png in Resources */,
DA67460D18DE7F0C00DFE240 /* Exo2.0-Thin.otf in Resources */,
DA4522451902355C008F650A /* icon_book@2x.png in Resources */,
DA5E0E5F24589C9B0007FBA7 /* Icon-Small-40@3x.png in Resources */,
DA32D04919D2F417004F3F0E /* thumb_fuel@2x.png in Resources */,
DAFC1F73243989FB00D1CD66 /* Launch Screen.storyboard in Resources */,
DABD39371711E29700CF925C /* avatar-0.png in Resources */,
@@ -3305,11 +3315,9 @@
DA67461018DE7F0C00DFE240 /* Exo2.0-Bold.otf in Resources */,
DABD39401711E29700CF925C /* avatar-13.png in Resources */,
DA32D07C19D7D784004F3F0E /* background.png in Resources */,
DA24EBE919DAD6DE00FF010B /* Icon-64.png in Resources */,
DABD39411711E29700CF925C /* avatar-13@2x.png in Resources */,
DABD39421711E29700CF925C /* avatar-14.png in Resources */,
DABD39431711E29700CF925C /* avatar-14@2x.png in Resources */,
DA24EBE819DAD6DE00FF010B /* Icon-320.png in Resources */,
DAA1764419D8B82B0044227B /* personal_pw.png in Resources */,
DABD39441711E29700CF925C /* avatar-15.png in Resources */,
DABD39451711E29700CF925C /* avatar-15@2x.png in Resources */,
@@ -3363,11 +3371,14 @@
DA32D00919CF5C55004F3F0E /* icon_question.png in Resources */,
DABD39561711E29700CF925C /* avatar-6@2x.png in Resources */,
DA32D07B19D7D784004F3F0E /* background@2x.png in Resources */,
DA5E0E6224589C9B0007FBA7 /* Icon-Small-20@2x.png in Resources */,
DABD39571711E29700CF925C /* avatar-7.png in Resources */,
DABD39581711E29700CF925C /* avatar-7@2x.png in Resources */,
DA5E0E5C24589C9B0007FBA7 /* Icon-Small-20@3x.png in Resources */,
DABD39591711E29700CF925C /* avatar-8.png in Resources */,
DA32D00A19CF5C55004F3F0E /* icon_question@2x.png in Resources */,
DAA1764019D8B82B0044227B /* site_new.png in Resources */,
DA5E0E5924589C9B0007FBA7 /* Icon-Small@2x.png in Resources */,
DABD395A1711E29700CF925C /* avatar-8@2x.png in Resources */,
DABD395B1711E29700CF925C /* avatar-9.png in Resources */,
DAA1765419D8B82B0044227B /* choose_type.png in Resources */,
@@ -3377,23 +3388,30 @@
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */,
DA32D04E19D2F59B004F3F0E /* meter_fuel@3x.png in Resources */,
DA25C5FE197DBF200046CDCF /* icon_thumbs-up.png in Resources */,
DA5E0E5624589C9B0007FBA7 /* Icon-Small-40.png in Resources */,
DABD39871711E29700CF925C /* SourceCodePro-Black.otf in Resources */,
DABD39881711E29700CF925C /* SourceCodePro-ExtraLight.otf in Resources */,
DABD39A01711E29700CF925C /* icon_action.png in Resources */,
DA5E0E5524589C9B0007FBA7 /* Icon-Small@3x.png in Resources */,
DABD39A11711E29700CF925C /* icon_action@2x.png in Resources */,
DABD39F21711E29700CF925C /* icon_cancel.png in Resources */,
DA25C5FB197CCAE00046CDCF /* icon_delete@2x.png in Resources */,
DA5E0E5724589C9B0007FBA7 /* Icon-Small-20.png in Resources */,
DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */,
DA29992F19C86F5700AF7DF1 /* thumb_generated_login@2x.png in Resources */,
DA73049F194E022B00E72520 /* ui_textfield.png in Resources */,
DABD39F31711E29700CF925C /* icon_cancel@2x.png in Resources */,
DA5E0E5B24589C9B0007FBA7 /* Icon-76@2x.png in Resources */,
DABD3A261711E29700CF925C /* icon_edit.png in Resources */,
DABD3A271711E29700CF925C /* icon_edit@2x.png in Resources */,
DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */,
DABD3A3A1711E29700CF925C /* icon_find.png in Resources */,
DA5E0E6324589C9B0007FBA7 /* Icon-60@2x.png in Resources */,
DABD3A3B1711E29700CF925C /* icon_find@2x.png in Resources */,
DAA1765319D8B82B0044227B /* choose_type@2x.png in Resources */,
DA5E0E6124589C9B0007FBA7 /* iTunesArtwork@2x.png in Resources */,
DABD3AA01711E29800CF925C /* icon_pause.png in Resources */,
DA5E0E5A24589C9B0007FBA7 /* Icon-Small-40@2x.png in Resources */,
DABD3AA11711E29800CF925C /* icon_pause@2x.png in Resources */,
DAA1764219D8B82B0044227B /* settings.png in Resources */,
DABD3AAA1711E29800CF925C /* icon_person.png in Resources */,
@@ -3418,7 +3436,6 @@
DABD3B951711E29800CF925C /* pull-down.png in Resources */,
DABD3B961711E29800CF925C /* pull-down@2x.png in Resources */,
DABD3B971711E29800CF925C /* pull-up.png in Resources */,
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */,
DA8495321A93049300B3053D /* icon_down@2x.png in Resources */,
DABD3B981711E29800CF925C /* pull-up@2x.png in Resources */,
DA7304A0194E022B00E72520 /* ui_textfield@2x.png in Resources */,
@@ -3427,8 +3444,8 @@
DA452249190628A1008F650A /* icon_wrench.png in Resources */,
DAA1764519D8B82B0044227B /* name_new@2x.png in Resources */,
DA45224819062899008F650A /* icon_settings@2x.png in Resources */,
DA5E0E5E24589C9B0007FBA7 /* Icon-83@2x.png in Resources */,
DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */,
DA24EBEB19DAD6EE00FF010B /* Icon-Small@2x.png in Resources */,
DAA1764B19D8B82B0044227B /* login_name@2x.png in Resources */,
DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */,
DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */,
@@ -3444,16 +3461,18 @@
DAA1764619D8B82B0044227B /* name_new.png in Resources */,
DA45224B190628B2008F650A /* icon_gear.png in Resources */,
DAAA1D4123CD145000F3DF56 /* Storyboard.storyboard in Resources */,
DA5E0E6024589C9B0007FBA7 /* Icon-60@3x.png in Resources */,
DA8495311A93049300B3053D /* icon_down.png in Resources */,
DA5E0E5D24589C9B0007FBA7 /* Icon-Small.png in Resources */,
DA25C5FF197DBF200046CDCF /* icon_thumbs-up@2x.png in Resources */,
DAE1EF2217E942DE00BC0086 /* Localizable.strings in Resources */,
DA5A09DF171A70E4005284AB /* play.png in Resources */,
DA24EBEC19DAD6EE00FF010B /* Icon-Small@3x.png in Resources */,
DA5A09E0171A70E4005284AB /* play@2x.png in Resources */,
DA5A09EA171BB0F7005284AB /* unlocked.png in Resources */,
DAA1762819D89B610044227B /* thumb_ios_integration.png in Resources */,
DA5A09EB171BB0F7005284AB /* unlocked@2x.png in Resources */,
DAA1764F19D8B82B0044227B /* counter@2x.png in Resources */,
DA5E0E5824589C9B0007FBA7 /* Icon-76.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3618,7 +3637,6 @@
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */,
DA46021D23D5E30B00398FF4 /* MPSecrets.m in Sources */,
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */,
93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */,
93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */,
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
@@ -3865,7 +3883,6 @@
SKIP_INSTALL = YES;
STRIP_INSTALLED_PRODUCT = NO;
STRIP_SWIFT_SYMBOLS = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
TARGETED_DEVICE_FAMILY = "1,2";
WARNING_CFLAGS = "-Wno-float-conversion";
};
@@ -3994,10 +4011,6 @@
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
);
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
@@ -4005,7 +4018,7 @@
);
OTHER_CFLAGS = (
"-DMPW_SODIUM=1",
"-DMPW_CPERCIVA=0",
"-DMPW_JSON=1",
);
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword;
PRODUCT_NAME = MasterPassword;
@@ -4013,9 +4026,6 @@
SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES;
STRIP_SWIFT_SYMBOLS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Source/iOS/MasterPassword-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
};
name = Debug;
};
@@ -4028,7 +4038,6 @@
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
CODE_SIGN_STYLE = Manual;
EXCLUDED_SOURCE_FILE_NAMES = libDCIntrospect.a;
GCC_C_LANGUAGE_STANDARD = c11;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
@@ -4038,10 +4047,6 @@
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
);
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
@@ -4049,7 +4054,7 @@
);
OTHER_CFLAGS = (
"-DMPW_SODIUM=1",
"-DMPW_CPERCIVA=0",
"-DMPW_JSON=1",
);
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword;
PRODUCT_NAME = MasterPassword;
@@ -4057,10 +4062,6 @@
SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES;
STRIP_SWIFT_SYMBOLS = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OBJC_BRIDGING_HEADER = "Source/iOS/MasterPassword-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 4.0;
};
name = Release;
};

View File

@@ -73,6 +73,13 @@
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Debug"

View File

@@ -3220,6 +3220,7 @@
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
OTHER_CFLAGS = (
"-DMPW_SODIUM=1",
"-DMPW_JSON=1",
@@ -3258,6 +3259,7 @@
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
OTHER_CFLAGS = (
"-DMPW_SODIUM=1",
"-DMPW_JSON=1",

View File

@@ -59,6 +59,17 @@
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Debug"

View File

@@ -59,6 +59,17 @@
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@@ -85,6 +85,13 @@
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@@ -60,6 +60,17 @@
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Debug"

View File

@@ -28,3 +28,14 @@ target 'MasterPassword-macOS' do
pod 'KCOrderedAccessorFix'
pod 'JRSwizzle'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == 'Countly-iOS' || target.name == 'Countly-macOS'
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'COUNTLY_EXCLUDE_IDFA=1'
end
end
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 843 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 879 KiB

After

Width:  |  Height:  |  Size: 879 KiB

View File

@@ -34,7 +34,7 @@ icons=(
# 57@1@iphone@:Icon.png
# 29@1@iphone@:Icon-Small.png
# iPad
# 76@1@ipad@7.0:Icon-76.png
76@1@ipad@7.0:Icon-76.png
152@2@ipad@7.0:Icon-76@2x.png
167@2@ipad@9.0:Icon-83@2x.png
20@1@ipad@7.0:Icon-Small-20.png

View File

@@ -46,9 +46,10 @@ esac
description=$(git describe --always --dirty --long --match "*-$platform*")
version=${description%-g*} build=${version##*-} version=${version%-$build}
version=${version//-$platform/} version=${version//-/.} commit=${description##*-g}
IFS=. read major minor build <<< "$version"
addPlistWithKey GITDescription string "$description"
setPlistWithKey CFBundleVersion "$(hr "${version%%.*}" 14).${version#*.}"
setPlistWithKey CFBundleVersion "$(hr "$major" 14)$(printf '%02d' "$minor")$(printf '%02d' "$build")"
setPlistWithKey CFBundleShortVersionString "$version"
setSettingWithTitle "Build" "$commit"

View File

@@ -81,7 +81,6 @@ static NSOperationQueue *_mpwQueue = nil;
}
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:operationBlock];
if ([operation respondsToSelector:@selector( qualityOfService )])
operation.qualityOfService = NSQualityOfServiceUserInitiated;
[_mpwQueue addOperations:@[ operation ] waitUntilFinished:YES];
}
@@ -622,7 +621,7 @@ static NSOperationQueue *_mpwQueue = nil;
- (NSString *)exportLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
if (!(site.type & MPSiteFeatureExportContent) || site.loginGenerated || ![site.loginName length])
if (site.loginGenerated || ![site.loginName length])
return nil;
__block NSData *state = nil;

View File

@@ -197,7 +197,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
forKey:transaction.payment.productIdentifier];
[queue finishTransaction:transaction];
if ([[MPConfig get].sendInfo boolValue]) {
SKProduct *product = self.products[transaction.payment.productIdentifier];
[attributes addEntriesFromDictionary:@{
@"id": product.productIdentifier,
@@ -208,7 +207,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
@"quantity": @(transaction.payment.quantity).description,
}];
[Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes];
}
break;
}
case SKPaymentTransactionStateRestored: {
@@ -224,7 +222,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
[queue finishTransaction:transaction];
if ([[MPConfig get].sendInfo boolValue]) {
SKProduct *product = self.products[transaction.payment.productIdentifier];
[Countly.sharedInstance recordEvent:@"purchase" segmentation:@{
@"id": product.productIdentifier,
@@ -235,7 +232,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
@"quantity": @(transaction.payment.quantity).description,
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
}];
}
break;
}
}

View File

@@ -173,13 +173,11 @@
else
dbg( @"Automatic login failed for user: %@", user.userID );
if ([[MPConfig get].sendInfo boolValue]) {
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
@"method" : password? @"Password": @"Automatic",
@"state" : @"failed",
@"algorithm": @(user.algorithm.version).description,
}];
}
return NO;
}
@@ -203,7 +201,7 @@
}
@try {
if ([[MPConfig get].sendInfo boolValue]) {
[SentrySDK setUser:[[SentryUser alloc] initWithUserId:user.userID]];
[Countly.sharedInstance userLoggedIn:user.userID];
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
@@ -212,7 +210,6 @@
@"algorithm": @(user.algorithm.version).description,
}];
}
}
@catch (id exception) {
err( @"While setting username: %@", exception );
}

View File

@@ -116,10 +116,8 @@ static MPAppDelegate_Shared *instance;
if (self.activeUserOID == activeUserOID || [self.activeUserOID isEqual:activeUserOID])
return;
if (self.key)
self.key = nil;
if ([[MPConfig get].sendInfo boolValue])
[SentrySDK setUser:nil];
[Countly.sharedInstance userLoggedOut];
self.activeUserOID = activeUserOID;

View File

@@ -39,8 +39,9 @@
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
result:(void ( ^ )(NSError *error))resultBlock;
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock;
- (NSString *)exportSitesFor:(MPUserEntity *)user
revealPasswords:(BOOL)revealPasswords
askExportPassword:(NSString *( ^ )(NSString *userName))askExportPassword
error:(__autoreleasing NSError **)error;
@end

View File

@@ -563,21 +563,20 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
// Read metadata for the import file.
MPMarshalledFile *file = mpw_marshal_read( NULL, importData.UTF8String );
MPMarshalledUser *importUser = nil;
@try {
if (!file)
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(MPMarshalErrorInternal),
NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
}]), @"While importing sites." );
if (file->error.type != MPMarshalSuccess) {
MPMarshalErrorType type = file->error.type;
mpw_marshal_file_free( &file );
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(type),
NSLocalizedDescriptionKey: @"Could not parse Master Password import data.",
@"type" : @(file->error.type),
NSLocalizedDescriptionKey: strf( @"Could not parse Master Password import data:\n%@", @(file->error.message) ),
}]), @"While importing sites." );
}
if (file->info->format == MPMarshalFormatNone) {
mpw_marshal_file_free( &file );
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(MPMarshalErrorFormat),
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
@@ -591,7 +590,6 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
importMasterPassword = askImportPassword( @(file->info->fullName) );
if (!importMasterPassword) {
inf( @"Import cancelled." );
mpw_marshal_file_free( &file );
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
}
@@ -600,13 +598,11 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame);
// Parse import data.
MPMarshalledUser *importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) );
@try {
importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) );
if (!importUser || file->error.type != MPMarshalSuccess)
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(file->error.type),
NSLocalizedDescriptionKey: @(file->error.message),
NSLocalizedDescriptionKey: strf( @"Could not authenticate Master Password import:\n%@", @(file->error.message) ),
}]), @"While importing sites." );
// Find an existing user to update.
@@ -708,31 +704,40 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
}
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock {
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *user = [self activeUserInContext:context];
- (NSString *)exportSitesFor:(MPUserEntity *)user
revealPasswords:(BOOL)revealPasswords
askExportPassword:(NSString *( ^ )(NSString *userName))askExportPassword
error:(__autoreleasing NSError **)error {
MPMarshalledUser *exportUser = NULL;
MPMarshalledFile *exportFile = NULL;
@try {
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", user.userID );
MPMarshalledUser *exportUser = mpw_marshal_user( user.name.UTF8String,
mpw_masterKeyProvider_str( askImportPassword( user.name ).UTF8String ), user.algorithm.version );
NSString *masterPassword = askExportPassword( user.name );
if (!masterPassword) {
inf( @"Export cancelled." );
return nil;
}
MPKey *key = [[MPKey alloc] initForFullName:user.name withMasterPassword:masterPassword];
exportUser = mpw_marshal_user( user.name.UTF8String,
mpw_masterKeyProvider_str( masterPassword.UTF8String ), user.algorithm.version );
exportUser->redacted = !revealPasswords;
exportUser->avatar = (unsigned int)user.avatar;
exportUser->keyID = mpw_strdup( [user.keyID encodeHex].UTF8String );
exportUser->defaultType = user.defaultType;
exportUser->lastUsed = (time_t)user.lastUsed.timeIntervalSince1970;
for (MPSiteEntity *site in user.sites) {
for (MPSiteEntity *site in [user.sites sortedArrayUsingDescriptors:@[
[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]
]]) {
MPCounterValue counter = MPCounterValueInitial;
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
counter = ((MPGeneratedSiteEntity *)site).counter;
MPMarshalledSite *exportSite = mpw_marshal_site( exportUser,
site.name.UTF8String, site.type, counter, site.algorithm.version );
exportSite->resultState = mpw_strdup( [site.algorithm exportPasswordForSite:site usingKey:self.key].UTF8String );
exportSite->loginState = mpw_strdup( [site.algorithm exportLoginForSite:site usingKey:self.key].UTF8String );
exportSite->loginType = site.loginGenerated? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
MPMarshalledSite *exportSite = mpw_marshal_site( exportUser, site.name.UTF8String, site.type, counter, site.algorithm.version );
exportSite->resultState = mpw_strdup( [site.algorithm exportPasswordForSite:site usingKey:key].UTF8String );
exportSite->loginState = mpw_strdup( [site.algorithm exportLoginForSite:site usingKey:key].UTF8String );
exportSite->loginType = site.loginGenerated || !exportSite->loginState? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
exportSite->url = mpw_strdup( site.url.UTF8String );
exportSite->uses = (unsigned int)site.uses;
exportSite->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
@@ -741,22 +746,26 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
mpw_marshal_question( exportSite, siteQuestion.keyword.UTF8String );
}
MPMarshalledFile *exportFile = NULL;
const char *export = mpw_marshal_write( MPMarshalFormatDefault, &exportFile, exportUser );
NSString *exportedUser = nil;
if (export && exportFile && exportFile->error.type == MPMarshalSuccess)
exportedUser = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
mpw_free_string( &export );
resultBlock( exportedUser, exportFile && exportFile->error.type == MPMarshalSuccess? nil:
if (error)
*error = exportFile && exportFile->error.type == MPMarshalSuccess? nil:
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
}] );
}];
return exportedUser;
}
@finally {
mpw_marshal_file_free( &exportFile );
mpw_marshal_user_free( &exportUser );
mpw_masterKeyProvider_free();
}];
}
}
@end

View File

@@ -21,11 +21,13 @@
@interface MPConfig : PearlConfig
@property(nonatomic, retain) NSNumber *sendInfo;
@property(nonatomic, retain) NSNumber *sendInfoDecided;
@property(nonatomic, retain) NSNumber *notificationsDecided;
@property(nonatomic, retain) NSNumber *rememberLogin;
@property(nonatomic, retain) NSNumber *hidePasswords;
@property(nonatomic, strong) NSNumber *siteAttacker;
@property(nonatomic, retain) NSNumber *checkInconsistency;
@property(nonatomic, strong) NSNumber *siteAttacker;
@end

View File

@@ -21,7 +21,7 @@
@implementation MPConfig
@dynamic sendInfo, rememberLogin, checkInconsistency, hidePasswords, siteAttacker;
@dynamic sendInfo, sendInfoDecided, notificationsDecided, rememberLogin, hidePasswords, siteAttacker, checkInconsistency;
- (id)init {
@@ -29,13 +29,16 @@
return nil;
[self.defaults registerDefaults:@{
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
NSStringFromSelector( @selector( sendInfo ) ) : @NO,
NSStringFromSelector( @selector( sendInfoDecided ) ) : @NO,
NSStringFromSelector( @selector( notificationsDecided ) ): @NO,
NSStringFromSelector( @selector( sendInfo ) ) : @YES,
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
NSStringFromSelector( @selector( checkInconsistency ) ) : @NO,
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
}];
self.delegate = [MPAppDelegate_Shared get];

View File

@@ -20,11 +20,19 @@
#import "base64.h"
// printf <secret> | openssl enc -[ed] -aes-128-cbc -a -A -K <appSecret> -iv 0
NSString *appSecret = @"";
NSString *appSalt = @"";
NSString *sentryDSN = @"";
NSString *countlyKey = @"";
NSString *countlySalt = @"";
#if TARGET_OS_IOS
NSString *appSecret = @"946a6b12e6e6e004cc35bad1ea11478c";
NSString *appSalt = @"uBcsbZeTB8TfSS7dDw4yUq6wMZD/2nREvR0mqzqsNXvv9guh+62hkt99ly6QcJ5n";
NSString *sentryDSN = @"tmVjdMN9DpZ+0EIrrvHi44hWfaBkwrlrxjBkdeau2rDk+zlvgSdAZkAvNj7m1V+5NUR7i8Y/NumNKOaYlWJvPynEMJ4ZBvPepSbivgVvmr8=";
NSString *countlyKey = @"mDnMZyxwoq4ENgYnGYTzW8wsyiJQlmNKxkRLj88/nrs0mzE+zVjs6Y5LAT3+AYBB";
NSString *countlySalt = @"2COFsZd+4FNAU6jvI/HUu297mkZALzRIyKv5mD3vs55BHXDowh62A7FursCYS+cG";
#elif TARGET_OS_MAC
NSString *appSecret = @"24fcbadccb5789b2a969c0c811f86702";
NSString *appSalt = @"0N1fzSanIOCb7OQ4hEshXSjwEPXAXMhPBKQJeEcYPor8FWz76IpdB8ZHa3Wyb7o9";
NSString *sentryDSN = @"2RbeS9wfzQEOKB9MG3EWLDe+N8iXYNtWc8tovMcBmhuMIeyAHYKqo5eclSEYyM6lA73Y7FFHqUyTLbEmOR6MAU2PtWAitLdxOZlq3VnbXjI=";
NSString *countlyKey = @"uiasXoQNtkPQHvpvNqEE5N/tw/F1Hnzm+4ViSJ38EMeoWGvDQPJ+Kt9zPhb8Qans";
NSString *countlySalt = @"/raQUNxKQdxXRR5VFmCDJdyyJE8f6SPrTO5Y4z0kJH+wCrjaZ1VvCq+JSmOsBkz2";
#endif
NSString *decrypt(NSString *secret) {

View File

@@ -41,7 +41,7 @@ __END_DECLS
\
if (__error && [[MPConfig get].sendInfo boolValue]) { \
SentryEvent *event = [[SentryEvent alloc] initWithLevel:kSentryLevelError]; \
event.message = strf(@"%@: %@", message_, [__error localizedDescription]); \
event.message = strf( message_ @": %@", ##__VA_ARGS__, [__error localizedDescription]); \
event.logger = @"MPError"; \
[SentrySDK captureEvent:event]; \
} \

View File

@@ -66,7 +66,7 @@
<action selector="exportSitesSecure:" target="494" id="LVH-es-imA"/>
</connections>
</menuItem>
<menuItem title="Your passwords are hidden." enabled="NO" id="ybY-P3-eao">
<menuItem title="Your passwords are not visible." enabled="NO" id="ybY-P3-eao">
<attributedString key="attributedTitle">
<fragment content="Your passwords are not visible.">
<attributes>
@@ -83,7 +83,7 @@
<action selector="exportSitesReveal:" target="494" id="1IW-VT-Oeu"/>
</connections>
</menuItem>
<menuItem title="Handy for backups - keep it in a safe location!" enabled="NO" id="cQu-oR-SUa">
<menuItem title="Keep this file secure or delete it when you're done with it!" enabled="NO" id="cQu-oR-SUa">
<attributedString key="attributedTitle">
<fragment content="Keep this file secure or delete it when you're done with it!">
<attributes>
@@ -210,7 +210,7 @@
</menuItem>
<menuItem title="Crash and usage information is anonymized and sent to development." enabled="NO" id="WfD-lX-C93">
<attributedString key="attributedTitle">
<fragment content="Save the password in your keychain so you don't need to enter it again.">
<fragment content="Crash and usage information is anonymized and sent to development.">
<attributes>
<font key="NSFont" size="11" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" firstLineHeadIndent="8"/>

View File

@@ -29,7 +29,6 @@
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
@implementation MPMacAppDelegate
#pragma clang diagnostic push
@@ -69,7 +68,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
@try {
// Sentry
[SentrySDK initWithOptions:@{
[SentrySDK startWithOptions:@{
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
#ifdef DEBUG
@"debug" : @(YES),
@@ -81,12 +80,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
@"debug" : @(NO),
@"environment" : @"Private",
#endif
@"enabled" : [MPMacConfig get].sendInfo,
@"enabled" : @([[MPMacConfig get].sendInfo boolValue] || ![[MPMacConfig get].sendInfoDecided boolValue]),
@"enableAutoSessionTracking": @(YES),
}];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
PearlLogLevel level = PearlLogLevelWarn;
if ([[MPConfig get].sendInfo boolValue])
if ([[MPMacConfig get].sendInfo boolValue])
level = PearlLogLevelDebug;
if (message.level >= level) {
@@ -145,7 +144,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
}
// Setup delegates and listeners.
[MPConfig get].delegate = self;
[MPMacConfig get].delegate = self;
__weak id weakSelf = self;
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
@@ -205,12 +204,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[NSApp activateIgnoringOtherApps:YES];
}
[self enableNotifications];
[self tryNotifications];
}
- (void)applicationWillResignActive:(NSNotification *)notification {
if (![[MPConfig get].rememberLogin boolValue])
if (![[MPMacConfig get].rememberLogin boolValue])
[self lock:nil];
}
@@ -231,7 +230,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return NSTerminateNow;
}
- (void)enableNotifications {
- (void)tryNotifications {
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
if (@available( macOS 10.14, * )) {
@@ -250,18 +249,19 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)askNotifications {
if ([[MPMacConfig get].notificationsDecided boolValue])
return;
PearlMainQueue( ^{
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
if (@available( macOS 10.14, * )) {
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
^(BOOL granted, NSError *error) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
[MPMacConfig get].notificationsDecided = @(YES);
}];
}
else {
[Countly.sharedInstance askForNotificationPermission];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
}
[MPMacConfig get].notificationsDecided = @(YES);
}
} );
}
@@ -409,11 +409,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)togglePreference:(id)sender {
if (sender == self.diagnosticsItem)
[MPConfig get].sendInfo = @(self.diagnosticsItem.state != NSOnState);
[MPMacConfig get].sendInfo = @(self.diagnosticsItem.state != NSOnState);
if (sender == self.hidePasswordsItem)
[MPConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
[MPMacConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
if (sender == self.rememberPasswordItem)
[MPConfig get].rememberLogin = @(self.rememberPasswordItem.state != NSOnState);
[MPMacConfig get].rememberLogin = @(self.rememberPasswordItem.state != NSOnState);
if (sender == self.openAtLoginItem)
[self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState];
if (sender == self.showFullScreenItem) {
@@ -546,15 +546,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return;
}
if (!self.key) {
NSAlert *alert = [NSAlert new];
alert.messageText = @"User Locked";
alert.informativeText = @"To export your sites, first unlock your user by opening Master Password.";
[alert runModal];
[self showPopup:nil];
return;
}
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
@@ -579,11 +570,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
if ([savePanel runModal] == NSFileHandlingPanelCancelButton)
return;
[self exportSitesRevealPasswords:revealPasswords
askExportPassword:^NSString *(NSString *userName) {
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSError *error = nil;
NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:
^NSString *(NSString *userName) {
return PearlMainQueueAwait( ^id {
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Import"];
[alert addButtonWithTitle:@"Export"];
[alert addButtonWithTitle:@"Cancel"];
alert.messageText = strf( @"Master Password For\n%@", userName );
alert.informativeText = @"Enter the current master password for this user.";
@@ -594,19 +587,20 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
else
return nil;
} );
} result:^(NSString *mpsites, NSError *error) {
if (!mpsites || error) {
} error:&error];
if (error)
PearlMainQueue( ^{
[[NSAlert alertWithError:MPError( error, @"Failed to export mpsites." )] runModal];
} );
if (!exportedUser)
return;
}
NSError *coordinateError = nil;
[[[NSFileCoordinator alloc] initWithFilePresenter:nil]
coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError byAccessor:^(NSURL *newURL) {
NSError *writeError = nil;
if (![mpsites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
if (![exportedUser writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
PearlMainQueue( ^{
[[NSAlert alertWithError:MPError( writeError, @"Could not write to the export file." )] runModal];
} );
@@ -723,10 +717,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
}
BOOL loginItemEnabled = [self loginItemEnabled];
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
self.showFullScreenItem.state = [[MPMacConfig get].fullScreen boolValue]? NSOnState: NSOffState;
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.rememberPasswordItem.state = [[MPMacConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.diagnosticsItem.state = [[MPMacConfig get].sendInfo boolValue]? NSOnState: NSOffState;
self.hidePasswordsItem.state = [[MPMacConfig get].hidePasswords boolValue]? NSOnState: NSOffState;
self.rememberPasswordItem.state = [[MPMacConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = activeUser.saveKey? NSOnState: NSOffState;
if (!activeUser) {
@@ -757,26 +754,29 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
PearlMainQueue( ^{
if (!key || [key isEqualToString:NSStringFromSelector( @selector( sendInfo ) )])
self.diagnosticsItem.state = [[MPConfig get].sendInfo boolValue]? NSOnState: NSOffState;
self.diagnosticsItem.state = [[MPMacConfig get].sendInfo boolValue]? NSOnState: NSOffState;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )])
self.hidePasswordsItem.state = [[MPConfig get].hidePasswords boolValue]? NSOnState: NSOffState;
self.hidePasswordsItem.state = [[MPMacConfig get].hidePasswords boolValue]? NSOnState: NSOffState;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )])
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.rememberPasswordItem.state = [[MPMacConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
} );
// Send info
NSArray *countlyFeatures = @[
CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
];
if ([[MPConfig get].sendInfo boolValue]) {
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
if ([[MPConfig get].sendInfo boolValue] || ![[MPConfig get].sendInfoDecided boolValue])
[Countly.sharedInstance giveConsentForFeature:CLYConsentSessions];
else
[Countly.sharedInstance cancelConsentForFeature:CLYConsentSessions];
if ([[MPMacConfig get].sendInfo boolValue]) {
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
[PearlLogger get].printLevel = PearlLogLevelInfo;
[SentrySDK.currentHub getClient].options.enabled = @YES;
[SentrySDK configureScope:^(SentryScope *scope) {
[scope setExtraValue:[MPConfig get].rememberLogin forKey:@"rememberLogin"];
[scope setExtraValue:[MPConfig get].sendInfo forKey:@"sendInfo"];
[scope setExtraValue:[MPMacConfig get].rememberLogin forKey:@"rememberLogin"];
[scope setExtraValue:[MPMacConfig get].sendInfo forKey:@"sendInfo"];
[scope setExtraValue:[MPMacConfig get].fullScreen forKey:@"fullScreen"];
[scope setExtraValue:[PearlConfig get].firstRun forKey:@"firstRun"];
[scope setExtraValue:[PearlConfig get].launchCount forKey:@"launchCount"];
@@ -787,10 +787,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[scope setExtraValue:@([PearlDeviceUtils isAppEncrypted]) forKey:@"encrypted"];
[scope setExtraValue:[PearlDeviceUtils platform] forKey:@"platform"];
}];
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
}
else {
[SentrySDK.currentHub getClient].options.enabled = @NO;
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
[SentrySDK.currentHub getClient].options.enabled = @NO;
}
}

View File

@@ -179,7 +179,8 @@
if (algorithmVersion == self.algorithm.version)
return;
[self willChangeValueForKey:@"outdated"];
self.algorithm = MPAlgorithmForVersion( algorithmVersion )?: self.algorithm;
self.algorithm =
MPAlgorithmForVersion( MIN( MPAlgorithmVersionLast, MAX( MPAlgorithmVersionFirst, algorithmVersion ) ) )?: self.algorithm;
[self didChangeValueForKey:@"outdated"];
if (self.entityOID)

View File

@@ -17,6 +17,8 @@
//==============================================================================
#import <QuartzCore/QuartzCore.h>
#import <Countly/Countly.h>
#import <UserNotifications/UserNotifications.h>
#import "MPSitesWindowController.h"
#import "MPMacAppDelegate.h"
#import "MPAppDelegate_Store.h"
@@ -42,13 +44,30 @@
prof_rewind( @"replaceFonts" );
PearlAddNotificationObserver( NSWindowDidBecomeKeyNotification, self.window, [NSOperationQueue mainQueue],
^(id host, NSNotification *note) {
(^(id host, NSNotification *note) {
prof_new( @"didBecomeKey" );
[self.window makeKeyAndOrderFront:nil];
prof_rewind( @"fadeIn" );
[self updateUser];
prof_finish( @"updateUser" );
} );
prof_rewind( @"updateUser" );
if (![[MPMacConfig get].sendInfoDecided boolValue]) {
NSAlert *alert = [NSAlert new];
alert.messageText = @"Welcome to Master Password!";
alert.informativeText = @"We want you to have a top-notch experience.\n"
@"Using diagnostics, we ensure the application keeps working as designed for you.\n"
@"\n"
@"We look out for application bugs, runtime issues, sudden crashes & usage counters.\n"
@"Needless to say, diagnostics are always scrubbed and personal details will never leave your device.";
[alert addButtonWithTitle:@"Thanks!"];
[alert addButtonWithTitle:@"Disable"];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
[MPMacConfig get].sendInfo = @(returnCode != NSAlertSecondButtonReturn);
[MPMacConfig get].sendInfoDecided = @(YES);
}];
}
prof_finish( @"sendInfoDecided" );
}) );
PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, [NSOperationQueue mainQueue],
^(id host, NSNotification *note) {
NSWindow *sheet = [self.window attachedSheet];
@@ -81,7 +100,16 @@
self.siteTable.superview.superview.layer.mask = self.siteGradient;
self.siteTable.controller = self;
prof_finish( @"ui" );
prof_rewind( @"ui" );
if (@available( macOS 10.14, * )) {
[[UNUserNotificationCenter currentNotificationCenter]
requestAuthorizationWithOptions:UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError *error) {
if (!granted)
err( @"Couldn't obtain notification authorization: %@", error );
}];
}
prof_finish( @"notifications" );
}
- (void)dealloc {
@@ -376,7 +404,52 @@
}];
}
- (IBAction)changeType:(id)sender {
- (IBAction)changeDefaultType:(id)sender {
MPSiteModel *site = self.selectedSite;
MPUserEntity *user = [MPMacAppDelegate get].activeUserForMainThread;
NSArray *types = [user.algorithm allTypes];
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
for (NSUInteger t = 0; t < [types count]; ++t) {
MPResultType type = (MPResultType)[types[t] unsignedIntegerValue];
NSString *title = [user.algorithm nameOfType:type];
if (type & MPResultTypeClassTemplate)
title = strf( @"%@ %@", [user.algorithm mpwTemplateForSiteNamed:site.name?: @"masterpassword.app" ofType:type
withCounter:site.counter?: MPCounterValueDefault
usingKey:[MPMacAppDelegate get].key], title );
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
cell.tag = type;
cell.state = type == site.type? NSOnState: NSOffState;
cell.title = title;
}
self.passwordTypesBox.title = strf( @"Choose a password type for new sites of %@:", user.name );
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Default Type"];
[alert setAccessoryView:self.passwordTypesBox];
[alert layout];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Save" button.
MPResultType type = (MPResultType)[self.passwordTypesMatrix.selectedCell tag];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[[MPMacAppDelegate get] activeUserInContext:context].defaultType = type;
[context saveToStore];
}];
break;
}
default:
break;
}
}];
}
- (IBAction)changeSiteType:(id)sender {
MPSiteModel *site = self.selectedSite;
NSArray *types = [site.algorithm allTypes];
@@ -487,13 +560,19 @@
[self copyContent:self.shiftPressed? selectedSite.loginName: selectedSite.content];
[NSApp hide:nil];
NSUserNotification *notification = [NSUserNotification new];
notification.title = @"Password Copied";
if (@available( macOS 10.14, * )) {
UNMutableNotificationContent *notification = [UNMutableNotificationContent new];
notification.title = self.shiftPressed? @"Login Copied": @"Password Copied";
if (selectedSite.loginName.length)
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
else
notification.subtitle = selectedSite.name;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:
[UNNotificationRequest requestWithIdentifier:selectedSite.name content:notification trigger:nil]
withCompletionHandler:^(NSError *error) {
dbg( @"notification: %@, completed w/errror: %@", notification, error );
}];
}
}
- (void)updateUser {

View File

@@ -31,20 +31,20 @@
<rect key="contentRect" x="0.0" y="0.0" width="640" height="577"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ" userLabel="Root">
<rect key="frame" x="0.0" y="0.0" width="640" height="557"/>
<rect key="frame" x="0.0" y="0.0" width="738" height="553"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView blendingMode="behindWindow" material="popover" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="eRe-Ef-AZx">
<rect key="frame" x="0.0" y="0.0" width="640" height="557"/>
<rect key="frame" x="0.0" y="0.0" width="738" height="553"/>
</visualEffectView>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="oSh-Ec-8Nf" userLabel="Progress Spinner">
<rect key="frame" x="312" y="521" width="16" height="16"/>
<rect key="frame" x="361" y="517" width="16" height="16"/>
</progressIndicator>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="NGk-Io-Buc" userLabel="Top Box">
<rect key="frame" x="20" y="381" width="600" height="132"/>
<rect key="frame" x="20" y="377" width="698" height="132"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ond-dT-x5d" userLabel="Site Password Label">
<rect key="frame" x="157" y="98" width="287" height="14"/>
<rect key="frame" x="206" y="98" width="287" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -72,7 +72,7 @@
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ia6-7b-dFr">
<rect key="frame" x="127" y="59" width="347" height="14"/>
<rect key="frame" x="176" y="59" width="347" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -108,13 +108,13 @@
</connections>
</textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OaQ-of-zmb" userLabel="Initial Help">
<rect key="frame" x="39" y="109" width="522" height="18"/>
<rect key="frame" x="113" y="109" width="473" height="17"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Master Password generates passwords for your sites (and other things)." id="YyD-hd-wi3">
<font key="font" size="16" name="HelveticaNeue"/>
<font key="font" metaFont="menu" size="14"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -136,18 +136,18 @@
</connections>
</textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sYt-eL-uwt" userLabel="Initial Tip">
<rect key="frame" x="-26" y="31" width="652" height="70"/>
<rect key="frame" x="-2" y="31" width="702" height="70"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="9c4-NI-NM0">
<font key="font" size="12" name="HelveticaNeue"/>
<string key="title"> When you create an account with a site, use Master Password to create a password for it.
For accounts you already have, change the password to that created by Master Password for it.
To get a password, enter the site's bare domain name in the "site name" field (eg. apple.com).
For your master password, think of a strong password (eg. a short sentence). Tell *nobody*.
It's OK to share your site passwords. They can be changed if necessary.</string>
<font key="font" metaFont="message" size="11"/>
<string key="title"> When you create an account on a site, open Master Password to create your account password.
⑵ Consider changing all your existing account passwords to the password Master Password creates for those sites.
To get the password for a site, just enter its domain name in the "site name" field (eg. "apple.com").
⑷ When chosing a master password, make it easy but long (eg. a short sentence).
⑸ Tell *nobody* your master password. It's OK to share your site passwords with people you trust: they can be changed if necessary.</string>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -169,7 +169,7 @@
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XUV-zU-Y9c" userLabel="Site Password">
<rect key="frame" x="-2" y="26" width="604" height="80"/>
<rect key="frame" x="-2" y="26" width="702" height="80"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
@@ -202,6 +202,7 @@
<constraint firstItem="XUV-zU-Y9c" firstAttribute="leading" secondItem="NGk-Io-Buc" secondAttribute="leading" id="6Kl-7u-r90"/>
<constraint firstAttribute="centerY" secondItem="XUV-zU-Y9c" secondAttribute="centerY" id="6Qf-5O-Cvk"/>
<constraint firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="7sl-qi-HY9"/>
<constraint firstItem="sYt-eL-uwt" firstAttribute="width" secondItem="NGk-Io-Buc" secondAttribute="width" id="Beb-mW-KDP"/>
<constraint firstItem="Ond-dT-x5d" firstAttribute="top" secondItem="NGk-Io-Buc" secondAttribute="top" constant="20" symbolic="YES" id="IX4-cd-VkJ"/>
<constraint firstItem="Ia6-7b-dFr" firstAttribute="centerY" secondItem="XUV-zU-Y9c" secondAttribute="centerY" id="KqM-uR-Obm"/>
<constraint firstItem="Ia6-7b-dFr" firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="NFQ-aw-8tm"/>
@@ -209,14 +210,13 @@
<constraint firstAttribute="trailing" secondItem="XUV-zU-Y9c" secondAttribute="trailing" id="TdB-QV-9JK"/>
<constraint firstItem="Ond-dT-x5d" firstAttribute="bottom" secondItem="XUV-zU-Y9c" secondAttribute="top" constant="8" symbolic="YES" id="UgV-J6-B5T"/>
<constraint firstItem="Ond-dT-x5d" firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="UhT-LQ-aZ8"/>
<constraint firstItem="sYt-eL-uwt" firstAttribute="leading" secondItem="NGk-Io-Buc" secondAttribute="leading" constant="-24" id="eaC-ow-ren"/>
<constraint firstItem="sYt-eL-uwt" firstAttribute="top" secondItem="OaQ-of-zmb" secondAttribute="bottom" constant="8" symbolic="YES" id="hjJ-f1-mFv"/>
<constraint firstItem="sYt-eL-uwt" firstAttribute="centerX" secondItem="OaQ-of-zmb" secondAttribute="centerX" id="mu2-se-Mtn"/>
<constraint firstAttribute="centerY" secondItem="sYt-eL-uwt" secondAttribute="centerY" id="zLS-QG-MKS"/>
</constraints>
</customView>
<scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="34" horizontalPageScroll="10" verticalLineScroll="34" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="Bme-XK-MMc" userLabel="Sites Table">
<rect key="frame" x="64" y="40" width="512" height="177"/>
<rect key="frame" x="113" y="40" width="512" height="177"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="e11-59-xSS">
<rect key="frame" x="0.0" y="0.0" width="512" height="177"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -251,7 +251,7 @@
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" lineBreakMode="truncatingTail" alignment="center" title="apple.com" id="o0g-Zv-pH4">
<font key="font" size="24" name="HelveticaNeue-Thin"/>
<font key="font" metaFont="systemUltraLight" size="24"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -310,7 +310,7 @@
</connections>
</scrollView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="nM8-O3-spM" customClass="MPGradientView">
<rect key="frame" x="0.0" y="0.0" width="640" height="212"/>
<rect key="frame" x="0.0" y="0.0" width="738" height="238"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="color" keyPath="startingColor">
<color key="value" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
@@ -324,7 +324,7 @@
</userDefinedRuntimeAttributes>
</customView>
<button translatesAutoresizingMaskIntoConstraints="NO" id="Aue-Zx-6Mf" userLabel="Settings Gear">
<rect key="frame" x="585" y="493" width="35" height="44"/>
<rect key="frame" x="683" y="489" width="35" height="44"/>
<shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
@@ -339,7 +339,7 @@
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gAU-xs-aae">
<rect key="frame" x="594" y="479" width="18" height="14"/>
<rect key="frame" x="692" y="475" width="18" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -358,7 +358,7 @@
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OnR-s6-d4P" userLabel="Site Name Label">
<rect key="frame" x="209" y="308" width="223" height="16"/>
<rect key="frame" x="258" y="306" width="223" height="16"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -370,7 +370,7 @@
</textFieldCell>
</textField>
<secureTextField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iGR-wo-ual" userLabel="Secure Master Password">
<rect key="frame" x="18" y="257" width="604" height="43"/>
<rect key="frame" x="18" y="255" width="702" height="43"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -405,7 +405,7 @@
</connections>
</secureTextField>
<textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="v80-wd-hUR" userLabel="Revealed Master Password">
<rect key="frame" x="18" y="257" width="604" height="43"/>
<rect key="frame" x="18" y="255" width="702" height="43"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -444,13 +444,13 @@
</connections>
</textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lW3-2z-cEa" userLabel="Master Password Tip">
<rect key="frame" x="195" y="237" width="251" height="12"/>
<rect key="frame" x="248" y="236" width="243" height="11"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Hold alt ⌥ to reveal tips and unmask ●●●●●●●●." id="4Ep-xX-Ky8">
<font key="font" size="11" name="HelveticaNeue"/>
<font key="font" metaFont="miniSystem"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -471,13 +471,13 @@
</connections>
</textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oy5-B7-hdN" userLabel="Revealed Master Password Tip">
<rect key="frame" x="195" y="237" width="250" height="12"/>
<rect key="frame" x="258" y="236" width="222" height="11"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Hold alt ⌥ to reveal tips and unmask passwords." id="bQ4-AN-S1A">
<font key="font" size="11" name="HelveticaNeue"/>
<font key="font" metaFont="miniSystem"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -499,7 +499,7 @@
</connections>
</textField>
<searchField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CnS-iI-dhr" userLabel="Site Name">
<rect key="frame" x="62" y="257" width="516" height="43"/>
<rect key="frame" x="111" y="255" width="516" height="43"/>
<constraints>
<constraint firstAttribute="width" constant="512" id="rW7-Vq-4Xy"/>
</constraints>
@@ -508,7 +508,7 @@
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<searchFieldCell key="cell" selectable="YES" editable="YES" focusRingType="none" alignment="center" placeholderString="Site Name" sendsSearchStringImmediately="YES" id="ppl-2c-1E9">
<font key="font" size="36" name="HelveticaNeue-Thin"/>
<font key="font" metaFont="system" size="36"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</searchFieldCell>
@@ -519,13 +519,13 @@
</connections>
</searchField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="npC-Kk-gUM" userLabel="Site Name Tip">
<rect key="frame" x="101" y="237" width="438" height="12"/>
<rect key="frame" x="172" y="236" width="395" height="11"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Type the name of your site (eg. apple.com), then hit enter ⏎ to create a password for it." id="QTI-cz-Onx">
<font key="font" size="11" name="HelveticaNeue"/>
<font key="font" metaFont="miniSystem"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -542,13 +542,13 @@
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rhm-sC-xFS" userLabel="Site Name Tip">
<rect key="frame" x="138" y="225" width="365" height="24"/>
<rect key="frame" x="206" y="225" width="326" height="22"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="n3W-XU-dya">
<font key="font" size="11" name="HelveticaNeue"/>
<font key="font" metaFont="miniSystem"/>
<string key="title">Hit enter ⏎ to copy the site's password, hold shift ⇧ for the login name.
Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@@ -568,7 +568,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pHt-gg-ZNX">
<rect key="frame" x="73" y="20" width="495" height="152"/>
<rect key="frame" x="122" y="20" width="495" height="178"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1Qo-iG-CQt">
<rect key="frame" x="0.0" y="-1" width="85" height="19"/>
@@ -591,10 +591,10 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</button>
<stackView distribution="fill" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DT0-RU-3LT">
<rect key="frame" x="93" y="0.0" width="177" height="152"/>
<rect key="frame" x="93" y="0.0" width="177" height="178"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uol-dE-I8H">
<rect key="frame" x="77" y="138" width="22" height="14"/>
<rect key="frame" x="77" y="164" width="22" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -631,7 +631,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="brI-fg-Kav">
<rect key="frame" x="40" y="111" width="96" height="19"/>
<rect key="frame" x="40" y="137" width="96" height="19"/>
<shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
@@ -659,6 +659,35 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</binding>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jl4-sS-xbm">
<rect key="frame" x="12" y="111" width="153" height="19"/>
<shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<buttonCell key="cell" type="recessed" title="Default Password Type" bezelStyle="recessed" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ah6-gK-Rm7" customClass="MPNoStateButtonCell">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="systemBold" size="12"/>
<string key="keyEquivalent">p</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
<connections>
<action selector="changeDefaultType:" target="-2" id="3Qg-xV-ewc"/>
<binding destination="-2" name="hidden2" keyPath="alternatePressed" previousBinding="2Mv-lM-iXB" id="6QO-NJ-Uyo">
<dictionary key="options">
<integer key="NSMultipleValuesPlaceholder" value="-1"/>
<integer key="NSNoSelectionPlaceholder" value="-1"/>
<integer key="NSNotApplicablePlaceholder" value="-1"/>
<integer key="NSNullPlaceholder" value="-1"/>
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="2Mv-lM-iXB">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="R46-fx-n14">
<rect key="frame" x="0.0" y="85" width="177" height="19"/>
<shadow key="shadow">
@@ -686,26 +715,26 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</button>
<stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Bgn-Ne-fQ7" userLabel="Version Container">
<rect key="frame" x="70" y="56" width="36" height="22"/>
<rect key="frame" x="71" y="56" width="34" height="22"/>
<subviews>
<stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mcq-qD-yte">
<rect key="frame" x="-3" y="-3" width="19" height="28"/>
<shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<stepperCell key="cell" continuous="YES" alignment="left" minValue="1" maxValue="1000" doubleValue="1" id="73y-03-zHt"/>
<stepperCell key="cell" continuous="YES" alignment="left" maxValue="4294967295" doubleValue="1" valueWraps="YES" id="73y-03-zHt"/>
<connections>
<binding destination="mcS-ik-b0n" name="value" keyPath="selection.algorithmVersion" id="GyA-hK-6cD"/>
</connections>
</stepper>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gyg-Fh-yn7">
<rect key="frame" x="19" y="7" width="19" height="15"/>
<rect key="frame" x="19" y="8" width="17" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="V1" id="Pjy-Fm-zwB">
<font key="font" size="12" name="HelveticaNeue-Medium"/>
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/>
</textFieldCell>
@@ -740,26 +769,26 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</stackView>
<stackView distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6II-KA-cNi" userLabel="Counter Container">
<rect key="frame" x="74" y="26" width="28" height="22"/>
<rect key="frame" x="75" y="26" width="27" height="22"/>
<subviews>
<stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XgA-Vl-CKh" userLabel="Counter Stepper">
<rect key="frame" x="-3" y="-3" width="19" height="28"/>
<shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<stepperCell key="cell" continuous="YES" alignment="left" minValue="1" maxValue="1000" doubleValue="1" id="ikF-n4-xiI"/>
<stepperCell key="cell" continuous="YES" alignment="left" minValue="1" maxValue="4294967295" doubleValue="1" valueWraps="YES" id="ikF-n4-xiI"/>
<connections>
<binding destination="mcS-ik-b0n" name="value" keyPath="selection.counter" id="qmm-6z-boy"/>
</connections>
</stepper>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NvO-kt-eZ2" userLabel="Counter Field">
<rect key="frame" x="19" y="4" width="11" height="15"/>
<rect key="frame" x="19" y="4" width="10" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="1" id="dhQ-bJ-rn3">
<font key="font" size="12" name="HelveticaNeue-Medium"/>
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/>
</textFieldCell>
@@ -805,7 +834,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
<connections>
<action selector="changeType:" target="-2" id="6Jj-7p-da9"/>
<action selector="changeSiteType:" target="-2" id="6Jj-7p-da9"/>
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="Hat-GU-hcQ">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
@@ -821,6 +850,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
@@ -829,6 +859,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="whJ-Bw-pr4">
@@ -886,7 +917,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</customSpacing>
</stackView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="luC-0j-BeV">
<rect key="frame" x="135" y="50" width="103" height="14"/>
<rect key="frame" x="185" y="50" width="103" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -923,7 +954,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gjx-bt-fKM">
<rect key="frame" x="134" y="80" width="100" height="14"/>
<rect key="frame" x="184" y="80" width="100" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -960,7 +991,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dbM-ja-dKO" userLabel="Version Tip">
<rect key="frame" x="88" y="106" width="332" height="14"/>
<rect key="frame" x="137" y="106" width="332" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -988,13 +1019,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ido-NQ-3MY">
<rect key="frame" x="104" y="4" width="22" height="12"/>
<rect key="frame" x="154" y="2" width="21" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘L" id="fUB-rF-7x8">
<font key="font" size="11" name="HelveticaNeue"/>
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -1016,7 +1047,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9b3-wy-KBb">
<rect key="frame" x="243" y="2" width="22" height="14"/>
<rect key="frame" x="292" y="2" width="22" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -1044,13 +1075,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qal-PP-YtO">
<rect key="frame" x="516" y="4" width="23" height="12"/>
<rect key="frame" x="565" y="2" width="23" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘D" id="PPC-be-w4E">
<font key="font" size="11" name="HelveticaNeue"/>
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -1072,13 +1103,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qLF-A6-ThX">
<rect key="frame" x="404" y="4" width="23" height="12"/>
<rect key="frame" x="453" y="2" width="22" height="14"/>
<shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘S" id="qh6-k2-MUr">
<font key="font" size="11" name="HelveticaNeue"/>
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>

View File

@@ -22,6 +22,8 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSBackgroundOnly</key>
<true/>
<key>LSMinimumSystemVersion</key>

View File

@@ -31,7 +31,6 @@
[super viewDidLoad];
self.mode = MPCombinedModeUserSelection;
[self performSegueWithIdentifier:@"users" sender:self];
}
- (void)viewWillAppear:(BOOL)animated {

View File

@@ -18,6 +18,7 @@
#import "MPNavigationController.h"
#import "MPWebViewController.h"
#import "MPiOSAppDelegate.h"
@implementation MPNavigationController
@@ -29,6 +30,16 @@
[self performSegueWithIdentifier:@"setup" sender:self];
}
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:@"web"] && [[(NSURL *)sender scheme] isEqualToString:@"masterpassword"]) {
[[MPiOSAppDelegate get] openURL:sender];
return;
}
[super performSegueWithIdentifier:identifier sender:sender];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"web"])

View File

@@ -33,13 +33,6 @@
self.dismissSegueByButton = [NSMutableDictionary dictionary];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self performSegueWithIdentifier:@"root" sender:self];
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return [self.childViewControllers lastObject];

View File

@@ -1,23 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#import <Foundation/Foundation.h>
@interface MPRootSegue : UIStoryboardSegue
@end

View File

@@ -1,35 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#import "MPRootSegue.h"
@implementation MPRootSegue
- (void)perform {
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
[sourceViewController addChildViewController:destinationViewController];
destinationViewController.view.frame = sourceViewController.view.bounds;
destinationViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[sourceViewController.view addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:sourceViewController];
[sourceViewController setNeedsStatusBarAppearanceUpdate];
}
@end

View File

@@ -263,6 +263,7 @@
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Delete %@?", site.name ) message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:sender];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete Site" style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull action) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
@@ -284,6 +285,7 @@
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Change Password Type" message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:sender];
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
MPResultType type = (MPResultType)[typeNumber unsignedIntegerValue];
NSString *typeName = [mainSite.algorithm nameOfType:type];
@@ -425,7 +427,7 @@
if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
return;
((MPGeneratedSiteEntity *)site).counter = 1;
((MPGeneratedSiteEntity *)site).counter = MPCounterValueInitial;
[context saveToStore];
[PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2];
@@ -444,6 +446,7 @@
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Create Site" message:
strf( @"Remember site named:\n%@", self.transientSite )
preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:sender];
[controller addAction:[UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *_Nonnull action) {
[[MPiOSAppDelegate get]

View File

@@ -43,7 +43,7 @@
UIView *sitesView = sitesVC.view;
sitesView.frame = combinedVC.view.bounds;
sitesView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[combinedVC.view insertSubview:sitesView belowSubview:combinedVC.usersVC.view];
[combinedVC.view insertSubview:sitesView belowSubview:combinedVC.usersVC.view.superview];
[sitesVC setActive:YES animated:self.animated completion:^(BOOL finished) {
if (!finished)

View File

@@ -33,9 +33,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
MPPasswordsBadNameTip = 1 << 0,
};
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate, SKStoreProductViewControllerDelegate>
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate>
@property(nonatomic, strong) SKStoreProductViewController *voltoViewController;
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property(nonatomic, strong) NSArray *fuzzyGroups;
@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
@@ -64,13 +63,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
[self.collectionView automaticallyAdjustInsetsForKeyboard];
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
if ([self.searchBar respondsToSelector:@selector( keyboardAppearance )])
self.searchBar.keyboardAppearance = UIKeyboardAppearanceDark;
else
[self.searchBar enumerateViews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
if ([subview isKindOfClass:[UITextField class]])
((UITextField *)subview).keyboardAppearance = UIKeyboardAppearanceDark;
} recurse:YES];
}
- (void)viewWillAppear:(BOOL)animated {
@@ -441,13 +434,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
} completion:completion];
}
#pragma mark - SKStoreProductViewControllerDelegate
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[viewController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - Actions
- (IBAction)dismissPopdown:(id)sender {
@@ -460,45 +446,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
- (IBAction)upgradeVolto:(UIButton *)sender {
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]) {
[[MPiOSAppDelegate get] exportSitesRevealPasswords:NO askExportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
message:@"Enter your master password to export the user."
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *action) { setResult( alert.textFields.firstObject.text ); }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:
^(UIAlertAction *action) { setResult( nil ); }]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} );
} result:^(NSString *exportedUser, NSError *error) {
if (!exportedUser || error) {
MPError( error, @"Failed to export user." );
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error"
message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
return;
}
NSURLComponents *components = [NSURLComponents new];
components.scheme = @"volto";
components.path = @"import";
components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
[UIApp openURL:components.URL];
}];
}
else if (self.voltoViewController)
[self presentViewController:self.voltoViewController animated:YES completion:nil];
[[MPiOSAppDelegate get] migrateFor:[MPiOSAppDelegate get].activeUserForMainThread];
}
#pragma mark - Private
@@ -511,23 +459,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
self.voltoMigrateAlert.visible = YES;
}
else {
self.voltoInstallAlert.visible = NO;
self.voltoInstallAlert.visible = [MPiOSAppDelegate get].voltoViewController != nil;
self.voltoMigrateAlert.visible = NO;
self.voltoViewController = [SKStoreProductViewController new];
self.voltoViewController.delegate = self;
[self.voltoViewController loadProductWithParameters:@{
SKStoreProductParameterCampaignToken : @"app-masterpassword.ios", /* Campaign: From MasterPassword iOS */
SKStoreProductParameterProviderToken : @153897, /* Provider: Maarten Billemont */
SKStoreProductParameterITunesItemIdentifier: @510296984, /* Application: MasterPassword iOS */
//SKStoreProductParameterITunesItemIdentifier: @1500430196, /* Application: Volto iOS */
} completionBlock:^(BOOL result, NSError *error) {
if (error)
err( @"Failed loading Volto product information: %@", error );
[UIView animateWithDuration:0.3f animations:^{
self.voltoInstallAlert.visible = result;
}];
}];
}
}

View File

@@ -231,13 +231,11 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
user.avatar = newUserAvatar;
user.name = newUserName;
if ([[MPConfig get].sendInfo boolValue]) {
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
@"algorithm": @(user.algorithm.version).description,
@"avatar" : @(user.avatar).description,
}];
}
}
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
usingMasterPassword:masterPassword];
@@ -434,6 +432,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
NSManagedObjectID *userID = user.permanentObjectID;
UIAlertController *controller = [UIAlertController alertControllerWithTitle:user.name message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:avatarCell];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
UIAlertController *controller_ = [UIAlertController alertControllerWithTitle:@"Deleting User" message:
@"The user and its sites will be deleted." preferredStyle:UIAlertControllerStyleAlert];

View File

@@ -17,10 +17,12 @@
//==============================================================================
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
@interface MPWebViewController : UIViewController<UIWebViewDelegate>
@interface MPWebViewController : UIViewController<WKNavigationDelegate>
@property(nonatomic) IBOutlet UIWebView *webView;
@property(nonatomic) IBOutlet WKWebView *webView;
@property(nonatomic) IBOutlet UINavigationBar *webNavigationBar;
@property(nonatomic) IBOutlet UINavigationItem *webNavigationItem;
@property(nonatomic) NSURL *initialURL;

View File

@@ -17,6 +17,7 @@
//==============================================================================
#import "MPWebViewController.h"
#import "MPiOSAppDelegate.h"
@implementation MPWebViewController
@@ -24,20 +25,30 @@
[super viewDidLoad];
[self.webView.scrollView insetOcclusion];
if (!self.initialURL)
self.initialURL = [NSURL URLWithString:@"https://masterpassword.app"];
self.webNavigationItem.title = self.initialURL.host;
// WKWebView can't be on the storyboard for iOS pre 11 due to an NSCoding bug.
[self.view insertSubview:self.webView = [WKWebView new] atIndex:0];
[self.webView setNavigationDelegate:self];
[self.webView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.webView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[self.webView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
[self.webView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
[self.webView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.initialURL]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
if (!self.initialURL)
self.initialURL = [NSURL URLWithString:@"https://masterpassword.app"];
self.webNavigationItem.title = self.initialURL.host;
self.webView.visible = NO;
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.initialURL]];
- (void)viewDidLayoutSubviews {
[self.webView.scrollView insetOcclusion];
[super viewDidLayoutSubviews];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
@@ -45,26 +56,24 @@
return UIStatusBarStyleLightContent;
}
#pragma mark - UIWebViewDelegate
#pragma mark - WKNavigationDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void ( ^ )(WKNavigationActionPolicy))decisionHandler {
if ([[request.URL absoluteString] rangeOfString:@"thanks.lhunath.com"].location != NSNotFound) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"tipped.thanks"];
if (![[NSUserDefaults standardUserDefaults] synchronize])
wrn( @"Couldn't synchronize thanks tip." );
if ([navigationAction.request.mainDocumentURL.scheme isEqualToString:@"masterpassword"]) {
[[MPiOSAppDelegate get] openURL:navigationAction.request.mainDocumentURL];
decisionHandler( WKNavigationActionPolicyCancel );
return;
}
if ([request.URL isEqual:request.mainDocumentURL]) {
self.webNavigationItem.title = request.URL.host;
decisionHandler( WKNavigationActionPolicyAllow );
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
self.webNavigationItem.title = webView.URL.host;
self.webNavigationItem.prompt = strl( @"Loading" );
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
UIActivityIndicatorView *activityView =
[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
@@ -72,19 +81,56 @@
[activityView startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[UIView animateWithDuration:0.3 animations:^{
self.webView.visible = YES;
if ([[webView.URL absoluteString] rangeOfString:@"thanks.lhunath.com"].location != NSNotFound) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"tipped.thanks"];
if (![[NSUserDefaults standardUserDefaults] synchronize])
wrn( @"Couldn't synchronize thanks tip." );
}
[self.webNavigationItem setLeftBarButtonItem:[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector( action: )]];
[webView evaluateJavaScript:@"document.title" completionHandler:^(id o, NSError *error) {
self.webNavigationItem.prompt = [o description];
}];
[self.webNavigationItem setLeftBarButtonItem:[webView canGoBack]? [[UIBarButtonItem alloc]
initWithTitle:@"⬅︎" style:UIBarButtonItemStylePlain target:webView action:@selector( goBack )]: nil];
self.webNavigationItem.prompt = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
}
#pragma mark - Actions
- (IBAction)action:(id)sender {
UIAlertController *controller = [UIAlertController new];
controller.title = self.webView.URL.host;
controller.message = self.webView.URL.absoluteString;
[controller addAction:[UIAlertAction actionWithTitle:@"Safari" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:self.webView.URL];
}]];
if ([UIApp canOpenURL:[NSURL URLWithString:@"firefox:"]]) {
[controller addAction:[UIAlertAction actionWithTitle:@"Firefox" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:[NSURL URLWithString:strf( @"firefox://open-url?url=%@",
[self.webView.URL.absoluteString stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet URLQueryAllowedCharacterSet]] )]];
}]];
}
if ([UIApp canOpenURL:[NSURL URLWithString:@"googlechrome:"]]) {
NSURL *url = [[NSURL alloc] initWithScheme:[self.webView.URL.scheme isEqualToString:@"http"]? @"googlechrome": @"googlechromes"
host:self.webView.URL.host path:self.webView.URL.path];
[controller addAction:[UIAlertAction actionWithTitle:@"Chrome" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:url];
}]];
}
if ([UIApp canOpenURL:[NSURL URLWithString:@"opera-http:"]]) {
NSURL *url = [[NSURL alloc] initWithScheme:[self.webView.URL.scheme isEqualToString:@"http"]? @"opera-http": @"opera-https"
host:self.webView.URL.host path:self.webView.URL.path];
[controller addAction:[UIAlertAction actionWithTitle:@"Opera" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:url];
}]];
}
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}
- (IBAction)done:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];

View File

@@ -17,15 +17,22 @@
//==============================================================================
#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>
#import "MPAppDelegate_Shared.h"
@interface MPiOSAppDelegate : MPAppDelegate_Shared
@interface MPiOSAppDelegate : MPAppDelegate_Shared <SKStoreProductViewControllerDelegate>
@property(nonatomic, strong) SKStoreProductViewController *voltoViewController;
- (void)openURL:(NSURL *)url;
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
- (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
- (void)showExportForVC:(UIViewController *)viewController;
- (void)migrateFor:(MPUserEntity *)user;
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset;
@end

View File

@@ -26,6 +26,20 @@
#import <Sentry/Sentry.h>
#import <Countly/Countly.h>
@interface CountlyPushNotifications
@end
@interface CountlyPushNotifications(MPNotifications)
@end
@implementation CountlyPushNotifications(MPNotifications)
- (void)openURL:(NSString *)URLString {
[[MPiOSAppDelegate get].navigationController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:URLString]];
}
@end
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
@property(nonatomic, strong) UIDocumentInteractionController *interactionController;
@@ -43,7 +57,7 @@
@try {
// Sentry
[SentrySDK initWithOptions:@{
[SentrySDK startWithOptions:@{
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
#ifdef DEBUG
@"debug" : @(YES),
@@ -55,7 +69,7 @@
@"debug" : @(NO),
@"environment" : @"Private",
#endif
@"enabled" : [MPiOSConfig get].sendInfo,
@"enabled" : @([[MPiOSConfig get].sendInfo boolValue] || ![[MPiOSConfig get].sendInfoDecided boolValue]),
@"enableAutoSessionTracking": @(YES),
}];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
@@ -172,11 +186,30 @@
}
} );
SKStoreProductViewController *migrateVC = [SKStoreProductViewController new];
[migrateVC loadProductWithParameters:@{
SKStoreProductParameterCampaignToken : @"app-masterpassword.ios", /* Campaign: From MasterPassword iOS */
SKStoreProductParameterProviderToken : @153897, /* Provider: Maarten Billemont */
SKStoreProductParameterITunesItemIdentifier: @510296984, /* Application: MasterPassword iOS */
//SKStoreProductParameterITunesItemIdentifier: @1500430196, /* Application: Volto iOS */
} completionBlock:^(BOOL result, NSError *error) {
if (error)
err( @"Failed loading Volto product information: %@", error );
if (result) {
self.voltoViewController = migrateVC;
self.voltoViewController.delegate = self;
} else {
self.voltoViewController = nil;
}
}];
PearlMainQueueOperation( ^{
[self.navigationController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:@"masterpassword://foo?bar=quux"]];
if ([[MPiOSConfig get].showSetup boolValue])
[self.navigationController performSegueWithIdentifier:@"setup" sender:self];
[self enableNotifications];
[self consentFeatures];
} );
}
@catch (id exception) {
@@ -193,6 +226,12 @@
if (!url)
return NO;
// masterpassword: URLs.
if ([url.scheme isEqualToString:@"masterpassword"]) {
[self openURL:url];
return YES;
}
// Arbitrary URL to mpsites data.
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
@@ -229,7 +268,43 @@
return YES;
}
- (void)enableNotifications {
- (void)consentFeatures {
if ([self askDiagnostics])
return;
[self tryNotifications];
}
- (BOOL)askDiagnostics {
if ([[MPiOSConfig get].sendInfoDecided boolValue])
return NO;
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Diagnostics" message:
@"We look for bugs, sudden crashes, runtime issues & statistics.\n\n"
@"Diagnostics are scrubbed and personal details will never leave your device."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Disable" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
[MPiOSConfig get].sendInfo = @(NO);
[MPiOSConfig get].sendInfoDecided = @(YES);
[self consentFeatures];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Engage" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[MPiOSConfig get].sendInfo = @(YES);
[MPiOSConfig get].sendInfoDecided = @(YES);
[self consentFeatures];
}]];
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController)
presentViewController:alert animated:YES completion:nil];
} );
return YES;
}
- (void)tryNotifications {
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
if (@available( iOS 12, * )) {
@@ -240,35 +315,36 @@
[self askNotifications];
}];
return;
}
else {
[self askNotifications];
}
}
- (void)askNotifications {
if ([[MPiOSConfig get].notificationsDecided boolValue])
return;
PearlMainQueue( ^{
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Coming Soon" message:
@"Master Password is rolling out a new modern personal security platform and we're excited to bring you along.\n\n"
@"Master Password is rolling out a brand new, updated version and we're excited to bring you along.\n\n"
@"When it's time, we'll send you a notification to help you make an effortless transition."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
if (@available( iOS 12, * )) {
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
^(BOOL granted, NSError *error) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
[MPiOSConfig get].notificationsDecided = @(YES);
}];
}
else {
[Countly.sharedInstance askForNotificationPermission];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
[MPiOSConfig get].notificationsDecided = @(YES);
}
}]];
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController)
presentViewController:alert animated:YES completion:nil];
}
} );
}
@@ -392,6 +468,42 @@
#pragma mark - Behavior
- (void)openURL:(NSURL *)url {
if ([url.scheme isEqualToString:@"masterpassword"]) {
if ([url.host isEqualToString:@"open-url"]) {
for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
if ([item.name isEqualToString:@"url"]) {
[UIApp openURL:[NSURL URLWithString:item.value]];
return;
}
}
else if ([url.host isEqualToString:@"show-url"]) {
for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
if ([item.name isEqualToString:@"url"]) {
[[MPiOSAppDelegate get].navigationController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:item.value]];
return;
}
}
else if ([url.host isEqualToString:@"migrate"]) {
for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
if ([item.name isEqualToString:@"fullName"]) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSFetchRequest
*fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", item.value];
NSArray *users = [context executeFetchRequest:fetchRequest error:nil];
[self migrateFor:users.firstObject];
}];
return;
}
[self migrateFor:nil];
return;
}
} else
[UIApp openURL:url];
}
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
if (![PearlEMail canSendMail]) {
@@ -486,7 +598,7 @@
@"Would you like to make all your passwords visible in the export file?\n\n"
@"A safe export will include all sites but make their passwords invisible.\n"
@"It is great as a backup and remains safe when fallen in the wrong hands."
preferredStyle:UIAlertControllerStyleActionSheet];
preferredStyle:UIAlertControllerStyleAlert];
[sheet addAction:[UIAlertAction actionWithTitle:@"Safe Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self showExportRevealPasswords:NO forVC:viewController];
}]];
@@ -512,35 +624,42 @@
return;
}
[self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSError *error = nil;
NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:
^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf(
@"Master Password For:\n%@", userName )
message:@"Enter your master password to export the user."
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
setResult( alert.textFields.firstObject.text );
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
setResult( nil );
}]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} );
} result:^(NSString *exportedUser, NSError *error) {
if (!exportedUser || error) {
MPError( error, @"Failed to export mpsites." );
} error:&error];
PearlMainQueue( ^{
if (error) {
MPError( error, @"Failed to export mpsites." );
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
return;
}
if (!exportedUser)
return;
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
@@ -548,7 +667,7 @@
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSString *message;
if (revealPasswords)
@@ -594,9 +713,81 @@
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
}];
}
- (void)migrateFor:(MPUserEntity *)user {
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]) {
if (!user) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
NSArray *users = [context executeFetchRequest:fetchRequest error:nil];
if (![users count])
return;
UIAlertController *usersSheet = [UIAlertController alertControllerWithTitle:@"Migrate User"
message:@"Choose a user to migrate out to Volto."
preferredStyle:UIAlertControllerStyleAlert];
[usersSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
for (MPUserEntity *user_ in users)
[usersSheet addAction:[UIAlertAction actionWithTitle:user_.name style:UIAlertActionStyleDefault handler:
^(UIAlertAction *action) { [self migrateFor:user_]; }]];
PearlMainQueue( ^{
[self.navigationController presentViewController:usersSheet animated:YES completion:nil];
} );
}];
return;
}
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSError *error = nil;
NSString *exportedUser = [[MPAppDelegate_Shared get] exportSitesFor:[MPUserEntity existingObjectWithID:user.objectID inContext:context]
revealPasswords:NO askExportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
message:@"Enter your master password to export the user."
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *action) { setResult( alert.textFields.firstObject.text ); }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:
^(UIAlertAction *action) { setResult( nil ); }]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} );
} error:&error];
PearlMainQueue( ^{
if (error) {
MPError( error, @"Failed to export user." );
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error"
message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
}
if (!exportedUser)
return;
NSURLComponents *components = [NSURLComponents new];
components.scheme = @"volto";
components.path = @"import";
components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
[UIApp openURL:components.URL];
} );
}];
}
else if (self.voltoViewController)
[self.navigationController presentViewController:self.voltoViewController animated:YES completion:nil];
}
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
PearlMainQueue( ^{
@@ -622,6 +813,13 @@
} );
}
#pragma mark - SKStoreProductViewControllerDelegate
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[viewController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - UIDocumentInteractionControllerDelegate
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
@@ -643,10 +841,13 @@
// Send info
NSArray *countlyFeatures = @[
CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
];
if ([[MPConfig get].sendInfo boolValue] || ![[MPConfig get].sendInfoDecided boolValue])
[Countly.sharedInstance giveConsentForFeature:CLYConsentSessions];
else
[Countly.sharedInstance cancelConsentForFeature:CLYConsentSessions];
if ([[MPConfig get].sendInfo boolValue]) {
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
[PearlLogger get].printLevel = PearlLogLevelInfo;
@@ -670,11 +871,13 @@
#else
[scope setExtraValue:@(NO) forKey:@"reviewedVersion"];
#endif
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
}];
}
else {
[SentrySDK.currentHub getClient].options.enabled = @NO;
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
[SentrySDK.currentHub getClient].options.enabled = @NO;
}
}

View File

@@ -11,9 +11,9 @@
<dict>
<key>CFBundleTypeIconFiles</key>
<array>
<string>Icon-Small</string>
<string>Icon-64.png</string>
<string>Icon-320.png</string>
<string>Icon-Small-40.png</string>
<string>Icon-Small-40@2x.png</string>
<string>Icon-Small-40@3x.png</string>
</array>
<key>CFBundleTypeName</key>
<string>Master Password sites</string>
@@ -21,6 +21,7 @@
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>com.lyndir.masterpassword.json</string>
<string>com.lyndir.masterpassword.sites</string>
</array>
</dict>
@@ -37,8 +38,30 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>[auto]</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLIconFile</key>
<string>Icon-Small-40@3x</string>
<key>CFBundleURLName</key>
<string>com.lyndir.masterpassword</string>
<key>CFBundleURLSchemes</key>
<array>
<string>masterpassword</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>[auto]</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>firefox</string>
<string>googlechrome</string>
<string>opera-http</string>
<string>volto</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
@@ -103,6 +126,34 @@
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Master Password JSON export</string>
<key>UTTypeIconFiles</key>
<array>
<string>Icon-Small-40.png</string>
<string>Icon-Small-40@2x.png</string>
<string>Icon-Small-40@3x.png</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.lyndir.masterpassword.json</string>
<key>UTTypeReferenceURL</key>
<string>https://gitlab.com/MasterPassword/MasterPassword/-/wikis/File-Format</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mpjson</string>
<string>mpsites.json</string>
</array>
<key>public.mime-type</key>
<string>text/plain</string>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
@@ -112,9 +163,9 @@
<string>Master Password sites</string>
<key>UTTypeIconFiles</key>
<array>
<string>Icon-Small.png</string>
<string>Icon-64.png</string>
<string>Icon-320.png</string>
<string>Icon-Small-40.png</string>
<string>Icon-Small-40@2x.png</string>
<string>Icon-Small-40@3x.png</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.lyndir.masterpassword.sites</string>
@@ -123,6 +174,7 @@
<key>public.filename-extension</key>
<array>
<string>mpsites</string>
<string>mpsites.txt</string>
</array>
</dict>
</dict>

View File

@@ -3,7 +3,7 @@
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<customFonts key="customFonts">
@@ -69,7 +69,7 @@
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPAvatarCell" id="Zab-uQ-uk9" customClass="MPAvatarCell">
<rect key="frame" x="80" y="114.5" width="215" height="667"/>
<rect key="frame" x="80" y="115" width="215" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="215" height="667"/>
@@ -472,17 +472,11 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<webView contentMode="scaleToFill" scalesPageToFit="YES" suppressesIncrementalRendering="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3aB-Hk-Fgd">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="delegate" destination="Sd5-eW-Cx2" id="ePX-qz-XXR"/>
</connections>
</webView>
<navigationBar contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="03x-KT-JQN">
<rect key="frame" x="0.0" y="0.0" width="414" height="90"/>
<items>
<navigationItem title="masterpassword.app" prompt="Loading" id="Wpf-6b-UJb">
<barButtonItem key="leftBarButtonItem" systemItem="action" id="d9P-Hd-rdw"/>
<barButtonItem key="rightBarButtonItem" systemItem="done" id="Tbg-c3-qOh">
<connections>
<action selector="done:" destination="Sd5-eW-Cx2" id="cuN-bV-cwl"/>
@@ -494,19 +488,14 @@
</subviews>
<color key="backgroundColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="3aB-Hk-Fgd" firstAttribute="top" secondItem="zXV-QC-CZm" secondAttribute="bottom" id="3Od-Xa-Ssc"/>
<constraint firstItem="03x-KT-JQN" firstAttribute="leading" secondItem="2Jz-8H-3nG" secondAttribute="leading" id="BAn-wU-k2A"/>
<constraint firstItem="03x-KT-JQN" firstAttribute="top" secondItem="zXV-QC-CZm" secondAttribute="bottom" id="JU6-uj-GA3"/>
<constraint firstAttribute="bottom" secondItem="3aB-Hk-Fgd" secondAttribute="bottom" id="S01-AA-E0a"/>
<constraint firstAttribute="trailing" secondItem="03x-KT-JQN" secondAttribute="trailing" id="sQs-u3-M3r"/>
<constraint firstItem="3aB-Hk-Fgd" firstAttribute="leading" secondItem="2Jz-8H-3nG" secondAttribute="leading" id="w1l-x3-qCi"/>
<constraint firstAttribute="trailing" secondItem="3aB-Hk-Fgd" secondAttribute="trailing" id="xdJ-WF-yw0"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="gmy-ym-z4I"/>
<connections>
<outlet property="webNavigationItem" destination="Wpf-6b-UJb" id="8gM-LU-pHk"/>
<outlet property="webView" destination="3aB-Hk-Fgd" id="Mie-rI-l4h"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="hNt-WS-DuE" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -542,12 +531,23 @@
<view key="view" contentMode="scaleToFill" id="c4P-nn-PjR">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qca-aL-Un8">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<connections>
<segue destination="Ac5-na-hOV" kind="embed" identifier="root" id="x4h-Hl-KXD"/>
</connections>
</containerView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="qca-aL-Un8" firstAttribute="top" secondItem="c4P-nn-PjR" secondAttribute="top" id="7ix-Oo-ELN"/>
<constraint firstAttribute="bottom" secondItem="qca-aL-Un8" secondAttribute="bottom" id="JRU-fk-cPG"/>
<constraint firstItem="qca-aL-Un8" firstAttribute="leading" secondItem="c4P-nn-PjR" secondAttribute="leading" id="OAO-XC-DQ1"/>
<constraint firstAttribute="trailing" secondItem="qca-aL-Un8" secondAttribute="trailing" id="R9n-bd-GVl"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="V6W-ql-3TD"/>
<connections>
<segue destination="Ac5-na-hOV" kind="custom" identifier="root" customClass="MPRootSegue" id="UKS-gd-oD2"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="fQY-fV-sIe" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
@@ -568,12 +568,25 @@
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="Lkg-xn-bce" userLabel="Background">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
</imageView>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="91I-wN-JQb">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
</userDefinedRuntimeAttributes>
<connections>
<segue destination="S8q-YF-Kt9" kind="embed" identifier="users" id="GzD-Zv-DzW"/>
</connections>
</containerView>
</subviews>
<constraints>
<constraint firstItem="91I-wN-JQb" firstAttribute="leading" secondItem="fkJ-D0-yue" secondAttribute="leading" id="E4w-Dw-Vip"/>
<constraint firstItem="Lkg-xn-bce" firstAttribute="top" secondItem="fkJ-D0-yue" secondAttribute="top" id="EIy-Cd-0vW"/>
<constraint firstItem="91I-wN-JQb" firstAttribute="top" secondItem="fkJ-D0-yue" secondAttribute="top" id="EeB-9X-PEK"/>
<constraint firstAttribute="trailing" secondItem="Lkg-xn-bce" secondAttribute="trailing" id="ROW-fK-z92"/>
<constraint firstItem="Lkg-xn-bce" firstAttribute="leading" secondItem="fkJ-D0-yue" secondAttribute="leading" id="UH5-Kk-taJ"/>
<constraint firstAttribute="bottom" secondItem="91I-wN-JQb" secondAttribute="bottom" id="mxv-9s-jxT"/>
<constraint firstAttribute="bottom" secondItem="Lkg-xn-bce" secondAttribute="bottom" id="txR-pf-v3l"/>
<constraint firstAttribute="trailing" secondItem="91I-wN-JQb" secondAttribute="trailing" id="uef-ig-RRl"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="MPa-zX-Kaq"/>
@@ -581,7 +594,6 @@
<connections>
<segue destination="osn-5H-SWW" kind="custom" identifier="emergency" customClass="MPOverlaySegue" id="gtX-Cx-AA2"/>
<segue destination="nkY-z6-8jd" kind="custom" identifier="passwords" customClass="MPSitesSegue" id="Ozp-YT-Utx"/>
<segue destination="S8q-YF-Kt9" kind="custom" identifier="users" customClass="MPRootSegue" id="StK-nr-nps"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="F33-Fe-Tb6" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -2324,7 +2336,7 @@ Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien.
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="OsH-Mq-gpM">
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="OsH-Mq-gpM">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
</imageView>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ol8-3I-X99">
@@ -2412,7 +2424,7 @@ However, it means that anyone who finds your device unlocked can do the same.</s
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="Hrg-vp-M2J">
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="Hrg-vp-M2J">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
</imageView>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fJc-xU-1xA">
@@ -3036,7 +3048,7 @@ Invested: 3.7 work hours</string>
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="rvb-7K-MHe">
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="rvb-7K-MHe">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
</imageView>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7A7-Yn-F6L">
@@ -3154,7 +3166,7 @@ Ut in geometria, prima si dederis, danda sunt omnia. Nonne igitur tibi videntur,
<attributes>
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="NSColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<font key="NSFont" metaFont="label" size="12"/>
<font key="NSFont" metaFont="controlContent"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
@@ -3268,7 +3280,7 @@ Ut in geometria, prima si dederis, danda sunt omnia. Nonne igitur tibi videntur,
</scene>
</scenes>
<inferredMetricsTieBreakers>
<segue reference="Ql4-wf-T8u"/>
<segue reference="k2G-nL-x3l"/>
<segue reference="GZk-I4-JyH"/>
</inferredMetricsTieBreakers>
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>

View File

@@ -176,6 +176,21 @@ MPMarshalledFile *mpw_marshal_file(
return file;
}
MPMarshalledFile *mpw_marshal_error(
MPMarshalledFile *file, MPMarshalErrorType type, const char *format, ...) {
file = mpw_marshal_file( file, NULL, NULL );
if (!file)
return NULL;
va_list args;
va_start( args, format );
file->error = (MPMarshalError){ type, mpw_strdup( mpw_vstr( format, args ) ) };
va_end( args );
return file;
}
void mpw_marshal_info_free(
MPMarshalledInfo **info) {
@@ -228,6 +243,7 @@ void mpw_marshal_file_free(
mpw_marshal_info_free( &(*file)->info );
mpw_marshal_data_free( &(*file)->data );
mpw_free_string( &(*file)->error.message );
mpw_free( file, sizeof( MPMarshalledFile ) );
}
@@ -539,7 +555,7 @@ static const char *mpw_marshal_write_flat(
const MPMarshalledData *data = file->data;
if (!data) {
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing data." };
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing data." );
return NULL;
}
@@ -583,9 +599,9 @@ static const char *mpw_marshal_write_flat(
}
if (!out)
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't encode JSON." };
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't encode JSON." );
else
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
mpw_marshal_error( file, MPMarshalSuccess, NULL );
return out;
}
@@ -637,7 +653,7 @@ static const char *mpw_marshal_write_json(
// Section: "export"
json_object *json_file = mpw_get_json_data( file->data );
if (!json_file) {
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't serialize export data." };
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't serialize export data." );
return NULL;
}
@@ -650,9 +666,9 @@ static const char *mpw_marshal_write_json(
json_object_put( json_file );
if (!out)
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't encode JSON." };
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't encode JSON." );
else
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
mpw_marshal_error( file, MPMarshalSuccess, NULL );
return out;
}
@@ -698,17 +714,17 @@ const char *mpw_marshal_write(
if (!file_)
mpw_marshal_file_free( &file );
else
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
return NULL;
}
if (!user->fullName || !strlen( user->fullName )) {
if (!file_)
mpw_marshal_file_free( &file );
else
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing full name." );
return NULL;
}
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
mpw_marshal_error( file, MPMarshalSuccess, NULL );
MPMasterKey masterKey = NULL;
if (user->masterKeyProvider)
@@ -749,7 +765,7 @@ const char *mpw_marshal_write(
if (!file_)
mpw_marshal_file_free( &file );
else
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
return NULL;
}
@@ -807,7 +823,7 @@ const char *mpw_marshal_write(
const char *out = NULL;
switch (outFormat) {
case MPMarshalFormatNone:
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
mpw_marshal_error( file, MPMarshalSuccess, NULL );
break;
case MPMarshalFormatFlat:
out = mpw_marshal_write_flat( file );
@@ -818,7 +834,7 @@ const char *mpw_marshal_write(
break;
#endif
default:
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
mpw_marshal_error( file, MPMarshalErrorFormat, "Unsupported output format: %u", outFormat );
break;
}
if (out && file->error.type == MPMarshalSuccess)
@@ -839,7 +855,7 @@ static void mpw_marshal_read_flat(
mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
if (!file->data) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
return;
}
@@ -891,10 +907,7 @@ static void mpw_marshal_read_flat(
const char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
const char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
if (!headerName || !headerValue) {
file->error = (MPMarshalError){
MPMarshalErrorStructure,
mpw_str( "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) )
};
mpw_marshal_error( file, MPMarshalErrorStructure, "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) );
mpw_free_strings( &headerName, &headerValue, NULL );
continue;
}
@@ -908,7 +921,7 @@ static void mpw_marshal_read_flat(
if (mpw_strcasecmp( headerName, "Algorithm" ) == OK) {
unsigned long value = strtoul( headerValue, NULL, 10 );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast)
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm version: %s", headerValue ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user algorithm version: %s", headerValue );
else
algorithm = (MPAlgorithmVersion)value;
}
@@ -923,7 +936,7 @@ static void mpw_marshal_read_flat(
if (mpw_strcasecmp( headerName, "Default Type" ) == OK) {
unsigned long value = strtoul( headerValue, NULL, 10 );
if (!mpw_type_short_name( (MPResultType)value ))
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %s", headerValue ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user default type: %s", headerValue );
else
defaultType = (MPResultType)value;
}
@@ -934,7 +947,7 @@ static void mpw_marshal_read_flat(
if (!headerEnded)
continue;
if (!fullName)
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" };
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing header: Full Name" );
if (positionInLine >= endOfLine)
continue;
@@ -973,7 +986,7 @@ static void mpw_marshal_read_flat(
break;
}
default: {
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) };
mpw_marshal_error( file, MPMarshalErrorFormat, "Unexpected import format: %u", format );
continue;
}
}
@@ -981,28 +994,24 @@ static void mpw_marshal_read_flat(
if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
MPResultType siteType = (MPResultType)strtoul( str_type, NULL, 10 );
if (!mpw_type_short_name( siteType )) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %s", siteName, str_type ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site type: %s: %s", siteName, str_type );
continue;
}
long long int value = strtoll( str_counter, NULL, 10 );
if (value < MPCounterValueFirst || value > MPCounterValueLast) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %s", siteName, str_counter ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site counter: %s: %s", siteName, str_counter );
continue;
}
MPCounterValue siteCounter = (MPCounterValue)value;
value = strtoll( str_algorithm, NULL, 0 );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
file->error = (MPMarshalError){
MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm )
};
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site algorithm: %s: %s", siteName, str_algorithm );
continue;
}
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
time_t siteLastUsed = mpw_timegm( str_lastUsed );
if (!siteLastUsed) {
file->error = (MPMarshalError){
MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed )
};
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site last used: %s: %s", siteName, str_lastUsed );
continue;
}
@@ -1020,11 +1029,9 @@ static void mpw_marshal_read_flat(
mpw_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL );
}
else {
file->error = (MPMarshalError){
MPMarshalErrorMissing,
mpw_str( "Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s",
str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginState, siteName )
};
mpw_marshal_error( file, MPMarshalErrorMissing,
"Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s",
str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginState, siteName );
continue;
}
@@ -1044,7 +1051,7 @@ static void mpw_marshal_read_json(
mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
if (!file->data) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." };
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
return;
}
@@ -1052,7 +1059,7 @@ static void mpw_marshal_read_json(
enum json_tokener_error json_error = json_tokener_success;
json_object *json_file = json_tokener_parse_verbose( in, &json_error );
if (!json_file || json_error != json_tokener_success) {
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Couldn't parse JSON: %s", json_tokener_error_desc( json_error ) ) };
mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't parse JSON: %s", json_tokener_error_desc( json_error ) );
return;
}
@@ -1074,9 +1081,9 @@ MPMarshalledFile *mpw_marshal_read(
if (!file)
return NULL;
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
mpw_marshal_error( file, MPMarshalSuccess, NULL );
if (!info) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate info." };
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate info." );
return file;
}
@@ -1090,6 +1097,8 @@ MPMarshalledFile *mpw_marshal_read(
info->format = MPMarshalFormatJSON;
#if MPW_JSON
mpw_marshal_read_json( file, in );
#else
mpw_marshal_error( file, MPMarshalErrorFormat, "JSON support is not enabled." );
#endif
}
}
@@ -1116,13 +1125,13 @@ MPMarshalledUser *mpw_marshal_auth(
if (!file)
return NULL;
file->error = (MPMarshalError){ MPMarshalSuccess, NULL };
mpw_marshal_error( file, MPMarshalSuccess, NULL );
if (!file->info) {
file->error = (MPMarshalError){ MPMarshalErrorMissing, "File wasn't parsed yet." };
mpw_marshal_error( file, MPMarshalErrorMissing, "File wasn't parsed yet." );
return NULL;
}
if (!file->data) {
file->error = (MPMarshalError){ MPMarshalErrorMissing, "No input data." };
mpw_marshal_error( file, MPMarshalErrorMissing, "No input data." );
return NULL;
}
@@ -1132,43 +1141,43 @@ MPMarshalledUser *mpw_marshal_auth(
MPAlgorithmVersion algorithm =
mpw_default_n( MPAlgorithmVersionCurrent, mpw_marshal_data_get_num( file->data, "user", "algorithm", NULL ) );
if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm: %u", algorithm ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user algorithm: %u", algorithm );
return NULL;
}
unsigned int avatar = mpw_default_n( 0U, mpw_marshal_data_get_num( file->data, "user", "avatar", NULL ) );
const char *fullName = mpw_marshal_data_get_str( file->data, "user", "full_name", NULL );
if (!fullName || !strlen( fullName )) {
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing value for full name." };
mpw_marshal_error( file, MPMarshalErrorMissing, "Missing value for full name." );
return NULL;
}
MPIdenticon identicon = mpw_identicon_encoded( mpw_marshal_data_get_str( file->data, "user", "identicon", NULL ) );
const char *keyID = mpw_marshal_data_get_str( file->data, "user", "key_id", NULL );
MPResultType defaultType = mpw_default_n( MPResultTypeDefault, mpw_marshal_data_get_num( file->data, "user", "default_type", NULL ) );
if (!mpw_type_short_name( defaultType )) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user default type: %u", defaultType );
return NULL;
}
const char *str_lastUsed = mpw_marshal_data_get_str( file->data, "user", "last_used", NULL );
time_t lastUsed = mpw_timegm( str_lastUsed );
if (!lastUsed) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user last used: %s", str_lastUsed );
return NULL;
}
MPMasterKey masterKey = NULL;
if (masterKeyProvider && !(masterKey = masterKeyProvider( algorithm, fullName ))) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
return NULL;
}
if (keyID && masterKey && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
file->error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master key doesn't match key ID." };
mpw_marshal_error( file, MPMarshalErrorMasterPassword, "Master key doesn't match key ID." );
mpw_free( &masterKey, MPMasterKeySize );
return NULL;
}
MPMarshalledUser *user = NULL;
if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." };
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate a new user." );
mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user );
return NULL;
@@ -1189,21 +1198,21 @@ MPMarshalledUser *mpw_marshal_auth(
algorithm = mpw_default_n( user->algorithm, mpw_marshal_data_get_num( siteData, "algorithm", NULL ) );
if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %u", siteName, algorithm ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site algorithm: %s: %u", siteName, algorithm );
mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user );
return NULL;
}
MPCounterValue siteCounter = mpw_default_n( MPCounterValueDefault, mpw_marshal_data_get_num( siteData, "counter", NULL ) );
if (siteCounter < MPCounterValueFirst || siteCounter > MPCounterValueLast) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, siteCounter ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site counter: %s: %d", siteName, siteCounter );
mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user );
return NULL;
}
MPResultType siteType = mpw_default_n( user->defaultType, mpw_marshal_data_get_num( siteData, "type", NULL ) );
if (!mpw_type_short_name( siteType )) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site type: %s: %u", siteName, siteType );
mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user );
return NULL;
@@ -1211,7 +1220,7 @@ MPMarshalledUser *mpw_marshal_auth(
const char *siteResultState = mpw_marshal_data_get_str( siteData, "password", NULL );
MPResultType siteLoginType = mpw_default_n( MPResultTypeTemplateName, mpw_marshal_data_get_num( siteData, "login_type", NULL ) );
if (!mpw_type_short_name( siteLoginType )) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site login type: %s: %u", siteName, siteLoginType ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site login type: %s: %u", siteName, siteLoginType );
mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user );
return NULL;
@@ -1221,7 +1230,7 @@ MPMarshalledUser *mpw_marshal_auth(
str_lastUsed = mpw_marshal_data_get_str( siteData, "last_used", NULL );
time_t siteLastUsed = mpw_timegm( str_lastUsed );
if (!siteLastUsed) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site last used: %s: %s", siteName, str_lastUsed );
mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user );
return NULL;
@@ -1231,7 +1240,7 @@ MPMarshalledUser *mpw_marshal_auth(
MPMarshalledSite *site = mpw_marshal_site( user, siteName, siteType, siteCounter, algorithm );
if (!site) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." };
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate a new site." );
mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user );
return NULL;
@@ -1245,10 +1254,7 @@ MPMarshalledUser *mpw_marshal_auth(
// Clear Text
mpw_free( &masterKey, MPMasterKeySize );
if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) {
file->error = (MPMarshalError){
MPMarshalErrorInternal,
"Couldn't derive master key."
};
mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user );
return NULL;

View File

@@ -246,6 +246,10 @@ MPMarshalledQuestion *mpw_marshal_question(
* @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */
MPMarshalledFile *mpw_marshal_file(
MPMarshalledFile *file, MPMarshalledInfo *info, MPMarshalledData *data);
/** Record a marshal error.
* @return The given file or new (allocated) if file is NULL; or NULL if the file couldn't be allocated. */
MPMarshalledFile *mpw_marshal_error(
MPMarshalledFile *file, MPMarshalErrorType type, const char *format, ...);
//// Disposing.

View File

@@ -25,6 +25,7 @@
#endif
MP_LIBS_BEGIN
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>