Re-organize the project into a better hierarchy.
This commit is contained in:
1
platform-ios/External/AttributedMarkdown
vendored
Submodule
1
platform-ios/External/AttributedMarkdown
vendored
Submodule
Submodule platform-ios/External/AttributedMarkdown added at d598fb4f5e
1
platform-ios/External/InAppSettingsKit
vendored
Submodule
1
platform-ios/External/InAppSettingsKit
vendored
Submodule
Submodule platform-ios/External/InAppSettingsKit added at b58b72563a
1
platform-ios/External/KCOrderedAccessorFix
vendored
Submodule
1
platform-ios/External/KCOrderedAccessorFix
vendored
Submodule
Submodule platform-ios/External/KCOrderedAccessorFix added at 1b8f8b79ad
1
platform-ios/External/Mac/Crashlytics.framework/Crashlytics
vendored
Symbolic link
1
platform-ios/External/Mac/Crashlytics.framework/Crashlytics
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Crashlytics
|
||||
1
platform-ios/External/Mac/Crashlytics.framework/Headers
vendored
Symbolic link
1
platform-ios/External/Mac/Crashlytics.framework/Headers
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Headers
|
||||
1
platform-ios/External/Mac/Crashlytics.framework/Modules
vendored
Symbolic link
1
platform-ios/External/Mac/Crashlytics.framework/Modules
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Modules
|
||||
1
platform-ios/External/Mac/Crashlytics.framework/Resources
vendored
Symbolic link
1
platform-ios/External/Mac/Crashlytics.framework/Resources
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Resources
|
||||
BIN
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Crashlytics
vendored
Executable file
BIN
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Crashlytics
vendored
Executable file
Binary file not shown.
31
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/ANSCompatibility.h
vendored
Normal file
31
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/ANSCompatibility.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// ANSCompatibility.h
|
||||
// AnswersKit
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
#define nonnull
|
||||
#define nullable
|
||||
#define _Nullable
|
||||
#define _Nonnull
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
#if __has_feature(objc_generics)
|
||||
#define ANS_GENERIC_NSARRAY(type) NSArray<type>
|
||||
#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
|
||||
#else
|
||||
#define ANS_GENERIC_NSARRAY(type) NSArray
|
||||
#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
|
||||
#endif
|
||||
210
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/Answers.h
vendored
Normal file
210
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/Answers.h
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// Answers.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ANSCompatibility.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* This class exposes the Answers Events API, allowing you to track key
|
||||
* user user actions and metrics in your app.
|
||||
*/
|
||||
@interface Answers : NSObject
|
||||
|
||||
/**
|
||||
* Log a Sign Up event to see users signing up for your app in real-time, understand how
|
||||
* many users are signing up with different methods and their success rate signing up.
|
||||
*
|
||||
* @param signUpMethodOrNil The method by which a user logged in, e.g. Twitter or Digits.
|
||||
* @param signUpSucceededOrNil The ultimate success or failure of the login
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logSignUpWithMethod:(nullable NSString *)signUpMethodOrNil
|
||||
success:(nullable NSNumber *)signUpSucceededOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Log In event to see users logging into your app in real-time, understand how many
|
||||
* users are logging in with different methods and their success rate logging into your app.
|
||||
*
|
||||
* @param loginMethodOrNil The method by which a user logged in, e.g. email, Twitter or Digits.
|
||||
* @param loginSucceededOrNil The ultimate success or failure of the login
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logLoginWithMethod:(nullable NSString *)loginMethodOrNil
|
||||
success:(nullable NSNumber *)loginSucceededOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Share event to see users sharing from your app in real-time, letting you
|
||||
* understand what content they're sharing from the type or genre down to the specific id.
|
||||
*
|
||||
* @param shareMethodOrNil The method by which a user shared, e.g. email, Twitter, SMS.
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logShareWithMethod:(nullable NSString *)shareMethodOrNil
|
||||
contentName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Invite Event to track how users are inviting other users into
|
||||
* your application.
|
||||
*
|
||||
* @param inviteMethodOrNil The method of invitation, e.g. GameCenter, Twitter, email.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logInviteWithMethod:(nullable NSString *)inviteMethodOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Purchase event to see your revenue in real-time, understand how many users are making purchases, see which
|
||||
* items are most popular, and track plenty of other important purchase-related metrics.
|
||||
*
|
||||
* @param itemPriceOrNil The purchased item's price.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param purchaseSucceededOrNil Was the purchase succesful or unsuccesful
|
||||
* @param itemNameOrNil The human-readable form of the item's name. Example:
|
||||
* @param itemTypeOrNil The type, or genre of the item. Example: Song
|
||||
* @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this purchase.
|
||||
*/
|
||||
+ (void)logPurchaseWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
success:(nullable NSNumber *)purchaseSucceededOrNil
|
||||
itemName:(nullable NSString *)itemNameOrNil
|
||||
itemType:(nullable NSString *)itemTypeOrNil
|
||||
itemId:(nullable NSString *)itemIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Level Start Event to track where users are in your game.
|
||||
*
|
||||
* @param levelNameOrNil The level name
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this level start event.
|
||||
*/
|
||||
+ (void)logLevelStart:(nullable NSString *)levelNameOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Level End event to track how users are completing levels in your game.
|
||||
*
|
||||
* @param levelNameOrNil The name of the level completed, E.G. "1" or "Training"
|
||||
* @param scoreOrNil The score the user completed the level with.
|
||||
* @param levelCompletedSuccesfullyOrNil A boolean representing whether or not the level was completed succesfully.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logLevelEnd:(nullable NSString *)levelNameOrNil
|
||||
score:(nullable NSNumber *)scoreOrNil
|
||||
success:(nullable NSNumber *)levelCompletedSuccesfullyOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Add to Cart event to see users adding items to a shopping cart in real-time, understand how
|
||||
* many users start the purchase flow, see which items are most popular, and track plenty of other important
|
||||
* purchase-related metrics.
|
||||
*
|
||||
* @param itemPriceOrNil The purchased item's price.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param itemNameOrNil The human-readable form of the item's name. Example:
|
||||
* @param itemTypeOrNil The type, or genre of the item. Example: Song
|
||||
* @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logAddToCartWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
itemName:(nullable NSString *)itemNameOrNil
|
||||
itemType:(nullable NSString *)itemTypeOrNil
|
||||
itemId:(nullable NSString *)itemIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Start Checkout event to see users moving through the purchase funnel in real-time, understand how many
|
||||
* users are doing this and how much they're spending per checkout, and see how it related to other important
|
||||
* purchase-related metrics.
|
||||
*
|
||||
* @param totalPriceOrNil The total price of the cart.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param itemCountOrNil The number of items in the cart.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logStartCheckoutWithPrice:(nullable NSDecimalNumber *)totalPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
itemCount:(nullable NSNumber *)itemCountOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Rating event to see users rating content within your app in real-time and understand what
|
||||
* content is most engaging, from the type or genre down to the specific id.
|
||||
*
|
||||
* @param ratingOrNil The integer rating given by the user.
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logRating:(nullable NSNumber *)ratingOrNil
|
||||
contentName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Content View event to see users viewing content within your app in real-time and
|
||||
* understand what content is most engaging, from the type or genre down to the specific id.
|
||||
*
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logContentViewWithName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Search event allows you to see users searching within your app in real-time and understand
|
||||
* exactly what they're searching for.
|
||||
*
|
||||
* @param queryOrNil The user's query.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logSearchWithQuery:(nullable NSString *)queryOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Custom Event to see user actions that are uniquely important for your app in real-time, to see how often
|
||||
* they're performing these actions with breakdowns by different categories you add. Use a human-readable name for
|
||||
* the name of the event, since this is how the event will appear in Answers.
|
||||
*
|
||||
* @param eventName The human-readable name for the event.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event. Attribute keys
|
||||
* must be <code>NSString</code> and and values must be <code>NSNumber</code> or <code>NSString</code>.
|
||||
* @discussion How we treat <code>NSNumbers</code>:
|
||||
* We will provide information about the distribution of values over time.
|
||||
*
|
||||
* How we treat <code>NSStrings</code>:
|
||||
* NSStrings are used as categorical data, allowing comparison across different category values.
|
||||
* Strings are limited to a maximum length of 100 characters, attributes over this length will be
|
||||
* truncated.
|
||||
*
|
||||
* When tracking the Tweet views to better understand user engagement, sending the tweet's length
|
||||
* and the type of media present in the tweet allows you to track how tweet length and the type of media influence
|
||||
* engagement.
|
||||
*/
|
||||
+ (void)logCustomEventWithName:(NSString *)eventName
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
33
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/CLSAttributes.h
vendored
Normal file
33
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/CLSAttributes.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// CLSAttributes.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CLS_DEPRECATED(x) __attribute__ ((deprecated(x)))
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
#define nonnull
|
||||
#define nullable
|
||||
#define _Nullable
|
||||
#define _Nonnull
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
#if __has_feature(objc_generics)
|
||||
#define CLS_GENERIC_NSARRAY(type) NSArray<type>
|
||||
#define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
|
||||
#else
|
||||
#define CLS_GENERIC_NSARRAY(type) NSArray
|
||||
#define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
|
||||
#endif
|
||||
64
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/CLSLogging.h
vendored
Normal file
64
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/CLSLogging.h
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// CLSLogging.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
#ifdef __OBJC__
|
||||
#import "CLSAttributes.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
|
||||
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
|
||||
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
|
||||
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
|
||||
* If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
|
||||
*
|
||||
* Example output:
|
||||
* -[AppDelegate login:] line 134 $ login start
|
||||
*
|
||||
* If you would like to change this macro, create a new header file, unset our define and then define
|
||||
* your own version. Make sure this new header file is imported after the Crashlytics header file.
|
||||
*
|
||||
* #undef CLS_LOG
|
||||
* #define CLS_LOG(__FORMAT__, ...) CLSNSLog...
|
||||
*
|
||||
**/
|
||||
#ifdef __OBJC__
|
||||
#ifdef DEBUG
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will not show up in the system.log
|
||||
* and will only be visible in your Crashlytics dashboard.
|
||||
*
|
||||
**/
|
||||
|
||||
#ifdef __OBJC__
|
||||
OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
OBJC_EXTERN void CLSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will show up in the system.log
|
||||
* and your Crashlytics dashboard. It is not recommended for Release builds.
|
||||
*
|
||||
**/
|
||||
OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
OBJC_EXTERN void CLSNSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
103
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/CLSReport.h
vendored
Normal file
103
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/CLSReport.h
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// CLSReport.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CLSAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details.
|
||||
**/
|
||||
@protocol CLSCrashReport <NSObject>
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||
@property (nonatomic, copy, readonly) NSDictionary *customKeys;
|
||||
@property (nonatomic, copy, readonly) NSString *bundleVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
|
||||
@property (nonatomic, copy, readonly) NSDate *crashedOnDate;
|
||||
@property (nonatomic, copy, readonly) NSString *OSVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can
|
||||
* use this class to get information about the event, and can also set some values after the
|
||||
* event has occured.
|
||||
**/
|
||||
@interface CLSReport : NSObject <CLSCrashReport>
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Returns the session identifier for the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||
|
||||
/**
|
||||
* Returns the custom key value data for the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSDictionary *customKeys;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleVersion of the application that generated the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *bundleVersion;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleShortVersionString of the application that generated the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
|
||||
|
||||
/**
|
||||
* Returns the date that the report was created.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSDate *dateCreated;
|
||||
|
||||
/**
|
||||
* Returns the os version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *OSVersion;
|
||||
|
||||
/**
|
||||
* Returns the os build version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
|
||||
|
||||
/**
|
||||
* Returns YES if the report contains any crash information, otherwise returns NO.
|
||||
**/
|
||||
@property (nonatomic, assign, readonly) BOOL isCrash;
|
||||
|
||||
/**
|
||||
* You can use this method to set, after the event, additional custom keys. The rules
|
||||
* and semantics for this method are the same as those documented in Crashlytics.h. Be aware
|
||||
* that the maximum size and count of custom keys is still enforced, and you can overwrite keys
|
||||
* and/or cause excess keys to be deleted by using this method.
|
||||
**/
|
||||
- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Record an application-specific user identifier. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userIdentifier;
|
||||
|
||||
/**
|
||||
* Record a user name. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userName;
|
||||
|
||||
/**
|
||||
* Record a user email. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userEmail;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
38
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/CLSStackFrame.h
vendored
Normal file
38
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/CLSStackFrame.h
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// CLSStackFrame.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CLSAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
*
|
||||
* This class is used in conjunction with -[Crashlytics recordCustomExceptionName:reason:frameArray:] to
|
||||
* record information about non-ObjC/C++ exceptions. All information included here will be displayed
|
||||
* in the Crashlytics UI, and can influence crash grouping. Be particularly careful with the use of the
|
||||
* address property. If set, Crashlytics will attempt symbolication and could overwrite other properities
|
||||
* in the process.
|
||||
*
|
||||
**/
|
||||
@interface CLSStackFrame : NSObject
|
||||
|
||||
+ (instancetype)stackFrame;
|
||||
+ (instancetype)stackFrameWithAddress:(NSUInteger)address;
|
||||
+ (instancetype)stackFrameWithSymbol:(NSString *)symbol;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSString *symbol;
|
||||
@property (nonatomic, copy, nullable) NSString *rawSymbol;
|
||||
@property (nonatomic, copy, nullable) NSString *library;
|
||||
@property (nonatomic, copy, nullable) NSString *fileName;
|
||||
@property (nonatomic, assign) uint32_t lineNumber;
|
||||
@property (nonatomic, assign) uint64_t offset;
|
||||
@property (nonatomic, assign) uint64_t address;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
266
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/Crashlytics.h
vendored
Normal file
266
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Headers/Crashlytics.h
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// Crashlytics.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "CLSAttributes.h"
|
||||
#import "CLSLogging.h"
|
||||
#import "CLSReport.h"
|
||||
#import "CLSStackFrame.h"
|
||||
#import "Answers.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol CrashlyticsDelegate;
|
||||
|
||||
/**
|
||||
* Crashlytics. Handles configuration and initialization of Crashlytics.
|
||||
*
|
||||
* Note: The Crashlytics class cannot be subclassed. If this is causing you pain for
|
||||
* testing, we suggest using either a wrapper class or a protocol extension.
|
||||
*/
|
||||
@interface Crashlytics : NSObject
|
||||
|
||||
@property (nonatomic, readonly, copy) NSString *APIKey;
|
||||
@property (nonatomic, readonly, copy) NSString *version;
|
||||
@property (nonatomic, assign) BOOL debugMode;
|
||||
|
||||
/**
|
||||
*
|
||||
* The delegate can be used to influence decisions on reporting and behavior, as well as reacting
|
||||
* to previous crashes.
|
||||
*
|
||||
* Make certain that the delegate is setup before starting Crashlytics with startWithAPIKey:... or
|
||||
* via +[Fabric with:...]. Failure to do will result in missing any delegate callbacks that occur
|
||||
* synchronously during start.
|
||||
*
|
||||
**/
|
||||
@property (nonatomic, assign, nullable) id <CrashlyticsDelegate> delegate;
|
||||
|
||||
/**
|
||||
* The recommended way to install Crashlytics into your application is to place a call to +startWithAPIKey:
|
||||
* in your -application:didFinishLaunchingWithOptions: or -applicationDidFinishLaunching:
|
||||
* method.
|
||||
*
|
||||
* Note: Starting with 3.0, the submission process has been significantly improved. The delay parameter
|
||||
* is no longer required to throttle submissions on launch, performance will be great without it.
|
||||
*
|
||||
* @param apiKey The Crashlytics API Key for this app
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey;
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey: instead.");
|
||||
|
||||
/**
|
||||
* If you need the functionality provided by the CrashlyticsDelegate protocol, you can use
|
||||
* these convenience methods to activate the framework and set the delegate in one call.
|
||||
*
|
||||
* @param apiKey The Crashlytics API Key for this app
|
||||
* @param delegate A delegate object which conforms to CrashlyticsDelegate.
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id<CrashlyticsDelegate>)delegate;
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id<CrashlyticsDelegate>)delegate afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey:delegate: instead.");
|
||||
|
||||
/**
|
||||
* Access the singleton Crashlytics instance.
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)sharedInstance;
|
||||
|
||||
/**
|
||||
* The easiest way to cause a crash - great for testing!
|
||||
*/
|
||||
- (void)crash;
|
||||
|
||||
/**
|
||||
* The easiest way to cause a crash with an exception - great for testing.
|
||||
*/
|
||||
- (void)throwException;
|
||||
|
||||
/**
|
||||
* Specify a user identifier which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* Many of our customers have requested the ability to tie crashes to specific end-users of their
|
||||
* application in order to facilitate responses to support requests or permit the ability to reach
|
||||
* out for more information. We allow you to specify up to three separate values for display within
|
||||
* the Crashlytics UI - but please be mindful of your end-user's privacy.
|
||||
*
|
||||
* We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
|
||||
* in your system. This could be a database id, hash, or other value that is meaningless to a
|
||||
* third-party observer but can be indexed and queried by you.
|
||||
*
|
||||
* Optionally, you may also specify the end-user's name or username, as well as email address if you
|
||||
* do not have a system that works well with obscured identifiers.
|
||||
*
|
||||
* Pursuant to our EULA, this data is transferred securely throughout our system and we will not
|
||||
* disseminate end-user data unless required to by law. That said, if you choose to provide end-user
|
||||
* contact information, we strongly recommend that you disclose this in your application's privacy
|
||||
* policy. Data privacy is of our utmost concern.
|
||||
*
|
||||
* @param identifier An arbitrary user identifier string which ties an end-user to a record in your system.
|
||||
*/
|
||||
- (void)setUserIdentifier:(nullable NSString *)identifier;
|
||||
|
||||
/**
|
||||
* Specify a user name which will be visible in the Crashlytics UI.
|
||||
* Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs.
|
||||
* @see setUserIdentifier:
|
||||
*
|
||||
* @param name An end user's name.
|
||||
*/
|
||||
- (void)setUserName:(nullable NSString *)name;
|
||||
|
||||
/**
|
||||
* Specify a user email which will be visible in the Crashlytics UI.
|
||||
* Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs.
|
||||
*
|
||||
* @see setUserIdentifier:
|
||||
*
|
||||
* @param email An end user's email address.
|
||||
*/
|
||||
- (void)setUserEmail:(nullable NSString *)email;
|
||||
|
||||
+ (void)setUserIdentifier:(nullable NSString *)identifier CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setUserName:(nullable NSString *)name CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setUserEmail:(nullable NSString *)email CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
|
||||
/**
|
||||
* Set a value for a for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
* When setting an object value, the object is converted to a string. This is typically done by calling
|
||||
* -[NSObject description].
|
||||
*
|
||||
* @param value The object to be associated with the key
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an int value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The integer value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setIntValue:(int)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an BOOL value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The BOOL value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an float value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The float value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setFloatValue:(float)value forKey:(NSString *)key;
|
||||
|
||||
+ (void)setObjectValue:(nullable id)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setIntValue:(int)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setBoolValue:(BOOL)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setFloatValue:(float)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
|
||||
/**
|
||||
* This method can be used to record a single exception structure in a report. This is particularly useful
|
||||
* when your code interacts with non-native languages like Lua, C#, or Javascript. This call can be
|
||||
* expensive and should only be used shortly before process termination. This API is not intended be to used
|
||||
* to log NSException objects. All safely-reportable NSExceptions are automatically captured by
|
||||
* Crashlytics.
|
||||
*
|
||||
* @param name The name of the custom exception
|
||||
* @param reason The reason this exception occured
|
||||
* @param frameArray An array of CLSStackFrame objects
|
||||
*/
|
||||
- (void)recordCustomExceptionName:(NSString *)name reason:(nullable NSString *)reason frameArray:(CLS_GENERIC_NSARRAY(CLSStackFrame *) *)frameArray;
|
||||
|
||||
/**
|
||||
*
|
||||
* This allows you to record a non-fatal event, described by an NSError object. These events will be grouped and
|
||||
* displayed similarly to crashes. Keep in mind that this method can be expensive. Also, the total number of
|
||||
* NSErrors that can be recorded during your app's life-cycle is limited by a fixed-size circular buffer. If the
|
||||
* buffer is overrun, the oldest data is dropped. Errors are relayed to Crashlytics on a subsequent launch
|
||||
* of your application.
|
||||
*
|
||||
* You can also use the -recordError:withAdditionalUserInfo: to include additional context not represented
|
||||
* by the NSError instance itself.
|
||||
*
|
||||
**/
|
||||
- (void)recordError:(NSError *)error;
|
||||
- (void)recordError:(NSError *)error withAdditionalUserInfo:(nullable CLS_GENERIC_NSDICTIONARY(NSString *, id) *)userInfo;
|
||||
|
||||
- (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
- (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
+ (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
+ (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
*
|
||||
* The CrashlyticsDelegate protocol provides a mechanism for your application to take
|
||||
* action on events that occur in the Crashlytics crash reporting system. You can make
|
||||
* use of these calls by assigning an object to the Crashlytics' delegate property directly,
|
||||
* or through the convenience +startWithAPIKey:delegate: method.
|
||||
*
|
||||
*/
|
||||
@protocol CrashlyticsDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
|
||||
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
|
||||
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
|
||||
|
||||
/**
|
||||
*
|
||||
* Called when a Crashlytics instance has determined that the last execution of the
|
||||
* application ended in a crash. This is called synchronously on Crashlytics
|
||||
* initialization. Your delegate must invoke the completionHandler, but does not need to do so
|
||||
* synchronously, or even on the main thread. Invoking completionHandler with NO will cause the
|
||||
* detected report to be deleted and not submitted to Crashlytics. This is useful for
|
||||
* implementing permission prompts, or other more-complex forms of logic around submitting crashes.
|
||||
*
|
||||
* @warning Failure to invoke the completionHandler will prevent submissions from being reported. Watch out.
|
||||
*
|
||||
* @warning Just implementing this delegate method will disable all forms of synchronous report submission. This can
|
||||
* impact the reliability of reporting crashes very early in application launch.
|
||||
*
|
||||
* @param report The CLSReport object representing the last detected crash
|
||||
* @param completionHandler The completion handler to call when your logic has completed.
|
||||
*
|
||||
*/
|
||||
- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report completionHandler:(void (^)(BOOL submit))completionHandler;
|
||||
|
||||
/**
|
||||
* If your app is running on an OS that supports it (OS X 10.9+, iOS 7.0+), Crashlytics will submit
|
||||
* most reports using out-of-process background networking operations. This results in a significant
|
||||
* improvement in reliability of reporting, as well as power and performance wins for your users.
|
||||
* If you don't want this functionality, you can disable by returning NO from this method.
|
||||
*
|
||||
* @warning Background submission is not supported for extensions on iOS or OS X.
|
||||
*
|
||||
* @param crashlytics The Crashlytics singleton instance
|
||||
*
|
||||
* @return Return NO if you don't want out-of-process background network operations.
|
||||
*
|
||||
*/
|
||||
- (BOOL)crashlyticsCanUseBackgroundSessions:(Crashlytics *)crashlytics;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, use Crashlytics.sharedInstance()
|
||||
*/
|
||||
#define CrashlyticsKit [Crashlytics sharedInstance]
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
14
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Modules/module.modulemap
vendored
Normal file
14
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Modules/module.modulemap
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
framework module Crashlytics {
|
||||
header "Crashlytics.h"
|
||||
header "Answers.h"
|
||||
header "ANSCompatibility.h"
|
||||
header "CLSLogging.h"
|
||||
header "CLSReport.h"
|
||||
header "CLSStackFrame.h"
|
||||
header "CLSAttributes.h"
|
||||
|
||||
export *
|
||||
|
||||
link "z"
|
||||
link "c++"
|
||||
}
|
||||
53
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Resources/Info.plist
vendored
Normal file
53
platform-ios/External/Mac/Crashlytics.framework/Versions/A/Resources/Info.plist
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>15F34</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Crashlytics</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.twitter.crashlytics.mac</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Crashlytics</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.7.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>112</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>7D175</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>GM</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>15E60</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.11</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0730</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>7D175</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2016 Crashlytics, Inc. All rights reserved.</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>3</integer>
|
||||
<integer>2</integer>
|
||||
<integer>1</integer>
|
||||
<integer>4</integer>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
1
platform-ios/External/Mac/Crashlytics.framework/Versions/Current
vendored
Symbolic link
1
platform-ios/External/Mac/Crashlytics.framework/Versions/Current
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
A
|
||||
28
platform-ios/External/Mac/Crashlytics.framework/run
vendored
Executable file
28
platform-ios/External/Mac/Crashlytics.framework/run
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
# run
|
||||
#
|
||||
# Copyright (c) 2015 Crashlytics. All rights reserved.
|
||||
|
||||
# Figure out where we're being called from
|
||||
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
# Quote path in case of spaces or special chars
|
||||
DIR="\"${DIR}"
|
||||
|
||||
PATH_SEP="/"
|
||||
VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script"
|
||||
UPLOAD_COMMAND="uploadDSYM\" $@ run-script"
|
||||
|
||||
# Ensure params are as expected, run in sync mode to validate
|
||||
eval $DIR$PATH_SEP$VALIDATE_COMMAND
|
||||
return_code=$?
|
||||
|
||||
if [[ $return_code != 0 ]]; then
|
||||
exit $return_code
|
||||
fi
|
||||
|
||||
# Verification passed, upload dSYM in background to prevent Xcode from waiting
|
||||
# Note: Validation is performed again before upload.
|
||||
# Output can still be found in Console.app
|
||||
eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 &
|
||||
BIN
platform-ios/External/Mac/Crashlytics.framework/submit
vendored
Executable file
BIN
platform-ios/External/Mac/Crashlytics.framework/submit
vendored
Executable file
Binary file not shown.
BIN
platform-ios/External/Mac/Crashlytics.framework/uploadDSYM
vendored
Executable file
BIN
platform-ios/External/Mac/Crashlytics.framework/uploadDSYM
vendored
Executable file
Binary file not shown.
1
platform-ios/External/Mac/Fabric.framework/Fabric
vendored
Symbolic link
1
platform-ios/External/Mac/Fabric.framework/Fabric
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Fabric
|
||||
1
platform-ios/External/Mac/Fabric.framework/Headers
vendored
Symbolic link
1
platform-ios/External/Mac/Fabric.framework/Headers
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Headers
|
||||
1
platform-ios/External/Mac/Fabric.framework/Modules
vendored
Symbolic link
1
platform-ios/External/Mac/Fabric.framework/Modules
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Modules
|
||||
1
platform-ios/External/Mac/Fabric.framework/Resources
vendored
Symbolic link
1
platform-ios/External/Mac/Fabric.framework/Resources
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Resources
|
||||
BIN
platform-ios/External/Mac/Fabric.framework/Versions/A/Fabric
vendored
Executable file
BIN
platform-ios/External/Mac/Fabric.framework/Versions/A/Fabric
vendored
Executable file
Binary file not shown.
60
platform-ios/External/Mac/Fabric.framework/Versions/A/Headers/FABAttributes.h
vendored
Normal file
60
platform-ios/External/Mac/Fabric.framework/Versions/A/Headers/FABAttributes.h
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// FABAttributes.h
|
||||
// Fabric
|
||||
//
|
||||
// Copyright (C) 2015 Twitter, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define FAB_UNAVAILABLE(x) __attribute__((unavailable(x)))
|
||||
|
||||
#if __has_feature(nullability)
|
||||
#define fab_nullable nullable
|
||||
#define fab_nonnull nonnull
|
||||
#define fab_null_unspecified null_unspecified
|
||||
#define fab_null_resettable null_resettable
|
||||
#define __fab_nullable __nullable
|
||||
#define __fab_nonnull __nonnull
|
||||
#define __fab_null_unspecified __null_unspecified
|
||||
#else
|
||||
#define fab_nullable
|
||||
#define fab_nonnull
|
||||
#define fab_null_unspecified
|
||||
#define fab_null_resettable
|
||||
#define __fab_nullable
|
||||
#define __fab_nonnull
|
||||
#define __fab_null_unspecified
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* The following macros are defined here to provide
|
||||
* backwards compatability. If you are still using
|
||||
* them you should migrate to the new versions that
|
||||
* are defined above.
|
||||
*/
|
||||
#define FAB_NONNULL __fab_nonnull
|
||||
#define FAB_NULLABLE __fab_nullable
|
||||
#define FAB_START_NONNULL NS_ASSUME_NONNULL_BEGIN
|
||||
#define FAB_END_NONNULL NS_ASSUME_NONNULL_END
|
||||
77
platform-ios/External/Mac/Fabric.framework/Versions/A/Headers/Fabric.h
vendored
Normal file
77
platform-ios/External/Mac/Fabric.framework/Versions/A/Headers/Fabric.h
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Fabric.h
|
||||
// Fabric
|
||||
//
|
||||
// Copyright (C) 2015 Twitter, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FABAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
|
||||
#error "Fabric's minimum iOS version is 6.0"
|
||||
#endif
|
||||
#else
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070
|
||||
#error "Fabric's minimum OS X version is 10.7"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Fabric Base. Coordinates configuration and starts all provided kits.
|
||||
*/
|
||||
@interface Fabric : NSObject
|
||||
|
||||
/**
|
||||
* Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use.
|
||||
*
|
||||
* For example, in Objective-C:
|
||||
*
|
||||
* `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];`
|
||||
*
|
||||
* Swift:
|
||||
*
|
||||
* `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])`
|
||||
*
|
||||
* Only the first call to this method is honored. Subsequent calls are no-ops.
|
||||
*
|
||||
* @param kitClasses An array of kit Class objects
|
||||
*
|
||||
* @return Returns the shared Fabric instance. In most cases this can be ignored.
|
||||
*/
|
||||
+ (instancetype)with:(NSArray *)kitClasses;
|
||||
|
||||
/**
|
||||
* Returns the Fabric singleton object.
|
||||
*/
|
||||
+ (instancetype)sharedSDK;
|
||||
|
||||
/**
|
||||
* This BOOL enables or disables debug logging, such as kit version information. The default value is NO.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL debug;
|
||||
|
||||
/**
|
||||
* Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance.
|
||||
*/
|
||||
- (id)init FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance.");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
6
platform-ios/External/Mac/Fabric.framework/Versions/A/Modules/module.modulemap
vendored
Normal file
6
platform-ios/External/Mac/Fabric.framework/Versions/A/Modules/module.modulemap
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module Fabric {
|
||||
umbrella header "Fabric.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
53
platform-ios/External/Mac/Fabric.framework/Versions/A/Resources/Info.plist
vendored
Normal file
53
platform-ios/External/Mac/Fabric.framework/Versions/A/Resources/Info.plist
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>14F1021</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Fabric</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.fabric.sdk.mac</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Fabric</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.6.7</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>53</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>7C1002</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>GM</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>15C43</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.11</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0721</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>7C1002</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 Twitter. All rights reserved.</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>3</integer>
|
||||
<integer>2</integer>
|
||||
<integer>1</integer>
|
||||
<integer>4</integer>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
1
platform-ios/External/Mac/Fabric.framework/Versions/Current
vendored
Symbolic link
1
platform-ios/External/Mac/Fabric.framework/Versions/Current
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
A
|
||||
28
platform-ios/External/Mac/Fabric.framework/run
vendored
Executable file
28
platform-ios/External/Mac/Fabric.framework/run
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
# run
|
||||
#
|
||||
# Copyright (c) 2015 Crashlytics. All rights reserved.
|
||||
|
||||
# Figure out where we're being called from
|
||||
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
# Quote path in case of spaces or special chars
|
||||
DIR="\"${DIR}"
|
||||
|
||||
PATH_SEP="/"
|
||||
VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script"
|
||||
UPLOAD_COMMAND="uploadDSYM\" $@ run-script"
|
||||
|
||||
# Ensure params are as expected, run in sync mode to validate
|
||||
eval $DIR$PATH_SEP$VALIDATE_COMMAND
|
||||
return_code=$?
|
||||
|
||||
if [[ $return_code != 0 ]]; then
|
||||
exit $return_code
|
||||
fi
|
||||
|
||||
# Verification passed, upload dSYM in background to prevent Xcode from waiting
|
||||
# Note: Validation is performed again before upload.
|
||||
# Output can still be found in Console.app
|
||||
eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 &
|
||||
BIN
platform-ios/External/Mac/Fabric.framework/uploadDSYM
vendored
Executable file
BIN
platform-ios/External/Mac/Fabric.framework/uploadDSYM
vendored
Executable file
Binary file not shown.
1
platform-ios/External/Pearl
vendored
Submodule
1
platform-ios/External/Pearl
vendored
Submodule
Submodule platform-ios/External/Pearl added at 4ac4add457
BIN
platform-ios/External/iOS/Crashlytics.framework/Crashlytics
vendored
Executable file
BIN
platform-ios/External/iOS/Crashlytics.framework/Crashlytics
vendored
Executable file
Binary file not shown.
31
platform-ios/External/iOS/Crashlytics.framework/Headers/ANSCompatibility.h
vendored
Normal file
31
platform-ios/External/iOS/Crashlytics.framework/Headers/ANSCompatibility.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// ANSCompatibility.h
|
||||
// AnswersKit
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
#define nonnull
|
||||
#define nullable
|
||||
#define _Nullable
|
||||
#define _Nonnull
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
#if __has_feature(objc_generics)
|
||||
#define ANS_GENERIC_NSARRAY(type) NSArray<type>
|
||||
#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
|
||||
#else
|
||||
#define ANS_GENERIC_NSARRAY(type) NSArray
|
||||
#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
|
||||
#endif
|
||||
210
platform-ios/External/iOS/Crashlytics.framework/Headers/Answers.h
vendored
Normal file
210
platform-ios/External/iOS/Crashlytics.framework/Headers/Answers.h
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// Answers.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ANSCompatibility.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* This class exposes the Answers Events API, allowing you to track key
|
||||
* user user actions and metrics in your app.
|
||||
*/
|
||||
@interface Answers : NSObject
|
||||
|
||||
/**
|
||||
* Log a Sign Up event to see users signing up for your app in real-time, understand how
|
||||
* many users are signing up with different methods and their success rate signing up.
|
||||
*
|
||||
* @param signUpMethodOrNil The method by which a user logged in, e.g. Twitter or Digits.
|
||||
* @param signUpSucceededOrNil The ultimate success or failure of the login
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logSignUpWithMethod:(nullable NSString *)signUpMethodOrNil
|
||||
success:(nullable NSNumber *)signUpSucceededOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Log In event to see users logging into your app in real-time, understand how many
|
||||
* users are logging in with different methods and their success rate logging into your app.
|
||||
*
|
||||
* @param loginMethodOrNil The method by which a user logged in, e.g. email, Twitter or Digits.
|
||||
* @param loginSucceededOrNil The ultimate success or failure of the login
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logLoginWithMethod:(nullable NSString *)loginMethodOrNil
|
||||
success:(nullable NSNumber *)loginSucceededOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Share event to see users sharing from your app in real-time, letting you
|
||||
* understand what content they're sharing from the type or genre down to the specific id.
|
||||
*
|
||||
* @param shareMethodOrNil The method by which a user shared, e.g. email, Twitter, SMS.
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logShareWithMethod:(nullable NSString *)shareMethodOrNil
|
||||
contentName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Invite Event to track how users are inviting other users into
|
||||
* your application.
|
||||
*
|
||||
* @param inviteMethodOrNil The method of invitation, e.g. GameCenter, Twitter, email.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logInviteWithMethod:(nullable NSString *)inviteMethodOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Purchase event to see your revenue in real-time, understand how many users are making purchases, see which
|
||||
* items are most popular, and track plenty of other important purchase-related metrics.
|
||||
*
|
||||
* @param itemPriceOrNil The purchased item's price.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param purchaseSucceededOrNil Was the purchase succesful or unsuccesful
|
||||
* @param itemNameOrNil The human-readable form of the item's name. Example:
|
||||
* @param itemTypeOrNil The type, or genre of the item. Example: Song
|
||||
* @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this purchase.
|
||||
*/
|
||||
+ (void)logPurchaseWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
success:(nullable NSNumber *)purchaseSucceededOrNil
|
||||
itemName:(nullable NSString *)itemNameOrNil
|
||||
itemType:(nullable NSString *)itemTypeOrNil
|
||||
itemId:(nullable NSString *)itemIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Level Start Event to track where users are in your game.
|
||||
*
|
||||
* @param levelNameOrNil The level name
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this level start event.
|
||||
*/
|
||||
+ (void)logLevelStart:(nullable NSString *)levelNameOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Level End event to track how users are completing levels in your game.
|
||||
*
|
||||
* @param levelNameOrNil The name of the level completed, E.G. "1" or "Training"
|
||||
* @param scoreOrNil The score the user completed the level with.
|
||||
* @param levelCompletedSuccesfullyOrNil A boolean representing whether or not the level was completed succesfully.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logLevelEnd:(nullable NSString *)levelNameOrNil
|
||||
score:(nullable NSNumber *)scoreOrNil
|
||||
success:(nullable NSNumber *)levelCompletedSuccesfullyOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Add to Cart event to see users adding items to a shopping cart in real-time, understand how
|
||||
* many users start the purchase flow, see which items are most popular, and track plenty of other important
|
||||
* purchase-related metrics.
|
||||
*
|
||||
* @param itemPriceOrNil The purchased item's price.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param itemNameOrNil The human-readable form of the item's name. Example:
|
||||
* @param itemTypeOrNil The type, or genre of the item. Example: Song
|
||||
* @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logAddToCartWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
itemName:(nullable NSString *)itemNameOrNil
|
||||
itemType:(nullable NSString *)itemTypeOrNil
|
||||
itemId:(nullable NSString *)itemIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Start Checkout event to see users moving through the purchase funnel in real-time, understand how many
|
||||
* users are doing this and how much they're spending per checkout, and see how it related to other important
|
||||
* purchase-related metrics.
|
||||
*
|
||||
* @param totalPriceOrNil The total price of the cart.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param itemCountOrNil The number of items in the cart.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logStartCheckoutWithPrice:(nullable NSDecimalNumber *)totalPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
itemCount:(nullable NSNumber *)itemCountOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Rating event to see users rating content within your app in real-time and understand what
|
||||
* content is most engaging, from the type or genre down to the specific id.
|
||||
*
|
||||
* @param ratingOrNil The integer rating given by the user.
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logRating:(nullable NSNumber *)ratingOrNil
|
||||
contentName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Content View event to see users viewing content within your app in real-time and
|
||||
* understand what content is most engaging, from the type or genre down to the specific id.
|
||||
*
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logContentViewWithName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Search event allows you to see users searching within your app in real-time and understand
|
||||
* exactly what they're searching for.
|
||||
*
|
||||
* @param queryOrNil The user's query.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logSearchWithQuery:(nullable NSString *)queryOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Custom Event to see user actions that are uniquely important for your app in real-time, to see how often
|
||||
* they're performing these actions with breakdowns by different categories you add. Use a human-readable name for
|
||||
* the name of the event, since this is how the event will appear in Answers.
|
||||
*
|
||||
* @param eventName The human-readable name for the event.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event. Attribute keys
|
||||
* must be <code>NSString</code> and and values must be <code>NSNumber</code> or <code>NSString</code>.
|
||||
* @discussion How we treat <code>NSNumbers</code>:
|
||||
* We will provide information about the distribution of values over time.
|
||||
*
|
||||
* How we treat <code>NSStrings</code>:
|
||||
* NSStrings are used as categorical data, allowing comparison across different category values.
|
||||
* Strings are limited to a maximum length of 100 characters, attributes over this length will be
|
||||
* truncated.
|
||||
*
|
||||
* When tracking the Tweet views to better understand user engagement, sending the tweet's length
|
||||
* and the type of media present in the tweet allows you to track how tweet length and the type of media influence
|
||||
* engagement.
|
||||
*/
|
||||
+ (void)logCustomEventWithName:(NSString *)eventName
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
33
platform-ios/External/iOS/Crashlytics.framework/Headers/CLSAttributes.h
vendored
Normal file
33
platform-ios/External/iOS/Crashlytics.framework/Headers/CLSAttributes.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// CLSAttributes.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CLS_DEPRECATED(x) __attribute__ ((deprecated(x)))
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
#define nonnull
|
||||
#define nullable
|
||||
#define _Nullable
|
||||
#define _Nonnull
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
#if __has_feature(objc_generics)
|
||||
#define CLS_GENERIC_NSARRAY(type) NSArray<type>
|
||||
#define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
|
||||
#else
|
||||
#define CLS_GENERIC_NSARRAY(type) NSArray
|
||||
#define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
|
||||
#endif
|
||||
64
platform-ios/External/iOS/Crashlytics.framework/Headers/CLSLogging.h
vendored
Normal file
64
platform-ios/External/iOS/Crashlytics.framework/Headers/CLSLogging.h
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// CLSLogging.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
#ifdef __OBJC__
|
||||
#import "CLSAttributes.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
|
||||
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
|
||||
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
|
||||
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
|
||||
* If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
|
||||
*
|
||||
* Example output:
|
||||
* -[AppDelegate login:] line 134 $ login start
|
||||
*
|
||||
* If you would like to change this macro, create a new header file, unset our define and then define
|
||||
* your own version. Make sure this new header file is imported after the Crashlytics header file.
|
||||
*
|
||||
* #undef CLS_LOG
|
||||
* #define CLS_LOG(__FORMAT__, ...) CLSNSLog...
|
||||
*
|
||||
**/
|
||||
#ifdef __OBJC__
|
||||
#ifdef DEBUG
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will not show up in the system.log
|
||||
* and will only be visible in your Crashlytics dashboard.
|
||||
*
|
||||
**/
|
||||
|
||||
#ifdef __OBJC__
|
||||
OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
OBJC_EXTERN void CLSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will show up in the system.log
|
||||
* and your Crashlytics dashboard. It is not recommended for Release builds.
|
||||
*
|
||||
**/
|
||||
OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
OBJC_EXTERN void CLSNSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
103
platform-ios/External/iOS/Crashlytics.framework/Headers/CLSReport.h
vendored
Normal file
103
platform-ios/External/iOS/Crashlytics.framework/Headers/CLSReport.h
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// CLSReport.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CLSAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details.
|
||||
**/
|
||||
@protocol CLSCrashReport <NSObject>
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||
@property (nonatomic, copy, readonly) NSDictionary *customKeys;
|
||||
@property (nonatomic, copy, readonly) NSString *bundleVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
|
||||
@property (nonatomic, copy, readonly) NSDate *crashedOnDate;
|
||||
@property (nonatomic, copy, readonly) NSString *OSVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can
|
||||
* use this class to get information about the event, and can also set some values after the
|
||||
* event has occured.
|
||||
**/
|
||||
@interface CLSReport : NSObject <CLSCrashReport>
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Returns the session identifier for the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||
|
||||
/**
|
||||
* Returns the custom key value data for the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSDictionary *customKeys;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleVersion of the application that generated the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *bundleVersion;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleShortVersionString of the application that generated the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
|
||||
|
||||
/**
|
||||
* Returns the date that the report was created.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSDate *dateCreated;
|
||||
|
||||
/**
|
||||
* Returns the os version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *OSVersion;
|
||||
|
||||
/**
|
||||
* Returns the os build version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
|
||||
|
||||
/**
|
||||
* Returns YES if the report contains any crash information, otherwise returns NO.
|
||||
**/
|
||||
@property (nonatomic, assign, readonly) BOOL isCrash;
|
||||
|
||||
/**
|
||||
* You can use this method to set, after the event, additional custom keys. The rules
|
||||
* and semantics for this method are the same as those documented in Crashlytics.h. Be aware
|
||||
* that the maximum size and count of custom keys is still enforced, and you can overwrite keys
|
||||
* and/or cause excess keys to be deleted by using this method.
|
||||
**/
|
||||
- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Record an application-specific user identifier. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userIdentifier;
|
||||
|
||||
/**
|
||||
* Record a user name. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userName;
|
||||
|
||||
/**
|
||||
* Record a user email. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userEmail;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
38
platform-ios/External/iOS/Crashlytics.framework/Headers/CLSStackFrame.h
vendored
Normal file
38
platform-ios/External/iOS/Crashlytics.framework/Headers/CLSStackFrame.h
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// CLSStackFrame.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CLSAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
*
|
||||
* This class is used in conjunction with -[Crashlytics recordCustomExceptionName:reason:frameArray:] to
|
||||
* record information about non-ObjC/C++ exceptions. All information included here will be displayed
|
||||
* in the Crashlytics UI, and can influence crash grouping. Be particularly careful with the use of the
|
||||
* address property. If set, Crashlytics will attempt symbolication and could overwrite other properities
|
||||
* in the process.
|
||||
*
|
||||
**/
|
||||
@interface CLSStackFrame : NSObject
|
||||
|
||||
+ (instancetype)stackFrame;
|
||||
+ (instancetype)stackFrameWithAddress:(NSUInteger)address;
|
||||
+ (instancetype)stackFrameWithSymbol:(NSString *)symbol;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSString *symbol;
|
||||
@property (nonatomic, copy, nullable) NSString *rawSymbol;
|
||||
@property (nonatomic, copy, nullable) NSString *library;
|
||||
@property (nonatomic, copy, nullable) NSString *fileName;
|
||||
@property (nonatomic, assign) uint32_t lineNumber;
|
||||
@property (nonatomic, assign) uint64_t offset;
|
||||
@property (nonatomic, assign) uint64_t address;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
266
platform-ios/External/iOS/Crashlytics.framework/Headers/Crashlytics.h
vendored
Normal file
266
platform-ios/External/iOS/Crashlytics.framework/Headers/Crashlytics.h
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// Crashlytics.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "CLSAttributes.h"
|
||||
#import "CLSLogging.h"
|
||||
#import "CLSReport.h"
|
||||
#import "CLSStackFrame.h"
|
||||
#import "Answers.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol CrashlyticsDelegate;
|
||||
|
||||
/**
|
||||
* Crashlytics. Handles configuration and initialization of Crashlytics.
|
||||
*
|
||||
* Note: The Crashlytics class cannot be subclassed. If this is causing you pain for
|
||||
* testing, we suggest using either a wrapper class or a protocol extension.
|
||||
*/
|
||||
@interface Crashlytics : NSObject
|
||||
|
||||
@property (nonatomic, readonly, copy) NSString *APIKey;
|
||||
@property (nonatomic, readonly, copy) NSString *version;
|
||||
@property (nonatomic, assign) BOOL debugMode;
|
||||
|
||||
/**
|
||||
*
|
||||
* The delegate can be used to influence decisions on reporting and behavior, as well as reacting
|
||||
* to previous crashes.
|
||||
*
|
||||
* Make certain that the delegate is setup before starting Crashlytics with startWithAPIKey:... or
|
||||
* via +[Fabric with:...]. Failure to do will result in missing any delegate callbacks that occur
|
||||
* synchronously during start.
|
||||
*
|
||||
**/
|
||||
@property (nonatomic, assign, nullable) id <CrashlyticsDelegate> delegate;
|
||||
|
||||
/**
|
||||
* The recommended way to install Crashlytics into your application is to place a call to +startWithAPIKey:
|
||||
* in your -application:didFinishLaunchingWithOptions: or -applicationDidFinishLaunching:
|
||||
* method.
|
||||
*
|
||||
* Note: Starting with 3.0, the submission process has been significantly improved. The delay parameter
|
||||
* is no longer required to throttle submissions on launch, performance will be great without it.
|
||||
*
|
||||
* @param apiKey The Crashlytics API Key for this app
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey;
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey: instead.");
|
||||
|
||||
/**
|
||||
* If you need the functionality provided by the CrashlyticsDelegate protocol, you can use
|
||||
* these convenience methods to activate the framework and set the delegate in one call.
|
||||
*
|
||||
* @param apiKey The Crashlytics API Key for this app
|
||||
* @param delegate A delegate object which conforms to CrashlyticsDelegate.
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id<CrashlyticsDelegate>)delegate;
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id<CrashlyticsDelegate>)delegate afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey:delegate: instead.");
|
||||
|
||||
/**
|
||||
* Access the singleton Crashlytics instance.
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)sharedInstance;
|
||||
|
||||
/**
|
||||
* The easiest way to cause a crash - great for testing!
|
||||
*/
|
||||
- (void)crash;
|
||||
|
||||
/**
|
||||
* The easiest way to cause a crash with an exception - great for testing.
|
||||
*/
|
||||
- (void)throwException;
|
||||
|
||||
/**
|
||||
* Specify a user identifier which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* Many of our customers have requested the ability to tie crashes to specific end-users of their
|
||||
* application in order to facilitate responses to support requests or permit the ability to reach
|
||||
* out for more information. We allow you to specify up to three separate values for display within
|
||||
* the Crashlytics UI - but please be mindful of your end-user's privacy.
|
||||
*
|
||||
* We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
|
||||
* in your system. This could be a database id, hash, or other value that is meaningless to a
|
||||
* third-party observer but can be indexed and queried by you.
|
||||
*
|
||||
* Optionally, you may also specify the end-user's name or username, as well as email address if you
|
||||
* do not have a system that works well with obscured identifiers.
|
||||
*
|
||||
* Pursuant to our EULA, this data is transferred securely throughout our system and we will not
|
||||
* disseminate end-user data unless required to by law. That said, if you choose to provide end-user
|
||||
* contact information, we strongly recommend that you disclose this in your application's privacy
|
||||
* policy. Data privacy is of our utmost concern.
|
||||
*
|
||||
* @param identifier An arbitrary user identifier string which ties an end-user to a record in your system.
|
||||
*/
|
||||
- (void)setUserIdentifier:(nullable NSString *)identifier;
|
||||
|
||||
/**
|
||||
* Specify a user name which will be visible in the Crashlytics UI.
|
||||
* Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs.
|
||||
* @see setUserIdentifier:
|
||||
*
|
||||
* @param name An end user's name.
|
||||
*/
|
||||
- (void)setUserName:(nullable NSString *)name;
|
||||
|
||||
/**
|
||||
* Specify a user email which will be visible in the Crashlytics UI.
|
||||
* Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs.
|
||||
*
|
||||
* @see setUserIdentifier:
|
||||
*
|
||||
* @param email An end user's email address.
|
||||
*/
|
||||
- (void)setUserEmail:(nullable NSString *)email;
|
||||
|
||||
+ (void)setUserIdentifier:(nullable NSString *)identifier CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setUserName:(nullable NSString *)name CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setUserEmail:(nullable NSString *)email CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
|
||||
/**
|
||||
* Set a value for a for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
* When setting an object value, the object is converted to a string. This is typically done by calling
|
||||
* -[NSObject description].
|
||||
*
|
||||
* @param value The object to be associated with the key
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an int value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The integer value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setIntValue:(int)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an BOOL value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The BOOL value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an float value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The float value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setFloatValue:(float)value forKey:(NSString *)key;
|
||||
|
||||
+ (void)setObjectValue:(nullable id)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setIntValue:(int)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setBoolValue:(BOOL)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setFloatValue:(float)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
|
||||
/**
|
||||
* This method can be used to record a single exception structure in a report. This is particularly useful
|
||||
* when your code interacts with non-native languages like Lua, C#, or Javascript. This call can be
|
||||
* expensive and should only be used shortly before process termination. This API is not intended be to used
|
||||
* to log NSException objects. All safely-reportable NSExceptions are automatically captured by
|
||||
* Crashlytics.
|
||||
*
|
||||
* @param name The name of the custom exception
|
||||
* @param reason The reason this exception occured
|
||||
* @param frameArray An array of CLSStackFrame objects
|
||||
*/
|
||||
- (void)recordCustomExceptionName:(NSString *)name reason:(nullable NSString *)reason frameArray:(CLS_GENERIC_NSARRAY(CLSStackFrame *) *)frameArray;
|
||||
|
||||
/**
|
||||
*
|
||||
* This allows you to record a non-fatal event, described by an NSError object. These events will be grouped and
|
||||
* displayed similarly to crashes. Keep in mind that this method can be expensive. Also, the total number of
|
||||
* NSErrors that can be recorded during your app's life-cycle is limited by a fixed-size circular buffer. If the
|
||||
* buffer is overrun, the oldest data is dropped. Errors are relayed to Crashlytics on a subsequent launch
|
||||
* of your application.
|
||||
*
|
||||
* You can also use the -recordError:withAdditionalUserInfo: to include additional context not represented
|
||||
* by the NSError instance itself.
|
||||
*
|
||||
**/
|
||||
- (void)recordError:(NSError *)error;
|
||||
- (void)recordError:(NSError *)error withAdditionalUserInfo:(nullable CLS_GENERIC_NSDICTIONARY(NSString *, id) *)userInfo;
|
||||
|
||||
- (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
- (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
+ (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
+ (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
*
|
||||
* The CrashlyticsDelegate protocol provides a mechanism for your application to take
|
||||
* action on events that occur in the Crashlytics crash reporting system. You can make
|
||||
* use of these calls by assigning an object to the Crashlytics' delegate property directly,
|
||||
* or through the convenience +startWithAPIKey:delegate: method.
|
||||
*
|
||||
*/
|
||||
@protocol CrashlyticsDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
|
||||
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
|
||||
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
|
||||
|
||||
/**
|
||||
*
|
||||
* Called when a Crashlytics instance has determined that the last execution of the
|
||||
* application ended in a crash. This is called synchronously on Crashlytics
|
||||
* initialization. Your delegate must invoke the completionHandler, but does not need to do so
|
||||
* synchronously, or even on the main thread. Invoking completionHandler with NO will cause the
|
||||
* detected report to be deleted and not submitted to Crashlytics. This is useful for
|
||||
* implementing permission prompts, or other more-complex forms of logic around submitting crashes.
|
||||
*
|
||||
* @warning Failure to invoke the completionHandler will prevent submissions from being reported. Watch out.
|
||||
*
|
||||
* @warning Just implementing this delegate method will disable all forms of synchronous report submission. This can
|
||||
* impact the reliability of reporting crashes very early in application launch.
|
||||
*
|
||||
* @param report The CLSReport object representing the last detected crash
|
||||
* @param completionHandler The completion handler to call when your logic has completed.
|
||||
*
|
||||
*/
|
||||
- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report completionHandler:(void (^)(BOOL submit))completionHandler;
|
||||
|
||||
/**
|
||||
* If your app is running on an OS that supports it (OS X 10.9+, iOS 7.0+), Crashlytics will submit
|
||||
* most reports using out-of-process background networking operations. This results in a significant
|
||||
* improvement in reliability of reporting, as well as power and performance wins for your users.
|
||||
* If you don't want this functionality, you can disable by returning NO from this method.
|
||||
*
|
||||
* @warning Background submission is not supported for extensions on iOS or OS X.
|
||||
*
|
||||
* @param crashlytics The Crashlytics singleton instance
|
||||
*
|
||||
* @return Return NO if you don't want out-of-process background network operations.
|
||||
*
|
||||
*/
|
||||
- (BOOL)crashlyticsCanUseBackgroundSessions:(Crashlytics *)crashlytics;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, use Crashlytics.sharedInstance()
|
||||
*/
|
||||
#define CrashlyticsKit [Crashlytics sharedInstance]
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
BIN
platform-ios/External/iOS/Crashlytics.framework/Info.plist
vendored
Normal file
BIN
platform-ios/External/iOS/Crashlytics.framework/Info.plist
vendored
Normal file
Binary file not shown.
14
platform-ios/External/iOS/Crashlytics.framework/Modules/module.modulemap
vendored
Normal file
14
platform-ios/External/iOS/Crashlytics.framework/Modules/module.modulemap
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
framework module Crashlytics {
|
||||
header "Crashlytics.h"
|
||||
header "Answers.h"
|
||||
header "ANSCompatibility.h"
|
||||
header "CLSLogging.h"
|
||||
header "CLSReport.h"
|
||||
header "CLSStackFrame.h"
|
||||
header "CLSAttributes.h"
|
||||
|
||||
export *
|
||||
|
||||
link "z"
|
||||
link "c++"
|
||||
}
|
||||
28
platform-ios/External/iOS/Crashlytics.framework/run
vendored
Executable file
28
platform-ios/External/iOS/Crashlytics.framework/run
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
# run
|
||||
#
|
||||
# Copyright (c) 2015 Crashlytics. All rights reserved.
|
||||
|
||||
# Figure out where we're being called from
|
||||
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
# Quote path in case of spaces or special chars
|
||||
DIR="\"${DIR}"
|
||||
|
||||
PATH_SEP="/"
|
||||
VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script"
|
||||
UPLOAD_COMMAND="uploadDSYM\" $@ run-script"
|
||||
|
||||
# Ensure params are as expected, run in sync mode to validate
|
||||
eval $DIR$PATH_SEP$VALIDATE_COMMAND
|
||||
return_code=$?
|
||||
|
||||
if [[ $return_code != 0 ]]; then
|
||||
exit $return_code
|
||||
fi
|
||||
|
||||
# Verification passed, upload dSYM in background to prevent Xcode from waiting
|
||||
# Note: Validation is performed again before upload.
|
||||
# Output can still be found in Console.app
|
||||
eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 &
|
||||
BIN
platform-ios/External/iOS/Crashlytics.framework/submit
vendored
Executable file
BIN
platform-ios/External/iOS/Crashlytics.framework/submit
vendored
Executable file
Binary file not shown.
BIN
platform-ios/External/iOS/Crashlytics.framework/uploadDSYM
vendored
Executable file
BIN
platform-ios/External/iOS/Crashlytics.framework/uploadDSYM
vendored
Executable file
Binary file not shown.
BIN
platform-ios/External/iOS/Fabric.framework/Fabric
vendored
Executable file
BIN
platform-ios/External/iOS/Fabric.framework/Fabric
vendored
Executable file
Binary file not shown.
60
platform-ios/External/iOS/Fabric.framework/Headers/FABAttributes.h
vendored
Normal file
60
platform-ios/External/iOS/Fabric.framework/Headers/FABAttributes.h
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// FABAttributes.h
|
||||
// Fabric
|
||||
//
|
||||
// Copyright (C) 2015 Twitter, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define FAB_UNAVAILABLE(x) __attribute__((unavailable(x)))
|
||||
|
||||
#if __has_feature(nullability)
|
||||
#define fab_nullable nullable
|
||||
#define fab_nonnull nonnull
|
||||
#define fab_null_unspecified null_unspecified
|
||||
#define fab_null_resettable null_resettable
|
||||
#define __fab_nullable __nullable
|
||||
#define __fab_nonnull __nonnull
|
||||
#define __fab_null_unspecified __null_unspecified
|
||||
#else
|
||||
#define fab_nullable
|
||||
#define fab_nonnull
|
||||
#define fab_null_unspecified
|
||||
#define fab_null_resettable
|
||||
#define __fab_nullable
|
||||
#define __fab_nonnull
|
||||
#define __fab_null_unspecified
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* The following macros are defined here to provide
|
||||
* backwards compatability. If you are still using
|
||||
* them you should migrate to the new versions that
|
||||
* are defined above.
|
||||
*/
|
||||
#define FAB_NONNULL __fab_nonnull
|
||||
#define FAB_NULLABLE __fab_nullable
|
||||
#define FAB_START_NONNULL NS_ASSUME_NONNULL_BEGIN
|
||||
#define FAB_END_NONNULL NS_ASSUME_NONNULL_END
|
||||
77
platform-ios/External/iOS/Fabric.framework/Headers/Fabric.h
vendored
Normal file
77
platform-ios/External/iOS/Fabric.framework/Headers/Fabric.h
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Fabric.h
|
||||
// Fabric
|
||||
//
|
||||
// Copyright (C) 2015 Twitter, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FABAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
|
||||
#error "Fabric's minimum iOS version is 6.0"
|
||||
#endif
|
||||
#else
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070
|
||||
#error "Fabric's minimum OS X version is 10.7"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Fabric Base. Coordinates configuration and starts all provided kits.
|
||||
*/
|
||||
@interface Fabric : NSObject
|
||||
|
||||
/**
|
||||
* Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use.
|
||||
*
|
||||
* For example, in Objective-C:
|
||||
*
|
||||
* `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];`
|
||||
*
|
||||
* Swift:
|
||||
*
|
||||
* `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])`
|
||||
*
|
||||
* Only the first call to this method is honored. Subsequent calls are no-ops.
|
||||
*
|
||||
* @param kitClasses An array of kit Class objects
|
||||
*
|
||||
* @return Returns the shared Fabric instance. In most cases this can be ignored.
|
||||
*/
|
||||
+ (instancetype)with:(NSArray *)kitClasses;
|
||||
|
||||
/**
|
||||
* Returns the Fabric singleton object.
|
||||
*/
|
||||
+ (instancetype)sharedSDK;
|
||||
|
||||
/**
|
||||
* This BOOL enables or disables debug logging, such as kit version information. The default value is NO.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL debug;
|
||||
|
||||
/**
|
||||
* Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance.
|
||||
*/
|
||||
- (id)init FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance.");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
57
platform-ios/External/iOS/Fabric.framework/Info.plist
vendored
Normal file
57
platform-ios/External/iOS/Fabric.framework/Info.plist
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>14F1021</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Fabric</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.fabric.sdk.ios</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Fabric</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.6.7</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>iPhoneOS</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>53</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>13C75</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>iphoneos</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>9.2</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>13C75</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>iphoneos9.2</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0721</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>7C1002</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 Twitter. All rights reserved.</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>3</integer>
|
||||
<integer>2</integer>
|
||||
<integer>1</integer>
|
||||
<integer>4</integer>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
6
platform-ios/External/iOS/Fabric.framework/Modules/module.modulemap
vendored
Normal file
6
platform-ios/External/iOS/Fabric.framework/Modules/module.modulemap
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module Fabric {
|
||||
umbrella header "Fabric.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
28
platform-ios/External/iOS/Fabric.framework/run
vendored
Executable file
28
platform-ios/External/iOS/Fabric.framework/run
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
# run
|
||||
#
|
||||
# Copyright (c) 2015 Crashlytics. All rights reserved.
|
||||
|
||||
# Figure out where we're being called from
|
||||
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
# Quote path in case of spaces or special chars
|
||||
DIR="\"${DIR}"
|
||||
|
||||
PATH_SEP="/"
|
||||
VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script"
|
||||
UPLOAD_COMMAND="uploadDSYM\" $@ run-script"
|
||||
|
||||
# Ensure params are as expected, run in sync mode to validate
|
||||
eval $DIR$PATH_SEP$VALIDATE_COMMAND
|
||||
return_code=$?
|
||||
|
||||
if [[ $return_code != 0 ]]; then
|
||||
exit $return_code
|
||||
fi
|
||||
|
||||
# Verification passed, upload dSYM in background to prevent Xcode from waiting
|
||||
# Note: Validation is performed again before upload.
|
||||
# Output can still be found in Console.app
|
||||
eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 &
|
||||
BIN
platform-ios/External/iOS/Fabric.framework/uploadDSYM
vendored
Executable file
BIN
platform-ios/External/iOS/Fabric.framework/uploadDSYM
vendored
Executable file
Binary file not shown.
1
platform-ios/External/iOS/Reveal.framework
vendored
Symbolic link
1
platform-ios/External/iOS/Reveal.framework
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/Reveal.framework
|
||||
1
platform-ios/External/jrswizzle
vendored
Submodule
1
platform-ios/External/jrswizzle
vendored
Submodule
Submodule platform-ios/External/jrswizzle added at 98d18aee73
1
platform-ios/External/uicolor-utilities
vendored
Submodule
1
platform-ios/External/uicolor-utilities
vendored
Submodule
Submodule platform-ios/External/uicolor-utilities added at ae96212a49
10
platform-ios/MasterPassword.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
platform-ios/MasterPassword.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>1DC75A27-0030-4493-ACE8-E1D49AB9A549</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>MasterPassword</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</key>
|
||||
<string>git://github.com/lhunath/InAppSettingsKit.git</string>
|
||||
<key>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</key>
|
||||
<string>git://github.com/Lyndir/love-lyndir.client.git</string>
|
||||
<key>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</key>
|
||||
<string>github.com:Lyndir/Lyndir.git</string>
|
||||
<key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key>
|
||||
<string>git://github.com/lhunath/uicolor-utilities.git</string>
|
||||
<key>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</key>
|
||||
<string>https://github.com/CFKevinRef/KCOrderedAccessorFix.git</string>
|
||||
<key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key>
|
||||
<string>git://github.com/lhunath/RHStatusItemView.git</string>
|
||||
<key>3ED8592497DB6A564366943C9AAD5A46341B5076</key>
|
||||
<string>https://github.com/dreamwieber/AttributedMarkdown.git</string>
|
||||
<key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key>
|
||||
<string>ssh://github.com/Lyndir/Pearl.git</string>
|
||||
<key>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</key>
|
||||
<string>git://github.com/jonmarimba/jrswizzle.git</string>
|
||||
<key>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</key>
|
||||
<string>git://github.com/lhunath/UbiquityStoreManager.git</string>
|
||||
<key>F788B28042EDBEF29EFE34687DA79A778C2CC260</key>
|
||||
<string>ssh://github.com/Lyndir/MasterPassword.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>MasterPassword.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</key>
|
||||
<string>../External/InAppSettingsKit</string>
|
||||
<key>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</key>
|
||||
<string>../External/LoveLyndir</string>
|
||||
<key>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</key>
|
||||
<string>../..</string>
|
||||
<key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key>
|
||||
<string>../External/Pearl/External/uicolor-utilities</string>
|
||||
<key>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</key>
|
||||
<string>../External/KCOrderedAccessorFix</string>
|
||||
<key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key>
|
||||
<string>../External/RHStatusItemView</string>
|
||||
<key>3ED8592497DB6A564366943C9AAD5A46341B5076</key>
|
||||
<string>../External/AttributedMarkdown</string>
|
||||
<key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key>
|
||||
<string>../External/Pearl</string>
|
||||
<key>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</key>
|
||||
<string>../External/Pearl/External/jrswizzle</string>
|
||||
<key>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</key>
|
||||
<string>../External/UbiquityStoreManager</string>
|
||||
<key>F788B28042EDBEF29EFE34687DA79A778C2CC260</key>
|
||||
<string>..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>ssh://github.com/Lyndir/MasterPassword.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>F788B28042EDBEF29EFE34687DA79A778C2CC260</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>3ED8592497DB6A564366943C9AAD5A46341B5076</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>AttributedMarkdown</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>InAppSettingsKit</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>jrswizzle</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>KCOrderedAccessorFix</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>LoveLyndir</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>F788B28042EDBEF29EFE34687DA79A778C2CC260</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>MasterPassword</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>Pearl</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>3E67FB08419C920516AAC3B00DAAF23073B8CF77</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>RHStatusItemView</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>UbiquityStoreManager</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>uicolor-utilities</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "F788B28042EDBEF29EFE34687DA79A778C2CC260",
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
|
||||
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
|
||||
"304AD0F97EA7B4893D91DFB8C3413D4E627B9472" : 0,
|
||||
"2A70319CE0F91B35406CA7D970AE7CB4957B0A75" : 0,
|
||||
"8A15A8EA0B3D0B497C4883425BC74DF995224BB3" : 0,
|
||||
"1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA" : 0,
|
||||
"1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A" : 0,
|
||||
"3E67FB08419C920516AAC3B00DAAF23073B8CF77" : 0,
|
||||
"3ED8592497DB6A564366943C9AAD5A46341B5076" : 0,
|
||||
"4DDCFFD91B41F00326AD14553BD66CFD366ABD91" : 0,
|
||||
"E47DEC29CB0D0FDE3560EF46E1808FA1C723D657" : 0,
|
||||
"2FE140B36B7D26140DC8D5E5C639DC5900EFCF35" : 0,
|
||||
"F788B28042EDBEF29EFE34687DA79A778C2CC260" : 0
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "1DC75A27-0030-4493-ACE8-E1D49AB9A549",
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
|
||||
"304AD0F97EA7B4893D91DFB8C3413D4E627B9472" : "MasterPassword\/External\/KCOrderedAccessorFix\/",
|
||||
"2A70319CE0F91B35406CA7D970AE7CB4957B0A75" : "",
|
||||
"8A15A8EA0B3D0B497C4883425BC74DF995224BB3" : "MasterPassword\/External\/jrswizzle\/",
|
||||
"1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA" : "MasterPassword\/External\/InAppSettingsKit\/",
|
||||
"1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A" : "MasterPassword\/External\/LoveLyndir",
|
||||
"3E67FB08419C920516AAC3B00DAAF23073B8CF77" : "MasterPassword\/External\/RHStatusItemView",
|
||||
"3ED8592497DB6A564366943C9AAD5A46341B5076" : "MasterPassword\/External\/AttributedMarkdown\/",
|
||||
"4DDCFFD91B41F00326AD14553BD66CFD366ABD91" : "MasterPassword\/External\/Pearl\/",
|
||||
"E47DEC29CB0D0FDE3560EF46E1808FA1C723D657" : "MasterPassword\/External\/UbiquityStoreManager",
|
||||
"2FE140B36B7D26140DC8D5E5C639DC5900EFCF35" : "MasterPassword\/External\/uicolor-utilities\/",
|
||||
"F788B28042EDBEF29EFE34687DA79A778C2CC260" : "MasterPassword\/"
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintNameKey" : "MasterPassword",
|
||||
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
|
||||
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "MasterPassword.xcworkspace",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git:\/\/github.com\/lhunath\/InAppSettingsKit.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git:\/\/github.com\/Lyndir\/love-lyndir.client.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:Lyndir\/Lyndir.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "2A70319CE0F91B35406CA7D970AE7CB4957B0A75"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git:\/\/github.com\/lhunath\/uicolor-utilities.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "2FE140B36B7D26140DC8D5E5C639DC5900EFCF35"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/CFKevinRef\/KCOrderedAccessorFix.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "304AD0F97EA7B4893D91DFB8C3413D4E627B9472"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git:\/\/github.com\/lhunath\/RHStatusItemView.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "3E67FB08419C920516AAC3B00DAAF23073B8CF77"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/dreamwieber\/AttributedMarkdown.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "3ED8592497DB6A564366943C9AAD5A46341B5076"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "ssh:\/\/github.com\/Lyndir\/Pearl.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "4DDCFFD91B41F00326AD14553BD66CFD366ABD91"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git:\/\/github.com\/jonmarimba\/jrswizzle.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8A15A8EA0B3D0B497C4883425BC74DF995224BB3"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git:\/\/github.com\/lhunath\/UbiquityStoreManager.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E47DEC29CB0D0FDE3560EF46E1808FA1C723D657"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "ssh:\/\/github.com\/Lyndir\/MasterPassword.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "F788B28042EDBEF29EFE34687DA79A778C2CC260"
|
||||
}
|
||||
]
|
||||
}
|
||||
98
platform-ios/ObjC/MPAlgorithm.h
Normal file
98
platform-ios/ObjC/MPAlgorithm.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithm
|
||||
//
|
||||
// Created by Maarten Billemont on 16/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPKey.h"
|
||||
#import "MPStoredSiteEntity.h"
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "mpw-algorithm.h"
|
||||
|
||||
#define MPAlgorithmDefaultVersion MPAlgorithmVersionCurrent
|
||||
#define MPAlgorithmDefault MPAlgorithmForVersion(MPAlgorithmDefaultVersion)
|
||||
|
||||
id<MPAlgorithm> MPAlgorithmForVersion(MPAlgorithmVersion version);
|
||||
id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion);
|
||||
|
||||
PearlEnum( MPAttacker,
|
||||
MPAttacker1, MPAttacker5K, MPAttacker20M, MPAttacker5B );
|
||||
|
||||
typedef struct TimeToCrack {
|
||||
unsigned long long hours;
|
||||
unsigned long long days;
|
||||
unsigned long long weeks;
|
||||
unsigned long long months;
|
||||
unsigned long long years;
|
||||
unsigned long long universes;
|
||||
} TimeToCrack;
|
||||
|
||||
NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
||||
|
||||
@protocol MPAlgorithm<NSObject>
|
||||
|
||||
@required
|
||||
- (MPAlgorithmVersion)version;
|
||||
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
|
||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
|
||||
|
||||
- (NSData *)keyIDForKeyData:(NSData *)keyData;
|
||||
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||
|
||||
- (NSString *)nameOfType:(MPSiteType)type;
|
||||
- (NSString *)shortNameOfType:(MPSiteType)type;
|
||||
- (NSString *)classNameOfType:(MPSiteType)type;
|
||||
- (Class)classOfType:(MPSiteType)type;
|
||||
- (NSArray *)allTypes;
|
||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
|
||||
- (MPSiteType)nextType:(MPSiteType)type;
|
||||
- (MPSiteType)previousType:(MPSiteType)type;
|
||||
|
||||
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
|
||||
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||
usingKey:(MPKey *)key;
|
||||
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
|
||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key;
|
||||
|
||||
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
||||
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
||||
|
||||
- (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||
|
||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey;
|
||||
|
||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
||||
result:(void ( ^ )(NSString *result))resultBlock;
|
||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
||||
result:(void ( ^ )(NSString *result))resultBlock;
|
||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
||||
result:(void ( ^ )(NSString *result))resultBlock;
|
||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
||||
result:(void ( ^ )(NSString *result))resultBlock;
|
||||
|
||||
- (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
|
||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||
- (void)importClearTextPassword:(NSString *)clearPassword intoSite:(MPSiteEntity *)site
|
||||
usingKey:(MPKey *)siteKey;
|
||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||
|
||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker;
|
||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
|
||||
|
||||
@end
|
||||
61
platform-ios/ObjC/MPAlgorithm.m
Normal file
61
platform-ios/ObjC/MPAlgorithm.m
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithm
|
||||
//
|
||||
// Created by Maarten Billemont on 16/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithm.h"
|
||||
|
||||
id<MPAlgorithm> MPAlgorithmForVersion(MPAlgorithmVersion version) {
|
||||
|
||||
static NSMutableDictionary *versionToAlgorithm = nil;
|
||||
if (!versionToAlgorithm)
|
||||
versionToAlgorithm = [NSMutableDictionary dictionary];
|
||||
|
||||
id<MPAlgorithm> algorithm = versionToAlgorithm[@(version)];
|
||||
if (!algorithm && (algorithm = (id<MPAlgorithm>)[NSClassFromString( strf( @"MPAlgorithmV%lu", (unsigned long)version ) ) new]))
|
||||
versionToAlgorithm[@(version)] = algorithm;
|
||||
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion) {
|
||||
|
||||
if (PearlCFBundleVersionCompare( bundleVersion, @"1.3" ) == NSOrderedAscending)
|
||||
// Pre-1.3
|
||||
return MPAlgorithmForVersion( 0 );
|
||||
if (PearlCFBundleVersionCompare( bundleVersion, @"2.1" ) == NSOrderedAscending)
|
||||
// Pre-2.1
|
||||
return MPAlgorithmForVersion( 1 );
|
||||
|
||||
return MPAlgorithmDefault;
|
||||
}
|
||||
|
||||
NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack) {
|
||||
|
||||
if (timeToCrack.universes > 1)
|
||||
return strl( @"> age of the universe" );
|
||||
else if (timeToCrack.years > 1)
|
||||
return strl( @"%d years", timeToCrack.years );
|
||||
else if (timeToCrack.months > 1)
|
||||
return strl( @"%d months", timeToCrack.months );
|
||||
else if (timeToCrack.weeks > 1)
|
||||
return strl( @"%d weeks", timeToCrack.weeks );
|
||||
else if (timeToCrack.days > 1)
|
||||
return strl( @"%d days", timeToCrack.days );
|
||||
else if (timeToCrack.hours > 1)
|
||||
return strl( @"%d hours", timeToCrack.hours );
|
||||
else
|
||||
return strl( @"trivial" );
|
||||
}
|
||||
21
platform-ios/ObjC/MPAlgorithmV0.h
Normal file
21
platform-ios/ObjC/MPAlgorithmV0.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithmV0
|
||||
//
|
||||
// Created by Maarten Billemont on 16/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithm.h"
|
||||
|
||||
@interface MPAlgorithmV0 : NSObject<MPAlgorithm>
|
||||
@end
|
||||
894
platform-ios/ObjC/MPAlgorithmV0.m
Normal file
894
platform-ios/ObjC/MPAlgorithmV0.m
Normal file
@@ -0,0 +1,894 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithmV0
|
||||
//
|
||||
// Created by Maarten Billemont on 16/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef trc
|
||||
#error error
|
||||
#endif
|
||||
#import "MPAlgorithmV0.h"
|
||||
#import "MPEntities.h"
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
#import "MPAppDelegate_InApp.h"
|
||||
#import "mpw-util.h"
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
/* An AMD HD 7970 calculates 2495M SHA-1 hashes per second at a cost of ~350$ per GPU */
|
||||
#define CRACKING_PER_SECOND 2495000000UL
|
||||
#define CRACKING_PRICE 350
|
||||
|
||||
NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
@implementation MPAlgorithmV0 {
|
||||
BN_CTX *_ctx;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
_ctx = BN_CTX_new();
|
||||
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
_mpwQueue = [NSOperationQueue new];
|
||||
_mpwQueue.maxConcurrentOperationCount = 1;
|
||||
_mpwQueue.name = @"mpw queue";
|
||||
} );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
|
||||
BN_CTX_free( _ctx );
|
||||
_ctx = NULL;
|
||||
}
|
||||
|
||||
- (MPAlgorithmVersion)version {
|
||||
|
||||
return MPAlgorithmVersion0;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
|
||||
return strf( @"V%lu", (unsigned long)self.version );
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
|
||||
return strf( @"<%@: version=%lu>", NSStringFromClass( [self class] ), (unsigned long)self.version );
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other {
|
||||
|
||||
if (other == self)
|
||||
return YES;
|
||||
if (!other || ![other conformsToProtocol:@protocol(MPAlgorithm)])
|
||||
return NO;
|
||||
|
||||
return [(id<MPAlgorithm>)other version] == [self version];
|
||||
}
|
||||
|
||||
- (void)mpw_perform:(void ( ^ )(void))operationBlock {
|
||||
|
||||
if ([NSOperationQueue currentQueue] == _mpwQueue) {
|
||||
operationBlock();
|
||||
return;
|
||||
}
|
||||
|
||||
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:operationBlock];
|
||||
if ([operation respondsToSelector:@selector( qualityOfService )])
|
||||
operation.qualityOfService = NSQualityOfServiceUserInitiated;
|
||||
[_mpwQueue addOperations:@[ operation ] waitUntilFinished:YES];
|
||||
}
|
||||
|
||||
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
|
||||
|
||||
NSError *error = nil;
|
||||
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
|
||||
NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
|
||||
if (!migrationSites) {
|
||||
err( @"While looking for sites to migrate: %@", [error fullDescription] );
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL success = YES;
|
||||
for (MPSiteEntity *migrationSite in migrationSites)
|
||||
if (![migrationSite tryMigrateExplicitly:NO])
|
||||
success = NO;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||
|
||||
if ([site.algorithm version] != [self version] - 1)
|
||||
// Only migrate from previous version.
|
||||
return NO;
|
||||
|
||||
if (!explicit) {
|
||||
// This migration requires explicit permission.
|
||||
site.requiresExplicitMigration = YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Apply migration.
|
||||
site.requiresExplicitMigration = NO;
|
||||
site.algorithm = self;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
||||
|
||||
__block NSData *keyData;
|
||||
[self mpw_perform:^{
|
||||
NSDate *start = [NSDate date];
|
||||
uint8_t const *masterKeyBytes = mpw_masterKeyForUser( fullName.UTF8String, masterPassword.UTF8String, [self version] );
|
||||
if (masterKeyBytes) {
|
||||
keyData = [NSData dataWithBytes:masterKeyBytes length:MP_dkLen];
|
||||
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", //
|
||||
fullName, masterPassword, [self keyIDForKeyData:keyData], -[start timeIntervalSinceNow] );
|
||||
mpw_free( masterKeyBytes, MP_dkLen );
|
||||
}
|
||||
}];
|
||||
|
||||
return keyData;
|
||||
}
|
||||
|
||||
- (NSData *)keyIDForKeyData:(NSData *)keyData {
|
||||
|
||||
return [keyData hashWith:PearlHashSHA256];
|
||||
}
|
||||
|
||||
- (NSString *)nameOfType:(MPSiteType)type {
|
||||
|
||||
if (!type)
|
||||
return nil;
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
return @"Maximum Security Password";
|
||||
|
||||
case MPSiteTypeGeneratedLong:
|
||||
return @"Long Password";
|
||||
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
return @"Medium Password";
|
||||
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
return @"Basic Password";
|
||||
|
||||
case MPSiteTypeGeneratedShort:
|
||||
return @"Short Password";
|
||||
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
return @"PIN";
|
||||
|
||||
case MPSiteTypeGeneratedName:
|
||||
return @"Name";
|
||||
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
return @"Phrase";
|
||||
|
||||
case MPSiteTypeStoredPersonal:
|
||||
return @"Personal Password";
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
return @"Device Private Password";
|
||||
}
|
||||
|
||||
Throw( @"Type not supported: %lu", (long)type );
|
||||
}
|
||||
|
||||
- (NSString *)shortNameOfType:(MPSiteType)type {
|
||||
|
||||
if (!type)
|
||||
return nil;
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
return @"Maximum";
|
||||
|
||||
case MPSiteTypeGeneratedLong:
|
||||
return @"Long";
|
||||
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
return @"Medium";
|
||||
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
return @"Basic";
|
||||
|
||||
case MPSiteTypeGeneratedShort:
|
||||
return @"Short";
|
||||
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
return @"PIN";
|
||||
|
||||
case MPSiteTypeGeneratedName:
|
||||
return @"Name";
|
||||
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
return @"Phrase";
|
||||
|
||||
case MPSiteTypeStoredPersonal:
|
||||
return @"Personal";
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
return @"Device";
|
||||
}
|
||||
|
||||
Throw( @"Type not supported: %lu", (long)type );
|
||||
}
|
||||
|
||||
- (NSString *)classNameOfType:(MPSiteType)type {
|
||||
|
||||
return NSStringFromClass( [self classOfType:type] );
|
||||
}
|
||||
|
||||
- (Class)classOfType:(MPSiteType)type {
|
||||
|
||||
if (!type)
|
||||
Throw( @"No type given." );
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedLong:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedShort:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedName:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeStoredPersonal:
|
||||
return [MPStoredSiteEntity class];
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
return [MPStoredSiteEntity class];
|
||||
}
|
||||
|
||||
Throw( @"Type not supported: %lu", (long)type );
|
||||
}
|
||||
|
||||
- (NSArray *)allTypes {
|
||||
|
||||
return [self allTypesStartingWith:MPSiteTypeGeneratedPhrase];
|
||||
}
|
||||
|
||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType {
|
||||
|
||||
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
|
||||
MPSiteType currentType = startingType;
|
||||
do {
|
||||
[allTypes addObject:@(currentType)];
|
||||
} while ((currentType = [self nextType:currentType]) != startingType);
|
||||
|
||||
return allTypes;
|
||||
}
|
||||
|
||||
- (MPSiteType)nextType:(MPSiteType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
return MPSiteTypeGeneratedName;
|
||||
case MPSiteTypeGeneratedName:
|
||||
return MPSiteTypeGeneratedMaximum;
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
return MPSiteTypeGeneratedLong;
|
||||
case MPSiteTypeGeneratedLong:
|
||||
return MPSiteTypeGeneratedMedium;
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
return MPSiteTypeGeneratedBasic;
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
return MPSiteTypeGeneratedShort;
|
||||
case MPSiteTypeGeneratedShort:
|
||||
return MPSiteTypeGeneratedPIN;
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
return MPSiteTypeStoredPersonal;
|
||||
case MPSiteTypeStoredPersonal:
|
||||
return MPSiteTypeStoredDevicePrivate;
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
return MPSiteTypeGeneratedPhrase;
|
||||
default:
|
||||
return MPSiteTypeGeneratedLong;
|
||||
}
|
||||
}
|
||||
|
||||
- (MPSiteType)previousType:(MPSiteType)type {
|
||||
|
||||
MPSiteType previousType = type, nextType = type;
|
||||
while ((nextType = [self nextType:nextType]) != type)
|
||||
previousType = nextType;
|
||||
|
||||
return previousType;
|
||||
}
|
||||
|
||||
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
|
||||
|
||||
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedName withCounter:1
|
||||
variant:MPSiteVariantLogin context:nil usingKey:key];
|
||||
}
|
||||
|
||||
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||
usingKey:(MPKey *)key {
|
||||
|
||||
return [self generateContentForSiteNamed:name ofType:type withCounter:counter
|
||||
variant:MPSiteVariantPassword context:nil usingKey:key];
|
||||
}
|
||||
|
||||
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key {
|
||||
|
||||
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedPhrase withCounter:1
|
||||
variant:MPSiteVariantAnswer context:question usingKey:key];
|
||||
}
|
||||
|
||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
|
||||
|
||||
__block NSString *content;
|
||||
[self mpw_perform:^{
|
||||
char const *contentBytes = mpw_passwordForSite( [key keyDataForAlgorithm:self].bytes,
|
||||
name.UTF8String, type, (uint32_t)counter, variant, context.UTF8String, [self version] );
|
||||
if (contentBytes) {
|
||||
content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding];
|
||||
mpw_free_string( contentBytes );
|
||||
}
|
||||
}];
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
|
||||
|
||||
return [self decryptContent:site.contentObject usingKey:key];
|
||||
}
|
||||
|
||||
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase: {
|
||||
wrn( @"Cannot save content to site with generated type %lu.", (long)site.type );
|
||||
return NO;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
||||
encryptWithSymmetricKey:encryptionKey padding:YES];
|
||||
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
|
||||
return NO;
|
||||
|
||||
((MPStoredSiteEntity *)site).contentObject = encryptedContent;
|
||||
return YES;
|
||||
}
|
||||
case MPSiteTypeStoredDevicePrivate: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
||||
encryptWithSymmetricKey:encryptionKey padding:YES];
|
||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||
if (!encryptedContent)
|
||||
[PearlKeyChain deleteItemForQuery:siteQuery];
|
||||
else
|
||||
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
|
||||
(__bridge id)kSecValueData : encryptedContent,
|
||||
#if TARGET_OS_IPHONE
|
||||
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
||||
#endif
|
||||
}];
|
||||
((MPStoredSiteEntity *)site).contentObject = nil;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
Throw( @"Unsupported type: %ld", (long)site.type );
|
||||
}
|
||||
|
||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
NSString *name = site.name;
|
||||
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
|
||||
NSString *loginName = site.loginName;
|
||||
id<MPAlgorithm> algorithm = nil;
|
||||
if (!name.length)
|
||||
err( @"Missing name." );
|
||||
else if (!siteKey)
|
||||
err( @"Missing key." );
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
resultBlock( loginName || !loginGenerated? loginName:
|
||||
[algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase: {
|
||||
if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
||||
wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
break;
|
||||
}
|
||||
|
||||
NSString *name = site.name;
|
||||
MPSiteType type = site.type;
|
||||
NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||
id<MPAlgorithm> algorithm = nil;
|
||||
if (!site.name.length)
|
||||
err( @"Missing name." );
|
||||
else if (!siteKey)
|
||||
err( @"Missing key." );
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
break;
|
||||
}
|
||||
|
||||
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
case MPSiteTypeStoredDevicePrivate: {
|
||||
NSAssert( [site isKindOfClass:[MPStoredSiteEntity class]],
|
||||
@"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", (long)site.type,
|
||||
[site class] );
|
||||
|
||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
NSString *name = site.name;
|
||||
id<MPAlgorithm> algorithm = nil;
|
||||
if (!site.name.length)
|
||||
err( @"Missing name." );
|
||||
else if (!siteKey)
|
||||
err( @"Missing key." );
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
||||
result:(void ( ^ )(NSString *result))resultBlock {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:question.site.user.algorithm] isEqualToData:question.site.user.keyID],
|
||||
@"Site does not belong to current user." );
|
||||
NSString *name = question.site.name;
|
||||
NSString *keyword = question.keyword;
|
||||
id<MPAlgorithm> algorithm = nil;
|
||||
if (!name.length)
|
||||
err( @"Missing name." );
|
||||
else if (!siteKey)
|
||||
err( @"Missing key." );
|
||||
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
|
||||
algorithm = question.site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
break;
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
break;
|
||||
}
|
||||
if ([[importKey keyIDForAlgorithm:self] isEqualToData:[siteKey keyIDForAlgorithm:self]])
|
||||
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
|
||||
|
||||
else {
|
||||
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
|
||||
[self importClearTextPassword:clearContent intoSite:site usingKey:siteKey];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)importClearTextPassword:(NSString *)clearContent intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
break;
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
[self savePassword:clearContent toSite:site usingKey:siteKey];
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
if (!(site.type & MPSiteFeatureExportContent))
|
||||
return nil;
|
||||
|
||||
NSString *result = nil;
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase: {
|
||||
result = nil;
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
break;
|
||||
}
|
||||
result = [((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate: {
|
||||
result = nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)migrateExplicitly:(BOOL)explicit {
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSDictionary *)queryForDevicePrivateSiteNamed:(NSString *)name {
|
||||
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService : @"DevicePrivate",
|
||||
(__bridge id)kSecAttrAccount : name
|
||||
}
|
||||
matches:nil];
|
||||
}
|
||||
|
||||
- (NSString *)decryptContent:(NSData *)encryptedContent usingKey:(MPKey *)key {
|
||||
|
||||
if (!key)
|
||||
return nil;
|
||||
NSData *decryptedContent = nil;
|
||||
if ([encryptedContent length]) {
|
||||
NSData *encryptionKey = [key keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||
decryptedContent = [encryptedContent decryptWithSymmetricKey:encryptionKey padding:YES];
|
||||
}
|
||||
if (!decryptedContent)
|
||||
return nil;
|
||||
|
||||
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
|
||||
|
||||
if (!(type & MPSiteTypeClassGenerated))
|
||||
return NO;
|
||||
size_t count = 0;
|
||||
const char **templates = mpw_templatesForType( type, &count );
|
||||
if (!templates)
|
||||
return NO;
|
||||
|
||||
BIGNUM *permutations = BN_new(), *templatePermutations = BN_new();
|
||||
for (size_t t = 0; t < count; ++t) {
|
||||
const char *template = templates[t];
|
||||
BN_one( templatePermutations );
|
||||
|
||||
for (NSUInteger c = 0; c < strlen( template ); ++c)
|
||||
BN_mul_word( templatePermutations,
|
||||
(BN_ULONG)strlen( mpw_charactersInClass( template[c] ) ) );
|
||||
|
||||
BN_add( permutations, permutations, templatePermutations );
|
||||
}
|
||||
BN_free( templatePermutations );
|
||||
free( templates );
|
||||
|
||||
return [self timeToCrack:timeToCrack permutations:permutations forAttacker:attacker];
|
||||
}
|
||||
|
||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker {
|
||||
|
||||
BIGNUM *permutations = BN_new();
|
||||
BN_one( permutations );
|
||||
|
||||
for (NSUInteger c = 0; c < [password length]; ++c) {
|
||||
const char passwordCharacter = [password substringWithRange:NSMakeRange( c, 1 )].UTF8String[0];
|
||||
|
||||
unsigned int characterEntropy = 0;
|
||||
for (NSString *characterClass in @[ @"v", @"c", @"a", @"x" ]) {
|
||||
char const *charactersForClass = mpw_charactersInClass( characterClass.UTF8String[0] );
|
||||
|
||||
if (strchr( charactersForClass, passwordCharacter )) {
|
||||
// Found class for password character.
|
||||
characterEntropy = (BN_ULONG)strlen( charactersForClass );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!characterEntropy)
|
||||
characterEntropy = 256 /* a byte */;
|
||||
|
||||
BN_mul_word( permutations, characterEntropy );
|
||||
}
|
||||
|
||||
return [self timeToCrack:timeToCrack permutations:permutations forAttacker:attacker];
|
||||
}
|
||||
|
||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack permutations:(BIGNUM *)permutations forAttacker:(MPAttacker)attacker {
|
||||
|
||||
// Determine base seconds needed to calculate the permutations.
|
||||
BIGNUM *secondsToCrack = BN_dup( permutations );
|
||||
BN_div_word( secondsToCrack, CRACKING_PER_SECOND );
|
||||
|
||||
// Modify seconds needed by applying our hardware budget.
|
||||
switch (attacker) {
|
||||
case MPAttacker1:
|
||||
break;
|
||||
case MPAttacker5K:
|
||||
BN_mul_word( secondsToCrack, CRACKING_PRICE );
|
||||
BN_div_word( secondsToCrack, 5000 );
|
||||
break;
|
||||
case MPAttacker20M:
|
||||
BN_mul_word( secondsToCrack, CRACKING_PRICE );
|
||||
BN_div_word( secondsToCrack, 20000000 );
|
||||
break;
|
||||
case MPAttacker5B:
|
||||
BN_mul_word( secondsToCrack, CRACKING_PRICE );
|
||||
BN_div_word( secondsToCrack, 5000 );
|
||||
BN_div_word( secondsToCrack, 1000000 );
|
||||
break;
|
||||
}
|
||||
|
||||
BIGNUM *max = BN_new();
|
||||
BN_set_word( max, (BN_ULONG)-1 );
|
||||
|
||||
BIGNUM *hoursToCrack = BN_dup( secondsToCrack );
|
||||
BN_div_word( hoursToCrack, 3600 );
|
||||
if (BN_cmp( hoursToCrack, max ) < 0)
|
||||
timeToCrack->hours = BN_get_word( hoursToCrack );
|
||||
else
|
||||
timeToCrack->hours = (BN_ULONG)-1;
|
||||
|
||||
BIGNUM *daysToCrack = BN_dup( hoursToCrack );
|
||||
BN_div_word( daysToCrack, 24 );
|
||||
if (BN_cmp( daysToCrack, max ) < 0)
|
||||
timeToCrack->days = BN_get_word( daysToCrack );
|
||||
else
|
||||
timeToCrack->days = (BN_ULONG)-1;
|
||||
|
||||
BIGNUM *weeksToCrack = BN_dup( daysToCrack );
|
||||
BN_div_word( weeksToCrack, 7 );
|
||||
if (BN_cmp( weeksToCrack, max ) < 0)
|
||||
timeToCrack->weeks = BN_get_word( weeksToCrack );
|
||||
else
|
||||
timeToCrack->weeks = (BN_ULONG)-1;
|
||||
|
||||
BIGNUM *monthsToCrack = BN_dup( daysToCrack );
|
||||
BN_div_word( monthsToCrack, 31 );
|
||||
if (BN_cmp( monthsToCrack, max ) < 0)
|
||||
timeToCrack->months = BN_get_word( monthsToCrack );
|
||||
else
|
||||
timeToCrack->months = (BN_ULONG)-1;
|
||||
|
||||
BIGNUM *yearsToCrack = BN_dup( daysToCrack );
|
||||
BN_div_word( yearsToCrack, 356 );
|
||||
if (BN_cmp( yearsToCrack, max ) < 0)
|
||||
timeToCrack->years = BN_get_word( yearsToCrack );
|
||||
else
|
||||
timeToCrack->years = (BN_ULONG)-1;
|
||||
|
||||
BIGNUM *universesToCrack = BN_dup( yearsToCrack );
|
||||
BN_div_word( universesToCrack, 14000 );
|
||||
BN_div_word( universesToCrack, 1000000 );
|
||||
if (BN_cmp( universesToCrack, max ) < 0)
|
||||
timeToCrack->universes = BN_get_word( universesToCrack );
|
||||
else
|
||||
timeToCrack->universes = (BN_ULONG)-1;
|
||||
|
||||
for (unsigned long error = ERR_get_error(); error; error = ERR_get_error())
|
||||
err( @"bignum error: %lu", error );
|
||||
|
||||
BN_free( max );
|
||||
BN_free( permutations );
|
||||
BN_free( secondsToCrack );
|
||||
BN_free( hoursToCrack );
|
||||
BN_free( daysToCrack );
|
||||
BN_free( weeksToCrack );
|
||||
BN_free( monthsToCrack );
|
||||
BN_free( yearsToCrack );
|
||||
BN_free( universesToCrack );
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
21
platform-ios/ObjC/MPAlgorithmV1.h
Normal file
21
platform-ios/ObjC/MPAlgorithmV1.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithmV1
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithmV0.h"
|
||||
|
||||
@interface MPAlgorithmV1 : MPAlgorithmV0
|
||||
@end
|
||||
48
platform-ios/ObjC/MPAlgorithmV1.m
Normal file
48
platform-ios/ObjC/MPAlgorithmV1.m
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithmV1
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithmV1.h"
|
||||
#import "MPEntities.h"
|
||||
|
||||
@implementation MPAlgorithmV1
|
||||
|
||||
- (MPAlgorithmVersion)version {
|
||||
|
||||
return MPAlgorithmVersion1;
|
||||
}
|
||||
|
||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||
|
||||
if ([site.algorithm version] != [self version] - 1)
|
||||
// Only migrate from previous version.
|
||||
return NO;
|
||||
|
||||
if (!explicit) {
|
||||
if (site.type & MPSiteTypeClassGenerated) {
|
||||
// This migration requires explicit permission for types of the generated class.
|
||||
site.requiresExplicitMigration = YES;
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply migration.
|
||||
site.requiresExplicitMigration = NO;
|
||||
site.algorithm = self;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
21
platform-ios/ObjC/MPAlgorithmV2.h
Normal file
21
platform-ios/ObjC/MPAlgorithmV2.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithmV2
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithmV1.h"
|
||||
|
||||
@interface MPAlgorithmV2 : MPAlgorithmV1
|
||||
@end
|
||||
48
platform-ios/ObjC/MPAlgorithmV2.m
Normal file
48
platform-ios/ObjC/MPAlgorithmV2.m
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithmV2
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithmV2.h"
|
||||
#import "MPEntities.h"
|
||||
|
||||
@implementation MPAlgorithmV2
|
||||
|
||||
- (MPAlgorithmVersion)version {
|
||||
|
||||
return MPAlgorithmVersion2;
|
||||
}
|
||||
|
||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||
|
||||
if ([site.algorithm version] != [self version] - 1)
|
||||
// Only migrate from previous version.
|
||||
return NO;
|
||||
|
||||
if (!explicit) {
|
||||
if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||
// This migration requires explicit permission for types of the generated class.
|
||||
site.requiresExplicitMigration = YES;
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply migration.
|
||||
site.requiresExplicitMigration = NO;
|
||||
site.algorithm = self;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
21
platform-ios/ObjC/MPAlgorithmV3.h
Normal file
21
platform-ios/ObjC/MPAlgorithmV3.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithmV3
|
||||
//
|
||||
// Created by Maarten Billemont on 13/01/15.
|
||||
// Copyright 2015 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithmV2.h"
|
||||
|
||||
@interface MPAlgorithmV3 : MPAlgorithmV2
|
||||
@end
|
||||
49
platform-ios/ObjC/MPAlgorithmV3.m
Normal file
49
platform-ios/ObjC/MPAlgorithmV3.m
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAlgorithmV3
|
||||
//
|
||||
// Created by Maarten Billemont on 13/01/15.
|
||||
// Copyright 2015 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithmV3.h"
|
||||
#import "MPEntities.h"
|
||||
|
||||
@implementation MPAlgorithmV3
|
||||
|
||||
- (MPAlgorithmVersion)version {
|
||||
|
||||
return MPAlgorithmVersion3;
|
||||
}
|
||||
|
||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||
|
||||
if ([site.algorithm version] != [self version] - 1)
|
||||
// Only migrate from previous version.
|
||||
return NO;
|
||||
|
||||
if (!explicit) {
|
||||
if (site.type & MPSiteTypeClassGenerated &&
|
||||
site.user.name.length != [site.user.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||
// This migration requires explicit permission for types of the generated class.
|
||||
site.requiresExplicitMigration = YES;
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply migration.
|
||||
site.requiresExplicitMigration = NO;
|
||||
site.algorithm = self;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
39
platform-ios/ObjC/MPAppDelegate_InApp.h
Normal file
39
platform-ios/ObjC/MPAppDelegate_InApp.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// MPAppDelegate_Key.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <StoreKit/StoreKit.h>
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
|
||||
#define MPProductGenerateLogins @"com.lyndir.masterpassword.products.generatelogins"
|
||||
#define MPProductGenerateAnswers @"com.lyndir.masterpassword.products.generateanswers"
|
||||
#define MPProductOSIntegration @"com.lyndir.masterpassword.products.osintegration"
|
||||
#define MPProductTouchID @"com.lyndir.masterpassword.products.touchid"
|
||||
#define MPProductFuel @"com.lyndir.masterpassword.products.fuel"
|
||||
|
||||
#define MP_FUEL_HOURLY_RATE 30.f /* Tier 1 purchases/h ~> USD/h */
|
||||
|
||||
@protocol MPInAppDelegate
|
||||
|
||||
- (void)updateWithProducts:(NSArray /* SKProduct */ *)products;
|
||||
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPAppDelegate_Shared(InApp)
|
||||
|
||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate;
|
||||
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate;
|
||||
|
||||
- (void)reloadProducts;
|
||||
- (BOOL)canMakePayments;
|
||||
- (BOOL)isFeatureUnlocked:(NSString *)productIdentifier;
|
||||
|
||||
- (void)restoreCompletedTransactions;
|
||||
- (void)purchaseProductWithIdentifier:(NSString *)productIdentifier quantity:(NSInteger)quantity;
|
||||
|
||||
@end
|
||||
181
platform-ios/ObjC/MPAppDelegate_InApp.m
Normal file
181
platform-ios/ObjC/MPAppDelegate_InApp.m
Normal file
@@ -0,0 +1,181 @@
|
||||
//
|
||||
// MPAppDelegate.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate_InApp.h"
|
||||
|
||||
@interface MPAppDelegate_Shared(InApp_Private)<SKProductsRequestDelegate, SKPaymentTransactionObserver>
|
||||
@end
|
||||
|
||||
@implementation MPAppDelegate_Shared(InApp)
|
||||
|
||||
PearlAssociatedObjectProperty( NSArray*, Products, products );
|
||||
PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObservers );
|
||||
|
||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate {
|
||||
|
||||
if (!self.productObservers)
|
||||
self.productObservers = [NSMutableArray array];
|
||||
[self.productObservers addObject:delegate];
|
||||
|
||||
if (self.products)
|
||||
[delegate updateWithProducts:self.products];
|
||||
else
|
||||
[self reloadProducts];
|
||||
}
|
||||
|
||||
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate {
|
||||
|
||||
[self.productObservers removeObject:delegate];
|
||||
}
|
||||
|
||||
- (void)reloadProducts {
|
||||
|
||||
SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:
|
||||
[[NSSet alloc] initWithObjects:MPProductGenerateLogins, MPProductGenerateAnswers, MPProductTouchID, MPProductFuel, nil]];
|
||||
productsRequest.delegate = self;
|
||||
[productsRequest start];
|
||||
}
|
||||
|
||||
- (SKPaymentQueue *)paymentQueue {
|
||||
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
|
||||
} );
|
||||
|
||||
return [SKPaymentQueue defaultQueue];
|
||||
}
|
||||
|
||||
- (BOOL)canMakePayments {
|
||||
|
||||
return [SKPaymentQueue canMakePayments];
|
||||
}
|
||||
|
||||
- (BOOL)isFeatureUnlocked:(NSString *)productIdentifier {
|
||||
|
||||
if (![productIdentifier length])
|
||||
// Missing a product.
|
||||
return NO;
|
||||
if ([productIdentifier isEqualToString:MPProductFuel])
|
||||
// Consumable product.
|
||||
return NO;
|
||||
|
||||
#if ADHOC || DEBUG
|
||||
// All features are unlocked for beta / debug / mac versions.
|
||||
return YES;
|
||||
#else
|
||||
// Check if product is purchased.
|
||||
return [[NSUserDefaults standardUserDefaults] objectForKey:productIdentifier] != nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)restoreCompletedTransactions {
|
||||
|
||||
[[self paymentQueue] restoreCompletedTransactions];
|
||||
}
|
||||
|
||||
- (void)purchaseProductWithIdentifier:(NSString *)productIdentifier quantity:(NSInteger)quantity {
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if (![[MPAppDelegate_Shared get] canMakePayments]) {
|
||||
[PearlAlert showAlertWithTitle:@"Store Not Set Up" message:
|
||||
@"Try logging using the App Store or from Settings."
|
||||
viewStyle:UIAlertViewStyleDefault initAlert:nil
|
||||
tappedButtonBlock:nil cancelTitle:@"Thanks" otherTitles:nil];
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (SKProduct *product in self.products)
|
||||
if ([product.productIdentifier isEqualToString:productIdentifier]) {
|
||||
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
|
||||
payment.quantity = quantity;
|
||||
[[self paymentQueue] addPayment:payment];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - SKProductsRequestDelegate
|
||||
|
||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
|
||||
|
||||
inf( @"products: %@, invalid: %@", response.products, response.invalidProductIdentifiers );
|
||||
self.products = response.products;
|
||||
|
||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
||||
[productObserver updateWithProducts:self.products];
|
||||
}
|
||||
|
||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
[PearlAlert showAlertWithTitle:@"Purchase Failed" message:
|
||||
strf( @"%@\n\n%@", error.localizedDescription,
|
||||
@"Ensure you are online and try logging out and back into iTunes from your device's Settings." )
|
||||
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:nil
|
||||
cancelTitle:@"OK" otherTitles:nil];
|
||||
#else
|
||||
#endif
|
||||
err( @"StoreKit request (%@) failed: %@", request, [error fullDescription] );
|
||||
}
|
||||
|
||||
- (void)requestDidFinish:(SKRequest *)request {
|
||||
|
||||
dbg( @"StoreKit request (%@) finished.", request );
|
||||
}
|
||||
|
||||
#pragma mark - SKPaymentTransactionObserver
|
||||
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
|
||||
|
||||
for (SKPaymentTransaction *transaction in transactions) {
|
||||
dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchased: {
|
||||
inf( @"purchased: %@", transaction.payment.productIdentifier );
|
||||
if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
|
||||
float currentFuel = [[MPiOSConfig get].developmentFuelRemaining floatValue];
|
||||
float purchasedFuel = transaction.payment.quantity / MP_FUEL_HOURLY_RATE;
|
||||
[MPiOSConfig get].developmentFuelRemaining = @(currentFuel + purchasedFuel);
|
||||
if (![MPiOSConfig get].developmentFuelChecked || currentFuel < DBL_EPSILON)
|
||||
[MPiOSConfig get].developmentFuelChecked = [NSDate date];
|
||||
}
|
||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
||||
forKey:transaction.payment.productIdentifier];
|
||||
[queue finishTransaction:transaction];
|
||||
break;
|
||||
}
|
||||
case SKPaymentTransactionStateRestored: {
|
||||
inf( @"restored: %@", transaction.payment.productIdentifier );
|
||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
||||
forKey:transaction.payment.productIdentifier];
|
||||
[queue finishTransaction:transaction];
|
||||
break;
|
||||
}
|
||||
case SKPaymentTransactionStatePurchasing:
|
||||
case SKPaymentTransactionStateDeferred:
|
||||
break;
|
||||
case SKPaymentTransactionStateFailed:
|
||||
err( @"Transaction failed: %@, reason: %@", transaction.payment.productIdentifier, [transaction.error fullDescription] );
|
||||
[queue finishTransaction:transaction];
|
||||
break;
|
||||
}
|
||||
|
||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
||||
[productObserver updateWithTransaction:transaction];
|
||||
}
|
||||
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
||||
wrn( @"Couldn't synchronize after transaction updates." );
|
||||
}
|
||||
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
|
||||
|
||||
err( @"StoreKit restore failed: %@", [error fullDescription] );
|
||||
}
|
||||
|
||||
@end
|
||||
19
platform-ios/ObjC/MPAppDelegate_Key.h
Normal file
19
platform-ios/ObjC/MPAppDelegate_Key.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// MPAppDelegate_Key.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
|
||||
@interface MPAppDelegate_Shared(Key)
|
||||
|
||||
- (BOOL)signInAsUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password;
|
||||
- (void)signOutAnimated:(BOOL)animated;
|
||||
|
||||
- (void)storeSavedKeyFor:(MPUserEntity *)user;
|
||||
- (void)forgetSavedKeyFor:(MPUserEntity *)user;
|
||||
|
||||
@end
|
||||
269
platform-ios/ObjC/MPAppDelegate_Key.m
Normal file
269
platform-ios/ObjC/MPAppDelegate_Key.m
Normal file
@@ -0,0 +1,269 @@
|
||||
//
|
||||
// MPAppDelegate.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
@interface MPAppDelegate_Shared()
|
||||
|
||||
@property(strong, nonatomic) MPKey *key;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAppDelegate_Shared(Key)
|
||||
|
||||
static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigin *keyOrigin) {
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if (user.touchID && kSecUseAuthenticationUI) {
|
||||
if (keyOrigin)
|
||||
*keyOrigin = MPKeyOriginKeyChainBiometric;
|
||||
|
||||
CFErrorRef acError = NULL;
|
||||
id accessControl = (__bridge_transfer id)SecAccessControlCreateWithFlags( kCFAllocatorDefault,
|
||||
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlTouchIDCurrentSet, &acError );
|
||||
if (!accessControl || acError)
|
||||
err( @"Could not use TouchID on this device: %@", acError );
|
||||
|
||||
else
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||
(__bridge id)kSecAttrAccessControl : accessControl,
|
||||
(__bridge id)kSecUseAuthenticationUI : (__bridge id)kSecUseAuthenticationUIAllow,
|
||||
(__bridge id)kSecUseOperationPrompt :
|
||||
strf( @"Access %@'s master password.", user.name ),
|
||||
}
|
||||
matches:nil];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (keyOrigin)
|
||||
*keyOrigin = MPKeyOriginKeyChain;
|
||||
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||
#if TARGET_OS_IPHONE
|
||||
(__bridge id)kSecAttrAccessible : (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
||||
#endif
|
||||
}
|
||||
matches:nil];
|
||||
}
|
||||
|
||||
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
|
||||
|
||||
MPKeyOrigin keyOrigin;
|
||||
NSDictionary *keyQuery = createKeyQuery( user, NO, &keyOrigin );
|
||||
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery];
|
||||
if (!keyData) {
|
||||
inf( @"No key found in keychain for user: %@", user.userID );
|
||||
return nil;
|
||||
}
|
||||
|
||||
inf( @"Found key in keychain for user: %@", user.userID );
|
||||
return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm keyOrigin:keyOrigin];
|
||||
}
|
||||
|
||||
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
||||
|
||||
if (user.saveKey) {
|
||||
NSData *keyData = [self.key keyDataForAlgorithm:user.algorithm];
|
||||
if (keyData) {
|
||||
[self forgetSavedKeyFor:user];
|
||||
|
||||
inf( @"Saving key in keychain for user: %@", user.userID );
|
||||
[PearlKeyChain addOrUpdateItemForQuery:createKeyQuery( user, YES, nil )
|
||||
withAttributes:@{ (__bridge id)kSecValueData : keyData }];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forgetSavedKeyFor:(MPUserEntity *)user {
|
||||
|
||||
OSStatus result = [PearlKeyChain deleteItemForQuery:createKeyQuery( user, NO, nil )];
|
||||
if (result == noErr) {
|
||||
inf( @"Removed key from keychain for user: %@", user.userID );
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPKeyForgottenNotification object:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)signOutAnimated:(BOOL)animated {
|
||||
|
||||
if (self.key)
|
||||
self.key = nil;
|
||||
|
||||
self.activeUser = nil;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self userInfo:@{ @"animated" : @(animated) }];
|
||||
}
|
||||
|
||||
- (BOOL)signInAsUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
|
||||
|
||||
NSAssert( ![NSThread isMainThread], @"Authentication should not happen on the main thread." );
|
||||
if (!user)
|
||||
return NO;
|
||||
|
||||
MPKey *tryKey = nil;
|
||||
|
||||
// Method 1: When the user has no keyID set, set a new key from the given master password.
|
||||
if (!user.keyID) {
|
||||
if ([password length] && (tryKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:password])) {
|
||||
user.keyID = [tryKey keyIDForAlgorithm:MPAlgorithmDefault];
|
||||
|
||||
// Migrate existing sites.
|
||||
[self migrateSitesForUser:user saveInContext:moc toKey:tryKey];
|
||||
}
|
||||
}
|
||||
|
||||
// Method 2: Depending on the user's saveKey, load or remove the key from the keychain.
|
||||
if (!user.saveKey)
|
||||
// Key should not be stored in keychain. Delete it.
|
||||
[self forgetSavedKeyFor:user];
|
||||
|
||||
else if (!tryKey) {
|
||||
// Key should be saved in keychain. Load it.
|
||||
if ((tryKey = [self loadSavedKeyFor:user]) && ![user.keyID isEqual:[tryKey keyIDForAlgorithm:user.algorithm]]) {
|
||||
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
|
||||
inf( @"Saved password doesn't match keyID for user: %@", user.userID );
|
||||
trc( @"user keyID: %@ (version: %d) != authentication keyID: %@",
|
||||
user.keyID, user.algorithm.version, [tryKey keyIDForAlgorithm:user.algorithm] );
|
||||
|
||||
tryKey = nil;
|
||||
[self forgetSavedKeyFor:user];
|
||||
}
|
||||
}
|
||||
|
||||
// Method 3: Check the given master password string.
|
||||
if (!tryKey && [password length] && (tryKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:password]) &&
|
||||
![user.keyID isEqual:[tryKey keyIDForAlgorithm:user.algorithm]]) {
|
||||
inf( @"Key derived from password doesn't match keyID for user: %@", user.userID );
|
||||
trc( @"user keyID: %@ (version: %u) != authentication keyID: %@",
|
||||
user.keyID, user.algorithm.version, [tryKey keyIDForAlgorithm:user.algorithm] );
|
||||
|
||||
tryKey = nil;
|
||||
}
|
||||
|
||||
// No more methods left, fail if key still not known.
|
||||
if (!tryKey) {
|
||||
if (password)
|
||||
inf( @"Password login failed for user: %@", user.userID );
|
||||
else
|
||||
dbg( @"Automatic login failed for user: %@", user.userID );
|
||||
|
||||
return NO;
|
||||
}
|
||||
inf( @"Logged in user: %@", user.userID );
|
||||
|
||||
if (![self.key isEqualToKey:tryKey]) {
|
||||
// Upgrade the user's keyID if not at the default version yet.
|
||||
if (user.algorithm.version != MPAlgorithmDefaultVersion) {
|
||||
user.algorithm = MPAlgorithmDefault;
|
||||
user.keyID = [tryKey keyIDForAlgorithm:user.algorithm];
|
||||
inf( @"Upgraded keyID to version %u for user: %@", user.algorithm.version, user.userID );
|
||||
}
|
||||
|
||||
self.key = tryKey;
|
||||
|
||||
// Update the key chain if necessary.
|
||||
[self storeSavedKeyFor:user];
|
||||
}
|
||||
|
||||
@try {
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
[[Crashlytics sharedInstance] setObjectValue:user.userID forKey:@"username"];
|
||||
[[Crashlytics sharedInstance] setUserName:user.userID];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"While setting username: %@", exception );
|
||||
}
|
||||
|
||||
user.lastUsed = [NSDate date];
|
||||
self.activeUser = user;
|
||||
[moc saveToStore];
|
||||
|
||||
// Perform a data sanity check now that we're logged in as the user to allow fixes that require the user's key.
|
||||
if ([[MPConfig get].checkInconsistency boolValue])
|
||||
[MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||
[self findAndFixInconsistenciesSaveInContext:context];
|
||||
}];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedInNotification object:self];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)migrateSitesForUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey {
|
||||
|
||||
if (![user.sites count])
|
||||
// Nothing to migrate.
|
||||
return;
|
||||
|
||||
MPKey *recoverKey = newKey;
|
||||
#ifdef PEARL_UIKIT
|
||||
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:PearlString( @"Migrating %ld sites...",
|
||||
(long)[user.sites count] )];
|
||||
#endif
|
||||
|
||||
for (MPSiteEntity *site in user.sites) {
|
||||
if (site.type & MPSiteTypeClassStored) {
|
||||
NSString *content;
|
||||
while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
|
||||
// Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
#ifdef PEARL_UIKIT
|
||||
dispatch_group_t recoverPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter( recoverPasswordGroup );
|
||||
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
|
||||
message:PearlString( @"Your old master password is required to migrate the stored password for %@",
|
||||
site.name )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
// Don't Migrate
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave( recoverPasswordGroup );
|
||||
}
|
||||
} cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
|
||||
dispatch_group_wait( recoverPasswordGroup, DISPATCH_TIME_FOREVER );
|
||||
#endif
|
||||
if (!masterPassword)
|
||||
// Don't Migrate
|
||||
break;
|
||||
|
||||
recoverKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:masterPassword];
|
||||
}
|
||||
|
||||
if (!content)
|
||||
// Don't Migrate
|
||||
break;
|
||||
|
||||
if (![recoverKey isEqualToKey:newKey])
|
||||
[site.algorithm savePassword:content toSite:site usingKey:newKey];
|
||||
}
|
||||
}
|
||||
|
||||
[moc saveToStore];
|
||||
|
||||
#ifdef PEARL_UIKIT
|
||||
[activityOverlay cancelOverlayAnimated:YES];
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
28
platform-ios/ObjC/MPAppDelegate_Shared.h
Normal file
28
platform-ios/ObjC/MPAppDelegate_Shared.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// MPAppDelegate_Shared.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPEntities.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
@interface MPAppDelegate_Shared : PearlAppDelegate
|
||||
#else
|
||||
@interface MPAppDelegate_Shared : NSObject <PearlConfigDelegate>
|
||||
#endif
|
||||
|
||||
@property(strong, nonatomic, readonly) MPKey *key;
|
||||
@property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, nonatomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
|
||||
+ (instancetype)get;
|
||||
|
||||
- (MPUserEntity *)activeUserForMainThread;
|
||||
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context;
|
||||
- (void)setActiveUser:(MPUserEntity *)activeUser;
|
||||
- (void)handleCoordinatorError:(NSError *)error;
|
||||
|
||||
@end
|
||||
78
platform-ios/ObjC/MPAppDelegate_Shared.m
Normal file
78
platform-ios/ObjC/MPAppDelegate_Shared.m
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// MPAppDelegate.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "NSManagedObjectModel+KCOrderedAccessorFix.h"
|
||||
|
||||
@interface MPAppDelegate_Shared ()
|
||||
|
||||
@property(strong, nonatomic) MPKey *key;
|
||||
@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, nonatomic) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAppDelegate_Shared
|
||||
|
||||
+ (MPAppDelegate_Shared *)get {
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
return (MPAppDelegate_Shared *)UIApp.delegate;
|
||||
#elif defined (__MAC_OS_X_VERSION_MIN_REQUIRED)
|
||||
return (MPAppDelegate_Shared *)[NSApplication sharedApplication].delegate;
|
||||
#else
|
||||
#error Unsupported OS.
|
||||
#endif
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
||||
[model kc_generateOrderedSetAccessors];
|
||||
self.storeCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (MPUserEntity *)activeUserForMainThread {
|
||||
|
||||
return [self activeUserInContext:[MPAppDelegate_Shared managedObjectContextForMainThreadIfReady]];
|
||||
}
|
||||
|
||||
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
NSManagedObjectID *activeUserOID = self.activeUserOID;
|
||||
if (!activeUserOID || !context)
|
||||
return nil;
|
||||
|
||||
MPUserEntity *activeUser = [MPUserEntity existingObjectWithID:activeUserOID inContext:context];
|
||||
if (!activeUser)
|
||||
[self signOutAnimated:YES];
|
||||
|
||||
return activeUser;
|
||||
}
|
||||
|
||||
- (void)setActiveUser:(MPUserEntity *)activeUser {
|
||||
|
||||
NSError *error;
|
||||
if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
|
||||
err(@"Failed to obtain a permanent object ID after setting active user: %@", [error fullDescription]);
|
||||
|
||||
self.activeUserOID = activeUser.objectID;
|
||||
}
|
||||
|
||||
- (void)handleCoordinatorError:(NSError *)error {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
40
platform-ios/ObjC/MPAppDelegate_Store.h
Normal file
40
platform-ios/ObjC/MPAppDelegate_Store.h
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// MPAppDelegate_Key.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
|
||||
#import "MPFixable.h"
|
||||
|
||||
typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
||||
MPImportResultSuccess,
|
||||
MPImportResultCancelled,
|
||||
MPImportResultInvalidPassword,
|
||||
MPImportResultMalformedInput,
|
||||
MPImportResultInternalError,
|
||||
};
|
||||
|
||||
@interface MPAppDelegate_Shared(Store)
|
||||
|
||||
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
|
||||
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
|
||||
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
|
||||
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock;
|
||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock;
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
||||
- (void)deleteAndResetStore;
|
||||
|
||||
/** @param completion The block to execute after adding the site, executed from the main thread with the new site in the main MOC. */
|
||||
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion;
|
||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type;
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
||||
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
|
||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords;
|
||||
|
||||
@end
|
||||
833
platform-ios/ObjC/MPAppDelegate_Store.m
Normal file
833
platform-ios/ObjC/MPAppDelegate_Store.m
Normal file
@@ -0,0 +1,833 @@
|
||||
//
|
||||
// MPAppDelegate.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
||||
#else
|
||||
#define STORE_OPTIONS
|
||||
#endif
|
||||
|
||||
#define MPMigrationLevelLocalStoreKey @"MPMigrationLevelLocalStoreKey"
|
||||
|
||||
typedef NS_ENUM( NSInteger, MPStoreMigrationLevel ) {
|
||||
MPStoreMigrationLevelV1,
|
||||
MPStoreMigrationLevelV2,
|
||||
MPStoreMigrationLevelV3,
|
||||
MPStoreMigrationLevelCurrent = MPStoreMigrationLevelV3,
|
||||
};
|
||||
|
||||
@implementation MPAppDelegate_Shared(Store)
|
||||
|
||||
PearlAssociatedObjectProperty( NSOperationQueue *, StoreQueue, storeQueue );
|
||||
|
||||
PearlAssociatedObjectProperty( NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext );
|
||||
|
||||
PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext );
|
||||
|
||||
PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
#pragma mark - Core Data setup
|
||||
|
||||
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady {
|
||||
|
||||
NSAssert( [[NSThread currentThread] isMainThread], @"Can only access main MOC from the main thread." );
|
||||
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
||||
if (!mainManagedObjectContext || ![[NSThread currentThread] isMainThread])
|
||||
return nil;
|
||||
|
||||
return mainManagedObjectContext;
|
||||
}
|
||||
|
||||
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void ( ^ )(NSManagedObjectContext *mainContext))mocBlock {
|
||||
|
||||
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
||||
if (!mainManagedObjectContext)
|
||||
return NO;
|
||||
|
||||
[mainManagedObjectContext performBlock:^{
|
||||
@try {
|
||||
mocBlock( mainManagedObjectContext );
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"While performing managed block:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *mainContext))mocBlock {
|
||||
|
||||
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
||||
if (!mainManagedObjectContext)
|
||||
return NO;
|
||||
|
||||
[mainManagedObjectContext performBlockAndWait:^{
|
||||
@try {
|
||||
mocBlock( mainManagedObjectContext );
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
err( @"While performing managed block:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)managedObjectContextPerformBlock:(void ( ^ )(NSManagedObjectContext *context))mocBlock {
|
||||
|
||||
NSManagedObjectContext *privateManagedObjectContextIfReady = [[self get] privateManagedObjectContextIfReady];
|
||||
if (!privateManagedObjectContextIfReady)
|
||||
return NO;
|
||||
|
||||
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
moc.parentContext = privateManagedObjectContextIfReady;
|
||||
[moc performBlock:^{
|
||||
@try {
|
||||
mocBlock( moc );
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
err( @"While performing managed block:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *context))mocBlock {
|
||||
|
||||
NSManagedObjectContext *privateManagedObjectContextIfReady = [[self get] privateManagedObjectContextIfReady];
|
||||
if (!privateManagedObjectContextIfReady)
|
||||
return NO;
|
||||
|
||||
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
moc.parentContext = privateManagedObjectContextIfReady;
|
||||
[moc performBlockAndWait:^{
|
||||
@try {
|
||||
mocBlock( moc );
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
err( @"While performing managed block:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
||||
|
||||
[self loadStore];
|
||||
return self.mainManagedObjectContext;
|
||||
}
|
||||
|
||||
- (NSManagedObjectContext *)privateManagedObjectContextIfReady {
|
||||
|
||||
[self loadStore];
|
||||
return self.privateManagedObjectContext;
|
||||
}
|
||||
|
||||
- (NSURL *)localStoreURL {
|
||||
|
||||
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
||||
inDomains:NSUserDomainMask] lastObject];
|
||||
return [[[applicationSupportURL
|
||||
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
|
||||
URLByAppendingPathComponent:@"MasterPassword" isDirectory:NO]
|
||||
URLByAppendingPathExtension:@"sqlite"];
|
||||
}
|
||||
|
||||
- (void)loadStore {
|
||||
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
(self.storeQueue = [NSOperationQueue new]).maxConcurrentOperationCount = 1;
|
||||
} );
|
||||
|
||||
// Do nothing if already fully set up, otherwise (re-)load the store.
|
||||
if (self.storeCoordinator && self.mainManagedObjectContext && self.privateManagedObjectContext)
|
||||
return;
|
||||
|
||||
[self.storeQueue addOperationWithBlock:^{
|
||||
// Do nothing if already fully set up, otherwise (re-)load the store.
|
||||
if (self.storeCoordinator && self.mainManagedObjectContext && self.privateManagedObjectContext)
|
||||
return;
|
||||
|
||||
// Unregister any existing observers and contexts.
|
||||
PearlRemoveNotificationObserversFrom( self.mainManagedObjectContext );
|
||||
[self.mainManagedObjectContext performBlockAndWait:^{
|
||||
[self.mainManagedObjectContext reset];
|
||||
self.mainManagedObjectContext = nil;
|
||||
}];
|
||||
[self.privateManagedObjectContext performBlockAndWait:^{
|
||||
[self.privateManagedObjectContext reset];
|
||||
self.privateManagedObjectContext = nil;
|
||||
}];
|
||||
|
||||
// Don't load when the store is corrupted.
|
||||
if ([self.storeCorrupted boolValue])
|
||||
return;
|
||||
|
||||
// Check if migration is necessary.
|
||||
[self migrateStore];
|
||||
|
||||
// Install managed object contexts and observers.
|
||||
self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
[self.privateManagedObjectContext performBlockAndWait:^{
|
||||
self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
||||
self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator;
|
||||
}];
|
||||
|
||||
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
|
||||
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
|
||||
|
||||
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
||||
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
||||
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainManagedObjectContext, NSNotification *note) {
|
||||
[mainManagedObjectContext performBlock:^{
|
||||
@try {
|
||||
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
err( @"While merging changes:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
} );
|
||||
|
||||
|
||||
// Create a new store coordinator.
|
||||
NSError *error = nil;
|
||||
NSURL *localStoreURL = [self localStoreURL];
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES attributes:nil error:&error]) {
|
||||
err( @"Couldn't create our application support directory: %@", [error fullDescription] );
|
||||
return;
|
||||
}
|
||||
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
|
||||
options:@{
|
||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
||||
NSInferMappingModelAutomaticallyOption : @YES,
|
||||
STORE_OPTIONS
|
||||
} error:&error]) {
|
||||
err( @"Failed to open store: %@", [error fullDescription] );
|
||||
self.storeCorrupted = @YES;
|
||||
[self handleCoordinatorError:error];
|
||||
return;
|
||||
}
|
||||
self.storeCorrupted = @NO;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
|
||||
#else
|
||||
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
|
||||
#endif
|
||||
^(MPAppDelegate_Shared *self, NSNotification *note) {
|
||||
[self.mainManagedObjectContext saveToStore];
|
||||
} );
|
||||
|
||||
// Perform a data sanity check on the newly loaded store to find and fix any issues.
|
||||
if ([[MPConfig get].checkInconsistency boolValue])
|
||||
[MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||
[self findAndFixInconsistenciesSaveInContext:context];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)deleteAndResetStore {
|
||||
|
||||
@synchronized (self) {
|
||||
// Unregister any existing observers and contexts.
|
||||
PearlRemoveNotificationObserversFrom( self.mainManagedObjectContext );
|
||||
[self.mainManagedObjectContext performBlockAndWait:^{
|
||||
[self.mainManagedObjectContext reset];
|
||||
self.mainManagedObjectContext = nil;
|
||||
}];
|
||||
[self.privateManagedObjectContext performBlockAndWait:^{
|
||||
[self.privateManagedObjectContext reset];
|
||||
self.privateManagedObjectContext = nil;
|
||||
}];
|
||||
NSError *error = nil;
|
||||
for (NSPersistentStore *store in self.storeCoordinator.persistentStores) {
|
||||
if (![self.storeCoordinator removePersistentStore:store error:&error])
|
||||
err( @"Couldn't remove persistence store from coordinator: %@", [error fullDescription] );
|
||||
}
|
||||
if (![[NSFileManager defaultManager] removeItemAtURL:self.localStoreURL error:&error])
|
||||
err( @"Couldn't remove persistence store at URL %@: %@", self.localStoreURL, [error fullDescription] );
|
||||
|
||||
[self loadStore];
|
||||
}
|
||||
}
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
NSError *error = nil;
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest new];
|
||||
fetchRequest.fetchBatchSize = 50;
|
||||
|
||||
MPFixableResult result = MPFixableResultNoProblems;
|
||||
for (NSEntityDescription *entity in [context.persistentStoreCoordinator.managedObjectModel entities])
|
||||
if (class_conformsToProtocol( NSClassFromString( entity.managedObjectClassName ), @protocol(MPFixable) )) {
|
||||
fetchRequest.entity = entity;
|
||||
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
|
||||
if (!objects) {
|
||||
err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] );
|
||||
continue;
|
||||
}
|
||||
|
||||
for (NSManagedObject<MPFixable> *object in objects)
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
return [object findAndFixInconsistenciesInContext:context];
|
||||
} );
|
||||
}
|
||||
|
||||
if (result == MPFixableResultNoProblems)
|
||||
inf( @"Sanity check found no problems in store." );
|
||||
|
||||
else {
|
||||
[context saveToStore];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPFoundInconsistenciesNotification object:nil userInfo:@{
|
||||
MPInconsistenciesFixResultUserKey : @(result)
|
||||
}];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)migrateStore {
|
||||
|
||||
MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
|
||||
if (migrationLevel >= MPStoreMigrationLevelCurrent)
|
||||
// Local store up-to-date.
|
||||
return;
|
||||
|
||||
inf( @"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPStoreMigrationLevelCurrent );
|
||||
if (migrationLevel <= MPStoreMigrationLevelV1 && ![self migrateV1LocalStore]) {
|
||||
inf( @"Failed to migrate old V1 to new local store." );
|
||||
return;
|
||||
}
|
||||
if (migrationLevel <= MPStoreMigrationLevelV2 && ![self migrateV2LocalStore]) {
|
||||
inf( @"Failed to migrate old V2 to new local store." );
|
||||
return;
|
||||
}
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:MPStoreMigrationLevelCurrent forKey:MPMigrationLevelLocalStoreKey];
|
||||
inf( @"Successfully migrated old to new local store." );
|
||||
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
||||
wrn( @"Couldn't synchronize after store migration." );
|
||||
}
|
||||
|
||||
- (BOOL)migrateV1LocalStore {
|
||||
|
||||
NSURL *applicationFilesDirectory = [[[NSFileManager defaultManager]
|
||||
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
|
||||
NSURL *oldLocalStoreURL = [[applicationFilesDirectory
|
||||
URLByAppendingPathComponent:@"MasterPassword" isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NULL]) {
|
||||
inf( @"No V1 local store to migrate." );
|
||||
return YES;
|
||||
}
|
||||
|
||||
inf( @"Migrating V1 local store" );
|
||||
NSURL *newLocalStoreURL = [self localStoreURL];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NULL]) {
|
||||
inf( @"New local store already exists." );
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{ STORE_OPTIONS }
|
||||
toStore:newLocalStoreURL withOptions:@{ STORE_OPTIONS }
|
||||
model:nil error:&error]) {
|
||||
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)migrateV2LocalStore {
|
||||
|
||||
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
||||
inDomains:NSUserDomainMask] lastObject];
|
||||
NSURL *oldLocalStoreURL;
|
||||
// On iOS, each app is in a sandbox so we don't need to app-scope this directory.
|
||||
#if TARGET_OS_IPHONE
|
||||
oldLocalStoreURL = [[applicationSupportURL
|
||||
URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO]
|
||||
URLByAppendingPathExtension:@"sqlite"];
|
||||
#else
|
||||
// The directory is shared between all apps on the system so we need to scope it for the running app.
|
||||
oldLocalStoreURL = [[[applicationSupportURL
|
||||
URLByAppendingPathComponent:[NSRunningApplication currentApplication].bundleIdentifier isDirectory:YES]
|
||||
URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO]
|
||||
URLByAppendingPathExtension:@"sqlite"];
|
||||
#endif
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NULL]) {
|
||||
inf( @"No V2 local store to migrate." );
|
||||
return YES;
|
||||
}
|
||||
|
||||
inf( @"Migrating V2 local store" );
|
||||
NSURL *newLocalStoreURL = [self localStoreURL];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NULL]) {
|
||||
inf( @"New local store already exists." );
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{
|
||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
||||
NSInferMappingModelAutomaticallyOption : @YES,
|
||||
STORE_OPTIONS
|
||||
} toStore:newLocalStoreURL withOptions:@{
|
||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
||||
NSInferMappingModelAutomaticallyOption : @YES,
|
||||
STORE_OPTIONS
|
||||
} model:nil error:&error]) {
|
||||
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Utilities
|
||||
|
||||
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion {
|
||||
|
||||
if (![siteName length]) {
|
||||
completion( nil, nil );
|
||||
return;
|
||||
}
|
||||
|
||||
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *activeUser = [self activeUserInContext:context];
|
||||
NSAssert( activeUser, @"Missing user." );
|
||||
if (!activeUser) {
|
||||
completion( nil, nil );
|
||||
return;
|
||||
}
|
||||
|
||||
MPSiteType type = activeUser.defaultType;
|
||||
id<MPAlgorithm> algorithm = MPAlgorithmDefault;
|
||||
Class entityType = [algorithm classOfType:type];
|
||||
|
||||
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||
site.name = siteName;
|
||||
site.user = activeUser;
|
||||
site.type = type;
|
||||
site.lastUsed = [NSDate date];
|
||||
site.algorithm = algorithm;
|
||||
|
||||
NSError *error = nil;
|
||||
if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
|
||||
err( @"Failed to obtain a permanent object ID after creating new site: %@", [error fullDescription] );
|
||||
|
||||
[context saveToStore];
|
||||
|
||||
completion( site, context );
|
||||
}];
|
||||
}
|
||||
|
||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type {
|
||||
|
||||
if (site.type == type)
|
||||
return site;
|
||||
|
||||
if ([site.algorithm classOfType:type] == site.typeClass) {
|
||||
site.type = type;
|
||||
[context saveToStore];
|
||||
}
|
||||
|
||||
else {
|
||||
// Type requires a different class of site. Recreate the site.
|
||||
Class entityType = [site.algorithm classOfType:type];
|
||||
MPSiteEntity *newSite = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||
newSite.type = type;
|
||||
newSite.name = site.name;
|
||||
newSite.user = site.user;
|
||||
newSite.uses = site.uses;
|
||||
newSite.lastUsed = site.lastUsed;
|
||||
newSite.algorithm = site.algorithm;
|
||||
newSite.loginName = site.loginName;
|
||||
|
||||
NSError *error = nil;
|
||||
if (![context obtainPermanentIDsForObjects:@[ newSite ] error:&error])
|
||||
err( @"Failed to obtain a permanent object ID after changing object type: %@", [error fullDescription] );
|
||||
|
||||
[context deleteObject:site];
|
||||
[context saveToStore];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
||||
site = newSite;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
||||
return site;
|
||||
}
|
||||
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword {
|
||||
|
||||
NSAssert( ![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread." );
|
||||
|
||||
__block MPImportResult result = MPImportResultCancelled;
|
||||
do {
|
||||
if ([MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||
result = [self importSites:importedSitesString askImportPassword:importPassword askUserPassword:userPassword
|
||||
saveInContext:context];
|
||||
}])
|
||||
break;
|
||||
usleep( (useconds_t)(USEC_PER_SEC * 0.2) );
|
||||
} while (YES);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||
askImportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))askUserPassword
|
||||
saveInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
// Compile patterns.
|
||||
static NSRegularExpression *headerPattern;
|
||||
static NSArray *sitePatterns;
|
||||
NSError *error = nil;
|
||||
if (!headerPattern) {
|
||||
headerPattern = [[NSRegularExpression alloc]
|
||||
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error];
|
||||
if (error) {
|
||||
err( @"Error loading the header pattern: %@", [error fullDescription] );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if (!sitePatterns) {
|
||||
sitePatterns = @[
|
||||
[[NSRegularExpression alloc] // Format 0
|
||||
initWithPattern:@"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)? +([^\t]+)\t(.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error],
|
||||
[[NSRegularExpression alloc] // Format 1
|
||||
initWithPattern:@"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)?(:[[:digit:]]+)? +([^\t]*)\t *([^\t]+)\t(.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error]
|
||||
];
|
||||
if (error) {
|
||||
err( @"Error loading the site patterns: %@", [error fullDescription] );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse import data.
|
||||
inf( @"Importing sites." );
|
||||
__block MPUserEntity *user = nil;
|
||||
id<MPAlgorithm> importAlgorithm = nil;
|
||||
NSUInteger importFormat = 0;
|
||||
NSUInteger importAvatar = NSNotFound;
|
||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
||||
NSData *importKeyID = nil;
|
||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||
NSMutableSet *sitesToDelete = [NSMutableSet set];
|
||||
NSMutableArray *importedSiteSites = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
|
||||
NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
for (NSString *importedSiteLine in importedSiteLines) {
|
||||
if ([importedSiteLine hasPrefix:@"#"]) {
|
||||
// Comment or header
|
||||
if (!headerStarted) {
|
||||
if ([importedSiteLine isEqualToString:@"##"])
|
||||
headerStarted = YES;
|
||||
continue;
|
||||
}
|
||||
if (headerEnded)
|
||||
continue;
|
||||
if ([importedSiteLine isEqualToString:@"##"]) {
|
||||
headerEnded = YES;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Header
|
||||
if ([headerPattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
||||
err( @"Invalid header format in line: %@", importedSiteLine );
|
||||
return MPImportResultMalformedInput;
|
||||
}
|
||||
NSTextCheckingResult *headerSites = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
||||
NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]];
|
||||
NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]];
|
||||
if ([headerName isEqualToString:@"User Name"]) {
|
||||
importUserName = headerValue;
|
||||
|
||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
||||
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||
if (!users) {
|
||||
err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([users count] > 1) {
|
||||
err( @"While looking for user: %@, found more than one: %lu", importUserName, (unsigned long)[users count] );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
|
||||
user = [users lastObject];
|
||||
dbg( @"Existing user? %@", [user debugDescription] );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Key ID"])
|
||||
importKeyID = [headerValue decodeHex];
|
||||
if ([headerName isEqualToString:@"Version"]) {
|
||||
importBundleVersion = headerValue;
|
||||
importAlgorithm = MPAlgorithmDefaultForBundleVersion( importBundleVersion );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Format"]) {
|
||||
importFormat = (NSUInteger)[headerValue integerValue];
|
||||
if (importFormat >= [sitePatterns count]) {
|
||||
err( @"Unsupported import format: %lu", (unsigned long)importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if ([headerName isEqualToString:@"Avatar"])
|
||||
importAvatar = (NSUInteger)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Passwords"]) {
|
||||
if ([headerValue isEqualToString:@"VISIBLE"])
|
||||
clearText = YES;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
if (!headerEnded)
|
||||
continue;
|
||||
if (![importUserName length])
|
||||
return MPImportResultMalformedInput;
|
||||
if (![importedSiteLine length])
|
||||
continue;
|
||||
|
||||
// Site
|
||||
NSRegularExpression *sitePattern = sitePatterns[importFormat];
|
||||
if ([sitePattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
||||
err( @"Invalid site format in line: %@", importedSiteLine );
|
||||
return MPImportResultMalformedInput;
|
||||
}
|
||||
NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
||||
NSString *lastUsed, *uses, *type, *version, *counter, *siteName, *loginName, *exportContent;
|
||||
switch (importFormat) {
|
||||
case 0:
|
||||
lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
||||
uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
||||
type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
||||
version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
||||
if ([version length])
|
||||
version = [version substringFromIndex:1]; // Strip the leading colon.
|
||||
counter = @"";
|
||||
loginName = @"";
|
||||
siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
||||
exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
||||
break;
|
||||
case 1:
|
||||
lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
||||
uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
||||
type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
||||
version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
||||
if ([version length])
|
||||
version = [version substringFromIndex:1]; // Strip the leading colon.
|
||||
counter = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
||||
if ([counter length])
|
||||
counter = [counter substringFromIndex:1]; // Strip the leading colon.
|
||||
loginName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
||||
siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:7]];
|
||||
exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:8]];
|
||||
break;
|
||||
default:
|
||||
err( @"Unexpected import format: %lu", (unsigned long)importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
|
||||
// Find existing site.
|
||||
if (user) {
|
||||
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
||||
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
||||
if (!existingSites) {
|
||||
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, [error fullDescription] );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([existingSites count]) {
|
||||
dbg( @"Existing sites: %@", existingSites );
|
||||
[sitesToDelete addObjectsFromArray:existingSites];
|
||||
}
|
||||
}
|
||||
[importedSiteSites addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]];
|
||||
dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%@",
|
||||
lastUsed, uses, type, version, counter, loginName, siteName, exportContent );
|
||||
}
|
||||
|
||||
// Ask for confirmation to import these sites and the master password of the user.
|
||||
inf( @"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteSites count],
|
||||
(unsigned long)[sitesToDelete count], [MPUserEntity idFor:importUserName] );
|
||||
NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteSites count],
|
||||
[sitesToDelete count] );
|
||||
if (!userMasterPassword) {
|
||||
inf( @"Import cancelled." );
|
||||
return MPImportResultCancelled;
|
||||
}
|
||||
MPKey *userKey = [[MPKey alloc] initForFullName:user? user.name: importUserName withMasterPassword:userMasterPassword];
|
||||
if (user && ![[userKey keyIDForAlgorithm:user.algorithm] isEqualToData:user.keyID])
|
||||
return MPImportResultInvalidPassword;
|
||||
__block MPKey *importKey = userKey;
|
||||
if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
|
||||
importKey = [[MPKey alloc] initForFullName:importUserName withMasterPassword:askImportPassword( importUserName )];
|
||||
if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
|
||||
return MPImportResultInvalidPassword;
|
||||
|
||||
// Delete existing sites.
|
||||
if (sitesToDelete.count)
|
||||
[sitesToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
||||
inf( @"Deleting site: %@, it will be replaced by an imported site.", [obj name] );
|
||||
[context deleteObject:obj];
|
||||
}];
|
||||
|
||||
// Make sure there is a user.
|
||||
if (user) {
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
dbg( @"Updating User: %@", [user debugDescription] );
|
||||
}
|
||||
else {
|
||||
user = [MPUserEntity insertNewObjectInContext:context];
|
||||
user.name = importUserName;
|
||||
user.algorithm = MPAlgorithmDefault;
|
||||
user.keyID = [userKey keyIDForAlgorithm:user.algorithm];
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
dbg( @"Created User: %@", [user debugDescription] );
|
||||
}
|
||||
|
||||
// Import new sites.
|
||||
for (NSArray *siteElements in importedSiteSites) {
|
||||
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]];
|
||||
NSUInteger uses = (unsigned)[siteElements[1] integerValue];
|
||||
MPSiteType type = (MPSiteType)[siteElements[2] integerValue];
|
||||
MPAlgorithmVersion version = (MPAlgorithmVersion)[siteElements[3] integerValue];
|
||||
NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound;
|
||||
NSString *loginName = [siteElements[5] length]? siteElements[5]: nil;
|
||||
NSString *siteName = siteElements[6];
|
||||
NSString *exportContent = siteElements[7];
|
||||
|
||||
// Create new site.
|
||||
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( version );
|
||||
Class entityType = [algorithm classOfType:type];
|
||||
if (!entityType) {
|
||||
err( @"Invalid site type in import file: %@ has type %lu", siteName, (long)type );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||
site.name = siteName;
|
||||
site.loginName = loginName;
|
||||
site.user = user;
|
||||
site.type = type;
|
||||
site.uses = uses;
|
||||
site.lastUsed = lastUsed;
|
||||
site.algorithm = algorithm;
|
||||
if ([exportContent length]) {
|
||||
if (clearText)
|
||||
[site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
|
||||
else
|
||||
[site.algorithm importProtectedPassword:exportContent protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||
}
|
||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]] && counter != NSNotFound)
|
||||
((MPGeneratedSiteEntity *)site).counter = counter;
|
||||
|
||||
dbg( @"Created Site: %@", [site debugDescription] );
|
||||
}
|
||||
|
||||
if (![context saveToStore])
|
||||
return MPImportResultInternalError;
|
||||
|
||||
inf( @"Import completed successfully." );
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSitesImportedNotification object:nil userInfo:@{
|
||||
MPSitesImportedNotificationUserKey : user
|
||||
}];
|
||||
|
||||
return MPImportResultSuccess;
|
||||
}
|
||||
|
||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords {
|
||||
|
||||
MPUserEntity *activeUser = [self activeUserForMainThread];
|
||||
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID );
|
||||
|
||||
// Header.
|
||||
NSMutableString *export = [NSMutableString new];
|
||||
[export appendFormat:@"# Master Password site export\n"];
|
||||
if (revealPasswords)
|
||||
[export appendFormat:@"# Export of site names and passwords in clear-text.\n"];
|
||||
else
|
||||
[export appendFormat:@"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n"];
|
||||
[export appendFormat:@"# \n"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"# User Name: %@\n", activeUser.name];
|
||||
[export appendFormat:@"# Avatar: %lu\n", (unsigned long)activeUser.avatar];
|
||||
[export appendFormat:@"# Key ID: %@\n", [activeUser.keyID encodeHex]];
|
||||
[export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
|
||||
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion];
|
||||
[export appendFormat:@"# Format: 1\n"];
|
||||
if (revealPasswords)
|
||||
[export appendFormat:@"# Passwords: VISIBLE\n"];
|
||||
else
|
||||
[export appendFormat:@"# Passwords: PROTECTED\n"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"#\n"];
|
||||
[export appendFormat:@"# Last Times Password Login\t Site\tSite\n"];
|
||||
[export appendFormat:@"# used used type name\t name\tpassword\n"];
|
||||
|
||||
// Sites.
|
||||
for (MPSiteEntity *site in activeUser.sites) {
|
||||
NSDate *lastUsed = site.lastUsed;
|
||||
NSUInteger uses = site.uses;
|
||||
MPSiteType type = site.type;
|
||||
id<MPAlgorithm> algorithm = site.algorithm;
|
||||
NSUInteger counter = 0;
|
||||
NSString *loginName = site.loginName;
|
||||
NSString *siteName = site.name;
|
||||
NSString *content = nil;
|
||||
|
||||
// Generated-specific
|
||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||
|
||||
|
||||
// Determine the content to export.
|
||||
if (!(type & MPSiteFeatureDevicePrivate)) {
|
||||
if (revealPasswords)
|
||||
content = [site.algorithm resolvePasswordForSite:site usingKey:self.key];
|
||||
else if (type & MPSiteFeatureExportContent)
|
||||
content = [site.algorithm exportPasswordForSite:site usingKey:self.key];
|
||||
}
|
||||
|
||||
NSString *lastUsedExport = [[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed];
|
||||
long usesExport = (long)uses;
|
||||
NSString *typeExport = strf( @"%lu:%lu:%lu", (long)type, (long)[algorithm version], (long)counter );
|
||||
NSString *loginNameExport = loginName?: @"";
|
||||
NSString *contentExport = content?: @"";
|
||||
[export appendFormat:@"%@ %8ld %8S %25S\t%25S\t%@\n",
|
||||
lastUsedExport, usesExport,
|
||||
(const unsigned short *)[typeExport cStringUsingEncoding:NSUTF16StringEncoding],
|
||||
(const unsigned short *)[loginNameExport cStringUsingEncoding:NSUTF16StringEncoding],
|
||||
(const unsigned short *)[siteName cStringUsingEncoding:NSUTF16StringEncoding],
|
||||
contentExport];
|
||||
}
|
||||
|
||||
return export;
|
||||
}
|
||||
|
||||
@end
|
||||
21
platform-ios/ObjC/MPConfig.h
Normal file
21
platform-ios/ObjC/MPConfig.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// MPConfig.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Pearl.h"
|
||||
|
||||
@interface MPConfig : PearlConfig
|
||||
|
||||
@property(nonatomic, retain) NSNumber *sendInfo;
|
||||
@property(nonatomic, retain) NSNumber *rememberLogin;
|
||||
@property(nonatomic, retain) NSNumber *hidePasswords;
|
||||
|
||||
@property(nonatomic, retain) NSNumber *checkInconsistency;
|
||||
|
||||
@property(nonatomic, strong) NSNumber *siteAttacker;
|
||||
|
||||
@end
|
||||
35
platform-ios/ObjC/MPConfig.m
Normal file
35
platform-ios/ObjC/MPConfig.m
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// MPConfig.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
|
||||
@implementation MPConfig
|
||||
|
||||
@dynamic sendInfo, rememberLogin, checkInconsistency, hidePasswords, siteAttacker;
|
||||
|
||||
- (id)init {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
[self.defaults registerDefaults:@{
|
||||
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
||||
|
||||
NSStringFromSelector( @selector( sendInfo ) ) : @NO,
|
||||
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
||||
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
||||
NSStringFromSelector( @selector( checkInconsistency ) ) : @NO,
|
||||
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
|
||||
}];
|
||||
|
||||
self.delegate = [MPAppDelegate_Shared get];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
73
platform-ios/ObjC/MPEntities.h
Normal file
73
platform-ios/ObjC/MPEntities.h
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// MPEntities.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 31/05/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPStoredSiteEntity.h"
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
#import "MPUserEntity.h"
|
||||
#import "MPAlgorithm.h"
|
||||
#import "MPFixable.h"
|
||||
|
||||
#define MPAvatarCount 19
|
||||
|
||||
@interface NSManagedObjectContext(MP)
|
||||
|
||||
- (BOOL)saveToStore;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPSiteQuestionEntity(MP)
|
||||
|
||||
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key;
|
||||
- (void)resolveQuestionAnswerUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPSiteEntity(MP)<MPFixable>
|
||||
|
||||
@property(assign) BOOL loginGenerated;
|
||||
@property(assign) MPSiteType type;
|
||||
@property(readonly) NSString *typeName;
|
||||
@property(readonly) NSString *typeShortName;
|
||||
@property(readonly) NSString *typeClassName;
|
||||
@property(readonly) Class typeClass;
|
||||
@property(assign) NSUInteger uses;
|
||||
@property(assign) BOOL requiresExplicitMigration;
|
||||
@property(strong) id<MPAlgorithm> algorithm;
|
||||
|
||||
- (NSUInteger)use;
|
||||
- (BOOL)tryMigrateExplicitly:(BOOL)explicit;
|
||||
- (NSString *)resolveLoginUsingKey:(MPKey *)key;
|
||||
- (NSString *)resolvePasswordUsingKey:(MPKey *)key;
|
||||
- (NSString *)resolveSiteAnswerUsingKey:(MPKey *)key;
|
||||
- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
|
||||
- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
|
||||
- (void)resolveSiteAnswerUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
|
||||
- (void)resolveAnswerUsingKey:(MPKey *)key forQuestion:(NSString *)question result:(void ( ^ )(NSString *))result;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPGeneratedSiteEntity(MP)
|
||||
|
||||
@property(assign) NSUInteger counter;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPUserEntity(MP)
|
||||
|
||||
@property(assign) NSUInteger avatar;
|
||||
@property(assign) BOOL saveKey;
|
||||
@property(assign) BOOL touchID;
|
||||
@property(assign) MPSiteType defaultType;
|
||||
@property(readonly) NSString *userID;
|
||||
@property(strong) id<MPAlgorithm> algorithm;
|
||||
|
||||
+ (NSString *)idFor:(NSString *)userName;
|
||||
|
||||
@end
|
||||
364
platform-ios/ObjC/MPEntities.m
Normal file
364
platform-ios/ObjC/MPEntities.m
Normal file
@@ -0,0 +1,364 @@
|
||||
//
|
||||
// MPEntities.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 31/05/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPEntities.h"
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_InApp.h"
|
||||
|
||||
@implementation NSManagedObjectContext(MP)
|
||||
|
||||
- (BOOL)saveToStore {
|
||||
|
||||
__block BOOL success = YES;
|
||||
if ([self hasChanges]) {
|
||||
[self performBlockAndWait:^{
|
||||
@try {
|
||||
NSError *error = nil;
|
||||
if (!(success = [self save:&error]))
|
||||
err( @"While saving: %@", [error fullDescription] );
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
success = NO;
|
||||
err( @"While saving: %@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
return success && (!self.parentContext || [self.parentContext saveToStore]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPSiteQuestionEntity(MP)
|
||||
|
||||
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key {
|
||||
|
||||
return [self.site.algorithm resolveAnswerForQuestion:self usingKey:key];
|
||||
}
|
||||
|
||||
- (void)resolveQuestionAnswerUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
|
||||
|
||||
[self.site.algorithm resolveAnswerForQuestion:self usingKey:key result:result];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPSiteEntity(MP)
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
return MPFixableResultNoProblems;
|
||||
}
|
||||
|
||||
- (MPSiteType)type {
|
||||
|
||||
return (MPSiteType)[self.type_ unsignedIntegerValue];
|
||||
}
|
||||
|
||||
- (void)setLoginGenerated:(BOOL)aLoginGenerated {
|
||||
|
||||
self.loginGenerated_ = @(aLoginGenerated);
|
||||
}
|
||||
|
||||
- (BOOL)loginGenerated {
|
||||
|
||||
return [self.loginGenerated_ boolValue];
|
||||
}
|
||||
|
||||
- (void)setType:(MPSiteType)aType {
|
||||
|
||||
self.type_ = @(aType);
|
||||
}
|
||||
|
||||
- (NSString *)typeName {
|
||||
|
||||
return [self.algorithm nameOfType:self.type];
|
||||
}
|
||||
|
||||
- (NSString *)typeShortName {
|
||||
|
||||
return [self.algorithm shortNameOfType:self.type];
|
||||
}
|
||||
|
||||
- (NSString *)typeClassName {
|
||||
|
||||
return [self.algorithm classNameOfType:self.type];
|
||||
}
|
||||
|
||||
- (Class)typeClass {
|
||||
|
||||
return [self.algorithm classOfType:self.type];
|
||||
}
|
||||
|
||||
- (NSUInteger)uses {
|
||||
|
||||
return [self.uses_ unsignedIntegerValue];
|
||||
}
|
||||
|
||||
- (void)setUses:(NSUInteger)anUses {
|
||||
|
||||
self.uses_ = @(anUses);
|
||||
}
|
||||
|
||||
- (id<MPAlgorithm>)algorithm {
|
||||
|
||||
return MPAlgorithmForVersion(
|
||||
MIN( MPAlgorithmVersionCurrent,
|
||||
MAX( MPAlgorithmVersion0, (MPAlgorithmVersion)[self.version_ unsignedIntegerValue] ) ) );
|
||||
}
|
||||
|
||||
- (void)setAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||
|
||||
self.version_ = @([algorithm version]);
|
||||
}
|
||||
|
||||
- (BOOL)requiresExplicitMigration {
|
||||
|
||||
return [self.requiresExplicitMigration_ boolValue];
|
||||
}
|
||||
|
||||
- (void)setRequiresExplicitMigration:(BOOL)requiresExplicitMigration {
|
||||
|
||||
self.requiresExplicitMigration_ = @(requiresExplicitMigration);
|
||||
}
|
||||
|
||||
- (NSUInteger)use {
|
||||
|
||||
self.lastUsed = [NSDate date];
|
||||
return ++self.uses;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
|
||||
return strf( @"%@:%@", [self class], [self name] );
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
|
||||
__block NSString *debugDescription = strf( @"{%@: [recursing]}", [self class] );
|
||||
|
||||
static BOOL recursing = NO;
|
||||
PearlIfNotRecursing( &recursing, ^{
|
||||
@try {
|
||||
debugDescription = strf(
|
||||
@"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
|
||||
NSStringFromClass( [self class] ), self.name, self.user.name, (long)self.type, (long)self.uses, self.lastUsed,
|
||||
(long)[self.algorithm version],
|
||||
self.loginName, self.requiresExplicitMigration );
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
debugDescription = strf( @"{%@: inaccessible: %@}",
|
||||
NSStringFromClass( [self class] ), [exception fullDescription] );
|
||||
}
|
||||
} );
|
||||
|
||||
return debugDescription;
|
||||
}
|
||||
|
||||
- (BOOL)tryMigrateExplicitly:(BOOL)explicit {
|
||||
|
||||
MPAlgorithmVersion algorithmVersion;
|
||||
while ((algorithmVersion = [self.algorithm version]) < MPAlgorithmDefaultVersion) {
|
||||
MPAlgorithmVersion toVersion = algorithmVersion + 1;
|
||||
if (![MPAlgorithmForVersion( toVersion ) tryMigrateSite:self explicit:explicit]) {
|
||||
wrn( @"%@ migration to version: %ld failed for site: %@",
|
||||
explicit? @"Explicit": @"Automatic", (long)toVersion, self );
|
||||
return NO;
|
||||
}
|
||||
|
||||
inf( @"%@ migration to version: %ld succeeded for site: %@",
|
||||
explicit? @"Explicit": @"Automatic", (long)toVersion, self );
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)resolveLoginUsingKey:(MPKey *)key {
|
||||
|
||||
return [self.algorithm resolveLoginForSite:self usingKey:key];
|
||||
}
|
||||
|
||||
- (NSString *)resolvePasswordUsingKey:(MPKey *)key {
|
||||
|
||||
return [self.algorithm resolvePasswordForSite:self usingKey:key];
|
||||
}
|
||||
|
||||
- (NSString *)resolveSiteAnswerUsingKey:(MPKey *)key {
|
||||
|
||||
return [self.algorithm resolveAnswerForSite:self usingKey:key];
|
||||
}
|
||||
|
||||
- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
|
||||
|
||||
[self.algorithm resolveLoginForSite:self usingKey:key result:result];
|
||||
}
|
||||
|
||||
- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
|
||||
|
||||
[self.algorithm resolvePasswordForSite:self usingKey:key result:result];
|
||||
}
|
||||
|
||||
- (void)resolveSiteAnswerUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
|
||||
|
||||
[self.algorithm resolveAnswerForSite:self usingKey:key result:result];
|
||||
}
|
||||
|
||||
- (void)resolveAnswerUsingKey:(MPKey *)key forQuestion:(NSString *)question result:(void ( ^ )(NSString *))result {
|
||||
|
||||
MPSiteQuestionEntity *questionEntity = [MPSiteQuestionEntity new];
|
||||
questionEntity.site = self;
|
||||
questionEntity.keyword = question;
|
||||
[questionEntity resolveQuestionAnswerUsingKey:key result:result];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPGeneratedSiteEntity(MP)
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
|
||||
|
||||
if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
||||
// Invalid self.type
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
||||
self.name, self.user.name, (long)self.type, (long)self.user.defaultType );
|
||||
self.type = self.user.defaultType;
|
||||
return MPFixableResultProblemsFixed;
|
||||
} );
|
||||
if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
||||
// Invalid self.user.defaultType
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
||||
self.name, self.user.name, (long)self.type, (long)MPSiteTypeGeneratedLong );
|
||||
self.type = MPSiteTypeGeneratedLong;
|
||||
return MPFixableResultProblemsFixed;
|
||||
} );
|
||||
if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
|
||||
// Mismatch between self.type and self.class
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
for (MPSiteType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);)
|
||||
if ([self isKindOfClass:[self.algorithm classOfType:newType]]) {
|
||||
wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.",
|
||||
self.name, self.user.name, (long)self.type, self.class, (long)newType );
|
||||
self.type = newType;
|
||||
return MPFixableResultProblemsFixed;
|
||||
}
|
||||
|
||||
err( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Couldn't find a type to fix problem with.",
|
||||
self.name, self.user.name, (long)self.type, self.class );
|
||||
return MPFixableResultProblemsNotFixed;
|
||||
} );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSUInteger)counter {
|
||||
|
||||
return [self.counter_ unsignedIntegerValue];
|
||||
}
|
||||
|
||||
- (void)setCounter:(NSUInteger)aCounter {
|
||||
|
||||
self.counter_ = @(aCounter);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPStoredSiteEntity(MP)
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
|
||||
|
||||
if (self.contentObject && ![self.contentObject isKindOfClass:[NSData class]])
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
MPKey *key = [MPAppDelegate_Shared get].key;
|
||||
if (key && [[MPAppDelegate_Shared get] activeUserInContext:context] == self.user) {
|
||||
wrn( @"Content object not encrypted for: %@ of %@. Will re-encrypt.", self.name, self.user.name );
|
||||
[self.algorithm savePassword:[self.contentObject description] toSite:self usingKey:key];
|
||||
return MPFixableResultProblemsFixed;
|
||||
}
|
||||
|
||||
err( @"Content object not encrypted for: %@ of %@. Couldn't fix, please sign in.", self.name, self.user.name );
|
||||
return MPFixableResultProblemsNotFixed;
|
||||
} );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPUserEntity(MP)
|
||||
|
||||
- (NSUInteger)avatar {
|
||||
|
||||
return [self.avatar_ unsignedIntegerValue];
|
||||
}
|
||||
|
||||
- (void)setAvatar:(NSUInteger)anAvatar {
|
||||
|
||||
self.avatar_ = @(anAvatar);
|
||||
}
|
||||
|
||||
- (BOOL)saveKey {
|
||||
|
||||
return [self.saveKey_ boolValue];
|
||||
}
|
||||
|
||||
- (void)setSaveKey:(BOOL)aSaveKey {
|
||||
|
||||
self.saveKey_ = @(aSaveKey);
|
||||
}
|
||||
|
||||
- (BOOL)touchID {
|
||||
|
||||
return [self.touchID_ boolValue] && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductTouchID];
|
||||
}
|
||||
|
||||
- (void)setTouchID:(BOOL)aTouchID {
|
||||
|
||||
self.touchID_ = @(aTouchID);
|
||||
}
|
||||
|
||||
- (MPSiteType)defaultType {
|
||||
|
||||
return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
|
||||
}
|
||||
|
||||
- (void)setDefaultType:(MPSiteType)aDefaultType {
|
||||
|
||||
self.defaultType_ = @(aDefaultType);
|
||||
}
|
||||
|
||||
- (id<MPAlgorithm>)algorithm {
|
||||
|
||||
return MPAlgorithmForVersion(
|
||||
MIN( MPAlgorithmVersionCurrent,
|
||||
MAX( MPAlgorithmVersion0, (MPAlgorithmVersion)[self.version_ unsignedIntegerValue] ) ) );
|
||||
}
|
||||
|
||||
- (void)setAlgorithm:(id<MPAlgorithm>)version {
|
||||
|
||||
self.version_ = @([version version]);
|
||||
[[MPAppDelegate_Shared get] forgetSavedKeyFor:self];
|
||||
}
|
||||
|
||||
- (NSString *)userID {
|
||||
|
||||
return [MPUserEntity idFor:self.name];
|
||||
}
|
||||
|
||||
+ (NSString *)idFor:(NSString *)userName {
|
||||
|
||||
return [[userName hashWith:PearlHashSHA1] encodeHex];
|
||||
}
|
||||
|
||||
@end
|
||||
33
platform-ios/ObjC/MPFixable.h
Normal file
33
platform-ios/ObjC/MPFixable.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPFixable.h
|
||||
// MPFixable
|
||||
//
|
||||
// Created by lhunath on 2014-04-26.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM( NSUInteger, MPFixableResult ) {
|
||||
MPFixableResultNoProblems,
|
||||
MPFixableResultProblemsFixed,
|
||||
MPFixableResultProblemsNotFixed,
|
||||
};
|
||||
|
||||
MPFixableResult MPApplyFix(MPFixableResult previousResult, MPFixableResult(^fixBlock)(void));
|
||||
|
||||
@protocol MPFixable<NSObject>
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context;
|
||||
|
||||
@end
|
||||
40
platform-ios/ObjC/MPFixable.m
Normal file
40
platform-ios/ObjC/MPFixable.m
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPFixable.m
|
||||
// MPFixable
|
||||
//
|
||||
// Created by lhunath on 2014-04-26.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPFixable.h"
|
||||
|
||||
MPFixableResult MPApplyFix(MPFixableResult previousResult, MPFixableResult(^fixBlock)(void)) {
|
||||
|
||||
MPFixableResult additionalResult = fixBlock();
|
||||
switch (previousResult) {
|
||||
case MPFixableResultNoProblems:
|
||||
return additionalResult;
|
||||
case MPFixableResultProblemsFixed:
|
||||
switch (additionalResult) {
|
||||
case MPFixableResultNoProblems:
|
||||
case MPFixableResultProblemsFixed:
|
||||
return previousResult;
|
||||
case MPFixableResultProblemsNotFixed:
|
||||
return additionalResult;
|
||||
}
|
||||
case MPFixableResultProblemsNotFixed:
|
||||
return additionalResult;
|
||||
}
|
||||
|
||||
Throw( @"Unexpected previous=%ld or additional=%ld result.", (long)previousResult, (long)additionalResult );
|
||||
}
|
||||
18
platform-ios/ObjC/MPGeneratedSiteEntity.h
Normal file
18
platform-ios/ObjC/MPGeneratedSiteEntity.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
|
||||
@interface MPGeneratedSiteEntity : MPSiteEntity
|
||||
|
||||
@property (nonatomic, retain) NSNumber * counter_;
|
||||
|
||||
@end
|
||||
16
platform-ios/ObjC/MPGeneratedSiteEntity.m
Normal file
16
platform-ios/ObjC/MPGeneratedSiteEntity.m
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
|
||||
|
||||
@implementation MPGeneratedSiteEntity
|
||||
|
||||
@dynamic counter_;
|
||||
|
||||
@end
|
||||
44
platform-ios/ObjC/MPKey.h
Normal file
44
platform-ios/ObjC/MPKey.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPKey
|
||||
//
|
||||
// Created by Maarten Billemont on 16/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPAlgorithm.h"
|
||||
|
||||
@protocol MPAlgorithm;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, MPKeyOrigin) {
|
||||
MPKeyOriginMasterPassword,
|
||||
MPKeyOriginKeyChain,
|
||||
MPKeyOriginKeyChainBiometric,
|
||||
};
|
||||
|
||||
@interface MPKey : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString *fullName;
|
||||
@property(nonatomic, readonly) MPKeyOrigin origin;
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
||||
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin;
|
||||
|
||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength;
|
||||
|
||||
- (BOOL)isEqualToKey:(MPKey *)key;
|
||||
|
||||
@end
|
||||
94
platform-ios/ObjC/MPKey.m
Normal file
94
platform-ios/ObjC/MPKey.m
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPKey
|
||||
//
|
||||
// Created by Maarten Billemont on 16/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAlgorithm.h"
|
||||
|
||||
@interface MPKey()
|
||||
|
||||
@property(nonatomic) NSString *fullName;
|
||||
@property(nonatomic) MPKeyOrigin origin;
|
||||
@property(nonatomic) NSString *masterPassword;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPKey {
|
||||
NSCache *_keyCache;
|
||||
};
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
_keyCache = [NSCache new];
|
||||
self.fullName = fullName;
|
||||
self.origin = MPKeyOriginMasterPassword;
|
||||
self.masterPassword = masterPassword;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
||||
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin {
|
||||
|
||||
if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
|
||||
return nil;
|
||||
|
||||
self.origin = origin;
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||
|
||||
return [algorithm keyIDForKeyData:[self keyDataForAlgorithm:algorithm]];
|
||||
}
|
||||
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||
|
||||
NSData *keyData = [_keyCache objectForKey:algorithm];
|
||||
if (keyData)
|
||||
return keyData;
|
||||
|
||||
keyData = [algorithm keyDataForFullName:self.fullName withMasterPassword:self.masterPassword];
|
||||
if (keyData)
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
|
||||
return keyData;
|
||||
}
|
||||
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength {
|
||||
|
||||
NSData *keyData = [self keyDataForAlgorithm:algorithm];
|
||||
return [keyData subdataWithRange:NSMakeRange( 0, MIN( subKeyLength, keyData.length ) )];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToKey:(MPKey *)key {
|
||||
|
||||
return [self.fullName isEqualToString:key.fullName] && [self.masterPassword isEqualToString:self.masterPassword];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
|
||||
if (![object isKindOfClass:[MPKey class]])
|
||||
return NO;
|
||||
|
||||
return [self isEqualToKey:object];
|
||||
}
|
||||
|
||||
@end
|
||||
41
platform-ios/ObjC/MPSiteEntity.h
Normal file
41
platform-ios/ObjC/MPSiteEntity.h
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// MPSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteQuestionEntity, MPUserEntity;
|
||||
|
||||
@interface MPSiteEntity : NSManagedObject
|
||||
|
||||
//@property (nonatomic, retain) id content; // Hide here, reveal in MPStoredSiteEntity
|
||||
@property (nonatomic, retain) NSDate * lastUsed;
|
||||
@property (nonatomic, retain) NSNumber * loginGenerated_;
|
||||
@property (nonatomic, retain) NSString * loginName;
|
||||
@property (nonatomic, retain) NSString * name;
|
||||
@property (nonatomic, retain) NSNumber * requiresExplicitMigration_;
|
||||
@property (nonatomic, retain) NSNumber * type_;
|
||||
@property (nonatomic, retain) NSNumber * uses_;
|
||||
@property (nonatomic, retain) NSNumber * version_;
|
||||
@property (nonatomic, retain) NSOrderedSet *questions;
|
||||
@property (nonatomic, retain) MPUserEntity *user;
|
||||
@end
|
||||
|
||||
@interface MPSiteEntity (CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)insertObject:(MPSiteQuestionEntity *)value inQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)removeObjectFromQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)insertQuestions:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
|
||||
- (void)removeQuestionsAtIndexes:(NSIndexSet *)indexes;
|
||||
- (void)replaceObjectInQuestionsAtIndex:(NSUInteger)idx withObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)replaceQuestionsAtIndexes:(NSIndexSet *)indexes withQuestions:(NSArray *)values;
|
||||
- (void)addQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)removeQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)addQuestions:(NSOrderedSet *)values;
|
||||
- (void)removeQuestions:(NSOrderedSet *)values;
|
||||
@end
|
||||
28
platform-ios/ObjC/MPSiteEntity.m
Normal file
28
platform-ios/ObjC/MPSiteEntity.m
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// MPSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPUserEntity.h"
|
||||
|
||||
|
||||
@implementation MPSiteEntity
|
||||
|
||||
//@dynamic content;
|
||||
@dynamic lastUsed;
|
||||
@dynamic loginGenerated_;
|
||||
@dynamic loginName;
|
||||
@dynamic name;
|
||||
@dynamic requiresExplicitMigration_;
|
||||
@dynamic type_;
|
||||
@dynamic uses_;
|
||||
@dynamic version_;
|
||||
@dynamic questions;
|
||||
@dynamic user;
|
||||
|
||||
@end
|
||||
19
platform-ios/ObjC/MPSiteQuestionEntity.h
Normal file
19
platform-ios/ObjC/MPSiteQuestionEntity.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// MPSiteQuestionEntity.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-27.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
@interface MPSiteQuestionEntity : NSManagedObject
|
||||
|
||||
@property (nonatomic, retain) NSString * keyword;
|
||||
@property (nonatomic, retain) MPSiteEntity *site;
|
||||
|
||||
@end
|
||||
18
platform-ios/ObjC/MPSiteQuestionEntity.m
Normal file
18
platform-ios/ObjC/MPSiteQuestionEntity.m
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// MPSiteQuestionEntity.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-27.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
|
||||
@implementation MPSiteQuestionEntity
|
||||
|
||||
@dynamic keyword;
|
||||
@dynamic site;
|
||||
|
||||
@end
|
||||
18
platform-ios/ObjC/MPStoredSiteEntity.h
Normal file
18
platform-ios/ObjC/MPStoredSiteEntity.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// MPStoredSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
|
||||
@interface MPStoredSiteEntity : MPSiteEntity
|
||||
|
||||
@property (nonatomic, retain) NSData *contentObject;
|
||||
|
||||
@end
|
||||
16
platform-ios/ObjC/MPStoredSiteEntity.m
Normal file
16
platform-ios/ObjC/MPStoredSiteEntity.m
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// MPStoredSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity.h"
|
||||
|
||||
|
||||
@implementation MPStoredSiteEntity
|
||||
|
||||
@dynamic contentObject;
|
||||
|
||||
@end
|
||||
22
platform-ios/ObjC/MPTypes.h
Normal file
22
platform-ios/ObjC/MPTypes.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPTypes.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
__BEGIN_DECLS
|
||||
extern NSString *const MPErrorDomain;
|
||||
|
||||
extern NSString *const MPSignedInNotification;
|
||||
extern NSString *const MPSignedOutNotification;
|
||||
extern NSString *const MPKeyForgottenNotification;
|
||||
extern NSString *const MPSiteUpdatedNotification;
|
||||
extern NSString *const MPCheckConfigNotification;
|
||||
extern NSString *const MPSitesImportedNotification;
|
||||
extern NSString *const MPFoundInconsistenciesNotification;
|
||||
|
||||
extern NSString *const MPSitesImportedNotificationUserKey;
|
||||
extern NSString *const MPInconsistenciesFixResultUserKey;
|
||||
__END_DECLS
|
||||
22
platform-ios/ObjC/MPTypes.m
Normal file
22
platform-ios/ObjC/MPTypes.m
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPTypes.c
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPTypes.h"
|
||||
|
||||
NSString *const MPErrorDomain = @"MPErrorDomain";
|
||||
|
||||
NSString *const MPSignedInNotification = @"MPSignedInNotification";
|
||||
NSString *const MPSignedOutNotification = @"MPSignedOutNotification";
|
||||
NSString *const MPKeyForgottenNotification = @"MPKeyForgottenNotification";
|
||||
NSString *const MPSiteUpdatedNotification = @"MPSiteUpdatedNotification";
|
||||
NSString *const MPCheckConfigNotification = @"MPCheckConfigNotification";
|
||||
NSString *const MPSitesImportedNotification = @"MPSitesImportedNotification";
|
||||
NSString *const MPFoundInconsistenciesNotification = @"MPFoundInconsistenciesNotification";
|
||||
|
||||
NSString *const MPSitesImportedNotificationUserKey = @"MPSitesImportedNotificationUserKey";
|
||||
NSString *const MPInconsistenciesFixResultUserKey = @"MPInconsistenciesFixResultUserKey";
|
||||
34
platform-ios/ObjC/MPUserEntity.h
Normal file
34
platform-ios/ObjC/MPUserEntity.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// MPUserEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
@interface MPUserEntity : NSManagedObject
|
||||
|
||||
@property (nonatomic, retain) NSNumber * avatar_;
|
||||
@property (nonatomic, retain) NSNumber * defaultType_;
|
||||
@property (nonatomic, retain) NSData * keyID;
|
||||
@property (nonatomic, retain) NSDate * lastUsed;
|
||||
@property (nonatomic, retain) NSString * name;
|
||||
@property (nonatomic, retain) NSNumber * saveKey_;
|
||||
@property (nonatomic, retain) NSNumber * touchID_;
|
||||
@property (nonatomic, retain) NSNumber * version_;
|
||||
@property (nonatomic, retain) NSSet *sites;
|
||||
@end
|
||||
|
||||
@interface MPUserEntity (CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)addSitesObject:(MPSiteEntity *)value;
|
||||
- (void)removeSitesObject:(MPSiteEntity *)value;
|
||||
- (void)addSites:(NSSet *)values;
|
||||
- (void)removeSites:(NSSet *)values;
|
||||
|
||||
@end
|
||||
25
platform-ios/ObjC/MPUserEntity.m
Normal file
25
platform-ios/ObjC/MPUserEntity.m
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// MPUserEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity.h"
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
|
||||
@implementation MPUserEntity
|
||||
|
||||
@dynamic avatar_;
|
||||
@dynamic defaultType_;
|
||||
@dynamic keyID;
|
||||
@dynamic lastUsed;
|
||||
@dynamic name;
|
||||
@dynamic saveKey_;
|
||||
@dynamic touchID_;
|
||||
@dynamic version_;
|
||||
@dynamic sites;
|
||||
|
||||
@end
|
||||
121
platform-ios/ObjC/Mac/MPAppsViewController.xib
Normal file
121
platform-ios/ObjC/Mac/MPAppsViewController.xib
Normal file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
|
||||
<data>
|
||||
<int key="IBDocument.SystemTarget">1070</int>
|
||||
<string key="IBDocument.SystemVersion">11C42</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">1938</string>
|
||||
<string key="IBDocument.AppKitVersion">1138.17</string>
|
||||
<string key="IBDocument.HIToolboxVersion">567.00</string>
|
||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="NS.object.0">1938</string>
|
||||
</object>
|
||||
<array key="IBDocument.IntegratedClassDependencies">
|
||||
<string>NSWindowTemplate</string>
|
||||
<string>NSView</string>
|
||||
<string>NSCustomObject</string>
|
||||
</array>
|
||||
<array key="IBDocument.PluginDependencies">
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
</array>
|
||||
<object class="NSMutableDictionary" key="IBDocument.Metadata">
|
||||
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
|
||||
<integer value="1" key="NS.object.0"/>
|
||||
</object>
|
||||
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
|
||||
<object class="NSCustomObject" id="1001">
|
||||
<string key="NSClassName">NSObject</string>
|
||||
</object>
|
||||
<object class="NSCustomObject" id="1003">
|
||||
<string key="NSClassName">FirstResponder</string>
|
||||
</object>
|
||||
<object class="NSCustomObject" id="1004">
|
||||
<string key="NSClassName">NSApplication</string>
|
||||
</object>
|
||||
<object class="NSWindowTemplate" id="1005">
|
||||
<int key="NSWindowStyleMask">15</int>
|
||||
<int key="NSWindowBacking">2</int>
|
||||
<string key="NSWindowRect">{{196, 240}, {480, 270}}</string>
|
||||
<int key="NSWTFlags">544735232</int>
|
||||
<string key="NSWindowTitle">Window</string>
|
||||
<string key="NSWindowClass">NSWindow</string>
|
||||
<nil key="NSViewClass"/>
|
||||
<nil key="NSUserInterfaceItemIdentifier"/>
|
||||
<object class="NSView" key="NSWindowView" id="1006">
|
||||
<reference key="NSNextResponder"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrameSize">{480, 270}</string>
|
||||
<reference key="NSSuperview"/>
|
||||
<reference key="NSWindow"/>
|
||||
</object>
|
||||
<string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
|
||||
<string key="NSMaxSize">{10000000000000, 10000000000000}</string>
|
||||
<bool key="NSWindowIsRestorable">YES</bool>
|
||||
</object>
|
||||
</array>
|
||||
<object class="IBObjectContainer" key="IBDocument.Objects">
|
||||
<array class="NSMutableArray" key="connectionRecords"/>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<array key="orderedObjects">
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">0</int>
|
||||
<array key="object" id="0"/>
|
||||
<reference key="children" ref="1000"/>
|
||||
<nil key="parent"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">-2</int>
|
||||
<reference key="object" ref="1001"/>
|
||||
<reference key="parent" ref="0"/>
|
||||
<string key="objectName">File's Owner</string>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">-1</int>
|
||||
<reference key="object" ref="1003"/>
|
||||
<reference key="parent" ref="0"/>
|
||||
<string key="objectName">First Responder</string>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">-3</int>
|
||||
<reference key="object" ref="1004"/>
|
||||
<reference key="parent" ref="0"/>
|
||||
<string key="objectName">Application</string>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">1</int>
|
||||
<reference key="object" ref="1005"/>
|
||||
<array class="NSMutableArray" key="children">
|
||||
<reference ref="1006"/>
|
||||
</array>
|
||||
<reference key="parent" ref="0"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">2</int>
|
||||
<reference key="object" ref="1006"/>
|
||||
<reference key="parent" ref="1005"/>
|
||||
</object>
|
||||
</array>
|
||||
</object>
|
||||
<dictionary class="NSMutableDictionary" key="flattenedProperties">
|
||||
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="1.IBWindowTemplateEditedContentRect">{{357, 418}, {480, 270}}</string>
|
||||
<integer value="1" key="1.NSWindowTemplate.visibleAtLaunch"/>
|
||||
<string key="2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
</dictionary>
|
||||
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
|
||||
<nil key="activeLocalization"/>
|
||||
<dictionary class="NSMutableDictionary" key="localizations"/>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">2</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes"/>
|
||||
<int key="IBDocument.localizationMode">0</int>
|
||||
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||
<bool key="IBDocument.UseAutolayout">YES</bool>
|
||||
</data>
|
||||
</archive>
|
||||
16
platform-ios/ObjC/Mac/MPGradientView.h
Normal file
16
platform-ios/ObjC/Mac/MPGradientView.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Created by Maarten Billemont on 2016-05-15.
|
||||
// Copyright (c) 2016 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
IB_DESIGNABLE
|
||||
@interface MPGradientView : NSView
|
||||
|
||||
@property(nonatomic, retain) IBInspectable NSColor *startingColor;
|
||||
@property(nonatomic, retain) IBInspectable NSColor *endingColor;
|
||||
@property(nonatomic, assign) IBInspectable NSInteger angle;
|
||||
@property(nonatomic, assign) IBInspectable CGFloat ratio;
|
||||
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user