diff --git a/.gitmodules b/.gitmodules index 7eea0c49..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +0,0 @@ -[submodule "External"] - path = External - url = ../../omivpn/privado-shared-apple.git - fetchrecursesubmodules = true - update = checkout diff --git a/External b/External deleted file mode 160000 index 14668bc2..00000000 --- a/External +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 14668bc213510a4dc16607ab8a31e9fb8da22c35 diff --git a/Vendors/Sparkle/1.24.0 b/Vendors/Sparkle/1.24.0 new file mode 100644 index 00000000..e69de29b diff --git a/Vendors/Sparkle/Sparkle.xcframework/Info.plist b/Vendors/Sparkle/Sparkle.xcframework/Info.plist new file mode 100644 index 00000000..21587458 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/Info.plist @@ -0,0 +1,28 @@ + + + + + AvailableLibraries + + + DebugSymbolsPath + dSYMs + LibraryIdentifier + macos-arm64_x86_64 + LibraryPath + Sparkle.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + macos + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Headers b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Headers new file mode 120000 index 00000000..a177d2a6 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Modules b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Modules new file mode 120000 index 00000000..5736f318 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/PrivateHeaders b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/PrivateHeaders new file mode 120000 index 00000000..d8e56452 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/PrivateHeaders @@ -0,0 +1 @@ +Versions/Current/PrivateHeaders \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Resources b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Resources new file mode 120000 index 00000000..953ee36f --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Sparkle b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Sparkle new file mode 120000 index 00000000..b2c52731 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Sparkle @@ -0,0 +1 @@ +Versions/Current/Sparkle \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h new file mode 100644 index 00000000..41cd5743 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h @@ -0,0 +1,43 @@ +// +// SPUDownloadData.h +// Sparkle +// +// Created by Mayur Pawashe on 8/10/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +#import "SUExport.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! + * A class for containing downloaded data along with some information about it. + */ +SU_EXPORT @interface SPUDownloadData : NSObject + +- (instancetype)initWithData:(NSData *)data textEncodingName:(NSString * _Nullable)textEncodingName MIMEType:(NSString * _Nullable)MIMEType; + +/*! + * The raw data that was downloaded. + */ +@property (nonatomic, readonly) NSData *data; + +/*! + * The IANA charset encoding name if available. Eg: "utf-8" + */ +@property (nonatomic, readonly, nullable, copy) NSString *textEncodingName; + +/*! + * The MIME type if available. Eg: "text/plain" + */ +@property (nonatomic, readonly, nullable, copy) NSString *MIMEType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloader.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloader.h new file mode 100644 index 00000000..5eee9bd5 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloader.h @@ -0,0 +1,25 @@ +// +// SPUDownloader.h +// Downloader +// +// Created by Mayur Pawashe on 4/1/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SPUDownloaderProtocol.h" + +@protocol SPUDownloaderDelegate; + +// This object implements the protocol which we have defined. It provides the actual behavior for the service. It is 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection. +@interface SPUDownloader : NSObject + +// Due to XPC remote object reasons, this delegate is strongly referenced +// Invoke cleanup when done with this instance +- (instancetype)initWithDelegate:(id )delegate; + +@end diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h new file mode 100644 index 00000000..76e7e750 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h @@ -0,0 +1,38 @@ +// +// SPUDownloaderDelegate.h +// Sparkle +// +// Created by Mayur Pawashe on 4/1/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SPUDownloadData; + +@protocol SPUDownloaderDelegate + +// This is only invoked for persistent downloads +- (void)downloaderDidSetDestinationName:(NSString *)destinationName temporaryDirectory:(NSString *)temporaryDirectory; + +// Under rare cases, this may be called more than once, in which case the current progress should be reset back to 0 +// This is only invoked for persistent downloads +- (void)downloaderDidReceiveExpectedContentLength:(int64_t)expectedContentLength; + +// This is only invoked for persistent downloads +- (void)downloaderDidReceiveDataOfLength:(uint64_t)length; + +// downloadData is nil if this is a persisent download, otherwise it's non-nil if it's a temporary download +- (void)downloaderDidFinishWithTemporaryDownloadData:(SPUDownloadData * _Nullable)downloadData; + +- (void)downloaderDidFailWithError:(NSError *)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h new file mode 100644 index 00000000..36302df4 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h @@ -0,0 +1,13 @@ +// +// SPUDownloaderDeprecated.h +// Sparkle +// +// Created by Deadpikle on 12/20/17. +// Copyright © 2017 Sparkle Project. All rights reserved. +// + +#import "SPUDownloader.h" + +@interface SPUDownloaderDeprecated : SPUDownloader + +@end diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h new file mode 100644 index 00000000..ebe477fe --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h @@ -0,0 +1,34 @@ +// +// SPUDownloaderProtocol.h +// PersistentDownloader +// +// Created by Mayur Pawashe on 4/1/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SPUURLRequest; + +// The protocol that this service will vend as its API. This header file will also need to be visible to the process hosting the service. +@protocol SPUDownloaderProtocol + +- (void)startPersistentDownloadWithRequest:(SPUURLRequest *)request bundleIdentifier:(NSString *)bundleIdentifier desiredFilename:(NSString *)desiredFilename; + +- (void)startTemporaryDownloadWithRequest:(SPUURLRequest *)request; + +- (void)downloadDidFinish; + +- (void)cleanup; + +- (void)cancel; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h new file mode 100644 index 00000000..4bde75aa --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h @@ -0,0 +1,20 @@ +// +// SPUDownloaderSession.h +// Sparkle +// +// Created by Deadpikle on 12/20/17. +// Copyright © 2017 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SPUDownloader.h" +#import "SPUDownloaderProtocol.h" + +NS_CLASS_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0) +@interface SPUDownloaderSession : SPUDownloader + +@end diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h new file mode 100644 index 00000000..69496147 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h @@ -0,0 +1,35 @@ +// +// SPUURLRequest.h +// Sparkle +// +// Created by Mayur Pawashe on 5/19/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +// A class that wraps NSURLRequest and implements NSSecureCoding +// This class exists because NSURLRequest did not support NSSecureCoding in macOS 10.8 +// I have not verified if NSURLRequest in 10.9 implements NSSecureCoding or not +@interface SPUURLRequest : NSObject + +// Creates a new URL request +// Only these properties are currently tracked: +// * URL +// * Cache policy +// * Timeout interval +// * HTTP header fields +// * networkServiceType ++ (instancetype)URLRequestWithRequest:(NSURLRequest *)request; + +@property (nonatomic, readonly) NSURLRequest *request; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUAppcast.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUAppcast.h new file mode 100644 index 00000000..34276b7d --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUAppcast.h @@ -0,0 +1,35 @@ +// +// SUAppcast.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCAST_H +#define SUAPPCAST_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +NS_ASSUME_NONNULL_BEGIN + +@class SUAppcastItem; +SU_EXPORT @interface SUAppcast : NSObject + +@property (copy, nullable) NSString *userAgentString; +@property (copy, nullable) NSDictionary *httpHeaders; + +- (void)fetchAppcastFromURL:(NSURL *)url inBackground:(BOOL)bg completionBlock:(void (^)(NSError *_Nullable))err; +- (SUAppcast *)copyWithoutDeltaUpdates; + +@property (readonly, copy, nullable) NSArray *items; +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h new file mode 100644 index 00000000..1d8b1d01 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h @@ -0,0 +1,54 @@ +// +// SUAppcastItem.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCASTITEM_H +#define SUAPPCASTITEM_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" +@class SUSignatures; + +SU_EXPORT @interface SUAppcastItem : NSObject +@property (copy, readonly) NSString *title; +@property (copy, readonly) NSString *dateString; +@property (copy, readonly) NSDate *date; +@property (copy, readonly) NSString *itemDescription; +@property (strong, readonly) NSURL *releaseNotesURL; +@property (strong, readonly) SUSignatures *signatures; +@property (copy, readonly) NSString *minimumSystemVersion; +@property (copy, readonly) NSString *maximumSystemVersion; +@property (strong, readonly) NSURL *fileURL; +@property (nonatomic, readonly) uint64_t contentLength; +@property (copy, readonly) NSString *versionString; +@property (copy, readonly) NSString *osString; +@property (copy, readonly) NSString *displayVersionString; +@property (copy, readonly) NSDictionary *deltaUpdates; +@property (strong, readonly) NSURL *infoURL; +@property (copy, readonly) NSNumber* phasedRolloutInterval; + +// Initializes with data from a dictionary provided by the RSS class. +- (instancetype)initWithDictionary:(NSDictionary *)dict; +- (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString **)error; + +@property (getter=isDeltaUpdate, readonly) BOOL deltaUpdate; +@property (getter=isCriticalUpdate, readonly) BOOL criticalUpdate; +@property (getter=isMacOsUpdate, readonly) BOOL macOsUpdate; +@property (getter=isInformationOnlyUpdate, readonly) BOOL informationOnlyUpdate; + +// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. +@property (readonly, copy) NSDictionary *propertiesDictionary; + +- (NSURL *)infoURL; + +@end + +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h new file mode 100644 index 00000000..3756a378 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h @@ -0,0 +1,26 @@ +// +// SUCodeSigningVerifier.h +// Sparkle +// +// Created by Andy Matuschak on 7/5/12. +// +// + +#ifndef SUCODESIGNINGVERIFIER_H +#define SUCODESIGNINGVERIFIER_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +SU_EXPORT @interface SUCodeSigningVerifier : NSObject ++ (BOOL)codeSignatureAtBundleURL:(NSURL *)oldBundlePath matchesSignatureAtBundleURL:(NSURL *)newBundlePath error:(NSError **)error; ++ (BOOL)codeSignatureIsValidAtBundleURL:(NSURL *)bundlePath error:(NSError **)error; ++ (BOOL)bundleAtURLIsCodeSigned:(NSURL *)bundlePath; ++ (NSDictionary *)codeSignatureInfoAtBundleURL:(NSURL *)bundlePath; +@end + +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUErrors.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUErrors.h new file mode 100644 index 00000000..4b160c4f --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUErrors.h @@ -0,0 +1,57 @@ +// +// SUErrors.h +// Sparkle +// +// Created by C.W. Betts on 10/13/14. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUERRORS_H +#define SUERRORS_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +/** + * Error domain used by Sparkle + */ +SU_EXPORT extern NSString *const SUSparkleErrorDomain; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +typedef NS_ENUM(OSStatus, SUError) { + // Appcast phase errors. + SUAppcastParseError = 1000, + SUNoUpdateError = 1001, + SUAppcastError = 1002, + SURunningFromDiskImageError = 1003, + SURunningTranslocated = 1004, + + // Download phase errors. + SUTemporaryDirectoryError = 2000, + SUDownloadError = 2001, + + // Extraction phase errors. + SUUnarchivingError = 3000, + SUSignatureError = 3001, + + // Installation phase errors. + SUFileCopyFailure = 4000, + SUAuthenticationFailure = 4001, + SUMissingUpdateError = 4002, + SUMissingInstallerToolError = 4003, + SURelaunchError = 4004, + SUInstallationError = 4005, + SUDowngradeError = 4006, + SUInstallationCancelledError = 4007, + + // System phase errors + SUSystemPowerOffError = 5000 +}; +#pragma clang diagnostic pop + +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUExport.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUExport.h new file mode 100644 index 00000000..3e3f8a16 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUExport.h @@ -0,0 +1,18 @@ +// +// SUExport.h +// Sparkle +// +// Created by Jake Petroules on 2014-08-23. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUEXPORT_H +#define SUEXPORT_H + +#ifdef BUILDING_SPARKLE +#define SU_EXPORT __attribute__((visibility("default"))) +#else +#define SU_EXPORT +#endif + +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h new file mode 100644 index 00000000..ed11921a --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h @@ -0,0 +1,52 @@ +// +// SUStandardVersionComparator.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUSTANDARDVERSIONCOMPARATOR_H +#define SUSTANDARDVERSIONCOMPARATOR_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" +#import "SUVersionComparisonProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! + Sparkle's default version comparator. + + This comparator is adapted from MacPAD, by Kevin Ballard. + It's "dumb" in that it does essentially string comparison, + in components split by character type. +*/ +SU_EXPORT @interface SUStandardVersionComparator : NSObject + +/*! + Initializes a new instance of the standard version comparator. + */ +- (instancetype)init; + +/*! + Returns a singleton instance of the comparator. + + It is usually preferred to alloc/init new a comparator instead. +*/ ++ (SUStandardVersionComparator *)defaultComparator; + +/*! + Compares version strings through textual analysis. + + See the implementation for more details. +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; +@end + +NS_ASSUME_NONNULL_END +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUUpdater.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUUpdater.h new file mode 100644 index 00000000..d05270f2 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUUpdater.h @@ -0,0 +1,233 @@ +// +// SUUpdater.h +// Sparkle +// +// Created by Andy Matuschak on 1/4/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUUPDATER_H +#define SUUPDATER_H + +#if __has_feature(modules) +@import Cocoa; +#else +#import +#endif +#import "SUExport.h" +#import "SUVersionComparisonProtocol.h" +#import "SUVersionDisplayProtocol.h" + +@class SUAppcastItem, SUAppcast; + +@protocol SUUpdaterDelegate; + +/*! + The main API in Sparkle for controlling the update mechanism. + + This class is used to configure the update paramters as well as manually + and automatically schedule and control checks for updates. + */ +SU_EXPORT @interface SUUpdater : NSObject + +@property (unsafe_unretained) IBOutlet id delegate; + +/*! + The shared updater for the main bundle. + + This is equivalent to passing [NSBundle mainBundle] to SUUpdater::updaterForBundle: + */ ++ (SUUpdater *)sharedUpdater; + +/*! + The shared updater for a specified bundle. + + If an updater has already been initialized for the provided bundle, that shared instance will be returned. + */ ++ (SUUpdater *)updaterForBundle:(NSBundle *)bundle; + +/*! + Designated initializer for SUUpdater. + + If an updater has already been initialized for the provided bundle, that shared instance will be returned. + */ +- (instancetype)initForBundle:(NSBundle *)bundle; + +/*! + Explicitly checks for updates and displays a progress dialog while doing so. + + This method is meant for a main menu item. + Connect any menu item to this action in Interface Builder, + and Sparkle will check for updates and report back its findings verbosely + when it is invoked. + + This will find updates that the user has opted into skipping. + */ +- (IBAction)checkForUpdates:(id)sender; + +/*! + The menu item validation used for the -checkForUpdates: action + */ +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem; + +/*! + Checks for updates, but does not display any UI unless an update is found. + + This is meant for programmatically initating a check for updates. That is, + it will display no UI unless it actually finds an update, in which case it + proceeds as usual. + + If automatic downloading of updates it turned on and allowed, however, + this will invoke that behavior, and if an update is found, it will be downloaded + in the background silently and will be prepped for installation. + + This will not find updates that the user has opted into skipping. + */ +- (void)checkForUpdatesInBackground; + +/*! + A property indicating whether or not to check for updates automatically. + + Setting this property will persist in the host bundle's user defaults. + The update schedule cycle will be reset in a short delay after the property's new value is set. + This is to allow reverting this property without kicking off a schedule change immediately + */ +@property BOOL automaticallyChecksForUpdates; + +/*! + A property indicating whether or not updates can be automatically downloaded in the background. + + Note that automatic downloading of updates can be disallowed by the developer + or by the user's system if silent updates cannot be done (eg: if they require authentication). + In this case, -automaticallyDownloadsUpdates will return NO regardless of how this property is set. + + Setting this property will persist in the host bundle's user defaults. + */ +@property BOOL automaticallyDownloadsUpdates; + +/*! + A property indicating the current automatic update check interval. + + Setting this property will persist in the host bundle's user defaults. + The update schedule cycle will be reset in a short delay after the property's new value is set. + This is to allow reverting this property without kicking off a schedule change immediately + */ +@property NSTimeInterval updateCheckInterval; + +/*! + Begins a "probing" check for updates which will not actually offer to + update to that version. + + However, the delegate methods + SUUpdaterDelegate::updater:didFindValidUpdate: and + SUUpdaterDelegate::updaterDidNotFindUpdate: will be called, + so you can use that information in your UI. + + Updates that have been skipped by the user will not be found. + */ +- (void)checkForUpdateInformation; + +/*! + The URL of the appcast used to download update information. + + Setting this property will persist in the host bundle's user defaults. + If you don't want persistence, you may want to consider instead implementing + SUUpdaterDelegate::feedURLStringForUpdater: or SUUpdaterDelegate::feedParametersForUpdater:sendingSystemProfile: + + This property must be called on the main thread. + */ +@property (copy) NSURL *feedURL; + +/*! + The host bundle that is being updated. + */ +@property (readonly, strong) NSBundle *hostBundle; + +/*! + The bundle this class (SUUpdater) is loaded into. + */ +@property (strong, readonly) NSBundle *sparkleBundle; + +/*! + The user agent used when checking for updates. + + The default implementation can be overrided. + */ +@property (nonatomic, copy) NSString *userAgentString; + +/*! + The HTTP headers used when checking for updates. + + The keys of this dictionary are HTTP header fields (NSString) and values are corresponding values (NSString) + */ +@property (copy) NSDictionary *httpHeaders; + +/*! + A property indicating whether or not the user's system profile information is sent when checking for updates. + + Setting this property will persist in the host bundle's user defaults. + */ +@property BOOL sendsSystemProfile; + +/*! + A property indicating the decryption password used for extracting updates shipped as Apple Disk Images (dmg) + */ +@property (nonatomic, copy) NSString *decryptionPassword; + +/*! + This function ignores normal update schedule, ignores user preferences, + and interrupts users with an unwanted immediate app update. + + WARNING: this function should not be used in regular apps. This function + is a user-unfriendly hack only for very special cases, like unstable + rapidly-changing beta builds that would not run correctly if they were + even one day out of date. + + Instead of this function you should set `SUAutomaticallyUpdate` to `YES`, + which will gracefully install updates when the app quits. + + For UI-less/daemon apps that aren't usually quit, instead of this function, + you can use the delegate method + SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation: + or + SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationBlock: + to immediately start installation when an update was found. + + A progress dialog is shown but the user will never be prompted to read the + release notes. + + This function will cause update to be downloaded twice if automatic updates are + enabled. + + You may want to respond to the userDidCancelDownload delegate method in case + the user clicks the "Cancel" button while the update is downloading. + */ +- (void)installUpdatesIfAvailable; + +/*! + Returns the date of last update check. + + \returns \c nil if no check has been performed. + */ +@property (readonly, copy) NSDate *lastUpdateCheckDate; + +/*! + Appropriately schedules or cancels the update checking timer according to + the preferences for time interval and automatic checks. + + This call does not change the date of the next check, + but only the internal NSTimer. + */ +- (void)resetUpdateCycle; + +/*! + A property indicating whether or not an update is in progress. + + Note this property is not indicative of whether or not user initiated updates can be performed. + Use SUUpdater::validateMenuItem: for that instead. + */ +@property (readonly) BOOL updateInProgress; + +@end + +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h new file mode 100644 index 00000000..ec844d04 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h @@ -0,0 +1,352 @@ +// +// SUUpdaterDelegate.h +// Sparkle +// +// Created by Mayur Pawashe on 12/25/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +#import "SUExport.h" + +@protocol SUVersionComparison, SUVersionDisplay; +@class SUUpdater, SUAppcast, SUAppcastItem; + +NS_ASSUME_NONNULL_BEGIN + +// ----------------------------------------------------------------------------- +// SUUpdater Notifications for events that might be interesting to more than just the delegate +// The updater will be the notification object +// ----------------------------------------------------------------------------- +SU_EXPORT extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; +SU_EXPORT extern NSString *const SUUpdaterDidFindValidUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterDidNotFindUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterWillRestartNotification; +#define SUUpdaterWillRelaunchApplicationNotification SUUpdaterWillRestartNotification; +#define SUUpdaterWillInstallUpdateNotification SUUpdaterWillRestartNotification; + +// Key for the SUAppcastItem object in the SUUpdaterDidFindValidUpdateNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; +// Key for the SUAppcast object in the SUUpdaterDidFinishLoadingAppCastNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; + +// ----------------------------------------------------------------------------- +// SUUpdater Delegate: +// ----------------------------------------------------------------------------- + +/*! + Provides methods to control the behavior of an SUUpdater object. + */ +@protocol SUUpdaterDelegate +@optional + +/*! + Returns whether to allow Sparkle to pop up. + + For example, this may be used to prevent Sparkle from interrupting a setup assistant. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)updater; + +/*! + Returns additional parameters to append to the appcast URL's query string. + + This is potentially based on whether or not Sparkle will also be sending along the system profile. + + \param updater The SUUpdater instance. + \param sendingProfile Whether the system profile will also be sent. + + \return An array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. + */ +- (NSArray *> *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; + +/*! + Returns a custom appcast URL. + + Override this to dynamically specify the entire URL. + + An alternative may be to use SUUpdaterDelegate::feedParametersForUpdater:sendingSystemProfile: + and let the server handle what kind of feed to provide. + + \param updater The SUUpdater instance. + */ +- (nullable NSString *)feedURLStringForUpdater:(SUUpdater *)updater; + +/*! + Returns whether Sparkle should prompt the user about automatic update checks. + + Use this to override the default behavior. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)updater; + +/*! + Called after Sparkle has downloaded the appcast from the remote server. + + Implement this if you want to do some special handling with the appcast once it finishes loading. + + \param updater The SUUpdater instance. + \param appcast The appcast that was downloaded from the remote server. + */ +- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; + +/*! + Returns the item in the appcast corresponding to the update that should be installed. + + If you're using special logic or extensions in your appcast, + implement this to use your own logic for finding a valid update, if any, + in the given appcast. + + \param appcast The appcast that was downloaded from the remote server. + \param updater The SUUpdater instance. + */ +- (nullable SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)updater; + +/*! + Called when a valid update is found by the update driver. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item; + +/*! + Called just before the scheduled update driver prompts the user to install an update. + + \param updater The SUUpdater instance. + + \return YES to allow the update prompt to be shown (the default behavior), or NO to suppress it. + */ +- (BOOL)updaterShouldShowUpdateAlertForScheduledUpdate:(SUUpdater *)updater forItem:(SUAppcastItem *)item; + +/*! + Called after the user dismisses the update alert. + + \param updater The SUUpdater instance. + \param permanently YES if the alert will not appear again for this update; NO if it may reappear. + */ +- (void)updater:(SUUpdater *)updater didDismissUpdateAlertPermanently:(BOOL)permanently forItem:(SUAppcastItem *)item; + +/*! + Called when a valid update is not found. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidNotFindUpdate:(SUUpdater *)updater; + +/*! + Called when the user clicks the Skip This Version button. + + \param updater The SUUpdater instance. + */ +- (void)updater:(SUUpdater *)updater userDidSkipThisVersion:(SUAppcastItem *)item; + +/*! + Called immediately before downloading the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be downloaded. + \param request The mutable URL request that will be used to download the update. + */ +- (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request; + +/*! + Called immediately after succesfull download of the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that has been downloaded. + */ +- (void)updater:(SUUpdater *)updater didDownloadUpdate:(SUAppcastItem *)item; + +/*! + Called after the specified update failed to download. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that failed to download. + \param error The error generated by the failed download. + */ +- (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error; + +/*! + Called when the user clicks the cancel button while and update is being downloaded. + + \param updater The SUUpdater instance. + */ +- (void)userDidCancelDownload:(SUUpdater *)updater; + +/*! + Called immediately before extracting the specified downloaded update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be extracted. + */ +- (void)updater:(SUUpdater *)updater willExtractUpdate:(SUAppcastItem *)item; + +/*! + Called immediately after extracting the specified downloaded update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that has been extracted. + */ +- (void)updater:(SUUpdater *)updater didExtractUpdate:(SUAppcastItem *)item; + +/*! + Called immediately before installing the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)item; + +/*! + Returns whether the relaunch should be delayed in order to perform other tasks. + + This is not called if the user didn't relaunch on the previous update, + in that case it will immediately restart. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param invocation The invocation that must be completed with `[invocation invoke]` before continuing with the relaunch. + + \return \c YES to delay the relaunch until \p invocation is invoked. + */ +- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvoking:(NSInvocation *)invocation; + +/*! + Returns whether the relaunch should be delayed in order to perform other tasks. + + This is not called if the user didn't relaunch on the previous update, + in that case it will immediately restart. + + This method acts as a simpler alternative to SUUpdaterDelegate::updater:shouldPostponeRelaunchForUpdate:untilInvoking: avoiding usage of NSInvocation, which is not available in Swift environments. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + + \return \c YES to delay the relaunch. + */ +- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item; + +/*! + Returns whether the application should be relaunched at all. + + Some apps \b cannot be relaunched under certain circumstances. + This method can be used to explicitly prevent a relaunch. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater; + +/*! + Called immediately before relaunching. + + \param updater The SUUpdater instance. + */ +- (void)updaterWillRelaunchApplication:(SUUpdater *)updater; + +/*! + Called immediately after relaunching. SUUpdater delegate must be set before applicationDidFinishLaunching: to catch this event. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidRelaunchApplication:(SUUpdater *)updater; + +/*! + Returns an object that compares version numbers to determine their arithmetic relation to each other. + + This method allows you to provide a custom version comparator. + If you don't implement this method or return \c nil, + the standard version comparator will be used. + + \sa SUStandardVersionComparator + + \param updater The SUUpdater instance. + */ +- (nullable id)versionComparatorForUpdater:(SUUpdater *)updater; + +/*! + Returns an object that formats version numbers for display to the user. + + If you don't implement this method or return \c nil, + the standard version formatter will be used. + + \sa SUUpdateAlert + + \param updater The SUUpdater instance. + */ +- (nullable id)versionDisplayerForUpdater:(SUUpdater *)updater; + +/*! + Returns the path which is used to relaunch the client after the update is installed. + + The default is the path of the host bundle. + + \param updater The SUUpdater instance. + */ +- (nullable NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater; + +/*! + Called before an updater shows a modal alert window, + to give the host the opportunity to hide attached windows that may get in the way. + + \param updater The SUUpdater instance. + */ +- (void)updaterWillShowModalAlert:(SUUpdater *)updater; + +/*! + Called after an updater shows a modal alert window, + to give the host the opportunity to hide attached windows that may get in the way. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidShowModalAlert:(SUUpdater *)updater; + +/*! + Called when an update is scheduled to be silently installed on quit. + This is after an update has been automatically downloaded in the background. + (i.e. SUUpdater::automaticallyDownloadsUpdates is YES) + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param invocation Can be used to trigger an immediate silent install and relaunch. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation; + +/*! + Called when an update is scheduled to be silently installed on quit. + This is after an update has been automatically downloaded in the background. + (i.e. SUUpdater::automaticallyDownloadsUpdates is YES) + This method acts as a more modern alternative to SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation: using a block instead of NSInvocation, which is not available in Swift environments. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param installationBlock Can be used to trigger an immediate silent install and relaunch. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationBlock:(void (^)(void))installationBlock; + +/*! + Calls after an update that was scheduled to be silently installed on quit has been canceled. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that was proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater didCancelInstallUpdateOnQuit:(SUAppcastItem *)item; + +/*! + Called after an update is aborted due to an error. + + \param updater The SUUpdater instance. + \param error The error that caused the abort + */ +- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h new file mode 100644 index 00000000..c654fc4d --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h @@ -0,0 +1,37 @@ +// +// SUVersionComparisonProtocol.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUVERSIONCOMPARISONPROTOCOL_H +#define SUVERSIONCOMPARISONPROTOCOL_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! + Provides version comparison facilities for Sparkle. +*/ +@protocol SUVersionComparison + +/*! + An abstract method to compare two version strings. + + Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, + and NSOrderedSame if they are equivalent. +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD! + +@end + +NS_ASSUME_NONNULL_END +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h new file mode 100644 index 00000000..980efb3f --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h @@ -0,0 +1,29 @@ +// +// SUVersionDisplayProtocol.h +// EyeTV +// +// Created by Uli Kusterer on 08.12.09. +// Copyright 2009 Elgato Systems GmbH. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +/*! + Applies special display formatting to version numbers. +*/ +@protocol SUVersionDisplay + +/*! + Formats two version strings. + + Both versions are provided so that important distinguishing information + can be displayed while also leaving out unnecessary/confusing parts. +*/ +- (void)formatVersion:(NSString *_Nonnull*_Nonnull)inOutVersionA andVersion:(NSString *_Nonnull*_Nonnull)inOutVersionB; + +@end diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/Sparkle.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/Sparkle.h new file mode 100644 index 00000000..1085d419 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Headers/Sparkle.h @@ -0,0 +1,39 @@ +// +// Sparkle.h +// Sparkle +// +// Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07) +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SPARKLE_H +#define SPARKLE_H + +// This list should include the shared headers. It doesn't matter if some of them aren't shared (unless +// there are name-space collisions) so we can list all of them to start with: + +#pragma clang diagnostic push +// Do not use <> style includes since 2.x has two frameworks that need to work: Sparkle and SparkleCore +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUStandardVersionComparator.h" +#import "SUUpdater.h" +#import "SUUpdaterDelegate.h" +#import "SUVersionComparisonProtocol.h" +#import "SUVersionDisplayProtocol.h" +#import "SUErrors.h" + +#import "SPUDownloader.h" +#import "SPUDownloaderDelegate.h" +#import "SPUDownloaderDeprecated.h" +#import "SPUDownloadData.h" +#import "SPUDownloaderProtocol.h" +#import "SPUDownloaderSession.h" +#import "SPUURLRequest.h" +#import "SUCodeSigningVerifier.h" + +#pragma clang diagnostic pop + +#endif diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Modules/module.modulemap b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 00000000..af3fe6d0 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module Sparkle { + umbrella header "Sparkle.h" + + export * + module * { export * } +} diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h new file mode 100644 index 00000000..a52bf5a2 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h @@ -0,0 +1,21 @@ +// +// SUUnarchiver.h +// Sparkle +// +// Created by Andy Matuschak on 3/16/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol SUUnarchiverProtocol; + +@interface SUUnarchiver : NSObject + ++ (nullable id )unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist new file mode 100644 index 00000000..8a45abac --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist @@ -0,0 +1,56 @@ + + + + + BuildMachineOSBuild + 20B28 + CFBundleDevelopmentRegion + English + CFBundleExecutable + Autoupdate + CFBundleIconFile + AppIcon.icns + CFBundleIdentifier + org.sparkle-project.Sparkle.Autoupdate + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.24.0 1-g489ae19e + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1.24.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 12C5020f + DTPlatformName + macosx + DTPlatformVersion + 11.1 + DTSDKBuild + 20C5048g + DTSDKName + macosx11.1 + DTXcode + 1230 + DTXcodeBuild + 12C5020f + LSBackgroundOnly + 1 + LSMinimumSystemVersion + 10.7 + LSUIElement + 1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate new file mode 100755 index 00000000..664c194e Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop new file mode 100755 index 00000000..5fcc19c7 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo new file mode 100644 index 00000000..bd04210f --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns new file mode 100644 index 00000000..7f2a571c Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib new file mode 100644 index 00000000..f9c39a03 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 00000000..e00af341 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..f2aea275 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 00000000..02e077cf Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings new file mode 100644 index 00000000..e0957c60 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings new file mode 100644 index 00000000..202e70b7 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings new file mode 100644 index 00000000..6ef15d4a Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings new file mode 100644 index 00000000..1d70063c Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings new file mode 100644 index 00000000..8a083f86 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..773f7c99 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 00000000..954abee0 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..ce63fdef Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings new file mode 100644 index 00000000..ab8fe1a3 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings new file mode 100644 index 00000000..d30ef64b Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings new file mode 100644 index 00000000..5f6ace28 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings new file mode 100644 index 00000000..5b4be9ea Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 00000000..f4685eda Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 00000000..f008e1ee Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 00000000..fa4cd97d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 00000000..76f3556b Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 00000000..4444f338 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 00000000..2a7ce299 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 00000000..18a287e8 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 00000000..967a4418 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 00000000..8a11ecf1 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 00000000..65aa28f2 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 00000000..caaf0603 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 00000000..e7c70db7 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings new file mode 100644 index 00000000..058b4ba6 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 00000000..ffc57672 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 00000000..263326c9 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 00000000..71cf325f Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 00000000..b9517885 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..845555e6 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources @@ -0,0 +1,860 @@ + + + + + files + + Resources/AppIcon.icns + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + + files2 + + MacOS/fileop + + cdhash + + XJ+GBX2nsbycOYdQ8KMcsgCsY90= + + requirement + cdhash H"398823ed847b72465c1250951cb61604021c473c" or cdhash H"5c9f86057da7b1bc9c398750f0a31cb200ac63dd" or cdhash H"2d16a10da663b63c288e2178c07a6169a615d4c5" or cdhash H"1c303c72c91b058bccb119dbc6ca74b9ffdf2c79" + + Resources/AppIcon.icns + + hash + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + hash2 + + nq7j0ugQwyNbJn/7zGFwxIR0njwU3i7hAYKEyZhvUfE= + + + Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/DarkAqua.css b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/DarkAqua.css new file mode 100644 index 00000000..a41e0f28 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/DarkAqua.css @@ -0,0 +1,9 @@ +html { + color: #FFFFFFD8; +} +:link { + color: #419CFF; +} +:link:active { + color: #FF1919; +} diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Info.plist b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Info.plist new file mode 100644 index 00000000..e3bdbfab --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 20B28 + CFBundleDevelopmentRegion + en + CFBundleExecutable + Sparkle + CFBundleIdentifier + org.sparkle-project.Sparkle + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Sparkle + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.24.0 1-g489ae19e + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1.24.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 12C5020f + DTPlatformName + macosx + DTPlatformVersion + 11.1 + DTSDKBuild + 20C5048g + DTSDKName + macosx11.1 + DTXcode + 1230 + DTXcodeBuild + 12C5020f + LSMinimumSystemVersion + 10.7 + + diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist new file mode 100644 index 00000000..1f75b248 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist @@ -0,0 +1,314 @@ + + + + + ADP2,1 + Developer Transition Kit + iMac1,1 + iMac G3 (Rev A-D) + iMac4,1 + iMac (Core Duo) + iMac4,2 + iMac for Education (17 inch, Core Duo) + iMac5,1 + iMac (Core 2 Duo, 17 or 20 inch, SuperDrive) + iMac5,2 + iMac (Core 2 Duo, 17 inch, Combo Drive) + iMac6,1 + iMac (Core 2 Duo, 24 inch, SuperDrive) + iMac7,1 + iMac Intel Core 2 Duo (aluminum enclosure) + iMac8,1 + iMac (Core 2 Duo, 20 or 24 inch, Early 2008 ) + iMac9,1 + iMac (Core 2 Duo, 20 or 24 inch, Early or Mid 2009 ) + iMac10,1 + iMac (Core 2 Duo, 21.5 or 27 inch, Late 2009 ) + iMac11,1 + iMac (Core i5 or i7, 27 inch Late 2009) + iMac11,2 + 21.5" iMac (mid 2010) + iMac11,3 + iMac (Core i5 or i7, 27 inch Mid 2010) + iMac12,1 + iMac (Core i3 or i5 or i7, 21.5 inch Mid 2010 or Late 2011) + iMac12,2 + iMac (Core i5 or i7, 27 inch Mid 2011) + iMac13,1 + iMac (Core i3 or i5 or i7, 21.5 inch Late 2012 or Early 2013) + iMac13,2 + iMac (Core i5 or i7, 27 inch Late 2012) + iMac14,1 + iMac (Core i5, 21.5 inch Late 2013) + iMac14,2 + iMac (Core i5 or i7, 27 inch Late 2013) + iMac14,3 + iMac (Core i5 or i7, 21.5 inch Late 2013) + iMac14,4 + iMac (Core i5, 21.5 inch Mid 2014) + iMac15,1 + iMac (Retina 5K Core i5 or i7, 27 inch Late 2014 or Mid 2015) + iMac16,1 + iMac (Core i5, 21,5 inch Late 2015) + iMac16,2 + iMac (Retina 4K Core i5 or i7, 21.5 inch Late 2015) + iMac17,1 + iMac (Retina 5K Core i5 or i7, 27 inch Late 2015) + MacBook1,1 + MacBook (Core Duo) + MacBook2,1 + MacBook (Core 2 Duo) + MacBook4,1 + MacBook (Core 2 Duo Feb 2008) + MacBook5,1 + MacBook (Core 2 Duo, Late 2008, Unibody) + MacBook5,2 + MacBook (Core 2 Duo, Early 2009, White) + MacBook6,1 + MacBook (Core 2 Duo, Late 2009, Unibody) + MacBook7,1 + MacBook (Core 2 Duo, Mid 2010, White) + MacBook8,1 + MacBook (Core M, 12 inch, Early 2015) + MacBookAir1,1 + MacBook Air (Core 2 Duo, 13 inch, Early 2008) + MacBookAir2,1 + MacBook Air (Core 2 Duo, 13 inch, Mid 2009) + MacBookAir3,1 + MacBook Air (Core 2 Duo, 11 inch, Late 2010) + MacBookAir3,2 + MacBook Air (Core 2 Duo, 13 inch, Late 2010) + MacBookAir4,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2011) + MacBookAir4,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2011) + MacBookAir5,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2012) + MacBookAir5,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2012) + MacBookAir6,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2013 or Early 2014) + MacBookAir6,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2013 or Early 2014) + MacBookAir7,1 + MacBook Air (Core i5 or i7, 11 inch, Early 2015) + MacBookAir7,2 + MacBook Air (Core i5 or i7, 13 inch, Early 2015) + MacBookPro1,1 + MacBook Pro Core Duo (15-inch) + MacBookPro1,2 + MacBook Pro Core Duo (17-inch) + MacBookPro2,1 + MacBook Pro Core 2 Duo (17-inch) + MacBookPro2,2 + MacBook Pro Core 2 Duo (15-inch) + MacBookPro3,1 + MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo) + MacBookPro3,2 + MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) + MacBookPro4,1 + MacBook Pro (Core 2 Duo Feb 2008) + MacBookPro5,1 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,2 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,3 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,4 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,5 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro6,1 + MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) + MacBookPro6,2 + MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) + MacBookPro7,1 + MacBook Pro Intel Core 2 Duo (mid 2010) + MacBookPro8,1 + MacBook Pro Intel Core i5, Intel Core i7, 13" (early 2011) + MacBookPro8,2 + MacBook Pro Intel Core i7, 15" (early 2011) + MacBookPro8,3 + MacBook Pro Intel Core i7, 17" (early 2011) + MacBookPro9,1 + MacBook Pro (15-inch, Mid 2012) + MacBookPro9,2 + MacBook Pro (13-inch, Mid 2012) + MacBookPro10,1 + MacBook Pro (Retina, Mid 2012) + MacBookPro10,2 + MacBook Pro (Retina, 13-inch, Late 2012) + MacBookPro11,1 + MacBook Pro (Retina, 13-inch, Late 2013) + MacBookPro11,2 + MacBook Pro (Retina, 15-inch, Late 2013) + MacBookPro11,3 + MacBook Pro (Retina, 15-inch, Late 2013) + MacbookPro11,4 + MacBook Pro (Retina, 15-inch, Mid 2015) + MacbookPro11,5 + MacBook Pro (Retina, 15-inch, Mid 2015) + MacbookPro12,1  + MacBook Pro (Retina, 13-inch, Early 2015) + Macmini1,1 + Mac Mini (Core Solo/Duo) + Macmini2,1 + Mac mini Intel Core + Macmini3,1 + Mac mini Intel Core + Macmini4,1 + Mac mini Intel Core (Mid 2010) + Macmini5,1 + Mac mini (Core i5, Mid 2011) + Macmini5,2 + Mac mini (Core i5 or Core i7, Mid 2011) + Macmini5,3 + Mac mini (Core i7, Server, Mid 2011) + Macmini6,1 + Mac mini (Core i5, Late 2012) + Macmini6,2 + Mac mini (Core i7, Normal or Server, Late 2012) + Macmini7,1 + Mac mini (Core i5 or Core i7, Late 2014) + MacPro1,1,Quad + Mac Pro + MacPro1,1 + Mac Pro (four-core) + MacPro2,1 + Mac Pro (eight-core) + MacPro3,1 + Mac Pro (January 2008 4- or 8- core "Harpertown") + MacPro4,1 + Mac Pro (March 2009) + MacPro5,1 + Mac Pro (2010 or 2012) + MacPro6,1 + Mac Pro (Late 2013) + PowerBook1,1 + PowerBook G3 + PowerBook2,1 + iBook G3 + PowerBook2,2 + iBook G3 (FireWire) + PowerBook2,3 + iBook G3 + PowerBook2,4 + iBook G3 + PowerBook3,1 + PowerBook G3 (FireWire) + PowerBook3,2 + PowerBook G4 + PowerBook3,3 + PowerBook G4 (Gigabit Ethernet) + PowerBook3,4 + PowerBook G4 (DVI) + PowerBook3,5 + PowerBook G4 (1GHz / 867MHz) + PowerBook4,1 + iBook G3 (Dual USB, Late 2001) + PowerBook4,2 + iBook G3 (16MB VRAM) + PowerBook4,3 + iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003) + PowerBook5,1 + PowerBook G4 (17 inch) + PowerBook5,2 + PowerBook G4 (15 inch FW 800) + PowerBook5,3 + PowerBook G4 (17-inch 1.33GHz) + PowerBook5,4 + PowerBook G4 (15 inch 1.5/1.33GHz) + PowerBook5,5 + PowerBook G4 (17-inch 1.5GHz) + PowerBook5,6 + PowerBook G4 (15 inch 1.67GHz/1.5GHz) + PowerBook5,7 + PowerBook G4 (17-inch 1.67GHz) + PowerBook5,8 + PowerBook G4 (Double layer SD, 15 inch) + PowerBook5,9 + PowerBook G4 (Double layer SD, 17 inch) + PowerBook6,1 + PowerBook G4 (12 inch) + PowerBook6,2 + PowerBook G4 (12 inch, DVI) + PowerBook6,3 + iBook G4 + PowerBook6,4 + PowerBook G4 (12 inch 1.33GHz) + PowerBook6,5 + iBook G4 (Early-Late 2004) + PowerBook6,7 + iBook G4 (Mid 2005) + PowerBook6,8 + PowerBook G4 (12 inch 1.5GHz) + PowerMac1,1 + Power Macintosh G3 (Blue & White) + PowerMac1,2 + Power Macintosh G4 (PCI Graphics) + PowerMac2,1 + iMac G3 (Slot-loading CD-ROM) + PowerMac2,2 + iMac G3 (Summer 2000) + PowerMac3,1 + Power Macintosh G4 (AGP Graphics) + PowerMac3,2 + Power Macintosh G4 (AGP Graphics) + PowerMac3,3 + Power Macintosh G4 (Gigabit Ethernet) + PowerMac3,4 + Power Macintosh G4 (Digital Audio) + PowerMac3,5 + Power Macintosh G4 (Quick Silver) + PowerMac3,6 + Power Macintosh G4 (Mirrored Drive Door) + PowerMac4,1 + iMac G3 (Early/Summer 2001) + PowerMac4,2 + iMac G4 (Flat Panel) + PowerMac4,4 + eMac + PowerMac4,5 + iMac G4 (17-inch Flat Panel) + PowerMac5,1 + Power Macintosh G4 Cube + PowerMac5,2 + Power Mac G4 Cube + PowerMac6,1 + iMac G4 (USB 2.0) + PowerMac6,3 + iMac G4 (20-inch Flat Panel) + PowerMac6,4 + eMac (USB 2.0, 2005) + PowerMac7,2 + Power Macintosh G5 + PowerMac7,3 + Power Macintosh G5 + PowerMac8,1 + iMac G5 + PowerMac8,2 + iMac G5 (Ambient Light Sensor) + PowerMac9,1 + Power Macintosh G5 (Late 2005) + PowerMac10,1 + Mac Mini G4 + PowerMac10,2 + Mac Mini (Late 2005) + PowerMac11,2 + Power Macintosh G5 (Late 2005) + PowerMac12,1 + iMac G5 (iSight) + RackMac1,1 + Xserve G4 + RackMac1,2 + Xserve G4 (slot-loading, cluster node) + RackMac3,1 + Xserve G5 + Xserve1,1 + Xserve (Intel Xeon) + Xserve2,1 + Xserve (January 2008 quad-core) + Xserve3,1 + Xserve (early 2009) + + diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/SUStatus.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/SUStatus.nib new file mode 100644 index 00000000..f9c39a03 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/SUStatus.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..65f118ac Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..44b6741d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..efa0260c Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 00000000..e00af341 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..f2aea275 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..103886fe Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..68e44511 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..d46524a6 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 00000000..02e077cf Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..d22bba56 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..dc1aa3fa Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..a567b284 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings new file mode 100644 index 00000000..e0957c60 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..cf626429 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..25873443 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..02987672 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings new file mode 100644 index 00000000..202e70b7 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..ddcb7b3a Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0085ef14 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..72d6b14c Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings new file mode 100644 index 00000000..6ef15d4a Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..c5732e8f Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..352a9a84 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..b92b0a3e Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings new file mode 100644 index 00000000..1d70063c Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..01694b25 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..9216cb74 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..06b80fea Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings new file mode 100644 index 00000000..8a083f86 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..86011659 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..795d05d3 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..1bdc120d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..773f7c99 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..74c1e5a3 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..dea32c6d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..b8aa1390 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 00000000..954abee0 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr_CA.lproj b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr_CA.lproj new file mode 120000 index 00000000..f9834a39 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/fr_CA.lproj @@ -0,0 +1 @@ +fr.lproj \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..ce63fdef Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..12476cc0 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..60659fe7 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..4627920e Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings new file mode 100644 index 00000000..ab8fe1a3 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..dd46e650 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..b2400c73 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..00a32eb2 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings new file mode 100644 index 00000000..d30ef64b Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..2e2c6025 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..43990e17 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..cb983787 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings new file mode 100644 index 00000000..5f6ace28 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..fb389d23 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..7eea1a8d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..a237886e Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings new file mode 100644 index 00000000..5b4be9ea Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..9200a22c Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..abe1d0bf Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..e073e797 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 00000000..f4685eda Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..b2942676 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..d5e01ade Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..7edae78d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 00000000..f008e1ee Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..cbc7a9fd Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..3a875f2b Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..f8c9d334 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 00000000..fa4cd97d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..96e672b3 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..ccec5f8f Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..aee614ab Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 00000000..76f3556b Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..aa19d7e3 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..4fcb4eec Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..2575e75b Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 00000000..4444f338 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt.lproj b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt.lproj new file mode 120000 index 00000000..3c1c9f6d --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt.lproj @@ -0,0 +1 @@ +pt_BR.lproj \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..0e8fdc60 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0a899437 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..36b9135d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 00000000..2a7ce299 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..7056ce7b Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..d77700f0 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..e2011df4 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 00000000..18a287e8 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..c5907ff1 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..9e45a85c Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..6805b00b Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 00000000..967a4418 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..9889fb2d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..4743f8f9 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..a79b43ed Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 00000000..8a11ecf1 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..912d1dd9 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0aa042e4 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..3db72727 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 00000000..65aa28f2 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..74526023 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..96b5964a Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..3302b8ec Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 00000000..caaf0603 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..ae7ceb14 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..07518c34 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..716ff36c Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 00000000..e7c70db7 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..0ee49a1f Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..5241b1f2 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..c6f35136 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings new file mode 100644 index 00000000..058b4ba6 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..24605f17 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..e722b695 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..47ec4cf8 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 00000000..ffc57672 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..dd540a60 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..8ebec67d Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..9ef61455 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 00000000..263326c9 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..1cb9d707 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..2c066da9 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..2bbe3816 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 00000000..71cf325f Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..3c202810 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..27d22201 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..5486b49a Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 00000000..b9517885 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Sparkle b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Sparkle new file mode 100755 index 00000000..126899eb Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/Sparkle differ diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/_CodeSignature/CodeResources b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/_CodeSignature/CodeResources new file mode 100644 index 00000000..1853f2f6 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/A/_CodeSignature/CodeResources @@ -0,0 +1,3900 @@ + + + + + files + + Resources/Autoupdate.app/Contents/Info.plist + + KWwWNYJdjhEgQdb+JKppt0+bKAw= + + Resources/Autoupdate.app/Contents/MacOS/Autoupdate + + wmoMz7rSIYpe2GRwL0iOO7x8XJM= + + Resources/Autoupdate.app/Contents/MacOS/fileop + + sLn9bRjeR7iPZnPSuCsiIUN5bzk= + + Resources/Autoupdate.app/Contents/PkgInfo + + n57qDP4tZfLD1rCS43W0B4LQjzE= + + Resources/Autoupdate.app/Contents/Resources/AppIcon.icns + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + Resources/Autoupdate.app/Contents/Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources + + gVbyGydhWFWErsmdSv38sD2CK5c= + + Resources/DarkAqua.css + + SCihC2/GG/DhF4xcXD9MYaxhawM= + + Resources/Info.plist + + 5bKlbfUn+UpXGvwWaLxm5i9hZdU= + + Resources/SUModelTranslation.plist + + iD2Ex40Usc4ZE6IAhRePqgwK/xw= + + Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/ar.lproj/SUAutomaticUpdateAlert.nib + + hash + + LtkONVbhTzwCPtbjkr06qSniXCI= + + optional + + + Resources/ar.lproj/SUUpdateAlert.nib + + hash + + JeZDdP1OuZbqkm8UKYiyH00A7ss= + + optional + + + Resources/ar.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2bUXVaur0g5gL09vMdkjjnG/gx8= + + optional + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/cs.lproj/SUAutomaticUpdateAlert.nib + + hash + + YpT086oHMS9O2TvSNLZh+39oy80= + + optional + + + Resources/cs.lproj/SUUpdateAlert.nib + + hash + + v2ac1JQZvkm8EHZiTUc/q4aBcU0= + + optional + + + Resources/cs.lproj/SUUpdatePermissionPrompt.nib + + hash + + i6pnEW/pEUtK6j2Ky5Sz4ePqb1c= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/da.lproj/SUAutomaticUpdateAlert.nib + + hash + + XCqcLv38cTpbjAE4zjN/JWeT3+U= + + optional + + + Resources/da.lproj/SUUpdateAlert.nib + + hash + + BY0imp6dA7C0GSOK81VXTJsRccM= + + optional + + + Resources/da.lproj/SUUpdatePermissionPrompt.nib + + hash + + dbG/KHSKzElkUxJa08TkzSUwdg4= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/de.lproj/SUAutomaticUpdateAlert.nib + + hash + + i/BaQXOSENNulhl0b5jssezuU3Y= + + optional + + + Resources/de.lproj/SUUpdateAlert.nib + + hash + + lieYpCoCaCKAA3EL3/EsBr46vqI= + + optional + + + Resources/de.lproj/SUUpdatePermissionPrompt.nib + + hash + + RPyksdcohNoYO9AulHe18aV0SZY= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/el.lproj/SUAutomaticUpdateAlert.nib + + hash + + s3rpfaKP5+1+vGc44qpcWy+h0t8= + + optional + + + Resources/el.lproj/SUUpdateAlert.nib + + hash + + sJcnQqAH4BsB+2rz9riB7iqePh0= + + optional + + + Resources/el.lproj/SUUpdatePermissionPrompt.nib + + hash + + WbqjimwnC4h+B7DynMFS4qPhljo= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/en.lproj/SUAutomaticUpdateAlert.nib + + hash + + Y6bIF/+bAP3t5gBwPcdqxsj4co4= + + optional + + + Resources/en.lproj/SUUpdateAlert.nib + + hash + + L3hoxekBQAtpmyDXNhTX7kRXRtc= + + optional + + + Resources/en.lproj/SUUpdatePermissionPrompt.nib + + hash + + g/8UXLSUBGROl64wXKn5Wr59X/c= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/es.lproj/SUAutomaticUpdateAlert.nib + + hash + + BoS6NAq1zyVcmkbrKJhcI9Zrezk= + + optional + + + Resources/es.lproj/SUUpdateAlert.nib + + hash + + UMa1QcJf8zfpVUnIZUGFfJ64wTk= + + optional + + + Resources/es.lproj/SUUpdatePermissionPrompt.nib + + hash + + rH20AzERYudrcbwIQQO107Z2zGo= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/fi.lproj/SUAutomaticUpdateAlert.nib + + hash + + 6aUnn3XSgWKnVuYVA/PVSrwora8= + + optional + + + Resources/fi.lproj/SUUpdateAlert.nib + + hash + + k8QjxmBhk5B6v1fGnFkwwX6oakg= + + optional + + + Resources/fi.lproj/SUUpdatePermissionPrompt.nib + + hash + + h7CaxzsvAjayHOo/I9uD1xZMMFo= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/fr.lproj/SUAutomaticUpdateAlert.nib + + hash + + Nj5v0wIECbpjSTU74xKBngH4DeY= + + optional + + + Resources/fr.lproj/SUUpdateAlert.nib + + hash + + RPgJubd38D/WH1H6B1jSejqILE8= + + optional + + + Resources/fr.lproj/SUUpdatePermissionPrompt.nib + + hash + + I2iLdKSjbtR5z2V21+jBWWV/cqQ= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/hr.lproj/SUAutomaticUpdateAlert.nib + + hash + + SkgPcXJYp5dizLAgiXfyl9EsPoI= + + optional + + + Resources/hr.lproj/SUUpdateAlert.nib + + hash + + 47iMWOA+94RZGJW+QJCeM4xOUsA= + + optional + + + Resources/hr.lproj/SUUpdatePermissionPrompt.nib + + hash + + KcZQYPdSeOeuTbFYdiS4NMY+gHM= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/hu.lproj/SUAutomaticUpdateAlert.nib + + hash + + PfYCBbOThC1gBDzoxD+ijdyQ3T0= + + optional + + + Resources/hu.lproj/SUUpdateAlert.nib + + hash + + 6RlHCvHc9GNh1M7iJhvn12iFGpg= + + optional + + + Resources/hu.lproj/SUUpdatePermissionPrompt.nib + + hash + + S4W9ksaDnuIRy8L1ekCKN9+WfP0= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/is.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9eOJ/dQvTMu45Z1UowMPaKHYQOI= + + optional + + + Resources/is.lproj/SUUpdateAlert.nib + + hash + + F0aP96zh7QOSkAdFsBlIzBhmCIg= + + optional + + + Resources/is.lproj/SUUpdatePermissionPrompt.nib + + hash + + KeGsuRgX16ROMEZ8ymQNHPp6/qY= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/it.lproj/SUAutomaticUpdateAlert.nib + + hash + + JXGMS3rtry8HTWEbBibxVCkBEmw= + + optional + + + Resources/it.lproj/SUUpdateAlert.nib + + hash + + W45+n9zTkxt1E3v6cZYLzXNtDlc= + + optional + + + Resources/it.lproj/SUUpdatePermissionPrompt.nib + + hash + + 6K3G2WyaGjrTeQHoweUjaKh6X3A= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/ja.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9fRyIZvZMUaRTaWCda7NsPqJle0= + + optional + + + Resources/ja.lproj/SUUpdateAlert.nib + + hash + + L07PCZt4pHgRzMPxV0N6F2QK9kM= + + optional + + + Resources/ja.lproj/SUUpdatePermissionPrompt.nib + + hash + + hKgWGHf0ha9Io7qZ2ChHjSsH6/s= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/ko.lproj/SUAutomaticUpdateAlert.nib + + hash + + gi+8llNjjuFSKRkNAA7vN/mf1uA= + + optional + + + Resources/ko.lproj/SUUpdateAlert.nib + + hash + + UNAQQTvtG7+MN/4w4ouu9ZHXfCM= + + optional + + + Resources/ko.lproj/SUUpdatePermissionPrompt.nib + + hash + + fUirMWEByjvEZ4Ur3uhDypdTX00= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/nb.lproj/SUAutomaticUpdateAlert.nib + + hash + + kJLypTD4VsCOwsXiOd6700pn0Cc= + + optional + + + Resources/nb.lproj/SUUpdateAlert.nib + + hash + + WIJIO1qR0uNQtJpVkhIarVOfgFw= + + optional + + + Resources/nb.lproj/SUUpdatePermissionPrompt.nib + + hash + + 4lQkM7OM2WfWZkvaBY8841eyT0E= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/nl.lproj/SUAutomaticUpdateAlert.nib + + hash + + wPmqlbK7p5mjPDrezXrYK8CG3NA= + + optional + + + Resources/nl.lproj/SUUpdateAlert.nib + + hash + + 7EXAJEeeN0k32jvOHKr7Icq4644= + + optional + + + Resources/nl.lproj/SUUpdatePermissionPrompt.nib + + hash + + hzjMuMEBooqc++naXBflhhc16vM= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/pl.lproj/SUAutomaticUpdateAlert.nib + + hash + + 4aIS8LbPLTj63EhS7xEk+qjNzD8= + + optional + + + Resources/pl.lproj/SUUpdateAlert.nib + + hash + + pLII26utl65JwmAFqLiMefDsrGs= + + optional + + + Resources/pl.lproj/SUUpdatePermissionPrompt.nib + + hash + + wIe21CUILU7RUksjexoQFfUNt6c= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib + + hash + + 8fQxJZw+LQiExfyPaqUbNnASsWU= + + optional + + + Resources/pt_BR.lproj/SUUpdateAlert.nib + + hash + + zW5zEa6rDpqLuTDfixuKKE93E5o= + + optional + + + Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib + + hash + + LnGHg/0zbDsX5GOE+RuItGOeM3g= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib + + hash + + Svc6e9pHrp1wA7rxw7oKc5HB8qQ= + + optional + + + Resources/pt_PT.lproj/SUUpdateAlert.nib + + hash + + NtD144OppS+BPXNmsReGJoN2Qdo= + + optional + + + Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib + + hash + + DngrIdQPeavGfwbVI7GS1bK98C0= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/ro.lproj/SUAutomaticUpdateAlert.nib + + hash + + 7HEo1dlbwSnit0+4DsAqKDz1jR4= + + optional + + + Resources/ro.lproj/SUUpdateAlert.nib + + hash + + FPWtaRuYrVSPrfAozq/4bSQfMK4= + + optional + + + Resources/ro.lproj/SUUpdatePermissionPrompt.nib + + hash + + NPLiF7WwQt7siCG8DLWh+ZGImWI= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/ru.lproj/SUAutomaticUpdateAlert.nib + + hash + + YHane6xWVhvpJGf5HuoxCyQ/gDs= + + optional + + + Resources/ru.lproj/SUUpdateAlert.nib + + hash + + NxM+W+qAegxK4lKy0uzCclpkVjo= + + optional + + + Resources/ru.lproj/SUUpdatePermissionPrompt.nib + + hash + + P0o42QteueF9d3aaoNcz6obcimA= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/sk.lproj/SUAutomaticUpdateAlert.nib + + hash + + 5k+e1kFtgoVo77RhyhZSXpRQGOQ= + + optional + + + Resources/sk.lproj/SUUpdateAlert.nib + + hash + + l8XR02tvN10SX2aM9CtZ7BpIcqw= + + optional + + + Resources/sk.lproj/SUUpdatePermissionPrompt.nib + + hash + + stqj1WPWzlJrXELqXKGjcxPeqvI= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/sl.lproj/SUAutomaticUpdateAlert.nib + + hash + + htsZnsf4S+omnk4Z8En8qOOjrPM= + + optional + + + Resources/sl.lproj/SUUpdateAlert.nib + + hash + + /9xrGwHXOdPKFp82w4OjF+Q0WG8= + + optional + + + Resources/sl.lproj/SUUpdatePermissionPrompt.nib + + hash + + ApIwZGSV2lqBRKNHGnwhpqmec9Q= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/sv.lproj/SUAutomaticUpdateAlert.nib + + hash + + K0QpkucJxh5CRA9TYJCT5+gSHZY= + + optional + + + Resources/sv.lproj/SUUpdateAlert.nib + + hash + + dOxfomMC/X9MFFdsk1MyjKv1yi8= + + optional + + + Resources/sv.lproj/SUUpdatePermissionPrompt.nib + + hash + + odTG4PkbDUjXrxuBzD+AnnKpOLo= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/th.lproj/SUAutomaticUpdateAlert.nib + + hash + + /iyQcDW4+Is40OSlKcWlEe9zKdo= + + optional + + + Resources/th.lproj/SUUpdateAlert.nib + + hash + + cQCWeOMdd6mZEd9k0pl3FrZDT9g= + + optional + + + Resources/th.lproj/SUUpdatePermissionPrompt.nib + + hash + + HNNvE7bGYAElmoeYbB7JREhcs3I= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/tr.lproj/SUAutomaticUpdateAlert.nib + + hash + + fjnMF3XlYLFPJSpmIFizoGwOVl8= + + optional + + + Resources/tr.lproj/SUUpdateAlert.nib + + hash + + Y9dabfD0a7F1cV9OuFnyQL5BIIc= + + optional + + + Resources/tr.lproj/SUUpdatePermissionPrompt.nib + + hash + + J3IIVJ3ytau8dSfjTtkWF9zh2nA= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/uk.lproj/SUAutomaticUpdateAlert.nib + + hash + + YRRVgJ26NZd9+ebTI3UGdpi35eo= + + optional + + + Resources/uk.lproj/SUUpdateAlert.nib + + hash + + PoeaXUHUKNIm0bkX+GNnvFHlq9w= + + optional + + + Resources/uk.lproj/SUUpdatePermissionPrompt.nib + + hash + + KTOBs3BNdg2ylIdMNsG3WLw5SsA= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib + + hash + + 2pvLfCu7EiI6OkCxu3+aLyeTPcU= + + optional + + + Resources/zh_CN.lproj/SUUpdateAlert.nib + + hash + + +xvQE3bFW1QXIUggZBlZkKn0gag= + + optional + + + Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib + + hash + + 9wwEreTf4JSKHFSiRGUKE2Ffs1g= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib + + hash + + KQcqbpIyw3yhI0eEyo06cNq5MlM= + + optional + + + Resources/zh_TW.lproj/SUUpdateAlert.nib + + hash + + 6/Rgln3/89vly1RFa1gBfRhITxU= + + optional + + + Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib + + hash + + K4vg/ZQjaDTe9PQehvRbUNOWIUc= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + + files2 + + Headers/SPUDownloadData.h + + hash + + 0uirAEMDYuzqSh3dbtfncvlTpvo= + + hash2 + + DkOXqPnwmXR6QeDI4ja3DtFxDW5L3Dcjf21hEPWImoY= + + + Headers/SPUDownloader.h + + hash + + OAA6tKxz1g4V1SQZaj3RPNmNMzo= + + hash2 + + OEDz5kY4WgtWuZ8SApjyqvVDEf3iEj7Sg2uhl+3J+jk= + + + Headers/SPUDownloaderDelegate.h + + hash + + UkBvHxzNYA/YON0ImE8m/8g4vkg= + + hash2 + + 8D6RyR2aziRQIiRN3e7qxlEM9jfgkwBBTWmSjDhLqm8= + + + Headers/SPUDownloaderDeprecated.h + + hash + + l7/+P82TkgRp/YLnQtJtjMWd3/A= + + hash2 + + A7PBMhwuTH0X/JDeyLlh1Xrdc0krwjPqdBPCiePFrxQ= + + + Headers/SPUDownloaderProtocol.h + + hash + + VsD58zsMmnH4mVuK456ekiJcYFU= + + hash2 + + 02oqvyfRCQp5SMSBQNzYZaz1/0VTso0GKnOSbuDEAVo= + + + Headers/SPUDownloaderSession.h + + hash + + IeDSEpUGRwB9HcDVlqbxb6k19A8= + + hash2 + + ZFXLQz90P2h4rR2v1VqRAKzrSSlItkO5cW/0AFy7eYQ= + + + Headers/SPUURLRequest.h + + hash + + 588/W6GUNXYFVar9nygqHNYeTX8= + + hash2 + + 4EG4qYbJgM0wBJCoSPD1lbbDTLHlm85m4xNehyXyU74= + + + Headers/SUAppcast.h + + hash + + puyTMIscPdgGWGQM4TITePIKSdA= + + hash2 + + R3/VQvZCv8Ab34CC17vtBHR3dqkJXnidTR5n8aX4DD0= + + + Headers/SUAppcastItem.h + + hash + + YQvyEYAn6UWDwU8NT5ccPwhdRXE= + + hash2 + + ZB27X0FfNZ54ez8FZAFDEi6/o4BYtQnl4A9Y3tAbkUI= + + + Headers/SUCodeSigningVerifier.h + + hash + + Wj90pgADRte0DhyUmlOXWORp830= + + hash2 + + PPwqqlfaR8hxIfD2YEKdVzyDN4kBVdul55HDAELxQ7U= + + + Headers/SUErrors.h + + hash + + fXyYJmeBH5lNthmGD2GJOWkMmjg= + + hash2 + + JKSoOJCqip87NB39v1ip2Gki8x1rGVcXB7D6kTCeBTI= + + + Headers/SUExport.h + + hash + + G2w3uU/V7JzIupuOjQ2LyPVGdo4= + + hash2 + + XO8CQmbFThLbYg949NEGhg3g+iouIw3/3+BCCLtEdFE= + + + Headers/SUStandardVersionComparator.h + + hash + + 6L4GrA8i1OjMLIGc/sFvEUeSfso= + + hash2 + + 5HrjOlX+uJHOw1ma+1BtG5tlYx/75N/gVzt+BX/GOxg= + + + Headers/SUUpdater.h + + hash + + krK593R1jTMhVN/yl8nXWo1WIfg= + + hash2 + + IJc5cjKEoEbG6onu/Dy+1OXT5YJ7GV3R1U0u2FamUsY= + + + Headers/SUUpdaterDelegate.h + + hash + + MHw1NrGxR116E8+4fIwJlj7xhlU= + + hash2 + + I9h48457oU5WcQiRBj2su4zWt34+hDtHwnoeLWmZ7Ko= + + + Headers/SUVersionComparisonProtocol.h + + hash + + pin8CrEV9eYWF9sdDu0N1YixWOE= + + hash2 + + rsm3T+GsIhDgSqY8EtkBpIxYgSZCZxf4HE9a/FcTRCc= + + + Headers/SUVersionDisplayProtocol.h + + hash + + D3yVFnts0j1IacN9dSN7iZSVlZg= + + hash2 + + AQITUMp8aq1UAOrmksAKmGFpgI24u9rDSBBZrgDqdN4= + + + Headers/Sparkle.h + + hash + + wr5DylsSwG9e5D1UOhUWQxcyz/g= + + hash2 + + OWWeiknWNtWrmGQq0pZ+Hst0PnbN6WbPhGHqIhk4MOM= + + + Modules/module.modulemap + + hash + + /2jgDcgH5+EG9ojcVskajfVo4yg= + + hash2 + + 1TF+JZkzFr6n8oH4WItto+C5Vf3K12f0H9KjqD0A5QU= + + + PrivateHeaders/SUUnarchiver.h + + hash + + 7mTpmnTkJ97DZQQzK/hdl2aZmXU= + + hash2 + + SQYAanTtlyX15CJapj5tDbhBEMtgQ7ZNdmpSij0+tD4= + + + Resources/Autoupdate.app/Contents/Info.plist + + hash + + KWwWNYJdjhEgQdb+JKppt0+bKAw= + + hash2 + + bgTg13jwvWOn92uv8/O2qhxZPPCp1emg0XMNJrZW01w= + + + Resources/Autoupdate.app/Contents/MacOS/Autoupdate + + hash + + wmoMz7rSIYpe2GRwL0iOO7x8XJM= + + hash2 + + lszgZb1NwAmFrPK6PeGYdNoHHn45fGBqfTBpRUKjmok= + + + Resources/Autoupdate.app/Contents/MacOS/fileop + + hash + + sLn9bRjeR7iPZnPSuCsiIUN5bzk= + + hash2 + + UHXMv35gO5TpirbNhatLHrciNfhFl8Ix57g5+l9WSag= + + + Resources/Autoupdate.app/Contents/PkgInfo + + hash + + n57qDP4tZfLD1rCS43W0B4LQjzE= + + hash2 + + glAhkclISwTWhTdPmHmgBmBpxJuKyuegSwHTjQfo7KA= + + + Resources/Autoupdate.app/Contents/Resources/AppIcon.icns + + hash + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + hash2 + + nq7j0ugQwyNbJn/7zGFwxIR0njwU3i7hAYKEyZhvUfE= + + + Resources/Autoupdate.app/Contents/Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources + + hash + + gVbyGydhWFWErsmdSv38sD2CK5c= + + hash2 + + xNUqUNyngfi8k/exRqWBSB5QM7uvIBXFKQndz+/Fav0= + + + Resources/DarkAqua.css + + hash + + SCihC2/GG/DhF4xcXD9MYaxhawM= + + hash2 + + 1G4+GJId47E8UijYTy9xeqA8RpLdOaGZQZ8B85ydROg= + + + Resources/Info.plist + + hash + + 5bKlbfUn+UpXGvwWaLxm5i9hZdU= + + hash2 + + 0S+PnP7Z3tIEwzreTid78L5apKkt5KGQB9SHn7H9JuY= + + + Resources/SUModelTranslation.plist + + hash + + iD2Ex40Usc4ZE6IAhRePqgwK/xw= + + hash2 + + bxnpRQhROJXTL2xuIeffR3p+hOuuji7eOoDeNqSYqTg= + + + Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/ar.lproj/SUAutomaticUpdateAlert.nib + + hash + + LtkONVbhTzwCPtbjkr06qSniXCI= + + hash2 + + CL6tBm495f4yM6z7y8UHRhtooR3NLGfDUOMHspa3d6k= + + optional + + + Resources/ar.lproj/SUUpdateAlert.nib + + hash + + JeZDdP1OuZbqkm8UKYiyH00A7ss= + + hash2 + + CE1qJ1jrwUiTKTlZajb/bhplzo/rdEH6pm5cABwD/rQ= + + optional + + + Resources/ar.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2bUXVaur0g5gL09vMdkjjnG/gx8= + + hash2 + + hd9zDyzNA+IOLbG1fnSYI8bz6/8/zSdgEWGIHWaR9QY= + + optional + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/cs.lproj/SUAutomaticUpdateAlert.nib + + hash + + YpT086oHMS9O2TvSNLZh+39oy80= + + hash2 + + YyE1WN1/ryPt2H0D9gYJv/r0SSv8VYTcxiiNeELiJIQ= + + optional + + + Resources/cs.lproj/SUUpdateAlert.nib + + hash + + v2ac1JQZvkm8EHZiTUc/q4aBcU0= + + hash2 + + LWRxgLZHNGYOe63gf0aOD8zoP03Z1s7ldxndzkWbzGw= + + optional + + + Resources/cs.lproj/SUUpdatePermissionPrompt.nib + + hash + + i6pnEW/pEUtK6j2Ky5Sz4ePqb1c= + + hash2 + + EdWfEPIFGhF3V7dnc+gNggkxMTDh21KimlCo1ZEC0ag= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/da.lproj/SUAutomaticUpdateAlert.nib + + hash + + XCqcLv38cTpbjAE4zjN/JWeT3+U= + + hash2 + + ivTLD912Rxy2BqIYFAQjsqh4PofwMLljqh6ncdYEdy8= + + optional + + + Resources/da.lproj/SUUpdateAlert.nib + + hash + + BY0imp6dA7C0GSOK81VXTJsRccM= + + hash2 + + BZL9bUc/f5RpZHoQGkA/XXKvykMh/LwkqI+1XW14Bxk= + + optional + + + Resources/da.lproj/SUUpdatePermissionPrompt.nib + + hash + + dbG/KHSKzElkUxJa08TkzSUwdg4= + + hash2 + + CPb7A1G9rBlQfgjTiO99mRzsCSfS47f1tcIfzdDq/f8= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/de.lproj/SUAutomaticUpdateAlert.nib + + hash + + i/BaQXOSENNulhl0b5jssezuU3Y= + + hash2 + + vZn/tXvSzWZPBBo0cVnIiPjRYfiMEtABs2gGlmJ3DKo= + + optional + + + Resources/de.lproj/SUUpdateAlert.nib + + hash + + lieYpCoCaCKAA3EL3/EsBr46vqI= + + hash2 + + pOQG4CEenyMCs6E53Yf2+yYR99NwtjC9ESL6Hp719iM= + + optional + + + Resources/de.lproj/SUUpdatePermissionPrompt.nib + + hash + + RPyksdcohNoYO9AulHe18aV0SZY= + + hash2 + + 7deRMHXIMZH/ik5ZD5oOaQ0ghLSq9ZDayRK0e271xlQ= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/el.lproj/SUAutomaticUpdateAlert.nib + + hash + + s3rpfaKP5+1+vGc44qpcWy+h0t8= + + hash2 + + 8Dy4OJ7vlhUCXCV6cjyExPoQWOtUSRnuNLpDxfel5ss= + + optional + + + Resources/el.lproj/SUUpdateAlert.nib + + hash + + sJcnQqAH4BsB+2rz9riB7iqePh0= + + hash2 + + oZ9SfHTeIGNZtJjH75VsT01y5Vo2tq2VCPVF8bDddeE= + + optional + + + Resources/el.lproj/SUUpdatePermissionPrompt.nib + + hash + + WbqjimwnC4h+B7DynMFS4qPhljo= + + hash2 + + 3y5vyIOkF/ex5pAymUtYTgRyhQN/RGoaJ5EeeGvx1Z0= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/en.lproj/SUAutomaticUpdateAlert.nib + + hash + + Y6bIF/+bAP3t5gBwPcdqxsj4co4= + + hash2 + + LK+XUVI/B5vkE00baFJQzgTVPcWQu2vfztwnjkmtAdg= + + optional + + + Resources/en.lproj/SUUpdateAlert.nib + + hash + + L3hoxekBQAtpmyDXNhTX7kRXRtc= + + hash2 + + c1eSzlRx9vqCBLiF84w+iiiGeii8RIOVaoC8Ds3gndI= + + optional + + + Resources/en.lproj/SUUpdatePermissionPrompt.nib + + hash + + g/8UXLSUBGROl64wXKn5Wr59X/c= + + hash2 + + +AnL33G+6dTHMl2qBN97TavuQbWNpQUbT/3LIIiYuvM= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/es.lproj/SUAutomaticUpdateAlert.nib + + hash + + BoS6NAq1zyVcmkbrKJhcI9Zrezk= + + hash2 + + GtD3UAnIT5BoshJo4areAKSruPfavkvTIyNd0gjejDM= + + optional + + + Resources/es.lproj/SUUpdateAlert.nib + + hash + + UMa1QcJf8zfpVUnIZUGFfJ64wTk= + + hash2 + + 3KPglR1oBAj4L7IA3Y4fYKtWrk2kpbl7jPZwPCByWfo= + + optional + + + Resources/es.lproj/SUUpdatePermissionPrompt.nib + + hash + + rH20AzERYudrcbwIQQO107Z2zGo= + + hash2 + + dX7VHzaMzRDfe7o9odqlRwNCeWMNiO9BwWJyE5+rP8Y= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/fi.lproj/SUAutomaticUpdateAlert.nib + + hash + + 6aUnn3XSgWKnVuYVA/PVSrwora8= + + hash2 + + QVnq+Bn52L27jHSSWCd5PGIBzAeU4HwlTMuL0+M3JMI= + + optional + + + Resources/fi.lproj/SUUpdateAlert.nib + + hash + + k8QjxmBhk5B6v1fGnFkwwX6oakg= + + hash2 + + R5U4ry1iLGtnxFs4Ex8GdV7tpXKbo1HoH0rsSQKIO1M= + + optional + + + Resources/fi.lproj/SUUpdatePermissionPrompt.nib + + hash + + h7CaxzsvAjayHOo/I9uD1xZMMFo= + + hash2 + + 81DxJoTMXVekTi4E76BrVDgvxP6BmZsp70shwndyUsc= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/fr.lproj/SUAutomaticUpdateAlert.nib + + hash + + Nj5v0wIECbpjSTU74xKBngH4DeY= + + hash2 + + iJPr/YNl1hGgBNcbjpEttX7EgNKwsu+R3oVtXyoxxV0= + + optional + + + Resources/fr.lproj/SUUpdateAlert.nib + + hash + + RPgJubd38D/WH1H6B1jSejqILE8= + + hash2 + + KtHl8n7bnlZ3Ir0ymG0RdRWNezTCdzyBh9HO0AB2TrA= + + optional + + + Resources/fr.lproj/SUUpdatePermissionPrompt.nib + + hash + + I2iLdKSjbtR5z2V21+jBWWV/cqQ= + + hash2 + + ENKVZHahuu5f9tyeaQzvs8X1vMqj4oKjd+CSoXN+G2U= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/fr_CA.lproj + + symlink + fr.lproj + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/hr.lproj/SUAutomaticUpdateAlert.nib + + hash + + SkgPcXJYp5dizLAgiXfyl9EsPoI= + + hash2 + + 2h4d04V7H95/KuIy1kBjzDQqtfxnZrRzKlxEjk3NYRo= + + optional + + + Resources/hr.lproj/SUUpdateAlert.nib + + hash + + 47iMWOA+94RZGJW+QJCeM4xOUsA= + + hash2 + + ReDJwTVXlm8iJWToPp6haL3A35LkgyNtEYfEYQ38l+A= + + optional + + + Resources/hr.lproj/SUUpdatePermissionPrompt.nib + + hash + + KcZQYPdSeOeuTbFYdiS4NMY+gHM= + + hash2 + + GDiDUxJaUabKqhCY0HBuzjqzgN7ZFWE9R/vUZiI0smE= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/hu.lproj/SUAutomaticUpdateAlert.nib + + hash + + PfYCBbOThC1gBDzoxD+ijdyQ3T0= + + hash2 + + C9vH4mEYy0VzQEvjXYfCMPM4ggBQF1APABRkUOUQwPA= + + optional + + + Resources/hu.lproj/SUUpdateAlert.nib + + hash + + 6RlHCvHc9GNh1M7iJhvn12iFGpg= + + hash2 + + 8vAkRUe47lFmMm7zUZM55/XRK21KahmSbRy0Axp6gw0= + + optional + + + Resources/hu.lproj/SUUpdatePermissionPrompt.nib + + hash + + S4W9ksaDnuIRy8L1ekCKN9+WfP0= + + hash2 + + D51KDsOJx7Lhz34vgOMk214ZPXrA7y4bZbuuSRMDbnM= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/is.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9eOJ/dQvTMu45Z1UowMPaKHYQOI= + + hash2 + + QpgLimdJiwdp2DVF/DMQyJ2Zg8L2ihsreE7vcI8Uqh0= + + optional + + + Resources/is.lproj/SUUpdateAlert.nib + + hash + + F0aP96zh7QOSkAdFsBlIzBhmCIg= + + hash2 + + t7A1i/lrse5T6UZtyfTdB/7HRH5vPNuOj2I+QkIjAEI= + + optional + + + Resources/is.lproj/SUUpdatePermissionPrompt.nib + + hash + + KeGsuRgX16ROMEZ8ymQNHPp6/qY= + + hash2 + + If8lcyvdX5O4+cop3EljOZMtV+jeZBSoKZJCBY+3e3A= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/it.lproj/SUAutomaticUpdateAlert.nib + + hash + + JXGMS3rtry8HTWEbBibxVCkBEmw= + + hash2 + + 5+DJlDvCzlPxtarex6vBp6GBNUjc//XUg1dP+YKgQvo= + + optional + + + Resources/it.lproj/SUUpdateAlert.nib + + hash + + W45+n9zTkxt1E3v6cZYLzXNtDlc= + + hash2 + + dMHKLXO9jQ/ephXEzJ8zaCiJ2TD94Xdtlfwqn0liUVo= + + optional + + + Resources/it.lproj/SUUpdatePermissionPrompt.nib + + hash + + 6K3G2WyaGjrTeQHoweUjaKh6X3A= + + hash2 + + XLe8J/L7G0xGBtgU9RWWm7QXrwq95DwQWDpVAeKiz0I= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/ja.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9fRyIZvZMUaRTaWCda7NsPqJle0= + + hash2 + + 17Hm2P7maddnurefS4zzxjw/i66hgAqMfPCnw5etp8c= + + optional + + + Resources/ja.lproj/SUUpdateAlert.nib + + hash + + L07PCZt4pHgRzMPxV0N6F2QK9kM= + + hash2 + + qzSS0s4cMsrK7155WvW8tp+ToVcFs5pmCwrBC9Lr/ec= + + optional + + + Resources/ja.lproj/SUUpdatePermissionPrompt.nib + + hash + + hKgWGHf0ha9Io7qZ2ChHjSsH6/s= + + hash2 + + JJ+lvbbytQjyaVr7QO9kZ2F+1tm26vRBySDIQu4Mcg8= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/ko.lproj/SUAutomaticUpdateAlert.nib + + hash + + gi+8llNjjuFSKRkNAA7vN/mf1uA= + + hash2 + + xhSLkCd/oWSClxzjFJOGmPOAS3u0od2to6thPF+1hi8= + + optional + + + Resources/ko.lproj/SUUpdateAlert.nib + + hash + + UNAQQTvtG7+MN/4w4ouu9ZHXfCM= + + hash2 + + rU7gnpi3PsnD0n0noPJN7LKUwxXFHCjixcW7WHa1q2Y= + + optional + + + Resources/ko.lproj/SUUpdatePermissionPrompt.nib + + hash + + fUirMWEByjvEZ4Ur3uhDypdTX00= + + hash2 + + sl25e8Utl+etgyKlDz26TCKdpYV9Ov0UuIf6MQHq05o= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/nb.lproj/SUAutomaticUpdateAlert.nib + + hash + + kJLypTD4VsCOwsXiOd6700pn0Cc= + + hash2 + + k7N16zCtXUz9SCO2rA2PSAb+B9zsqeF9kPAlIPeRQq0= + + optional + + + Resources/nb.lproj/SUUpdateAlert.nib + + hash + + WIJIO1qR0uNQtJpVkhIarVOfgFw= + + hash2 + + 8g/ElSO4yIXKBPWT/sbST8vdzCsbEVIeHZSxJbCzJ4M= + + optional + + + Resources/nb.lproj/SUUpdatePermissionPrompt.nib + + hash + + 4lQkM7OM2WfWZkvaBY8841eyT0E= + + hash2 + + QtnNxA2VJD7KdQfHWg59EMi5v7J5L71p7yR3PleFD50= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/nl.lproj/SUAutomaticUpdateAlert.nib + + hash + + wPmqlbK7p5mjPDrezXrYK8CG3NA= + + hash2 + + zV9zmt6+b1fbS8Nzwh35PAkW5vdYcJ64kjUjQyde+Mo= + + optional + + + Resources/nl.lproj/SUUpdateAlert.nib + + hash + + 7EXAJEeeN0k32jvOHKr7Icq4644= + + hash2 + + 3I8r+QxaqIEqBcdZogXU5VuDg87Ls1S5Ss1nhmfM8Po= + + optional + + + Resources/nl.lproj/SUUpdatePermissionPrompt.nib + + hash + + hzjMuMEBooqc++naXBflhhc16vM= + + hash2 + + +b1IimJ65Nqv25/b377zZMtCfLFvfNRnoqkA5BVf/HQ= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/pl.lproj/SUAutomaticUpdateAlert.nib + + hash + + 4aIS8LbPLTj63EhS7xEk+qjNzD8= + + hash2 + + PvUb+gVq9mfBJ4c4Rp3cyMOr6Fw7EqVwOSYCXdskwUA= + + optional + + + Resources/pl.lproj/SUUpdateAlert.nib + + hash + + pLII26utl65JwmAFqLiMefDsrGs= + + hash2 + + KNo3HyQuwczwWYBLgpAwz1nk4Lo/IuCN4SZpFhn8diY= + + optional + + + Resources/pl.lproj/SUUpdatePermissionPrompt.nib + + hash + + wIe21CUILU7RUksjexoQFfUNt6c= + + hash2 + + j9wbkXk9pjZU0oi6k9M2CWvM+AoNdAouR4pBikigplw= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/pt.lproj + + symlink + pt_BR.lproj + + Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib + + hash + + 8fQxJZw+LQiExfyPaqUbNnASsWU= + + hash2 + + 7Xu6H76LF/BEqK9o0LJHpt54NV3JbY/TPoinZv9GfSQ= + + optional + + + Resources/pt_BR.lproj/SUUpdateAlert.nib + + hash + + zW5zEa6rDpqLuTDfixuKKE93E5o= + + hash2 + + fbgvSsPkysitkmLfP26OmYpP/r+044gpfMHqQoQp9Ns= + + optional + + + Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib + + hash + + LnGHg/0zbDsX5GOE+RuItGOeM3g= + + hash2 + + po6G27I+x6t4LrP0snclOJG6cAG8Atm7AtOKw5Cnv7o= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib + + hash + + Svc6e9pHrp1wA7rxw7oKc5HB8qQ= + + hash2 + + L4KDh1UJEm3ta+qgzCe3s3RI7xXrAY8y5h5eqneS3Uo= + + optional + + + Resources/pt_PT.lproj/SUUpdateAlert.nib + + hash + + NtD144OppS+BPXNmsReGJoN2Qdo= + + hash2 + + NOQ75dz/Mq7PLhwssExcWXdtTFQzx8m/lLpLBDCaZCI= + + optional + + + Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib + + hash + + DngrIdQPeavGfwbVI7GS1bK98C0= + + hash2 + + 2HpG3aXDkQ0/a8zlZK8m5uxKJDlomNUyxvb07aXbRp4= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/ro.lproj/SUAutomaticUpdateAlert.nib + + hash + + 7HEo1dlbwSnit0+4DsAqKDz1jR4= + + hash2 + + KUqcmkrRCh+XjXh9F7fudt94MreG4bKqDh1PLat/FpI= + + optional + + + Resources/ro.lproj/SUUpdateAlert.nib + + hash + + FPWtaRuYrVSPrfAozq/4bSQfMK4= + + hash2 + + 2mIpfTwoRvVuhY/Aa3Bqw5VsjpV93xFNcBMQG83Q8DU= + + optional + + + Resources/ro.lproj/SUUpdatePermissionPrompt.nib + + hash + + NPLiF7WwQt7siCG8DLWh+ZGImWI= + + hash2 + + 3y6PRBSQUrxkSKoKqEFZHnQsUfwARyIbhNB+G8h6vCM= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/ru.lproj/SUAutomaticUpdateAlert.nib + + hash + + YHane6xWVhvpJGf5HuoxCyQ/gDs= + + hash2 + + qwuyYXCO4H5IuXiRhZR3ucGl5S1CtG2e7kxxCkM8vtA= + + optional + + + Resources/ru.lproj/SUUpdateAlert.nib + + hash + + NxM+W+qAegxK4lKy0uzCclpkVjo= + + hash2 + + Kvykt9h+o+SUuINROlMCXornVL/uEH2Uz5Kd0bNSm6k= + + optional + + + Resources/ru.lproj/SUUpdatePermissionPrompt.nib + + hash + + P0o42QteueF9d3aaoNcz6obcimA= + + hash2 + + vAV0lcBRi3i8mLnOGxAZKMlzMoV1dKkHtntkRWq8cl0= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/sk.lproj/SUAutomaticUpdateAlert.nib + + hash + + 5k+e1kFtgoVo77RhyhZSXpRQGOQ= + + hash2 + + qZSxDMjtIXyNwOZYeUz2g9w6NFmKFStqy5UbKU9N3BA= + + optional + + + Resources/sk.lproj/SUUpdateAlert.nib + + hash + + l8XR02tvN10SX2aM9CtZ7BpIcqw= + + hash2 + + fD0W6cMr/MZ0C0qrsMD3jEbXdK+eq8L7tl87ZPDspmY= + + optional + + + Resources/sk.lproj/SUUpdatePermissionPrompt.nib + + hash + + stqj1WPWzlJrXELqXKGjcxPeqvI= + + hash2 + + vZ2+3fnyHP5H7mAHBcs6vMW7zRWMAdUrYEImvCvX9Jo= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/sl.lproj/SUAutomaticUpdateAlert.nib + + hash + + htsZnsf4S+omnk4Z8En8qOOjrPM= + + hash2 + + 9kzvdHcdVqdxsioITBt+Am06twXcJpKMreMKHDDj4RY= + + optional + + + Resources/sl.lproj/SUUpdateAlert.nib + + hash + + /9xrGwHXOdPKFp82w4OjF+Q0WG8= + + hash2 + + r48ahwyC8EFi+44X/EtUfWvh8QCu9klpjqwwwzQHiXE= + + optional + + + Resources/sl.lproj/SUUpdatePermissionPrompt.nib + + hash + + ApIwZGSV2lqBRKNHGnwhpqmec9Q= + + hash2 + + Me9gqc96/bwV4oypq11RLh7TIcFaMug+ZahAJbaIoJc= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/sv.lproj/SUAutomaticUpdateAlert.nib + + hash + + K0QpkucJxh5CRA9TYJCT5+gSHZY= + + hash2 + + 2GEt92QWRuf/s8NnoWD/HidalRNoLUoeJoC7UM3Y2Ng= + + optional + + + Resources/sv.lproj/SUUpdateAlert.nib + + hash + + dOxfomMC/X9MFFdsk1MyjKv1yi8= + + hash2 + + wIPOqj0JUePKauMIl6sT8YUjoxw1q36g+rrQvwkxfw4= + + optional + + + Resources/sv.lproj/SUUpdatePermissionPrompt.nib + + hash + + odTG4PkbDUjXrxuBzD+AnnKpOLo= + + hash2 + + DtpAT3NKA19p+MDugG6j9RILg2unD6vPWJC+uoL0pEI= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/th.lproj/SUAutomaticUpdateAlert.nib + + hash + + /iyQcDW4+Is40OSlKcWlEe9zKdo= + + hash2 + + gFeu5nmtM/aL1yTSsW9TPh9ZdN8r/lX5jhns53LlcfA= + + optional + + + Resources/th.lproj/SUUpdateAlert.nib + + hash + + cQCWeOMdd6mZEd9k0pl3FrZDT9g= + + hash2 + + vmbxfC0cI7IAAQRNDQ43V6vocH93TiejQmBr23NDAlQ= + + optional + + + Resources/th.lproj/SUUpdatePermissionPrompt.nib + + hash + + HNNvE7bGYAElmoeYbB7JREhcs3I= + + hash2 + + /zWPufbtGluhIyEvJFseifKQgvYqQvq7457HQxtnUyo= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/tr.lproj/SUAutomaticUpdateAlert.nib + + hash + + fjnMF3XlYLFPJSpmIFizoGwOVl8= + + hash2 + + 3T/cO/cEIAM8QBGVg53bYvcYseEMPOhn0C+yReVVJEU= + + optional + + + Resources/tr.lproj/SUUpdateAlert.nib + + hash + + Y9dabfD0a7F1cV9OuFnyQL5BIIc= + + hash2 + + qwss8xg3cxWWQXqCLxXd6z1ygf6MHYjMDKtVGzTpF0M= + + optional + + + Resources/tr.lproj/SUUpdatePermissionPrompt.nib + + hash + + J3IIVJ3ytau8dSfjTtkWF9zh2nA= + + hash2 + + WyAEArMYGvG0SCp5KciPapfVxAQBnaICw19bEmpvZGA= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/uk.lproj/SUAutomaticUpdateAlert.nib + + hash + + YRRVgJ26NZd9+ebTI3UGdpi35eo= + + hash2 + + /lUZP3n163WFPUjg+ohhY7pnbmtrxuc164ew0tFmDd4= + + optional + + + Resources/uk.lproj/SUUpdateAlert.nib + + hash + + PoeaXUHUKNIm0bkX+GNnvFHlq9w= + + hash2 + + Pmyouw5QFzbN7VYg1RXFNm3IB4jOmZagTi9k8g2CyQE= + + optional + + + Resources/uk.lproj/SUUpdatePermissionPrompt.nib + + hash + + KTOBs3BNdg2ylIdMNsG3WLw5SsA= + + hash2 + + rWhCViQiI/EprwUb+/vwBLyeqJo4ehG9K9rc9Gh/Ils= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib + + hash + + 2pvLfCu7EiI6OkCxu3+aLyeTPcU= + + hash2 + + tu9oMdBCiHiyRSJMEmm5x3oGY3aCnmwXPsRf1hjj7fY= + + optional + + + Resources/zh_CN.lproj/SUUpdateAlert.nib + + hash + + +xvQE3bFW1QXIUggZBlZkKn0gag= + + hash2 + + b6nNjM4vCBrXBbjH5GtbkPjZjJyMeSSBXO/tCRn+LFY= + + optional + + + Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib + + hash + + 9wwEreTf4JSKHFSiRGUKE2Ffs1g= + + hash2 + + Ie8hCqTFOwfwP/10iRUcwHWqeLqthgUYgB27mwd+7us= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib + + hash + + KQcqbpIyw3yhI0eEyo06cNq5MlM= + + hash2 + + POFOdG9uGjcVXkx39sW2Rwl2FJ2zs6A007yF1UpDskE= + + optional + + + Resources/zh_TW.lproj/SUUpdateAlert.nib + + hash + + 6/Rgln3/89vly1RFa1gBfRhITxU= + + hash2 + + AFd6Yy2xbmYtz1+r9u+r9qNka3oTzho3/n3DCwxWKoE= + + optional + + + Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib + + hash + + K4vg/ZQjaDTe9PQehvRbUNOWIUc= + + hash2 + + rpkcehO73xm6acIRoN/58VJhQEdvxwKbattoChh2Ycw= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/Current b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/Current new file mode 120000 index 00000000..8c7e5a66 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/Sparkle.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/dSYMs/Sparkle.framework.dSYM/Contents/Info.plist b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/dSYMs/Sparkle.framework.dSYM/Contents/Info.plist new file mode 100644 index 00000000..be06c406 --- /dev/null +++ b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/dSYMs/Sparkle.framework.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.org.sparkle-project.Sparkle + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.24.0 + CFBundleVersion + 1.24.0 + + diff --git a/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/dSYMs/Sparkle.framework.dSYM/Contents/Resources/DWARF/Sparkle b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/dSYMs/Sparkle.framework.dSYM/Contents/Resources/DWARF/Sparkle new file mode 100644 index 00000000..ed8cdd90 Binary files /dev/null and b/Vendors/Sparkle/Sparkle.xcframework/macos-arm64_x86_64/dSYMs/Sparkle.framework.dSYM/Contents/Resources/DWARF/Sparkle differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc.dSYM/Contents/Info.plist b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc.dSYM/Contents/Info.plist new file mode 100644 index 00000000..a20478ea --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.org.sparkle-project.InstallerConnection + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 2.0.0 + CFBundleVersion + 2.0.0 + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerConnection b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerConnection new file mode 100644 index 00000000..4394f7c8 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerConnection differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/Info.plist b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/Info.plist new file mode 100644 index 00000000..f228ec22 --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/Info.plist @@ -0,0 +1,53 @@ + + + + + BuildMachineOSBuild + 19E266 + CFBundleDevelopmentRegion + en + CFBundleExecutable + org.sparkle-project.InstallerConnection + CFBundleIdentifier + org.sparkle-project.InstallerConnection + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + org.sparkle-project.InstallerConnection + CFBundlePackageType + XPC! + CFBundleShortVersionString + 2.0.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 2.0.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 11E146 + DTPlatformVersion + GM + DTSDKBuild + 19E258 + DTSDKName + macosx10.15 + DTXcode + 1140 + DTXcodeBuild + 11E146 + LSMinimumSystemVersion + 10.9 + NSHumanReadableCopyright + Copyright © 2016 Sparkle Project. All rights reserved. + XPCService + + ServiceType + Application + + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/MacOS/org.sparkle-project.InstallerConnection b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/MacOS/org.sparkle-project.InstallerConnection new file mode 100755 index 00000000..ec4a7e62 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/MacOS/org.sparkle-project.InstallerConnection differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/_CodeSignature/CodeResources b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..d5d0fd74 --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerConnection.xpc/Contents/_CodeSignature/CodeResources @@ -0,0 +1,115 @@ + + + + + files + + files2 + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc.dSYM/Contents/Info.plist b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc.dSYM/Contents/Info.plist new file mode 100644 index 00000000..baf3fce4 --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.org.sparkle-project.InstallerLauncher + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 2.0.0 + CFBundleVersion + 2.0.0 + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerLauncher b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerLauncher new file mode 100644 index 00000000..771f6e9e Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerLauncher differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/Info.plist b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/Info.plist new file mode 100644 index 00000000..f6f9c08f --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/Info.plist @@ -0,0 +1,55 @@ + + + + + BuildMachineOSBuild + 19E266 + CFBundleDevelopmentRegion + en + CFBundleExecutable + org.sparkle-project.InstallerLauncher + CFBundleIdentifier + org.sparkle-project.InstallerLauncher + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + org.sparkle-project.InstallerLauncher + CFBundlePackageType + XPC! + CFBundleShortVersionString + 2.0.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 2.0.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 11E146 + DTPlatformVersion + GM + DTSDKBuild + 19E258 + DTSDKName + macosx10.15 + DTXcode + 1140 + DTXcodeBuild + 11E146 + LSMinimumSystemVersion + 10.9 + NSHumanReadableCopyright + Copyright © 2016 Sparkle Project. All rights reserved. + XPCService + + JoinExistingSession + + ServiceType + Application + + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Autoupdate b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Autoupdate new file mode 100755 index 00000000..5c17a04f Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Autoupdate differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Info.plist b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Info.plist new file mode 100644 index 00000000..069830c2 --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Info.plist @@ -0,0 +1,50 @@ + + + + + BuildMachineOSBuild + 19E266 + CFBundleDevelopmentRegion + en + CFBundleExecutable + Updater + CFBundleIdentifier + org.sparkle-project.Sparkle.Updater + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Updater + CFBundlePackageType + APPL + CFBundleShortVersionString + 2.0.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 2.0.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 11E146 + DTPlatformVersion + GM + DTSDKBuild + 19E258 + DTSDKName + macosx10.15 + DTXcode + 1140 + DTXcodeBuild + 11E146 + LSMinimumSystemVersion + 10.9 + LSUIElement + 1 + NSPrincipalClass + NSApplication + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/MacOS/Updater b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/MacOS/Updater new file mode 100755 index 00000000..d4b08bd4 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/MacOS/Updater differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/PkgInfo b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/PkgInfo new file mode 100644 index 00000000..bd04210f --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/SUStatus.nib b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/SUStatus.nib new file mode 100644 index 00000000..0db55089 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/SUStatus.nib differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ar.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 00000000..4cd92c0d Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ar.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ca.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..cc238f68 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ca.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/cs.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 00000000..5204db76 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/cs.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/da.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/da.lproj/Sparkle.strings new file mode 100644 index 00000000..10e3c5a5 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/da.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/de.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/de.lproj/Sparkle.strings new file mode 100644 index 00000000..7654e85b Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/de.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/el.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/el.lproj/Sparkle.strings new file mode 100644 index 00000000..deed9efb Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/el.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/en.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/en.lproj/Sparkle.strings new file mode 100644 index 00000000..588c7376 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/en.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/es.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/es.lproj/Sparkle.strings new file mode 100644 index 00000000..4f2015f0 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/es.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fi.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..dab921c6 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fi.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fr.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 00000000..a602f21d Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fr.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fr_CA.lproj b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fr_CA.lproj new file mode 120000 index 00000000..f9834a39 --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/fr_CA.lproj @@ -0,0 +1 @@ +fr.lproj \ No newline at end of file diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/he.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..99124ccc Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/he.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/is.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/is.lproj/Sparkle.strings new file mode 100644 index 00000000..74ae7280 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/is.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/it.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/it.lproj/Sparkle.strings new file mode 100644 index 00000000..68b6d366 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/it.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ja.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 00000000..7f7c4acc Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ja.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ko.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 00000000..92c18eeb Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ko.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/nb.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 00000000..ec2561b8 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/nb.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/nl.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 00000000..58be0e82 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/nl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pl.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 00000000..2b9c4615 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt.lproj b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt.lproj new file mode 120000 index 00000000..3c1c9f6d --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt.lproj @@ -0,0 +1 @@ +pt_BR.lproj \ No newline at end of file diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt_BR.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 00000000..e55c6fd1 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt_PT.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 00000000..00df86ff Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ro.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 00000000..318baa96 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ro.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ru.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 00000000..c33086d8 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/ru.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sk.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 00000000..a7d2ebce Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sk.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sl.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 00000000..1be2a807 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sl.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sv.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 00000000..738c9008 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/sv.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/th.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/th.lproj/Sparkle.strings new file mode 100644 index 00000000..eca25702 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/th.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/tr.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 00000000..4def140e Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/tr.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/uk.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 00000000..f7eb257b Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/uk.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/zh_CN.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 00000000..e356c79d Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/zh_TW.lproj/Sparkle.strings b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 00000000..533e2086 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/_CodeSignature/CodeResources b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..74e7bcfd --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/_CodeSignature/CodeResources @@ -0,0 +1,802 @@ + + + + + files + + Resources/SUStatus.nib + + +3Tn7V2+cRw0Aw2VoWT1DNAPpkE= + + Resources/ar.lproj/Sparkle.strings + + hash + + 9n6+2ab5/d3baNlcFRfSpztHdKc= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + K1BEF6sG2vXMLgibwfo3j2h588E= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + qmZIcgaZTr//z9PjOI776B5GQ3E= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 88FAIY52ex+k6CHvZHUHiYpaSdQ= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + FnTKeC2WOm3Wo79G5tYK17ssA4g= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + gQTKA4Zd4FpsXRLWTcEfqV3Czu0= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + HMDIP8J6ekyxwFQ6/Gn+q3WSTl4= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + saEdp9H51NgvY5tzYYY5QoM5dsg= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + Xfk3iYvY4+ymcoVUpHQATY5FNLg= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + N3afKcO8erR7VUa2Cq4bwqxw/DY= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + ONZyQ7mMihp025wvYCm+YH5p9t8= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + bKE7f6KUVWbXzh+cBrwa31j6sXU= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + PGQtWau2xbYKJPKZjSvkwnPSSJU= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + iD89mxaGjEzXuqTCpr1SbfWzdyM= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + 36Fahhtf/RNpPA22ntiODYGqG30= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + lxEVDkftYdIz5tpFIlCBRzjq1G8= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + 3esiRzch9B/dcmSDuZOlhGRmvhI= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + 5DAYxRDmzfZJHVzkzmq9B33cV+Q= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + 9OEsTkc4OnLubR99mP0Br13Mflo= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + DXgfdoW9r94wdvH+tYnJNakKzDs= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + Yk1UW9SBQyAtNbFvLmiIjW/UCcc= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + Px2O36VmsQbjS8ywxoJ/Pp+xQiQ= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + 8A/scZSblfhf9/SAyz5Di2EqrqM= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + YRXBwzauFczYTqobmqCxBBPR4DE= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + K+ak+cmJ5S1D27ODU3IntD0wITI= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + anxUgZs0IJsgMZlzI1HUeCjvmrc= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + 4L5cXvWM1KkQdn5c+uYML/PX6xg= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + uhJ3st+FckuLz8HIH0r/RtUVGsw= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + kFXz9LiX6VmEsvEWZcZOIMmUE5o= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + fq2MGchNCsDkfRX6i950z9hnHAM= + + optional + + + + files2 + + Resources/SUStatus.nib + + hash + + +3Tn7V2+cRw0Aw2VoWT1DNAPpkE= + + hash2 + + 5qy5Hmt7D8O4gK4CkA7/lWikR3cX9ZSe6yTyluAeOh8= + + + Resources/ar.lproj/Sparkle.strings + + hash + + 9n6+2ab5/d3baNlcFRfSpztHdKc= + + hash2 + + kEBNsn9OraKT0YF/n5ZaJC14Y/+GW/HI/CjiahPHgwM= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + K1BEF6sG2vXMLgibwfo3j2h588E= + + hash2 + + D01nO0KWUvaVR/PR0E95dLAlJCYEKPRh858t+lcxFto= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + qmZIcgaZTr//z9PjOI776B5GQ3E= + + hash2 + + 6sIHusRLkghCkCVemdyAqniiTfJ68E6t0qswH/A+Aac= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 88FAIY52ex+k6CHvZHUHiYpaSdQ= + + hash2 + + YtLfD1azWIUD2eqATgQak+tKys3x9ZFjo91mSYwSY68= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + FnTKeC2WOm3Wo79G5tYK17ssA4g= + + hash2 + + zG5B5gvBrmrL31eAFv8JQ0xYZrAGgvpcePzhSL9lRSI= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + gQTKA4Zd4FpsXRLWTcEfqV3Czu0= + + hash2 + + DpBU2fltmtw85+0U85gXwPH8qApgI0zbG6K0qIn2X0c= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + HMDIP8J6ekyxwFQ6/Gn+q3WSTl4= + + hash2 + + T0siv9/ri/ulfofXL+GzB1ClarT02vlzl4QRomTIy9A= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + saEdp9H51NgvY5tzYYY5QoM5dsg= + + hash2 + + Rv71G/XkSv/4JZd+ejfFkpu4HKXFsM0Nxe094rw3mAQ= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + Xfk3iYvY4+ymcoVUpHQATY5FNLg= + + hash2 + + DwdjkY2nc5XvSzY7wbwHcwKnnCfJXwDl1bO6PbtoeUU= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + N3afKcO8erR7VUa2Cq4bwqxw/DY= + + hash2 + + nGZJLdRUiRSWfcROzRsVZzoM/Pyl+C6y0c7WJdZ++ME= + + optional + + + Resources/fr_CA.lproj + + symlink + fr.lproj + + Resources/he.lproj/Sparkle.strings + + hash + + ONZyQ7mMihp025wvYCm+YH5p9t8= + + hash2 + + 35ECtsAW7lQQpZTAtYBIKgel5ItYO6FvWJaSueWWqVU= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + bKE7f6KUVWbXzh+cBrwa31j6sXU= + + hash2 + + Dh4VgRSkntzRdCDvUFT0O91wxRUTyfKmsonwoD8JO3s= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + PGQtWau2xbYKJPKZjSvkwnPSSJU= + + hash2 + + 6KWPm6/BMUnxP7kax40a/akTj6RVSNWSgXpS2+5bkMg= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + iD89mxaGjEzXuqTCpr1SbfWzdyM= + + hash2 + + P8h6uv3ksdrzPVBgsLywrDU+NA6c3at5YNW9MyQ5+i0= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + 36Fahhtf/RNpPA22ntiODYGqG30= + + hash2 + + oX2Hsbm8fF05oGgMFXazS+rqg3KswApukPT1inQKxs8= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + lxEVDkftYdIz5tpFIlCBRzjq1G8= + + hash2 + + j1Ga6bYhYJ7h65dfZiX0udIIngNspVWPJaqKaEZhdIY= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + 3esiRzch9B/dcmSDuZOlhGRmvhI= + + hash2 + + Ft3lAx+eG7MsySkCRtYN7wT7zRTPWDsJDJnghgcNWrA= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + 5DAYxRDmzfZJHVzkzmq9B33cV+Q= + + hash2 + + tv/j3ywfuO1E3J5/vmrVFQ3cbZPi3EudMtacnjqVqWA= + + optional + + + Resources/pt.lproj + + symlink + pt_BR.lproj + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + 9OEsTkc4OnLubR99mP0Br13Mflo= + + hash2 + + p12hYL8AHpuT+aXzheKTHwZEQFpPfc/qCoaYe7NmP6I= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + DXgfdoW9r94wdvH+tYnJNakKzDs= + + hash2 + + xjNkmadedPLED0QHUgWiGXlJ/d0rZeHWkUmAyGdURyA= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + Yk1UW9SBQyAtNbFvLmiIjW/UCcc= + + hash2 + + IffqR5gxQdL9YEeJj/L9jauu1eduqT1taxe3hKDDXOk= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + Px2O36VmsQbjS8ywxoJ/Pp+xQiQ= + + hash2 + + MBWSZcnNsYWJkCrv3YDWyANbEghjnWl8TFrApZqIh8c= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + 8A/scZSblfhf9/SAyz5Di2EqrqM= + + hash2 + + hKJVJbokW6LXrUqrf3FyGAxdnXJe+NAM1IzwtfMpPTs= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + YRXBwzauFczYTqobmqCxBBPR4DE= + + hash2 + + mO9OxrL9L5y2wDXWsMt11pjcxa4wJrXVXM26w/TWqpE= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + K+ak+cmJ5S1D27ODU3IntD0wITI= + + hash2 + + OXVaG3Vrb1xKlSXHj2qnMe/+X3r5r+huDymhPpx7j5w= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + anxUgZs0IJsgMZlzI1HUeCjvmrc= + + hash2 + + uFBTQa44/YKNE5qHbmLqdlZUuLF0Zfk0LepBeIQ7ZQ8= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + 4L5cXvWM1KkQdn5c+uYML/PX6xg= + + hash2 + + rOuDu7og0MYRXCQMAZ48ge5FRTN4+ZBYl9DxJEDnDaY= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + uhJ3st+FckuLz8HIH0r/RtUVGsw= + + hash2 + + AdON9wb2iTlde8P8StWkzdTMy8iL7M6mj94hIj6ixA0= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + kFXz9LiX6VmEsvEWZcZOIMmUE5o= + + hash2 + + oT/+oPtd/EjVyWINXmlilXd0HUk9MdcNrJQsHA5Mfys= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + fq2MGchNCsDkfRX6i950z9hnHAM= + + hash2 + + 4bQfH6cx4JPlejfZbFtgdDFbRS9FENa0UFlKJqZqhtg= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/org.sparkle-project.InstallerLauncher b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/org.sparkle-project.InstallerLauncher new file mode 100755 index 00000000..5115c5fa Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/org.sparkle-project.InstallerLauncher differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/_CodeSignature/CodeResources b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..27f8faae --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/_CodeSignature/CodeResources @@ -0,0 +1,134 @@ + + + + + files + + files2 + + MacOS/Autoupdate + + cdhash + + K4iXbW0JqeTVYp89RtNmzOuuXnc= + + requirement + identifier Autoupdate and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "4D7YV49724" + + MacOS/Updater.app + + cdhash + + 5GIik0iyAWIwoKrxwm0JiI+Zc3A= + + requirement + identifier "org.sparkle-project.Sparkle.Updater" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "4D7YV49724" + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc.dSYM/Contents/Info.plist b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc.dSYM/Contents/Info.plist new file mode 100644 index 00000000..cdb88a95 --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.org.sparkle-project.InstallerStatus + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 2.0.0 + CFBundleVersion + 2.0.0 + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerStatus b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerStatus new file mode 100644 index 00000000..f5de3643 Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc.dSYM/Contents/Resources/DWARF/org.sparkle-project.InstallerStatus differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/Info.plist b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/Info.plist new file mode 100644 index 00000000..4483728a --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/Info.plist @@ -0,0 +1,53 @@ + + + + + BuildMachineOSBuild + 19E266 + CFBundleDevelopmentRegion + en + CFBundleExecutable + org.sparkle-project.InstallerStatus + CFBundleIdentifier + org.sparkle-project.InstallerStatus + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + org.sparkle-project.InstallerStatus + CFBundlePackageType + XPC! + CFBundleShortVersionString + 2.0.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 2.0.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 11E146 + DTPlatformVersion + GM + DTSDKBuild + 19E258 + DTSDKName + macosx10.15 + DTXcode + 1140 + DTXcodeBuild + 11E146 + LSMinimumSystemVersion + 10.9 + NSHumanReadableCopyright + Copyright © 2016 Sparkle Project. All rights reserved. + XPCService + + ServiceType + Application + + + diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/MacOS/org.sparkle-project.InstallerStatus b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/MacOS/org.sparkle-project.InstallerStatus new file mode 100755 index 00000000..57f63deb Binary files /dev/null and b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/MacOS/org.sparkle-project.InstallerStatus differ diff --git a/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/_CodeSignature/CodeResources b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..d5d0fd74 --- /dev/null +++ b/Vendors/Sparkle/XPCServices/org.sparkle-project.InstallerStatus.xpc/Contents/_CodeSignature/CodeResources @@ -0,0 +1,115 @@ + + + + + files + + files2 + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Vendors/Sparkle/bin/BinaryDelta b/Vendors/Sparkle/bin/BinaryDelta new file mode 100755 index 00000000..c3e8da9c Binary files /dev/null and b/Vendors/Sparkle/bin/BinaryDelta differ diff --git a/Vendors/Sparkle/bin/BinaryDelta.dSYM/Contents/Info.plist b/Vendors/Sparkle/bin/BinaryDelta.dSYM/Contents/Info.plist new file mode 100644 index 00000000..325809b9 --- /dev/null +++ b/Vendors/Sparkle/bin/BinaryDelta.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.BinaryDelta + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Vendors/Sparkle/bin/BinaryDelta.dSYM/Contents/Resources/DWARF/BinaryDelta b/Vendors/Sparkle/bin/BinaryDelta.dSYM/Contents/Resources/DWARF/BinaryDelta new file mode 100644 index 00000000..65e5dc08 Binary files /dev/null and b/Vendors/Sparkle/bin/BinaryDelta.dSYM/Contents/Resources/DWARF/BinaryDelta differ diff --git a/Vendors/Sparkle/bin/generate_appcast b/Vendors/Sparkle/bin/generate_appcast new file mode 100755 index 00000000..93e10700 Binary files /dev/null and b/Vendors/Sparkle/bin/generate_appcast differ diff --git a/Vendors/Sparkle/bin/generate_appcast.dSYM/Contents/Info.plist b/Vendors/Sparkle/bin/generate_appcast.dSYM/Contents/Info.plist new file mode 100644 index 00000000..85e47831 --- /dev/null +++ b/Vendors/Sparkle/bin/generate_appcast.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.generate_appcast + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Vendors/Sparkle/bin/generate_appcast.dSYM/Contents/Resources/DWARF/generate_appcast b/Vendors/Sparkle/bin/generate_appcast.dSYM/Contents/Resources/DWARF/generate_appcast new file mode 100644 index 00000000..45e1ed71 Binary files /dev/null and b/Vendors/Sparkle/bin/generate_appcast.dSYM/Contents/Resources/DWARF/generate_appcast differ diff --git a/Vendors/Sparkle/bin/generate_keys b/Vendors/Sparkle/bin/generate_keys new file mode 100755 index 00000000..16fdb700 Binary files /dev/null and b/Vendors/Sparkle/bin/generate_keys differ diff --git a/Vendors/Sparkle/bin/generate_keys.dSYM/Contents/Info.plist b/Vendors/Sparkle/bin/generate_keys.dSYM/Contents/Info.plist new file mode 100644 index 00000000..3caf8909 --- /dev/null +++ b/Vendors/Sparkle/bin/generate_keys.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.generate_keys + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Vendors/Sparkle/bin/generate_keys.dSYM/Contents/Resources/DWARF/generate_keys b/Vendors/Sparkle/bin/generate_keys.dSYM/Contents/Resources/DWARF/generate_keys new file mode 100644 index 00000000..852e349f Binary files /dev/null and b/Vendors/Sparkle/bin/generate_keys.dSYM/Contents/Resources/DWARF/generate_keys differ diff --git a/Vendors/Sparkle/bin/old_dsa_scripts/generate_dsa_keys_macos_10.12_only b/Vendors/Sparkle/bin/old_dsa_scripts/generate_dsa_keys_macos_10.12_only new file mode 100755 index 00000000..56f195fd --- /dev/null +++ b/Vendors/Sparkle/bin/old_dsa_scripts/generate_dsa_keys_macos_10.12_only @@ -0,0 +1,24 @@ +#!/bin/bash +set -e +for file in "dsaparam.pem" "dsa_priv.pem" "dsa_pub.pem"; do + if [ -e "$file" ]; then + echo "There's already a $file here! Move it aside or be more careful!" + exit 1 + fi +done + +openssl="/usr/bin/openssl" +$openssl gendsa <($openssl dsaparam 2047) -out dsa_priv.pem +chmod 0400 dsa_priv.pem +$openssl dsa -in dsa_priv.pem -pubout -out dsa_pub.pem + +echo " +Generated two files: +dsa_priv.pem: your private key. Keep it secret and don't share it! +dsa_pub.pem: public counterpart to include in the app bundle. + +BACK UP YOUR PRIVATE KEY AND KEEP IT SAFE! +If you lose it, your users will be unable to upgrade! +" + +open -R dsa_priv.pem diff --git a/Vendors/Sparkle/bin/old_dsa_scripts/sign_update b/Vendors/Sparkle/bin/old_dsa_scripts/sign_update new file mode 100755 index 00000000..fddaae83 --- /dev/null +++ b/Vendors/Sparkle/bin/old_dsa_scripts/sign_update @@ -0,0 +1,18 @@ +#!/bin/bash +set -e +set -o pipefail +if [ "$#" -ne 2 ]; then + echo "Usage: $0 update_archive_file dsa_priv.pem" + echo "This is an old DSA signing script for deprecated DSA keys." + echo "Do not use this for new applications." + exit 1 +fi +openssl=/usr/bin/openssl +version=`$openssl version` +if [[ $version =~ "OpenSSL 0.9" ]]; then + # pre-10.13 system: Fall back to OpenSSL DSS1 digest because it does not like the -sha1 option + $openssl dgst -sha1 -binary < "$1" | $openssl dgst -dss1 -sign "$2" | $openssl enc -base64 +else + # 10.13 and later: Use LibreSSL SHA1 digest + $openssl dgst -sha1 -binary < "$1" | $openssl dgst -sha1 -sign "$2" | $openssl enc -base64 +fi diff --git a/Vendors/Sparkle/bin/sign_update b/Vendors/Sparkle/bin/sign_update new file mode 100755 index 00000000..2f57f935 Binary files /dev/null and b/Vendors/Sparkle/bin/sign_update differ diff --git a/Vendors/Sparkle/bin/sign_update.dSYM/Contents/Info.plist b/Vendors/Sparkle/bin/sign_update.dSYM/Contents/Info.plist new file mode 100644 index 00000000..07393896 --- /dev/null +++ b/Vendors/Sparkle/bin/sign_update.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.sign_update + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Vendors/Sparkle/bin/sign_update.dSYM/Contents/Resources/DWARF/sign_update b/Vendors/Sparkle/bin/sign_update.dSYM/Contents/Resources/DWARF/sign_update new file mode 100644 index 00000000..9e82b2b6 Binary files /dev/null and b/Vendors/Sparkle/bin/sign_update.dSYM/Contents/Resources/DWARF/sign_update differ diff --git a/Vendors/openvpn b/Vendors/openvpn new file mode 100755 index 00000000..cacb181d Binary files /dev/null and b/Vendors/openvpn differ diff --git a/Vendors/spm/Starscream/.gitignore b/Vendors/spm/Starscream/.gitignore new file mode 100644 index 00000000..69ce2544 --- /dev/null +++ b/Vendors/spm/Starscream/.gitignore @@ -0,0 +1,118 @@ +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? +# + Pods/ + +# Xcode +.DS_Store +build +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +profile +*.moved-aside +DerivedData +.idea/ +*.hmap +*.xccheckout +*.xcodeproj/*.xcworkspace + + +# Created by https://www.gitignore.io/api/swift,swiftpm + +### Swift ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +### SwiftPM ### +Packages +xcuserdata +*.xcodeproj + + +# End of https://www.gitignore.io/api/swift,swiftpm diff --git a/Vendors/spm/Starscream/.travis.yml b/Vendors/spm/Starscream/.travis.yml new file mode 100644 index 00000000..8bd0dce0 --- /dev/null +++ b/Vendors/spm/Starscream/.travis.yml @@ -0,0 +1,7 @@ +osx_image: xcode11.3 +language: objective-c +before_install: + - gem install cocoapods --pre + - gem cleanup +script: +- ./build.sh diff --git a/Vendors/spm/Starscream/CHANGELOG.md b/Vendors/spm/Starscream/CHANGELOG.md new file mode 100644 index 00000000..1819fbb6 --- /dev/null +++ b/Vendors/spm/Starscream/CHANGELOG.md @@ -0,0 +1,269 @@ +# Change Log +All notable changes to this project will be documented in this file. +`Starscream` adheres to [Semantic Versioning](http://semver.org/). + +### [4.0.4](https://github.com/daltoniam/Starscream/tree/4.0.4) + +Bug fixes for 4.0.3. + +[#808](https://github.com/daltoniam/Starscream/pull/808) +[#807](https://github.com/daltoniam/Starscream/pull/807) +[#799](https://github.com/daltoniam/Starscream/pull/799) +[#797](https://github.com/daltoniam/Starscream/pull/797) +[#790](https://github.com/daltoniam/Starscream/pull/790) +[#788](https://github.com/daltoniam/Starscream/pull/788) +[#777](https://github.com/daltoniam/Starscream/pull/777) +[#768](https://github.com/daltoniam/Starscream/pull/768) +[#766](https://github.com/daltoniam/Starscream/pull/766) +[#764](https://github.com/daltoniam/Starscream/pull/764) + +### [4.0.3](https://github.com/daltoniam/Starscream/tree/4.0.3) + +Bug fixes for 4.0.2. + +[#760](https://github.com/daltoniam/Starscream/issues/760) + +### [4.0.2](https://github.com/daltoniam/Starscream/tree/4.0.2) + +Bug fixes for 4.0.1. Fixed native engine is connected/disconnected. Native engine isn't the default since the API lacks features. + +[#697](https://github.com/daltoniam/Starscream/pull/697) + +### [4.0.1](https://github.com/daltoniam/Starscream/tree/4.0.1) + +Bug fixes for 4.0.0. Enabled Native engine now that the API is out of beta and works properly. + +[#749](https://github.com/daltoniam/Starscream/pull/749) +[#755](https://github.com/daltoniam/Starscream/pull/755) + +### [4.0.0](https://github.com/daltoniam/Starscream/tree/4.0.0) + +Major API refactor. + +### [3.1.1](https://github.com/daltoniam/Starscream/tree/3.1.1) + +Small version number fix for 3.1.0: [#703](https://github.com/daltoniam/Starscream/issues/703) + +### [3.1.0](https://github.com/daltoniam/Starscream/tree/3.1.0) + +* Swift 5.0 and Xcode 10.2 support + +#### [3.0.6](https://github.com/daltoniam/Starscream/tree/3.0.6) + +* Swift 4.2 and Xcode 10 support +* added pongDelegate +* moved CommonCrypto and zlib dependencies +* HTTP proxy support + +#### [3.0.5](https://github.com/daltoniam/Starscream/tree/3.0.5) + +Swift 4.1 support and bug fixes. + +Pull Requests: +[#492](https://github.com/daltoniam/Starscream/pull/492) +[#461](https://github.com/daltoniam/Starscream/pull/461) +[#476](https://github.com/daltoniam/Starscream/pull/476) + +Issues: +[#494](https://github.com/daltoniam/Starscream/issues/494) +[#491](https://github.com/daltoniam/Starscream/issues/491) +[#474](https://github.com/daltoniam/Starscream/issues/474) +[#471](https://github.com/daltoniam/Starscream/issues/471) +[#437](https://github.com/daltoniam/Starscream/issues/437) +[#445](https://github.com/daltoniam/Starscream/issues/445) +[#466](https://github.com/daltoniam/Starscream/issues/466) + + +#### [3.0.4](https://github.com/daltoniam/Starscream/tree/3.0.4) + +Improved error handling. Timeout fix. Small assorted fixes. + +Pull Requests: +[#452](https://github.com/daltoniam/Starscream/pull/452) +[#448](https://github.com/daltoniam/Starscream/pull/448) +[#444](https://github.com/daltoniam/Starscream/pull/444) +[#443](https://github.com/daltoniam/Starscream/pull/443) + +Issues: +[#415](https://github.com/daltoniam/Starscream/issues/415) +[#422](https://github.com/daltoniam/Starscream/issues/422) +[#429](https://github.com/daltoniam/Starscream/issues/429) +[#433](https://github.com/daltoniam/Starscream/issues/433) +[#439](https://github.com/daltoniam/Starscream/issues/439) + +#### [3.0.3](https://github.com/daltoniam/Starscream/tree/3.0.3) + +Assorted fixes. + +Pull Requests: +[#438](https://github.com/daltoniam/Starscream/pull/438) +[#423](https://github.com/daltoniam/Starscream/pull/423) +[#420](https://github.com/daltoniam/Starscream/pull/420) +[#418](https://github.com/daltoniam/Starscream/pull/418) +[#410](https://github.com/daltoniam/Starscream/pull/410) +[#405](https://github.com/daltoniam/Starscream/pull/405) +[#404](https://github.com/daltoniam/Starscream/pull/404) +[#400](https://github.com/daltoniam/Starscream/pull/400) + +Issues: +[#435](https://github.com/daltoniam/Starscream/issues/435) +[#431](https://github.com/daltoniam/Starscream/issues/431) +[#426](https://github.com/daltoniam/Starscream/issues/426) +[#409](https://github.com/daltoniam/Starscream/issues/409) +[#408](https://github.com/daltoniam/Starscream/issues/408) +[#401](https://github.com/daltoniam/Starscream/issues/401) +[#399](https://github.com/daltoniam/Starscream/issues/399) +[#378](https://github.com/daltoniam/Starscream/issues/378) + +#### [3.0.2](https://github.com/daltoniam/Starscream/tree/3.0.2) + +Small fixes for 3.0.1. + +[#394](https://github.com/daltoniam/Starscream/issues/394) +[#392](https://github.com/daltoniam/Starscream/issues/392) +[#391](https://github.com/daltoniam/Starscream/issues/391) + +#### [3.0.1](https://github.com/daltoniam/Starscream/tree/3.0.1) + +Small fixes for 3.0.0. + +[#389](https://github.com/daltoniam/Starscream/issues/389) +[#354](https://github.com/daltoniam/Starscream/issues/354) +[#386](https://github.com/daltoniam/Starscream/pull/386) +[#388](https://github.com/daltoniam/Starscream/pull/388) +[#390](https://github.com/daltoniam/Starscream/pull/390) + +#### [3.0.0](https://github.com/daltoniam/Starscream/tree/3.0.0) + +Major refactor and Swift 4 support. Additions include: + +- Watchos support. +- Linux support. +- New Stream class to allow custom socket implementations if desired. +- Protocol added for mocking (dependency injection). +- Single framework (no more platform suffixes! e.g. StarscreamOSX, StarscreamTVOS, etc). + +[#384](https://github.com/daltoniam/Starscream/issues/384) +[#377](https://github.com/daltoniam/Starscream/pull/377) +[#374](https://github.com/daltoniam/Starscream/issues/374) +[#346](https://github.com/daltoniam/Starscream/issues/346) +[#335](https://github.com/daltoniam/Starscream/issues/335) +[#311](https://github.com/daltoniam/Starscream/pull/311) +[#269](https://github.com/daltoniam/Starscream/issues/269) + +#### [2.1.1](https://github.com/daltoniam/Starscream/tree/2.1.1) + +Fixes race condition. Updated to avoid SPM dependencies. + +[#370](https://github.com/daltoniam/Starscream/issues/370) +[#367](https://github.com/daltoniam/Starscream/issues/367) +[#364](https://github.com/daltoniam/Starscream/pull/364) +[#357](https://github.com/daltoniam/Starscream/pull/357) +[#355](https://github.com/daltoniam/Starscream/pull/355) + +#### [2.1.0](https://github.com/daltoniam/Starscream/tree/2.1.0) + +Adds WebSocket compression. Also adds advance WebSocket delegate for extra control. Bug Fixes. + +[#349](https://github.com/daltoniam/Starscream/pull/349) +[#344](https://github.com/daltoniam/Starscream/pull/344) +[#339](https://github.com/daltoniam/Starscream/pull/339) +[#337](https://github.com/daltoniam/Starscream/pull/337) +[#334](https://github.com/daltoniam/Starscream/issues/334) +[#333](https://github.com/daltoniam/Starscream/pull/333) +[#319](https://github.com/daltoniam/Starscream/issues/319) +[#309](https://github.com/daltoniam/Starscream/issues/309) +[#329](https://github.com/daltoniam/Starscream/issues/329) + +#### [2.0.4](https://github.com/daltoniam/Starscream/tree/2.0.4) + +SSL Pinning fix by Giuliano Galea as reported by Lukas Futera of [Centralway](https://www.centralway.com/de/). +Warning fixes for Swift 3.1 + +#### [2.0.3](https://github.com/daltoniam/Starscream/tree/2.0.3) + +[#302](https://github.com/daltoniam/Starscream/issues/302) +[#301](https://github.com/daltoniam/Starscream/issues/301) +[#300](https://github.com/daltoniam/Starscream/issues/300) +[#296](https://github.com/daltoniam/Starscream/issues/296) +[#294](https://github.com/daltoniam/Starscream/issues/294) +[#292](https://github.com/daltoniam/Starscream/issues/292) +[#289](https://github.com/daltoniam/Starscream/issues/289) +[#288](https://github.com/daltoniam/Starscream/issues/288) + +#### [2.0.2](https://github.com/daltoniam/Starscream/tree/2.0.2) + +Fix for the Swift Package Manager. + +Fixed: +[#277](https://github.com/daltoniam/Starscream/issues/277) + +#### [2.0.1](https://github.com/daltoniam/Starscream/tree/2.0.1) + +Bug fixes. + +Fixed: +[#261](https://github.com/daltoniam/Starscream/issues/261) +[#276](https://github.com/daltoniam/Starscream/issues/276) +[#267](https://github.com/daltoniam/Starscream/issues/267) +[#266](https://github.com/daltoniam/Starscream/issues/266) +[#259](https://github.com/daltoniam/Starscream/issues/259) + +#### [2.0.0](https://github.com/daltoniam/Starscream/tree/2.0.0) + +Added Swift 3 support. + +Fixed: +[#229](https://github.com/daltoniam/Starscream/issues/229) +[#232](https://github.com/daltoniam/Starscream/issues/232) + +#### [1.1.4](https://github.com/daltoniam/Starscream/tree/1.1.4) + +Swift 2.3 support. + +#### [1.1.3](https://github.com/daltoniam/Starscream/tree/1.1.3) + +Changed: +[#170](https://github.com/daltoniam/Starscream/issues/170) +[#171](https://github.com/daltoniam/Starscream/issues/171) +[#174](https://github.com/daltoniam/Starscream/issues/174) +[#177](https://github.com/daltoniam/Starscream/issues/177) +[#178](https://github.com/daltoniam/Starscream/issues/178) + +#### [1.1.2](https://github.com/daltoniam/Starscream/tree/1.1.2) + +Fixed: +[#158](https://github.com/daltoniam/Starscream/issues/158) +[#161](https://github.com/daltoniam/Starscream/issues/161) +[#164](https://github.com/daltoniam/Starscream/issues/164) + +#### [1.1.1](https://github.com/daltoniam/Starscream/tree/1.1.1) + +Fixed: +[#157](https://github.com/daltoniam/Starscream/issues/157) + +#### [1.1.0](https://github.com/daltoniam/Starscream/tree/1.1.0) + +Changed: +Moved over to Runloop/default GCD queues to shared queue. + +Fixed: +[#153](https://github.com/daltoniam/Starscream/issues/153) +[#151](https://github.com/daltoniam/Starscream/issues/151) +[#150](https://github.com/daltoniam/Starscream/issues/150) +[#149](https://github.com/daltoniam/Starscream/issues/149) +[#147](https://github.com/daltoniam/Starscream/issues/147) +[#139](https://github.com/daltoniam/Starscream/issues/139) +[#77](https://github.com/daltoniam/Starscream/issues/77) + +#### [1.0.2](https://github.com/daltoniam/Starscream/tree/1.0.2) + +Added TVOS support. + +#### [1.0.1](https://github.com/daltoniam/Starscream/tree/1.0.1) + +Fixes for #121, #123 + +#### [1.0.0](https://github.com/daltoniam/Starscream/tree/1.0.0) + +first release of Swift 2 support. diff --git a/Vendors/spm/Starscream/Gemfile b/Vendors/spm/Starscream/Gemfile new file mode 100644 index 00000000..82d1e304 --- /dev/null +++ b/Vendors/spm/Starscream/Gemfile @@ -0,0 +1,4 @@ +source "https://rubygems.org" + +gem "fastlane" +gem "cocoapods" diff --git a/Vendors/spm/Starscream/Gemfile.lock b/Vendors/spm/Starscream/Gemfile.lock new file mode 100644 index 00000000..989fe592 --- /dev/null +++ b/Vendors/spm/Starscream/Gemfile.lock @@ -0,0 +1,217 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.2) + activesupport (4.2.11.1) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.1) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + babosa (1.0.3) + claide (1.0.3) + cocoapods (1.8.4) + activesupport (>= 4.0.2, < 5) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.8.4) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-stats (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.6.6) + nap (~> 1.0) + ruby-macho (~> 1.4) + xcodeproj (>= 1.11.1, < 2.0) + cocoapods-core (1.8.4) + activesupport (>= 4.0.2, < 6) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + cocoapods-deintegrate (1.0.4) + cocoapods-downloader (1.3.0) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.0) + cocoapods-stats (1.1.0) + cocoapods-trunk (1.4.1) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander-fastlane (4.4.6) + highline (~> 1.7.2) + concurrent-ruby (1.1.5) + declarative (0.0.10) + declarative-option (0.1.0) + digest-crc (0.4.1) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.7.5) + emoji_regex (1.0.1) + escape (0.0.4) + excon (0.71.1) + faraday (0.17.3) + multipart-post (>= 1.2, < 3) + faraday-cookie_jar (0.0.6) + faraday (>= 0.7.4) + http-cookie (~> 1.0.0) + faraday_middleware (0.13.1) + faraday (>= 0.7.4, < 1.0) + fastimage (2.1.7) + fastlane (2.139.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + babosa (>= 1.0.2, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander-fastlane (>= 4.4.6, < 5.0.0) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 2.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 0.17) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 0.13.1) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-api-client (>= 0.29.2, < 0.37.0) + google-cloud-storage (>= 1.15.0, < 2.0.0) + highline (>= 1.7.2, < 2.0.0) + json (< 3.0.0) + jwt (~> 2.1.0) + mini_magick (>= 4.9.4, < 5.0.0) + multi_xml (~> 0.5) + multipart-post (~> 2.0.0) + plist (>= 3.1.0, < 4.0.0) + public_suffix (~> 2.0.0) + rubyzip (>= 1.3.0, < 2.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + slack-notifier (>= 2.0.0, < 3.0.0) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + google-api-client (0.36.3) + addressable (~> 2.5, >= 2.5.1) + googleauth (~> 0.9) + httpclient (>= 2.8.1, < 3.0) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + signet (~> 0.12) + google-cloud-core (1.4.1) + google-cloud-env (~> 1.0) + google-cloud-env (1.3.0) + faraday (~> 0.11) + google-cloud-storage (1.25.0) + addressable (~> 2.5) + digest-crc (~> 0.4) + google-api-client (~> 0.33) + google-cloud-core (~> 1.2) + googleauth (~> 0.9) + mini_mime (~> 1.0) + googleauth (0.10.0) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.12) + highline (1.7.10) + http-cookie (1.0.3) + domain_name (~> 0.5) + httpclient (2.8.3) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + json (2.3.0) + jwt (2.1.0) + memoist (0.16.2) + mini_magick (4.9.5) + mini_mime (1.0.2) + minitest (5.13.0) + molinillo (0.6.6) + multi_json (1.14.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nanaimo (0.2.6) + nap (1.1.0) + naturally (2.2.0) + netrc (0.11.0) + os (1.0.1) + plist (3.5.0) + public_suffix (2.0.5) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rouge (2.0.7) + ruby-macho (1.4.0) + rubyzip (1.3.0) + security (0.1.3) + signet (0.12.0) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.7) + CFPropertyList + naturally + slack-notifier (2.3.2) + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + thread_safe (0.3.6) + tty-cursor (0.7.0) + tty-screen (0.7.0) + tty-spinner (0.9.2) + tty-cursor (~> 0.7) + tzinfo (1.2.6) + thread_safe (~> 0.1) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.6) + unicode-display_width (1.6.0) + word_wrap (1.0.0) + xcodeproj (1.14.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.0) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods + fastlane + +BUNDLED WITH + 2.1.3 diff --git a/Vendors/spm/Starscream/LICENSE b/Vendors/spm/Starscream/LICENSE new file mode 100644 index 00000000..d6ab2f1f --- /dev/null +++ b/Vendors/spm/Starscream/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + Copyright (c) 2014-2016 Dalton Cherry. + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/Vendors/spm/Starscream/Package.resolved b/Vendors/spm/Starscream/Package.resolved new file mode 100644 index 00000000..e49c8be4 --- /dev/null +++ b/Vendors/spm/Starscream/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "swift-nio-zlib-support", + "repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git", + "state": { + "branch": null, + "revision": "37760e9a52030bb9011972c5213c3350fa9d41fd", + "version": "1.0.0" + } + } + ] + }, + "version": 1 +} diff --git a/Vendors/spm/Starscream/Package.swift b/Vendors/spm/Starscream/Package.swift new file mode 100644 index 00000000..f45b4cbd --- /dev/null +++ b/Vendors/spm/Starscream/Package.swift @@ -0,0 +1,39 @@ +// swift-tools-version:5.2 + +// +// Package.Swift +// Starscream +// +// Created by Dalton Cherry on 5/16/15. +// Copyright (c) 2014-2016 Dalton Cherry. +// +// 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 PackageDescription + +let package = Package( + name: "Starscream", + products: [ + .library(name: "Starscream", targets: ["Starscream"]) + ], + dependencies: [], + targets: [ + .target(name: "Starscream", + path: "Sources") + ] +) + +#if os(Linux) + package.dependencies.append(.package(url: "https://github.com/apple/swift-nio-zlib-support.git", from: "1.0.0")) +#endif diff --git a/Vendors/spm/Starscream/README.md b/Vendors/spm/Starscream/README.md new file mode 100644 index 00000000..634723aa --- /dev/null +++ b/Vendors/spm/Starscream/README.md @@ -0,0 +1,308 @@ +![starscream](https://raw.githubusercontent.com/daltoniam/starscream/assets/starscream.jpg) + +Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6455)) library in Swift. + +## Features + +- Conforms to all of the base [Autobahn test suite](https://crossbar.io/autobahn/). +- Nonblocking. Everything happens in the background, thanks to GCD. +- TLS/WSS support. +- Compression Extensions support ([RFC 7692](https://tools.ietf.org/html/rfc7692)) + +### Import the framework + +First thing is to import the framework. See the Installation instructions on how to add the framework to your project. + +```swift +import Starscream +``` + +### Connect to the WebSocket Server + +Once imported, you can open a connection to your WebSocket server. Note that `socket` is probably best as a property, so it doesn't get deallocated right after being setup. + +```swift +var request = URLRequest(url: URL(string: "http://localhost:8080")!) +request.timeoutInterval = 5 +socket = WebSocket(request: request) +socket.delegate = self +socket.connect() +``` + +After you are connected, there is either a delegate or closure you can use for process WebSocket events. + +### Receiving data from a WebSocket + +`didReceive` receives all the WebSocket events in a single easy to handle enum. + +```swift +func didReceive(event: WebSocketEvent, client: WebSocket) { + switch event { + case .connected(let headers): + isConnected = true + print("websocket is connected: \(headers)") + case .disconnected(let reason, let code): + isConnected = false + print("websocket is disconnected: \(reason) with code: \(code)") + case .text(let string): + print("Received text: \(string)") + case .binary(let data): + print("Received data: \(data.count)") + case .ping(_): + break + case .pong(_): + break + case .viabilityChanged(_): + break + case .reconnectSuggested(_): + break + case .cancelled: + isConnected = false + case .error(let error): + isConnected = false + handleError(error) + } +} +``` + +The closure of this would be: + +```swift +socket.onEvent = { event in + switch event { + // handle events just like above... + } +} +``` + +### Writing to a WebSocket + +### write a binary frame + +The writeData method gives you a simple way to send `Data` (binary) data to the server. + +```swift +socket.write(data: data) //write some Data over the socket! +``` + +### write a string frame + +The writeString method is the same as writeData, but sends text/string. + +```swift +socket.write(string: "Hi Server!") //example on how to write text over the socket! +``` + +### write a ping frame + +The writePing method is the same as write, but sends a ping control frame. + +```swift +socket.write(ping: Data()) //example on how to write a ping control frame over the socket! +``` + +### write a pong frame + + +the writePong method is the same as writePing, but sends a pong control frame. + +```swift +socket.write(pong: Data()) //example on how to write a pong control frame over the socket! +``` + +Starscream will automatically respond to incoming `ping` control frames so you do not need to manually send `pong`s. + +However if for some reason you need to control this process you can turn off the automatic `ping` response by disabling `respondToPingWithPong`. + +```swift +socket.respondToPingWithPong = false //Do not automaticaly respond to incoming pings with pongs. +``` + +In most cases you will not need to do this. + +### disconnect + +The disconnect method does what you would expect and closes the socket. + +```swift +socket.disconnect() +``` + +The disconnect method can also send a custom close code if desired. + +```swift +socket.disconnect(closeCode: CloseCode.normal.rawValue) +``` + +### Custom Headers, Protocols and Timeout + +You can override the default websocket headers, add your own custom ones and set a timeout: + +```swift +var request = URLRequest(url: URL(string: "ws://localhost:8080/")!) +request.timeoutInterval = 5 // Sets the timeout for the connection +request.setValue("someother protocols", forHTTPHeaderField: "Sec-WebSocket-Protocol") +request.setValue("14", forHTTPHeaderField: "Sec-WebSocket-Version") +request.setValue("chat,superchat", forHTTPHeaderField: "Sec-WebSocket-Protocol") +request.setValue("Everything is Awesome!", forHTTPHeaderField: "My-Awesome-Header") +let socket = WebSocket(request: request) +``` + +### SSL Pinning + +SSL Pinning is also supported in Starscream. + + +Allow Self-signed certificates: + +```swift +var request = URLRequest(url: URL(string: "ws://localhost:8080/")!) +let pinner = FoundationSecurity(allowSelfSigned: true) // don't validate SSL certificates +let socket = WebSocket(request: request, certPinner: pinner) +``` + +TODO: Update docs on how to load certificates and public keys into an app bundle, use the builtin pinner and TrustKit. + +### Compression Extensions + +Compression Extensions ([RFC 7692](https://tools.ietf.org/html/rfc7692)) is supported in Starscream. Compression is enabled by default, however compression will only be used if it is supported by the server as well. You may enable or disable compression via the `.enableCompression` property: + +```swift +var request = URLRequest(url: URL(string: "ws://localhost:8080/")!) +let compression = WSCompression() +let socket = WebSocket(request: request, compressionHandler: compression) +``` + +Compression should be disabled if your application is transmitting already-compressed, random, or other uncompressable data. + +### Custom Queue + +A custom queue can be specified when delegate methods are called. By default `DispatchQueue.main` is used, thus making all delegate methods calls run on the main thread. It is important to note that all WebSocket processing is done on a background thread, only the delegate method calls are changed when modifying the queue. The actual processing is always on a background thread and will not pause your app. + +```swift +socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"]) +//create a custom queue +socket.callbackQueue = DispatchQueue(label: "com.vluxe.starscream.myapp") +``` + +## Example Project + +Check out the SimpleTest project in the examples directory to see how to setup a simple connection to a WebSocket server. + +## Requirements + +Starscream works with iOS 8/10.10 or above for CocoaPods/framework support. To use Starscream with a project targeting iOS 7, you must include all Swift files directly in your project. + +## Installation + +### CocoaPods + +Check out [Get Started](http://cocoapods.org/) tab on [cocoapods.org](http://cocoapods.org/). + +To use Starscream in your project add the following 'Podfile' to your project + + source 'https://github.com/CocoaPods/Specs.git' + platform :ios, '9.0' + use_frameworks! + + pod 'Starscream', '~> 4.0.0' + +Then run: + + pod install + +### Carthage + +Check out the [Carthage](https://github.com/Carthage/Carthage) docs on how to add a install. The `Starscream` framework is already setup with shared schemes. + +[Carthage Install](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) + +You can install Carthage with [Homebrew](http://brew.sh/) using the following command: + +```bash +$ brew update +$ brew install carthage +``` + +To integrate Starscream into your Xcode project using Carthage, specify it in your `Cartfile`: + +``` +github "daltoniam/Starscream" >= 4.0.0 +``` + +### Accio + +Check out the [Accio](https://github.com/JamitLabs/Accio) docs on how to add a install. + +Add the following to your Package.swift: + +```swift +.package(url: "https://github.com/daltoniam/Starscream.git", .upToNextMajor(from: "4.0.0")), +``` + +Next, add `Starscream` to your App targets dependencies like so: + +```swift +.target( + name: "App", + dependencies: [ + "Starscream", + ] +), +``` + +Then run `accio update`. + +### Rogue + +First see the [installation docs](https://github.com/acmacalister/Rogue) for how to install Rogue. + +To install Starscream run the command below in the directory you created the rogue file. + +``` +rogue add https://github.com/daltoniam/Starscream +``` + +Next open the `libs` folder and add the `Starscream.xcodeproj` to your Xcode project. Once that is complete, in your "Build Phases" add the `Starscream.framework` to your "Link Binary with Libraries" phase. Make sure to add the `libs` folder to your `.gitignore` file. + +### Swift Package Manager + +The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. + +Once you have your Swift package set up, adding Starscream as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`. + +```swift +dependencies: [ + .Package(url: "https://github.com/daltoniam/Starscream.git", majorVersion: 4) +] +``` + +### Other + +Simply grab the framework (either via git submodule or another package manager). + +Add the `Starscream.xcodeproj` to your Xcode project. Once that is complete, in your "Build Phases" add the `Starscream.framework` to your "Link Binary with Libraries" phase. + +### Add Copy Frameworks Phase + +If you are running this in an OSX app or on a physical iOS device you will need to make sure you add the `Starscream.framework` to be included in your app bundle. To do this, in Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar. In the tab bar at the top of that window, open the "Build Phases" panel. Expand the "Link Binary with Libraries" group, and add `Starscream.framework`. Click on the + button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add `Starscream.framework` respectively. + +## TODOs + +- [ ] Proxy support + +## License + +Starscream is licensed under the Apache v2 License. + +## Contact + +### Dalton Cherry +* https://github.com/daltoniam +* http://twitter.com/daltoniam +* http://daltoniam.com + +### Austin Cherry ### +* https://github.com/acmacalister +* http://twitter.com/acmacalister +* http://austincherry.me diff --git a/Vendors/spm/Starscream/Sources/Compression/Compression.swift b/Vendors/spm/Starscream/Sources/Compression/Compression.swift new file mode 100644 index 00000000..0e7fae5a --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Compression/Compression.swift @@ -0,0 +1,29 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Compression.swift +// Starscream +// +// Created by Dalton Cherry on 2/4/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +public protocol CompressionHandler { + func load(headers: [String: String]) + func decompress(data: Data, isFinal: Bool) -> Data? + func compress(data: Data) -> Data? +} diff --git a/Vendors/spm/Starscream/Sources/Compression/WSCompression.swift b/Vendors/spm/Starscream/Sources/Compression/WSCompression.swift new file mode 100644 index 00000000..21295841 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Compression/WSCompression.swift @@ -0,0 +1,247 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// WSCompression.swift +// +// Created by Joseph Ross on 7/16/14. +// Copyright © 2017 Joseph Ross & Vluxe. All rights reserved. +// +// 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. +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Compression implementation is implemented in conformance with RFC 7692 Compression Extensions +// for WebSocket: https://tools.ietf.org/html/rfc7692 +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +import Foundation +import zlib + +public class WSCompression: CompressionHandler { + let headerWSExtensionName = "Sec-WebSocket-Extensions" + var decompressor: Decompressor? + var compressor: Compressor? + var decompressorTakeOver = false + var compressorTakeOver = false + + public init() { + + } + + public func load(headers: [String: String]) { + guard let extensionHeader = headers[headerWSExtensionName] else { return } + decompressorTakeOver = false + compressorTakeOver = false + + let parts = extensionHeader.components(separatedBy: ";") + for p in parts { + let part = p.trimmingCharacters(in: .whitespaces) + if part.hasPrefix("server_max_window_bits=") { + let valString = part.components(separatedBy: "=")[1] + if let val = Int(valString.trimmingCharacters(in: .whitespaces)) { + decompressor = Decompressor(windowBits: val) + } + } else if part.hasPrefix("client_max_window_bits=") { + let valString = part.components(separatedBy: "=")[1] + if let val = Int(valString.trimmingCharacters(in: .whitespaces)) { + compressor = Compressor(windowBits: val) + } + } else if part == "client_no_context_takeover" { + compressorTakeOver = true + } else if part == "server_no_context_takeover" { + decompressorTakeOver = true + } + } + } + + public func decompress(data: Data, isFinal: Bool) -> Data? { + guard let decompressor = decompressor else { return nil } + do { + let decompressedData = try decompressor.decompress(data, finish: isFinal) + if decompressorTakeOver { + try decompressor.reset() + } + return decompressedData + } catch { + //do nothing with the error for now + } + return nil + } + + public func compress(data: Data) -> Data? { + guard let compressor = compressor else { return nil } + do { + let compressedData = try compressor.compress(data) + if compressorTakeOver { + try compressor.reset() + } + return compressedData + } catch { + //do nothing with the error for now + } + return nil + } + + +} + +class Decompressor { + private var strm = z_stream() + private var buffer = [UInt8](repeating: 0, count: 0x2000) + private var inflateInitialized = false + private let windowBits: Int + + init?(windowBits: Int) { + self.windowBits = windowBits + guard initInflate() else { return nil } + } + + private func initInflate() -> Bool { + if Z_OK == inflateInit2_(&strm, -CInt(windowBits), + ZLIB_VERSION, CInt(MemoryLayout.size)) + { + inflateInitialized = true + return true + } + return false + } + + func reset() throws { + teardownInflate() + guard initInflate() else { throw WSError(type: .compressionError, message: "Error for decompressor on reset", code: 0) } + } + + func decompress(_ data: Data, finish: Bool) throws -> Data { + return try data.withUnsafeBytes { (bytes: UnsafePointer) -> Data in + return try decompress(bytes: bytes, count: data.count, finish: finish) + } + } + + func decompress(bytes: UnsafePointer, count: Int, finish: Bool) throws -> Data { + var decompressed = Data() + try decompress(bytes: bytes, count: count, out: &decompressed) + + if finish { + let tail:[UInt8] = [0x00, 0x00, 0xFF, 0xFF] + try decompress(bytes: tail, count: tail.count, out: &decompressed) + } + + return decompressed + } + + private func decompress(bytes: UnsafePointer, count: Int, out: inout Data) throws { + var res: CInt = 0 + strm.next_in = UnsafeMutablePointer(mutating: bytes) + strm.avail_in = CUnsignedInt(count) + + repeat { + buffer.withUnsafeMutableBytes { (bufferPtr) in + strm.next_out = bufferPtr.bindMemory(to: UInt8.self).baseAddress + strm.avail_out = CUnsignedInt(bufferPtr.count) + + res = inflate(&strm, 0) + } + + let byteCount = buffer.count - Int(strm.avail_out) + out.append(buffer, count: byteCount) + } while res == Z_OK && strm.avail_out == 0 + + guard (res == Z_OK && strm.avail_out > 0) + || (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count) + else { + throw WSError(type: .compressionError, message: "Error on decompressing", code: 0) + } + } + + private func teardownInflate() { + if inflateInitialized, Z_OK == inflateEnd(&strm) { + inflateInitialized = false + } + } + + deinit { + teardownInflate() + } +} + +class Compressor { + private var strm = z_stream() + private var buffer = [UInt8](repeating: 0, count: 0x2000) + private var deflateInitialized = false + private let windowBits: Int + + init?(windowBits: Int) { + self.windowBits = windowBits + guard initDeflate() else { return nil } + } + + private func initDeflate() -> Bool { + if Z_OK == deflateInit2_(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + -CInt(windowBits), 8, Z_DEFAULT_STRATEGY, + ZLIB_VERSION, CInt(MemoryLayout.size)) + { + deflateInitialized = true + return true + } + return false + } + + func reset() throws { + teardownDeflate() + guard initDeflate() else { throw WSError(type: .compressionError, message: "Error for compressor on reset", code: 0) } + } + + func compress(_ data: Data) throws -> Data { + var compressed = Data() + var res: CInt = 0 + data.withUnsafeBytes { (ptr:UnsafePointer) -> Void in + strm.next_in = UnsafeMutablePointer(mutating: ptr) + strm.avail_in = CUnsignedInt(data.count) + + repeat { + buffer.withUnsafeMutableBytes { (bufferPtr) in + strm.next_out = bufferPtr.bindMemory(to: UInt8.self).baseAddress + strm.avail_out = CUnsignedInt(bufferPtr.count) + + res = deflate(&strm, Z_SYNC_FLUSH) + } + + let byteCount = buffer.count - Int(strm.avail_out) + compressed.append(buffer, count: byteCount) + } + while res == Z_OK && strm.avail_out == 0 + + } + + guard res == Z_OK && strm.avail_out > 0 + || (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count) + else { + throw WSError(type: .compressionError, message: "Error on compressing", code: 0) + } + + compressed.removeLast(4) + return compressed + } + + private func teardownDeflate() { + if deflateInitialized, Z_OK == deflateEnd(&strm) { + deflateInitialized = false + } + } + + deinit { + teardownDeflate() + } +} diff --git a/Vendors/spm/Starscream/Sources/DataBytes/Data+Extensions.swift b/Vendors/spm/Starscream/Sources/DataBytes/Data+Extensions.swift new file mode 100644 index 00000000..1d0852d7 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/DataBytes/Data+Extensions.swift @@ -0,0 +1,53 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Data+Extensions.swift +// Starscream +// +// Created by Dalton Cherry on 3/27/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// Fix for deprecation warnings +// +// 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 + +internal extension Data { + struct ByteError: Swift.Error {} + + #if swift(>=5.0) + func withUnsafeBytes(_ completion: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { + return try withUnsafeBytes { + if let baseAddress = $0.baseAddress, $0.count > 0 { + return try completion(baseAddress.assumingMemoryBound(to: ContentType.self)) + } else { + throw ByteError() + } + } + } + #endif + + #if swift(>=5.0) + mutating func withUnsafeMutableBytes(_ completion: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { + return try withUnsafeMutableBytes { + if let baseAddress = $0.baseAddress, $0.count > 0 { + return try completion(baseAddress.assumingMemoryBound(to: ContentType.self)) + } else { + throw ByteError() + } + } + } + #endif +} diff --git a/Vendors/spm/Starscream/Sources/Engine/Engine.swift b/Vendors/spm/Starscream/Sources/Engine/Engine.swift new file mode 100644 index 00000000..a60ef7e8 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Engine/Engine.swift @@ -0,0 +1,22 @@ +// +// Engine.swift +// Starscream +// +// Created by Dalton Cherry on 6/15/19. +// Copyright © 2019 Vluxe. All rights reserved. +// + +import Foundation + +public protocol EngineDelegate: class { + func didReceive(event: WebSocketEvent) +} + +public protocol Engine { + func register(delegate: EngineDelegate) + func start(request: URLRequest) + func stop(closeCode: UInt16) + func forceStop() + func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) + func write(string: String, completion: (() -> ())?) +} diff --git a/Vendors/spm/Starscream/Sources/Engine/NativeEngine.swift b/Vendors/spm/Starscream/Sources/Engine/NativeEngine.swift new file mode 100644 index 00000000..7294e364 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Engine/NativeEngine.swift @@ -0,0 +1,96 @@ +// +// NativeEngine.swift +// Starscream +// +// Created by Dalton Cherry on 6/15/19. +// Copyright © 2019 Vluxe. All rights reserved. +// + +import Foundation + +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +public class NativeEngine: NSObject, Engine, URLSessionDataDelegate, URLSessionWebSocketDelegate { + private var task: URLSessionWebSocketTask? + weak var delegate: EngineDelegate? + + public func register(delegate: EngineDelegate) { + self.delegate = delegate + } + + public func start(request: URLRequest) { + let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil) + task = session.webSocketTask(with: request) + doRead() + task?.resume() + } + + public func stop(closeCode: UInt16) { + let closeCode = URLSessionWebSocketTask.CloseCode(rawValue: Int(closeCode)) ?? .normalClosure + task?.cancel(with: closeCode, reason: nil) + } + + public func forceStop() { + stop(closeCode: UInt16(URLSessionWebSocketTask.CloseCode.abnormalClosure.rawValue)) + } + + public func write(string: String, completion: (() -> ())?) { + task?.send(.string(string), completionHandler: { (error) in + completion?() + }) + } + + public func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) { + switch opcode { + case .binaryFrame: + task?.send(.data(data), completionHandler: { (error) in + completion?() + }) + case .textFrame: + let text = String(data: data, encoding: .utf8)! + write(string: text, completion: completion) + case .ping: + task?.sendPing(pongReceiveHandler: { (error) in + completion?() + }) + default: + break //unsupported + } + } + + private func doRead() { + task?.receive { [weak self] (result) in + switch result { + case .success(let message): + switch message { + case .string(let string): + self?.broadcast(event: .text(string)) + case .data(let data): + self?.broadcast(event: .binary(data)) + @unknown default: + break + } + break + case .failure(let error): + self?.broadcast(event: .error(error)) + } + self?.doRead() + } + } + + private func broadcast(event: WebSocketEvent) { + delegate?.didReceive(event: event) + } + + public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { + let p = `protocol` ?? "" + broadcast(event: .connected([HTTPWSHeader.protocolName: p])) + } + + public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { + var r = "" + if let d = reason { + r = String(data: d, encoding: .utf8) ?? "" + } + broadcast(event: .disconnected(r, UInt16(closeCode.rawValue))) + } +} diff --git a/Vendors/spm/Starscream/Sources/Engine/WSEngine.swift b/Vendors/spm/Starscream/Sources/Engine/WSEngine.swift new file mode 100644 index 00000000..decca641 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Engine/WSEngine.swift @@ -0,0 +1,234 @@ +// +// WSEngine.swift +// Starscream +// +// Created by Dalton Cherry on 6/15/19. +// Copyright © 2019 Vluxe. All rights reserved. +// + +import Foundation + +public class WSEngine: Engine, TransportEventClient, FramerEventClient, +FrameCollectorDelegate, HTTPHandlerDelegate { + private let transport: Transport + private let framer: Framer + private let httpHandler: HTTPHandler + private let compressionHandler: CompressionHandler? + private let certPinner: CertificatePinning? + private let headerChecker: HeaderValidator + private var request: URLRequest! + + private let frameHandler = FrameCollector() + private var didUpgrade = false + private var secKeyValue = "" + private let writeQueue = DispatchQueue(label: "com.vluxe.starscream.writequeue") + private let mutex = DispatchSemaphore(value: 1) + private var canSend = false + + weak var delegate: EngineDelegate? + public var respondToPingWithPong: Bool = true + + public init(transport: Transport, + certPinner: CertificatePinning? = nil, + headerValidator: HeaderValidator = FoundationSecurity(), + httpHandler: HTTPHandler = FoundationHTTPHandler(), + framer: Framer = WSFramer(), + compressionHandler: CompressionHandler? = nil) { + self.transport = transport + self.framer = framer + self.httpHandler = httpHandler + self.certPinner = certPinner + self.headerChecker = headerValidator + self.compressionHandler = compressionHandler + framer.updateCompression(supports: compressionHandler != nil) + frameHandler.delegate = self + } + + public func register(delegate: EngineDelegate) { + self.delegate = delegate + } + + public func start(request: URLRequest) { + mutex.wait() + let isConnected = canSend + mutex.signal() + if isConnected { + return + } + + self.request = request + transport.register(delegate: self) + framer.register(delegate: self) + httpHandler.register(delegate: self) + frameHandler.delegate = self + guard let url = request.url else { + return + } + transport.connect(url: url, timeout: request.timeoutInterval, certificatePinning: certPinner) + } + + public func stop(closeCode: UInt16 = CloseCode.normal.rawValue) { + let capacity = MemoryLayout.size + var pointer = [UInt8](repeating: 0, count: capacity) + writeUint16(&pointer, offset: 0, value: closeCode) + let payload = Data(bytes: pointer, count: MemoryLayout.size) + write(data: payload, opcode: .connectionClose, completion: { [weak self] in + self?.reset() + self?.forceStop() + }) + } + + public func forceStop() { + transport.disconnect() + } + + public func write(string: String, completion: (() -> ())?) { + let data = string.data(using: .utf8)! + write(data: data, opcode: .textFrame, completion: completion) + } + + public func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) { + writeQueue.async { [weak self] in + guard let s = self else { return } + s.mutex.wait() + let canWrite = s.canSend + s.mutex.signal() + if !canWrite { + return + } + + var isCompressed = false + var sendData = data + if let compressedData = s.compressionHandler?.compress(data: data) { + sendData = compressedData + isCompressed = true + } + + let frameData = s.framer.createWriteFrame(opcode: opcode, payload: sendData, isCompressed: isCompressed) + s.transport.write(data: frameData, completion: {_ in + completion?() + }) + } + } + + // MARK: - TransportEventClient + + public func connectionChanged(state: ConnectionState) { + switch state { + case .connected: + secKeyValue = HTTPWSHeader.generateWebSocketKey() + let wsReq = HTTPWSHeader.createUpgrade(request: request, supportsCompression: framer.supportsCompression(), secKeyValue: secKeyValue) + let data = httpHandler.convert(request: wsReq) + transport.write(data: data, completion: {_ in }) + case .waiting: + break + case .failed(let error): + handleError(error) + case .viability(let isViable): + broadcast(event: .viabilityChanged(isViable)) + case .shouldReconnect(let status): + broadcast(event: .reconnectSuggested(status)) + case .receive(let data): + if didUpgrade { + framer.add(data: data) + } else { + let offset = httpHandler.parse(data: data) + if offset > 0 { + let extraData = data.subdata(in: offset.. Data? { + return compressionHandler?.decompress(data: data, isFinal: isFinal) + } + + public func didForm(event: FrameCollector.Event) { + switch event { + case .text(let string): + broadcast(event: .text(string)) + case .binary(let data): + broadcast(event: .binary(data)) + case .pong(let data): + broadcast(event: .pong(data)) + case .ping(let data): + broadcast(event: .ping(data)) + if respondToPingWithPong { + write(data: data ?? Data(), opcode: .pong, completion: nil) + } + case .closed(let reason, let code): + broadcast(event: .disconnected(reason, code)) + stop(closeCode: code) + case .error(let error): + handleError(error) + } + } + + private func broadcast(event: WebSocketEvent) { + delegate?.didReceive(event: event) + } + + //This call can be coming from a lot of different queues/threads. + //be aware of that when modifying shared variables + private func handleError(_ error: Error?) { + if let wsError = error as? WSError { + stop(closeCode: wsError.code) + } else { + stop() + } + + delegate?.didReceive(event: .error(error)) + } + + private func reset() { + mutex.wait() + canSend = false + didUpgrade = false + mutex.signal() + } + + +} diff --git a/Vendors/spm/Starscream/Sources/Framer/FoundationHTTPHandler.swift b/Vendors/spm/Starscream/Sources/Framer/FoundationHTTPHandler.swift new file mode 100644 index 00000000..fb024aaa --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Framer/FoundationHTTPHandler.swift @@ -0,0 +1,123 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// FoundationHTTPHandler.swift +// Starscream +// +// Created by Dalton Cherry on 1/25/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 +#if os(watchOS) +public typealias FoundationHTTPHandler = StringHTTPHandler +#else +public class FoundationHTTPHandler: HTTPHandler { + + var buffer = Data() + weak var delegate: HTTPHandlerDelegate? + + public init() { + + } + + public func convert(request: URLRequest) -> Data { + let msg = CFHTTPMessageCreateRequest(kCFAllocatorDefault, request.httpMethod! as CFString, + request.url! as CFURL, kCFHTTPVersion1_1).takeRetainedValue() + if let headers = request.allHTTPHeaderFields { + for (aKey, aValue) in headers { + CFHTTPMessageSetHeaderFieldValue(msg, aKey as CFString, aValue as CFString) + } + } + if let body = request.httpBody { + CFHTTPMessageSetBody(msg, body as CFData) + } + guard let data = CFHTTPMessageCopySerializedMessage(msg) else { + return Data() + } + return data.takeRetainedValue() as Data + } + + public func parse(data: Data) -> Int { + let offset = findEndOfHTTP(data: data) + if offset > 0 { + buffer.append(data.subdata(in: 0.. Bool { + var pointer = [UInt8]() + data.withUnsafeBytes { pointer.append(contentsOf: $0) } + + let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue() + if !CFHTTPMessageAppendBytes(response, pointer, data.count) { + return false //not enough data, wait for more + } + if !CFHTTPMessageIsHeaderComplete(response) { + return false //not enough data, wait for more + } + + let code = CFHTTPMessageGetResponseStatusCode(response) + if code != HTTPWSHeader.switchProtocolCode { + delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.notAnUpgrade(code))) + return true + } + + if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) { + let nsHeaders = cfHeaders.takeRetainedValue() as NSDictionary + var headers = [String: String]() + for (key, value) in nsHeaders { + if let key = key as? String, let value = value as? String { + headers[key] = value + } + } + delegate?.didReceiveHTTP(event: .success(headers)) + return true + } + + delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData)) + return true + } + + public func register(delegate: HTTPHandlerDelegate) { + self.delegate = delegate + } + + private func findEndOfHTTP(data: Data) -> Int { + let endBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")] + var pointer = [UInt8]() + data.withUnsafeBytes { pointer.append(contentsOf: $0) } + var k = 0 + for i in 0.. Data { + #if os(watchOS) + //TODO: build response header + return Data() + #else + let response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, HTTPWSHeader.switchProtocolCode, + nil, kCFHTTPVersion1_1).takeRetainedValue() + + //TODO: add other values to make a proper response here... + //TODO: also sec key thing (Sec-WebSocket-Key) + for (key, value) in headers { + CFHTTPMessageSetHeaderFieldValue(response, key as CFString, value as CFString) + } + guard let cfData = CFHTTPMessageCopySerializedMessage(response)?.takeRetainedValue() else { + return Data() + } + return cfData as Data + #endif + } + + public func parse(data: Data) { + buffer.append(data) + if parseContent(data: buffer) { + buffer = Data() + } + } + + //returns true when the buffer should be cleared + func parseContent(data: Data) -> Bool { + var pointer = [UInt8]() + data.withUnsafeBytes { pointer.append(contentsOf: $0) } + #if os(watchOS) + //TODO: parse data + return false + #else + let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true).takeRetainedValue() + if !CFHTTPMessageAppendBytes(response, pointer, data.count) { + return false //not enough data, wait for more + } + if !CFHTTPMessageIsHeaderComplete(response) { + return false //not enough data, wait for more + } + if let method = CFHTTPMessageCopyRequestMethod(response)?.takeRetainedValue() { + if (method as NSString) != getVerb { + delegate?.didReceive(event: .failure(HTTPUpgradeError.invalidData)) + return true + } + } + + if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) { + let nsHeaders = cfHeaders.takeRetainedValue() as NSDictionary + var headers = [String: String]() + for (key, value) in nsHeaders { + if let key = key as? String, let value = value as? String { + headers[key] = value + } + } + delegate?.didReceive(event: .success(headers)) + return true + } + + delegate?.didReceive(event: .failure(HTTPUpgradeError.invalidData)) + return true + #endif + } +} diff --git a/Vendors/spm/Starscream/Sources/Framer/FrameCollector.swift b/Vendors/spm/Starscream/Sources/Framer/FrameCollector.swift new file mode 100644 index 00000000..3ec1084c --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Framer/FrameCollector.swift @@ -0,0 +1,107 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// FrameCollector.swift +// Starscream +// +// Created by Dalton Cherry on 1/24/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +public protocol FrameCollectorDelegate: class { + func didForm(event: FrameCollector.Event) + func decompress(data: Data, isFinal: Bool) -> Data? +} + +public class FrameCollector { + public enum Event { + case text(String) + case binary(Data) + case pong(Data?) + case ping(Data?) + case error(Error) + case closed(String, UInt16) + } + weak var delegate: FrameCollectorDelegate? + var buffer = Data() + var frameCount = 0 + var isText = false //was the first frame a text frame or a binary frame? + var needsDecompression = false + + public func add(frame: Frame) { + //check single frame action and out of order frames + if frame.opcode == .connectionClose { + var code = frame.closeCode + var reason = "connection closed by server" + if let customCloseReason = String(data: frame.payload, encoding: .utf8) { + reason = customCloseReason + } else { + code = CloseCode.protocolError.rawValue + } + delegate?.didForm(event: .closed(reason, code)) + return + } else if frame.opcode == .pong { + delegate?.didForm(event: .pong(frame.payload)) + return + } else if frame.opcode == .ping { + delegate?.didForm(event: .ping(frame.payload)) + return + } else if frame.opcode == .continueFrame && frameCount == 0 { + let errCode = CloseCode.protocolError.rawValue + delegate?.didForm(event: .error(WSError(type: .protocolError, message: "first frame can't be a continue frame", code: errCode))) + reset() + return + } else if frameCount > 0 && frame.opcode != .continueFrame { + let errCode = CloseCode.protocolError.rawValue + delegate?.didForm(event: .error(WSError(type: .protocolError, message: "second and beyond of fragment message must be a continue frame", code: errCode))) + reset() + return + } + if frameCount == 0 { + isText = frame.opcode == .textFrame + needsDecompression = frame.needsDecompression + } + + let payload: Data + if needsDecompression { + payload = delegate?.decompress(data: frame.payload, isFinal: frame.isFin) ?? frame.payload + } else { + payload = frame.payload + } + buffer.append(payload) + frameCount += 1 + + if frame.isFin { + if isText { + if let string = String(data: buffer, encoding: .utf8) { + delegate?.didForm(event: .text(string)) + } else { + let errCode = CloseCode.protocolError.rawValue + delegate?.didForm(event: .error(WSError(type: .protocolError, message: "not valid UTF-8 data", code: errCode))) + } + } else { + delegate?.didForm(event: .binary(buffer)) + } + reset() + } + } + + func reset() { + buffer = Data() + frameCount = 0 + } +} diff --git a/Vendors/spm/Starscream/Sources/Framer/Framer.swift b/Vendors/spm/Starscream/Sources/Framer/Framer.swift new file mode 100644 index 00000000..f77d5b80 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Framer/Framer.swift @@ -0,0 +1,365 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Framer.swift +// Starscream +// +// Created by Dalton Cherry on 1/23/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +let FinMask: UInt8 = 0x80 +let OpCodeMask: UInt8 = 0x0F +let RSVMask: UInt8 = 0x70 +let RSV1Mask: UInt8 = 0x40 +let MaskMask: UInt8 = 0x80 +let PayloadLenMask: UInt8 = 0x7F +let MaxFrameSize: Int = 32 + +// Standard WebSocket close codes +public enum CloseCode: UInt16 { + case normal = 1000 + case goingAway = 1001 + case protocolError = 1002 + case protocolUnhandledType = 1003 + // 1004 reserved. + case noStatusReceived = 1005 + //1006 reserved. + case encoding = 1007 + case policyViolated = 1008 + case messageTooBig = 1009 +} + +public enum FrameOpCode: UInt8 { + case continueFrame = 0x0 + case textFrame = 0x1 + case binaryFrame = 0x2 + // 3-7 are reserved. + case connectionClose = 0x8 + case ping = 0x9 + case pong = 0xA + // B-F reserved. + case unknown = 100 +} + +public struct Frame { + let isFin: Bool + let needsDecompression: Bool + let isMasked: Bool + let opcode: FrameOpCode + let payloadLength: UInt64 + let payload: Data + let closeCode: UInt16 //only used by connectionClose opcode +} + +public enum FrameEvent { + case frame(Frame) + case error(Error) +} + +public protocol FramerEventClient: class { + func frameProcessed(event: FrameEvent) +} + +public protocol Framer { + func add(data: Data) + func register(delegate: FramerEventClient) + func createWriteFrame(opcode: FrameOpCode, payload: Data, isCompressed: Bool) -> Data + func updateCompression(supports: Bool) + func supportsCompression() -> Bool +} + +public class WSFramer: Framer { + private let queue = DispatchQueue(label: "com.vluxe.starscream.wsframer", attributes: []) + private weak var delegate: FramerEventClient? + private var buffer = Data() + public var compressionEnabled = false + private let isServer: Bool + + public init(isServer: Bool = false) { + self.isServer = isServer + } + + public func updateCompression(supports: Bool) { + compressionEnabled = supports + } + + public func supportsCompression() -> Bool { + return compressionEnabled + } + + enum ProcessEvent { + case needsMoreData + case processedFrame(Frame, Int) + case failed(Error) + } + + public func add(data: Data) { + queue.async { [weak self] in + self?.buffer.append(data) + while(true) { + let event = self?.process() ?? .needsMoreData + switch event { + case .needsMoreData: + return + case .processedFrame(let frame, let split): + guard let s = self else { return } + s.delegate?.frameProcessed(event: .frame(frame)) + if split >= s.buffer.count { + s.buffer = Data() + return + } + s.buffer = s.buffer.advanced(by: split) + case .failed(let error): + self?.delegate?.frameProcessed(event: .error(error)) + self?.buffer = Data() + return + } + } + } + } + + public func register(delegate: FramerEventClient) { + self.delegate = delegate + } + + private func process() -> ProcessEvent { + if buffer.count < 2 { + return .needsMoreData + } + var pointer = [UInt8]() + buffer.withUnsafeBytes { pointer.append(contentsOf: $0) } + + let isFin = (FinMask & pointer[0]) + let opcodeRawValue = (OpCodeMask & pointer[0]) + let opcode = FrameOpCode(rawValue: opcodeRawValue) ?? .unknown + let isMasked = (MaskMask & pointer[1]) + let payloadLen = (PayloadLenMask & pointer[1]) + let RSV1 = (RSVMask & pointer[0]) + var needsDecompression = false + + if compressionEnabled && opcode != .continueFrame { + needsDecompression = (RSV1Mask & pointer[0]) > 0 + } + if !isServer && (isMasked > 0 || RSV1 > 0) && opcode != .pong && !needsDecompression { + let errCode = CloseCode.protocolError.rawValue + return .failed(WSError(type: .protocolError, message: "masked and rsv data is not currently supported", code: errCode)) + } + let isControlFrame = (opcode == .connectionClose || opcode == .ping) + if !isControlFrame && (opcode != .binaryFrame && opcode != .continueFrame && + opcode != .textFrame && opcode != .pong) { + let errCode = CloseCode.protocolError.rawValue + return .failed(WSError(type: .protocolError, message: "unknown opcode: \(opcodeRawValue)", code: errCode)) + } + if isControlFrame && isFin == 0 { + let errCode = CloseCode.protocolError.rawValue + return .failed(WSError(type: .protocolError, message: "control frames can't be fragmented", code: errCode)) + } + + var offset = 2 + + if isControlFrame && payloadLen > 125 { + return .failed(WSError(type: .protocolError, message: "payload length is longer than allowed for a control frame", code: CloseCode.protocolError.rawValue)) + } + + var dataLength = UInt64(payloadLen) + var closeCode = CloseCode.normal.rawValue + if opcode == .connectionClose { + if payloadLen == 1 { + closeCode = CloseCode.protocolError.rawValue + dataLength = 0 + } else if payloadLen > 1 { + if pointer.count < 4 { + return .needsMoreData + } + let size = MemoryLayout.size + closeCode = pointer.readUint16(offset: offset) + offset += size + dataLength -= UInt64(size) + if closeCode < 1000 || (closeCode > 1003 && closeCode < 1007) || (closeCode > 1013 && closeCode < 3000) { + closeCode = CloseCode.protocolError.rawValue + } + } + } + + if payloadLen == 127 { + let size = MemoryLayout.size + if size + offset > pointer.count { + return .needsMoreData + } + dataLength = pointer.readUint64(offset: offset) + offset += size + } else if payloadLen == 126 { + let size = MemoryLayout.size + if size + offset > pointer.count { + return .needsMoreData + } + dataLength = UInt64(pointer.readUint16(offset: offset)) + offset += size + } + + let maskStart = offset + if isServer { + offset += MemoryLayout.size + } + + if dataLength > (pointer.count - offset) { + return .needsMoreData + } + + //I don't like this cast, but Data's count returns an Int. + //Might be a problem with huge payloads. Need to revisit. + let readDataLength = Int(dataLength) + + let payload: Data + if readDataLength == 0 { + payload = Data() + } else { + if isServer { + payload = pointer.unmaskData(maskStart: maskStart, offset: offset, length: readDataLength) + } else { + let end = offset + readDataLength + payload = Data(pointer[offset.. 0, needsDecompression: needsDecompression, isMasked: isMasked > 0, opcode: opcode, payloadLength: dataLength, payload: payload, closeCode: closeCode) + return .processedFrame(frame, offset) + } + + public func createWriteFrame(opcode: FrameOpCode, payload: Data, isCompressed: Bool) -> Data { + let payloadLength = payload.count + + let capacity = payloadLength + MaxFrameSize + var pointer = [UInt8](repeating: 0, count: capacity) + + //set the framing info + pointer[0] = FinMask | opcode.rawValue + if isCompressed { + pointer[0] |= RSV1Mask + } + + var offset = 2 //skip pass the framing info + if payloadLength < 126 { + pointer[1] = UInt8(payloadLength) + } else if payloadLength <= Int(UInt16.max) { + pointer[1] = 126 + writeUint16(&pointer, offset: offset, value: UInt16(payloadLength)) + offset += MemoryLayout.size + } else { + pointer[1] = 127 + writeUint64(&pointer, offset: offset, value: UInt64(payloadLength)) + offset += MemoryLayout.size + } + + //clients are required to mask the payload data, but server don't according to the RFC + if !isServer { + pointer[1] |= MaskMask + + //write the random mask key in + let maskKey: UInt32 = UInt32.random(in: 0...UInt32.max) + + writeUint32(&pointer, offset: offset, value: maskKey) + let maskStart = offset + offset += MemoryLayout.size + + //now write the payload data in + for i in 0...size)] + offset += 1 + } + } else { + for i in 0.. UInt16 { + return (UInt16(self[offset + 0]) << 8) | UInt16(self[offset + 1]) + } + + /** + Read a UInt64 from a buffer. + - parameter offset: is the offset index to start the read from (e.g. buffer[0], buffer[1], etc). + - returns: a UInt64 of the value from the buffer + */ + func readUint64(offset: Int) -> UInt64 { + var value = UInt64(0) + for i in 0...7 { + value = (value << 8) | UInt64(self[offset + i]) + } + return value + } + + func unmaskData(maskStart: Int, offset: Int, length: Int) -> Data { + var unmaskedBytes = [UInt8](repeating: 0, count: length) + let maskSize = MemoryLayout.size + for i in 0..> 8) + buffer[offset + 1] = UInt8(value & 0xff) +} + +/** + Write a UInt32 to the buffer. It fills the 4 array "slots" of the UInt8 array. + - parameter buffer: is the UInt8 array (pointer) to write the value too. + - parameter offset: is the offset index to start the write from (e.g. buffer[0], buffer[1], etc). + */ +public func writeUint32( _ buffer: inout [UInt8], offset: Int, value: UInt32) { + for i in 0...3 { + buffer[offset + i] = UInt8((value >> (8*UInt32(3 - i))) & 0xff) + } +} + +/** + Write a UInt64 to the buffer. It fills the 8 array "slots" of the UInt8 array. + - parameter buffer: is the UInt8 array (pointer) to write the value too. + - parameter offset: is the offset index to start the write from (e.g. buffer[0], buffer[1], etc). + */ +public func writeUint64( _ buffer: inout [UInt8], offset: Int, value: UInt64) { + for i in 0...7 { + buffer[offset + i] = UInt8((value >> (8*UInt64(7 - i))) & 0xff) + } +} diff --git a/Vendors/spm/Starscream/Sources/Framer/HTTPHandler.swift b/Vendors/spm/Starscream/Sources/Framer/HTTPHandler.swift new file mode 100644 index 00000000..70941e75 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Framer/HTTPHandler.swift @@ -0,0 +1,148 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// HTTPHandler.swift +// Starscream +// +// Created by Dalton Cherry on 1/24/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +public enum HTTPUpgradeError: Error { + case notAnUpgrade(Int) + case invalidData +} + +public struct HTTPWSHeader { + static let upgradeName = "Upgrade" + static let upgradeValue = "websocket" + static let hostName = "Host" + static let connectionName = "Connection" + static let connectionValue = "Upgrade" + static let protocolName = "Sec-WebSocket-Protocol" + static let versionName = "Sec-WebSocket-Version" + static let versionValue = "13" + static let extensionName = "Sec-WebSocket-Extensions" + static let keyName = "Sec-WebSocket-Key" + static let originName = "Origin" + static let acceptName = "Sec-WebSocket-Accept" + static let switchProtocolCode = 101 + static let defaultSSLSchemes = ["wss", "https"] + + /// Creates a new URLRequest based off the source URLRequest. + /// - Parameter request: the request to "upgrade" the WebSocket request by adding headers. + /// - Parameter supportsCompression: set if the client support text compression. + /// - Parameter secKeyName: the security key to use in the WebSocket request. https://tools.ietf.org/html/rfc6455#section-1.3 + /// - returns: A URLRequest request to be converted to data and sent to the server. + public static func createUpgrade(request: URLRequest, supportsCompression: Bool, secKeyValue: String) -> URLRequest { + guard let url = request.url, let parts = url.getParts() else { + return request + } + + var req = request + if request.value(forHTTPHeaderField: HTTPWSHeader.originName) == nil { + var origin = url.absoluteString + if let hostUrl = URL (string: "/", relativeTo: url) { + origin = hostUrl.absoluteString + origin.remove(at: origin.index(before: origin.endIndex)) + } + req.setValue(origin, forHTTPHeaderField: HTTPWSHeader.originName) + } + req.setValue(HTTPWSHeader.upgradeValue, forHTTPHeaderField: HTTPWSHeader.upgradeName) + req.setValue(HTTPWSHeader.connectionValue, forHTTPHeaderField: HTTPWSHeader.connectionName) + req.setValue(HTTPWSHeader.versionValue, forHTTPHeaderField: HTTPWSHeader.versionName) + req.setValue(secKeyValue, forHTTPHeaderField: HTTPWSHeader.keyName) + + if let cookies = HTTPCookieStorage.shared.cookies(for: url), !cookies.isEmpty { + let headers = HTTPCookie.requestHeaderFields(with: cookies) + for (key, val) in headers { + req.setValue(val, forHTTPHeaderField: key) + } + } + + if supportsCompression { + let val = "permessage-deflate; client_max_window_bits; server_max_window_bits=15" + req.setValue(val, forHTTPHeaderField: HTTPWSHeader.extensionName) + } + let hostValue = req.allHTTPHeaderFields?[HTTPWSHeader.hostName] ?? "\(parts.host):\(parts.port)" + req.setValue(hostValue, forHTTPHeaderField: HTTPWSHeader.hostName) + return req + } + + // generateWebSocketKey 16 random characters between a-z and return them as a base64 string + public static func generateWebSocketKey() -> String { + return Data((0..<16).map{ _ in UInt8.random(in: 97...122) }).base64EncodedString() + } +} + +public enum HTTPEvent { + case success([String: String]) + case failure(Error) +} + +public protocol HTTPHandlerDelegate: class { + func didReceiveHTTP(event: HTTPEvent) +} + +public protocol HTTPHandler { + func register(delegate: HTTPHandlerDelegate) + func convert(request: URLRequest) -> Data + func parse(data: Data) -> Int +} + +public protocol HTTPServerDelegate: class { + func didReceive(event: HTTPEvent) +} + +public protocol HTTPServerHandler { + func register(delegate: HTTPServerDelegate) + func parse(data: Data) + func createResponse(headers: [String: String]) -> Data +} + +public struct URLParts { + let port: Int + let host: String + let isTLS: Bool +} + +public extension URL { + /// isTLSScheme returns true if the scheme is https or wss + var isTLSScheme: Bool { + guard let scheme = self.scheme else { + return false + } + return HTTPWSHeader.defaultSSLSchemes.contains(scheme) + } + + /// getParts pulls host and port from the url. + func getParts() -> URLParts? { + guard let host = self.host else { + return nil // no host, this isn't a valid url + } + let isTLS = isTLSScheme + var port = self.port ?? 0 + if self.port == nil { + if isTLS { + port = 443 + } else { + port = 80 + } + } + return URLParts(port: port, host: host, isTLS: isTLS) + } +} diff --git a/Vendors/spm/Starscream/Sources/Framer/StringHTTPHandler.swift b/Vendors/spm/Starscream/Sources/Framer/StringHTTPHandler.swift new file mode 100644 index 00000000..33f5f9be --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Framer/StringHTTPHandler.swift @@ -0,0 +1,143 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// StringHTTPHandler.swift +// Starscream +// +// Created by Dalton Cherry on 8/25/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +public class StringHTTPHandler: HTTPHandler { + + var buffer = Data() + weak var delegate: HTTPHandlerDelegate? + + public init() { + + } + + public func convert(request: URLRequest) -> Data { + guard let url = request.url else { + return Data() + } + + var path = url.absoluteString + let offset = (url.scheme?.count ?? 2) + 3 + path = String(path[path.index(path.startIndex, offsetBy: offset).. Int { + let offset = findEndOfHTTP(data: data) + if offset > 0 { + buffer.append(data.subdata(in: 0.. Bool { + guard let str = String(data: data, encoding: .utf8) else { + delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData)) + return true + } + let splitArr = str.components(separatedBy: "\r\n") + var code = -1 + var i = 0 + var headers = [String: String]() + for str in splitArr { + if i == 0 { + let responseSplit = str.components(separatedBy: .whitespaces) + guard responseSplit.count > 1 else { + delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData)) + return true + } + if let c = Int(responseSplit[1]) { + code = c + } + } else { + guard let separatorIndex = str.firstIndex(of: ":") else { break } + let key = str.prefix(upTo: separatorIndex).trimmingCharacters(in: .whitespaces) + let val = str.suffix(from: str.index(after: separatorIndex)).trimmingCharacters(in: .whitespaces) + headers[key.lowercased()] = val + } + i += 1 + } + + if code != HTTPWSHeader.switchProtocolCode { + delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.notAnUpgrade(code))) + return true + } + + delegate?.didReceiveHTTP(event: .success(headers)) + return true + } + + public func register(delegate: HTTPHandlerDelegate) { + self.delegate = delegate + } + + private func findEndOfHTTP(data: Data) -> Int { + let endBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")] + var pointer = [UInt8]() + data.withUnsafeBytes { pointer.append(contentsOf: $0) } + var k = 0 + for i in 0.. + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Vendors/spm/Starscream/Sources/Security/FoundationSecurity.swift b/Vendors/spm/Starscream/Sources/Security/FoundationSecurity.swift new file mode 100644 index 00000000..c4dde8ce --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Security/FoundationSecurity.swift @@ -0,0 +1,101 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// FoundationSecurity.swift +// Starscream +// +// Created by Dalton Cherry on 3/16/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 +import CommonCrypto + +public enum FoundationSecurityError: Error { + case invalidRequest +} + +public class FoundationSecurity { + var allowSelfSigned = false + + public init(allowSelfSigned: Bool = false) { + self.allowSelfSigned = allowSelfSigned + } + + +} + +extension FoundationSecurity: CertificatePinning { + public func evaluateTrust(trust: SecTrust, domain: String?, completion: ((PinningState) -> ())) { + if allowSelfSigned { + completion(.success) + return + } + + if let validateDomain = domain { + SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, validateDomain as NSString?)) + } + + handleSecurityTrust(trust: trust, completion: completion) + } + + private func handleSecurityTrust(trust: SecTrust, completion: ((PinningState) -> ())) { + if #available(iOS 12.0, OSX 10.14, watchOS 5.0, tvOS 12.0, *) { + var error: CFError? + if SecTrustEvaluateWithError(trust, &error) { + completion(.success) + } else { + completion(.failed(error)) + } + } else { + handleOldSecurityTrust(trust: trust, completion: completion) + } + } + + private func handleOldSecurityTrust(trust: SecTrust, completion: ((PinningState) -> ())) { + var result: SecTrustResultType = .unspecified + SecTrustEvaluate(trust, &result) + if result == .unspecified || result == .proceed { + completion(.success) + } else { + let e = CFErrorCreate(kCFAllocatorDefault, "FoundationSecurityError" as NSString?, Int(result.rawValue), nil) + completion(.failed(e)) + } + } +} + +extension FoundationSecurity: HeaderValidator { + public func validate(headers: [String: String], key: String) -> Error? { + if let acceptKey = headers[HTTPWSHeader.acceptName] { + let sha = "\(key)258EAFA5-E914-47DA-95CA-C5AB0DC85B11".sha1Base64() + if sha != acceptKey { + return WSError(type: .securityError, message: "accept header doesn't match", code: SecurityErrorCode.acceptFailed.rawValue) + } + } + return nil + } +} + +private extension String { + func sha1Base64() -> String { + let data = self.data(using: .utf8)! + let pointer = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in + var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) + CC_SHA1(bytes.baseAddress, CC_LONG(data.count), &digest) + return digest + } + return Data(pointer).base64EncodedString() + } +} diff --git a/Vendors/spm/Starscream/Sources/Security/Security.swift b/Vendors/spm/Starscream/Sources/Security/Security.swift new file mode 100644 index 00000000..a64a7713 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Security/Security.swift @@ -0,0 +1,45 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Security.swift +// Starscream +// +// Created by Dalton Cherry on 3/16/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +public enum SecurityErrorCode: UInt16 { + case acceptFailed = 1 + case pinningFailed = 2 +} + +public enum PinningState { + case success + case failed(CFError?) +} + +// CertificatePinning protocol provides an interface for Transports to handle Certificate +// or Public Key Pinning. +public protocol CertificatePinning: class { + func evaluateTrust(trust: SecTrust, domain: String?, completion: ((PinningState) -> ())) +} + +// validates the "Sec-WebSocket-Accept" header as defined 1.3 of the RFC 6455 +// https://tools.ietf.org/html/rfc6455#section-1.3 +public protocol HeaderValidator: class { + func validate(headers: [String: String], key: String) -> Error? +} diff --git a/Vendors/spm/Starscream/Sources/Server/Server.swift b/Vendors/spm/Starscream/Sources/Server/Server.swift new file mode 100644 index 00000000..527c851d --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Server/Server.swift @@ -0,0 +1,56 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Server.swift +// Starscream +// +// Created by Dalton Cherry on 4/2/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +public enum ConnectionEvent { + case connected([String: String]) + case disconnected(String, UInt16) + case text(String) + case binary(Data) + case pong(Data?) + case ping(Data?) + case error(Error) +} + +public protocol Connection { + func write(data: Data, opcode: FrameOpCode) +} + +public protocol ConnectionDelegate: class { + func didReceive(event: ServerEvent) +} + +public enum ServerEvent { + case connected(Connection, [String: String]) + case disconnected(Connection, String, UInt16) + case text(Connection, String) + case binary(Connection, Data) + case pong(Connection, Data?) + case ping(Connection, Data?) +} + +public protocol Server { + func start(address: String, port: UInt16) -> Error? +} + + diff --git a/Vendors/spm/Starscream/Sources/Server/WebSocketServer.swift b/Vendors/spm/Starscream/Sources/Server/WebSocketServer.swift new file mode 100644 index 00000000..4d3b9af5 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Server/WebSocketServer.swift @@ -0,0 +1,196 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// WebSocketServer.swift +// Starscream +// +// Created by Dalton Cherry on 4/5/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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. +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +#if canImport(Network) +import Foundation +import Network + +/// WebSocketServer is a Network.framework implementation of a WebSocket server +@available(watchOS, unavailable) +@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) +public class WebSocketServer: Server, ConnectionDelegate { + public var onEvent: ((ServerEvent) -> Void)? + private var connections = [String: ServerConnection]() + private var listener: NWListener? + private let queue = DispatchQueue(label: "com.vluxe.starscream.server.networkstream", attributes: []) + + public init() { + + } + + public func start(address: String, port: UInt16) -> Error? { + //TODO: support TLS cert adding/binding + let parameters = NWParameters(tls: nil, tcp: NWProtocolTCP.Options()) + let p = NWEndpoint.Port(rawValue: port)! + parameters.requiredLocalEndpoint = NWEndpoint.hostPort(host: NWEndpoint.Host.name(address, nil), port: p) + + guard let listener = try? NWListener(using: parameters, on: p) else { + return WSError(type: .serverError, message: "unable to start the listener at: \(address):\(port)", code: 0) + } + listener.newConnectionHandler = {[weak self] conn in + let transport = TCPTransport(connection: conn) + let c = ServerConnection(transport: transport) + c.delegate = self + self?.connections[c.uuid] = c + } +// listener.stateUpdateHandler = { state in +// switch state { +// case .ready: +// print("ready to get sockets!") +// case .setup: +// print("setup to get sockets!") +// case .cancelled: +// print("server cancelled!") +// case .waiting(let error): +// print("waiting error: \(error)") +// case .failed(let error): +// print("server failed: \(error)") +// @unknown default: +// print("wat?") +// } +// } + self.listener = listener + listener.start(queue: queue) + return nil + } + + public func didReceive(event: ServerEvent) { + onEvent?(event) + switch event { + case .disconnected(let conn, _, _): + guard let conn = conn as? ServerConnection else { + return + } + connections.removeValue(forKey: conn.uuid) + default: + break + } + } +} + +@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) +public class ServerConnection: Connection, HTTPServerDelegate, FramerEventClient, FrameCollectorDelegate, TransportEventClient { + let transport: TCPTransport + private let httpHandler = FoundationHTTPServerHandler() + private let framer = WSFramer(isServer: true) + private let frameHandler = FrameCollector() + private var didUpgrade = false + public var onEvent: ((ConnectionEvent) -> Void)? + public weak var delegate: ConnectionDelegate? + private let id: String + var uuid: String { + return id + } + + init(transport: TCPTransport) { + self.id = UUID().uuidString + self.transport = transport + transport.register(delegate: self) + httpHandler.register(delegate: self) + framer.register(delegate: self) + frameHandler.delegate = self + } + + public func write(data: Data, opcode: FrameOpCode) { + let wsData = framer.createWriteFrame(opcode: opcode, payload: data, isCompressed: false) + transport.write(data: wsData, completion: {_ in }) + } + + // MARK: - TransportEventClient + + public func connectionChanged(state: ConnectionState) { + switch state { + case .connected: + break + case .waiting: + break + case .failed(let error): + print("server connection error: \(error ?? WSError(type: .protocolError, message: "default error, no extra data", code: 0))") //handleError(error) + case .viability(_): + break + case .shouldReconnect(_): + break + case .receive(let data): + if didUpgrade { + framer.add(data: data) + } else { + httpHandler.parse(data: data) + } + case .cancelled: + print("server connection cancelled!") + //broadcast(event: .cancelled) + } + } + + /// MARK: - HTTPServerDelegate + + public func didReceive(event: HTTPEvent) { + switch event { + case .success(let headers): + didUpgrade = true + let response = httpHandler.createResponse(headers: [:]) + transport.write(data: response, completion: {_ in }) + delegate?.didReceive(event: .connected(self, headers)) + onEvent?(.connected(headers)) + case .failure(let error): + onEvent?(.error(error)) + } + } + + /// MARK: - FrameCollectorDelegate + + public func frameProcessed(event: FrameEvent) { + switch event { + case .frame(let frame): + frameHandler.add(frame: frame) + case .error(let error): + onEvent?(.error(error)) + } + } + + public func didForm(event: FrameCollector.Event) { + switch event { + case .text(let string): + delegate?.didReceive(event: .text(self, string)) + onEvent?(.text(string)) + case .binary(let data): + delegate?.didReceive(event: .binary(self, data)) + onEvent?(.binary(data)) + case .pong(let data): + delegate?.didReceive(event: .pong(self, data)) + onEvent?(.pong(data)) + case .ping(let data): + delegate?.didReceive(event: .ping(self, data)) + onEvent?(.ping(data)) + case .closed(let reason, let code): + delegate?.didReceive(event: .disconnected(self, reason, code)) + onEvent?(.disconnected(reason, code)) + case .error(let error): + onEvent?(.error(error)) + } + } + + public func decompress(data: Data, isFinal: Bool) -> Data? { + return nil + } +} +#endif diff --git a/Vendors/spm/Starscream/Sources/Starscream.h b/Vendors/spm/Starscream/Sources/Starscream.h new file mode 100644 index 00000000..38ff9dfb --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Starscream.h @@ -0,0 +1,19 @@ +// +// Starscream.h +// Starscream +// +// Created by Austin Cherry on 9/25/14. +// Copyright (c) 2014 Vluxe. All rights reserved. +// + +#import + +//! Project version number for Starscream. +FOUNDATION_EXPORT double StarscreamVersionNumber; + +//! Project version string for Starscream. +FOUNDATION_EXPORT const unsigned char StarscreamVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Vendors/spm/Starscream/Sources/Starscream/WebSocket.swift b/Vendors/spm/Starscream/Sources/Starscream/WebSocket.swift new file mode 100644 index 00000000..1d3545c3 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Starscream/WebSocket.swift @@ -0,0 +1,178 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Websocket.swift +// Starscream +// +// Created by Dalton Cherry on 7/16/14. +// Copyright (c) 2014-2019 Dalton Cherry. +// +// 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 + +public enum ErrorType: Error { + case compressionError + case securityError + case protocolError //There was an error parsing the WebSocket frames + case serverError +} + +public struct WSError: Error { + public let type: ErrorType + public let message: String + public let code: UInt16 + + public init(type: ErrorType, message: String, code: UInt16) { + self.type = type + self.message = message + self.code = code + } +} + +public protocol WebSocketClient: class { + func connect() + func disconnect(closeCode: UInt16) + func write(string: String, completion: (() -> ())?) + func write(stringData: Data, completion: (() -> ())?) + func write(data: Data, completion: (() -> ())?) + func write(ping: Data, completion: (() -> ())?) + func write(pong: Data, completion: (() -> ())?) +} + +//implements some of the base behaviors +extension WebSocketClient { + public func write(string: String) { + write(string: string, completion: nil) + } + + public func write(data: Data) { + write(data: data, completion: nil) + } + + public func write(ping: Data) { + write(ping: ping, completion: nil) + } + + public func write(pong: Data) { + write(pong: pong, completion: nil) + } + + public func disconnect() { + disconnect(closeCode: CloseCode.normal.rawValue) + } +} + +public enum WebSocketEvent { + case connected([String: String]) + case disconnected(String, UInt16) + case text(String) + case binary(Data) + case pong(Data?) + case ping(Data?) + case error(Error?) + case viabilityChanged(Bool) + case reconnectSuggested(Bool) + case cancelled +} + +public protocol WebSocketDelegate: class { + func didReceive(event: WebSocketEvent, client: WebSocket) +} + +open class WebSocket: WebSocketClient, EngineDelegate { + private let engine: Engine + public weak var delegate: WebSocketDelegate? + public var onEvent: ((WebSocketEvent) -> Void)? + + public var request: URLRequest + // Where the callback is executed. It defaults to the main UI thread queue. + public var callbackQueue = DispatchQueue.main + public var respondToPingWithPong: Bool { + set { + guard let e = engine as? WSEngine else { return } + e.respondToPingWithPong = newValue + } + get { + guard let e = engine as? WSEngine else { return true } + return e.respondToPingWithPong + } + } + + // serial write queue to ensure writes happen in order + private let writeQueue = DispatchQueue(label: "com.vluxe.starscream.writequeue") + private var canSend = false + private let mutex = DispatchSemaphore(value: 1) + + public init(request: URLRequest, engine: Engine) { + self.request = request + self.engine = engine + } + + public convenience init(request: URLRequest, certPinner: CertificatePinning? = FoundationSecurity(), compressionHandler: CompressionHandler? = nil, useCustomEngine: Bool = true) { + if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *), !useCustomEngine { + self.init(request: request, engine: NativeEngine()) + } else if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) { + self.init(request: request, engine: WSEngine(transport: TCPTransport(), certPinner: certPinner, compressionHandler: compressionHandler)) + } else { + self.init(request: request, engine: WSEngine(transport: FoundationTransport(), certPinner: certPinner, compressionHandler: compressionHandler)) + } + } + + public func connect() { + engine.register(delegate: self) + engine.start(request: request) + } + + public func disconnect(closeCode: UInt16 = CloseCode.normal.rawValue) { + engine.stop(closeCode: closeCode) + } + + public func forceDisconnect() { + engine.forceStop() + } + + public func write(data: Data, completion: (() -> ())?) { + write(data: data, opcode: .binaryFrame, completion: completion) + } + + public func write(string: String, completion: (() -> ())?) { + engine.write(string: string, completion: completion) + } + + public func write(stringData: Data, completion: (() -> ())?) { + write(data: stringData, opcode: .textFrame, completion: completion) + } + + public func write(ping: Data, completion: (() -> ())?) { + write(data: ping, opcode: .ping, completion: completion) + } + + public func write(pong: Data, completion: (() -> ())?) { + write(data: pong, opcode: .pong, completion: completion) + } + + private func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) { + engine.write(data: data, opcode: opcode, completion: completion) + } + + // MARK: - EngineDelegate + public func didReceive(event: WebSocketEvent) { + callbackQueue.async { [weak self] in + guard let s = self else { return } + s.delegate?.didReceive(event: event, client: s) + s.onEvent?(event) + } + } +} diff --git a/Vendors/spm/Starscream/Sources/Transport/FoundationTransport.swift b/Vendors/spm/Starscream/Sources/Transport/FoundationTransport.swift new file mode 100644 index 00000000..8d304f88 --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Transport/FoundationTransport.swift @@ -0,0 +1,218 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// FoundationTransport.swift +// Starscream +// +// Created by Dalton Cherry on 1/23/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +public enum FoundationTransportError: Error { + case invalidRequest + case invalidOutputStream + case timeout +} + +public class FoundationTransport: NSObject, Transport, StreamDelegate { + private weak var delegate: TransportEventClient? + private let workQueue = DispatchQueue(label: "com.vluxe.starscream.websocket", attributes: []) + private var inputStream: InputStream? + private var outputStream: OutputStream? + private var isOpen = false + private var onConnect: ((InputStream, OutputStream) -> Void)? + private var isTLS = false + private var certPinner: CertificatePinning? + + public var usingTLS: Bool { + return self.isTLS + } + + public init(streamConfiguration: ((InputStream, OutputStream) -> Void)? = nil) { + super.init() + onConnect = streamConfiguration + } + + deinit { + inputStream?.delegate = nil + outputStream?.delegate = nil + } + + public func connect(url: URL, timeout: Double = 10, certificatePinning: CertificatePinning? = nil) { + guard let parts = url.getParts() else { + delegate?.connectionChanged(state: .failed(FoundationTransportError.invalidRequest)) + return + } + self.certPinner = certificatePinning + self.isTLS = parts.isTLS + var readStream: Unmanaged? + var writeStream: Unmanaged? + let h = parts.host as NSString + CFStreamCreatePairWithSocketToHost(nil, h, UInt32(parts.port), &readStream, &writeStream) + inputStream = readStream!.takeRetainedValue() + outputStream = writeStream!.takeRetainedValue() + guard let inStream = inputStream, let outStream = outputStream else { + return + } + inStream.delegate = self + outStream.delegate = self + + if isTLS { + let key = CFStreamPropertyKey(rawValue: kCFStreamPropertySocketSecurityLevel) + CFReadStreamSetProperty(inStream, key, kCFStreamSocketSecurityLevelNegotiatedSSL) + CFWriteStreamSetProperty(outStream, key, kCFStreamSocketSecurityLevelNegotiatedSSL) + } + + onConnect?(inStream, outStream) + + isOpen = false + CFReadStreamSetDispatchQueue(inStream, workQueue) + CFWriteStreamSetDispatchQueue(outStream, workQueue) + inStream.open() + outStream.open() + + + workQueue.asyncAfter(deadline: .now() + timeout, execute: { [weak self] in + guard let s = self else { return } + if !s.isOpen { + s.delegate?.connectionChanged(state: .failed(FoundationTransportError.timeout)) + } + }) + } + + public func disconnect() { + if let stream = inputStream { + stream.delegate = nil + CFReadStreamSetDispatchQueue(stream, nil) + stream.close() + } + if let stream = outputStream { + stream.delegate = nil + CFWriteStreamSetDispatchQueue(stream, nil) + stream.close() + } + isOpen = false + outputStream = nil + inputStream = nil + } + + public func register(delegate: TransportEventClient) { + self.delegate = delegate + } + + public func write(data: Data, completion: @escaping ((Error?) -> ())) { + guard let outStream = outputStream else { + completion(FoundationTransportError.invalidOutputStream) + return + } + var total = 0 + let buffer = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self) + //NOTE: this might need to be dispatched to the work queue instead of being written inline. TBD. + while total < data.count { + let written = outStream.write(buffer, maxLength: data.count) + if written < 0 { + completion(FoundationTransportError.invalidOutputStream) + return + } + total += written + } + completion(nil) + } + + private func getSecurityData() -> (SecTrust?, String?) { + #if os(watchOS) + return (nil, nil) + #else + guard let outputStream = outputStream else { + return (nil, nil) + } + let trust = outputStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust? + var domain = outputStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as! String? + + if domain == nil, + let sslContextOut = CFWriteStreamCopyProperty(outputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext? { + var peerNameLen: Int = 0 + SSLGetPeerDomainNameLength(sslContextOut, &peerNameLen) + var peerName = Data(count: peerNameLen) + let _ = peerName.withUnsafeMutableBytes { (peerNamePtr: UnsafeMutablePointer) in + SSLGetPeerDomainName(sslContextOut, peerNamePtr, &peerNameLen) + } + if let peerDomain = String(bytes: peerName, encoding: .utf8), peerDomain.count > 0 { + domain = peerDomain + } + } + return (trust, domain) + #endif + } + + private func read() { + guard let stream = inputStream else { + return + } + let maxBuffer = 4096 + let buf = NSMutableData(capacity: maxBuffer) + let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self) + let length = stream.read(buffer, maxLength: maxBuffer) + if length < 1 { + return + } + let data = Data(bytes: buffer, count: length) + delegate?.connectionChanged(state: .receive(data)) + } + + // MARK: - StreamDelegate + + open func stream(_ aStream: Stream, handle eventCode: Stream.Event) { + switch eventCode { + case .hasBytesAvailable: + if aStream == inputStream { + read() + } + case .errorOccurred: + delegate?.connectionChanged(state: .failed(aStream.streamError)) + case .endEncountered: + if aStream == inputStream { + delegate?.connectionChanged(state: .cancelled) + } + case .openCompleted: + if aStream == inputStream { + let (trust, domain) = getSecurityData() + if let pinner = certPinner, let trust = trust { + pinner.evaluateTrust(trust: trust, domain: domain, completion: { [weak self] (state) in + switch state { + case .success: + self?.isOpen = true + self?.delegate?.connectionChanged(state: .connected) + case .failed(let error): + self?.delegate?.connectionChanged(state: .failed(error)) + } + + }) + } else { + isOpen = true + delegate?.connectionChanged(state: .connected) + } + } + case .endEncountered: + if aStream == inputStream { + delegate?.connectionChanged(state: .cancelled) + } + default: + break + } + } +} diff --git a/Vendors/spm/Starscream/Sources/Transport/TCPTransport.swift b/Vendors/spm/Starscream/Sources/Transport/TCPTransport.swift new file mode 100644 index 00000000..459cb2ed --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Transport/TCPTransport.swift @@ -0,0 +1,159 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// HTTPTransport.swift +// Starscream +// +// Created by Dalton Cherry on 1/23/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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. +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +#if canImport(Network) +import Foundation +import Network + +public enum TCPTransportError: Error { + case invalidRequest +} + +@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) +public class TCPTransport: Transport { + private var connection: NWConnection? + private let queue = DispatchQueue(label: "com.vluxe.starscream.networkstream", attributes: []) + private weak var delegate: TransportEventClient? + private var isRunning = false + private var isTLS = false + + public var usingTLS: Bool { + return self.isTLS + } + + public init(connection: NWConnection) { + self.connection = connection + start() + } + + public init() { + //normal connection, will use the "connect" method below + } + + public func connect(url: URL, timeout: Double = 10, certificatePinning: CertificatePinning? = nil) { + guard let parts = url.getParts() else { + delegate?.connectionChanged(state: .failed(TCPTransportError.invalidRequest)) + return + } + self.isTLS = parts.isTLS + let options = NWProtocolTCP.Options() + options.connectionTimeout = Int(timeout.rounded(.up)) + + let tlsOptions = isTLS ? NWProtocolTLS.Options() : nil + if let tlsOpts = tlsOptions { + sec_protocol_options_set_verify_block(tlsOpts.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in + let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue() + guard let pinner = certificatePinning else { + sec_protocol_verify_complete(true) + return + } + pinner.evaluateTrust(trust: trust, domain: parts.host, completion: { (state) in + switch state { + case .success: + sec_protocol_verify_complete(true) + case .failed(_): + sec_protocol_verify_complete(false) + } + }) + }, queue) + } + let parameters = NWParameters(tls: tlsOptions, tcp: options) + let conn = NWConnection(host: NWEndpoint.Host.name(parts.host, nil), port: NWEndpoint.Port(rawValue: UInt16(parts.port))!, using: parameters) + connection = conn + start() + } + + public func disconnect() { + isRunning = false + connection?.cancel() + } + + public func register(delegate: TransportEventClient) { + self.delegate = delegate + } + + public func write(data: Data, completion: @escaping ((Error?) -> ())) { + connection?.send(content: data, completion: .contentProcessed { (error) in + completion(error) + }) + } + + private func start() { + guard let conn = connection else { + return + } + conn.stateUpdateHandler = { [weak self] (newState) in + switch newState { + case .ready: + self?.delegate?.connectionChanged(state: .connected) + case .waiting: + self?.delegate?.connectionChanged(state: .waiting) + case .cancelled: + self?.delegate?.connectionChanged(state: .cancelled) + case .failed(let error): + self?.delegate?.connectionChanged(state: .failed(error)) + case .setup, .preparing: + break + @unknown default: + break + } + } + + conn.viabilityUpdateHandler = { [weak self] (isViable) in + self?.delegate?.connectionChanged(state: .viability(isViable)) + } + + conn.betterPathUpdateHandler = { [weak self] (isBetter) in + self?.delegate?.connectionChanged(state: .shouldReconnect(isBetter)) + } + + conn.start(queue: queue) + isRunning = true + readLoop() + } + + //readLoop keeps reading from the connection to get the latest content + private func readLoop() { + if !isRunning { + return + } + connection?.receive(minimumIncompleteLength: 2, maximumLength: 4096, completion: {[weak self] (data, context, isComplete, error) in + guard let s = self else {return} + if let data = data { + s.delegate?.connectionChanged(state: .receive(data)) + } + + // Refer to https://developer.apple.com/documentation/network/implementing_netcat_with_network_framework + if let context = context, context.isFinal, isComplete { + return + } + + if error == nil { + s.readLoop() + } + + }) + } +} +#else +typealias TCPTransport = FoundationTransport +#endif diff --git a/Vendors/spm/Starscream/Sources/Transport/Transport.swift b/Vendors/spm/Starscream/Sources/Transport/Transport.swift new file mode 100644 index 00000000..e645651f --- /dev/null +++ b/Vendors/spm/Starscream/Sources/Transport/Transport.swift @@ -0,0 +1,55 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Transport.swift +// Starscream +// +// Created by Dalton Cherry on 1/23/19. +// Copyright © 2019 Vluxe. All rights reserved. +// +// 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 + +public enum ConnectionState { + case connected + case waiting + case cancelled + case failed(Error?) + + //the viability (connection status) of the connection has updated + //e.g. connection is down, connection came back up, etc + case viability(Bool) + + //the connection has upgrade to wifi from cellular. + //you should consider reconnecting to take advantage of this + case shouldReconnect(Bool) + + //the connection receive data + case receive(Data) +} + +public protocol TransportEventClient: class { + func connectionChanged(state: ConnectionState) +} + +public protocol Transport: class { + func register(delegate: TransportEventClient) + func connect(url: URL, timeout: Double, certificatePinning: CertificatePinning?) + func disconnect() + func write(data: Data, completion: @escaping ((Error?) -> ())) + var usingTLS: Bool { get } +} diff --git a/Vendors/spm/Starscream/Starscream.podspec b/Vendors/spm/Starscream/Starscream.podspec new file mode 100644 index 00000000..e470d01f --- /dev/null +++ b/Vendors/spm/Starscream/Starscream.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = "Starscream" + s.version = "4.0.4" + s.summary = "A conforming WebSocket RFC 6455 client library in Swift." + s.homepage = "https://github.com/daltoniam/Starscream" + s.license = 'Apache License, Version 2.0' + s.author = {'Dalton Cherry' => 'http://daltoniam.com', 'Austin Cherry' => 'http://austincherry.me'} + s.source = { :git => 'https://github.com/daltoniam/Starscream.git', :tag => "#{s.version}"} + s.social_media_url = 'http://twitter.com/daltoniam' + s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.10' + s.tvos.deployment_target = '9.0' + s.watchos.deployment_target = '2.0' + s.source_files = 'Sources/**/*.swift' + s.swift_version = '5.0' +end diff --git a/Vendors/spm/Starscream/Tests/CompressionTests.swift b/Vendors/spm/Starscream/Tests/CompressionTests.swift new file mode 100644 index 00000000..d62560bd --- /dev/null +++ b/Vendors/spm/Starscream/Tests/CompressionTests.swift @@ -0,0 +1,67 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// CompressionTests.swift +// +// Created by Joseph Ross on 7/16/14. +// Copyright © 2017 Joseph Ross. +// +// 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 XCTest +@testable import Starscream + +class CompressionTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testBasic() { + let compressor = Compressor(windowBits: 15)! + let decompressor = Decompressor(windowBits: 15)! + + let rawData = "Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!".data(using: .utf8)! + + let compressed = try! compressor.compress(rawData) + let uncompressed = try! decompressor.decompress(compressed, finish: true) + + XCTAssert(rawData == uncompressed) + } + + func testHugeData() { + let compressor = Compressor(windowBits: 15)! + let decompressor = Decompressor(windowBits: 15)! + + // 2 Gigs! +// var rawData = Data(repeating: 0, count: 0x80000000) + var rawData = Data(repeating: 0, count: 0x80000) + let rawDataLen = rawData.count + rawData.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer) -> Void in + arc4random_buf(ptr, rawDataLen) + } + + let compressed = try! compressor.compress(rawData) + let uncompressed = try! decompressor.decompress(compressed, finish: true) + + XCTAssert(rawData == uncompressed) + } + +} diff --git a/Vendors/spm/Starscream/Tests/FuzzingTests.swift b/Vendors/spm/Starscream/Tests/FuzzingTests.swift new file mode 100644 index 00000000..a6c94451 --- /dev/null +++ b/Vendors/spm/Starscream/Tests/FuzzingTests.swift @@ -0,0 +1,185 @@ +// +// FuzzingTests.swift +// Starscream +// +// Created by Dalton Cherry on 1/28/19. +// Copyright © 2019 Vluxe. All rights reserved. +// + +import XCTest +@testable import Starscream + +class FuzzingTests: XCTestCase { + + var websocket: WebSocket! + var server: MockServer! + var uuid = "" + + override func setUp() { + super.setUp() + + let s = MockServer() + let _ = s.start(address: "", port: 0) + server = s + + let transport = MockTransport(server: s) + uuid = transport.uuid + + let url = URL(string: "http://vluxe.io/ws")! //domain doesn't matter with the mock transport + let request = URLRequest(url: url) + websocket = WebSocket(request: request, engine: WSEngine(transport: transport)) + + } + + override func tearDown() { + super.tearDown() + } + + func runWebsocket(timeout: TimeInterval = 10, serverAction: @escaping ((ServerEvent) -> Bool)) { + let e = expectation(description: "Websocket event timeout") + server.onEvent = { event in + let done = serverAction(event) + if done { + e.fulfill() + } + } + + websocket.onEvent = { event in + switch event { + case .text(let string): + self.websocket.write(string: string) + case .binary(let data): + self.websocket.write(data: data) + case .ping(_): + break + case .pong(_): + break + case .connected(_): + break + case .disconnected(let reason, let code): + print("reason: \(reason) code: \(code)") + case .error(_): + break + case .viabilityChanged(_): + break + case .reconnectSuggested(_): + break + case .cancelled: + break + } + } + websocket.connect() + waitForExpectations(timeout: timeout) { error in + if let error = error { + XCTFail("waitForExpectationsWithTimeout errored: \(error)") + } + } + } + + func sendMessage(string: String, isBinary: Bool) { + let payload = string.data(using: .utf8)! + let code: FrameOpCode = isBinary ? .binaryFrame : .textFrame + runWebsocket { event in + switch event { + case .connected(let conn, _): + conn.write(data: payload, opcode: code) + case .text(let conn, let text): + if text == string && !isBinary { + conn.write(data: Data(), opcode: .connectionClose) + return true //success! + } else { + XCTFail("text does not match: source: [\(string)] response: [\(text)]") + } + case .binary(let conn, let data): + if payload.count == data.count && isBinary { + conn.write(data: Data(), opcode: .connectionClose) + return true //success! + } else { + XCTFail("binary does not match: source: [\(payload.count)] response: [\(data.count)]") + } + case .disconnected(_, _, _): + return false + default: + XCTFail("recieved unexpected server event: \(event)") + } + return false + } + } + + //These are the Autobahn test cases as unit tests + + + /// MARK : - Framing cases + + // case 1.1.1 + func testCase1() { + sendMessage(string: "", isBinary: false) + } + + // case 1.1.2 + func testCase2() { + sendMessage(string: String(repeating: "*", count: 125), isBinary: false) + } + + // case 1.1.3 + func testCase3() { + sendMessage(string: String(repeating: "*", count: 126), isBinary: false) + } + + // case 1.1.4 + func testCase4() { + sendMessage(string: String(repeating: "*", count: 127), isBinary: false) + } + + // case 1.1.5 + func testCase5() { + sendMessage(string: String(repeating: "*", count: 128), isBinary: false) + } + + // case 1.1.6 + func testCase6() { + sendMessage(string: String(repeating: "*", count: 65535), isBinary: false) + } + + // case 1.1.7, 1.1.8 + func testCase7() { + sendMessage(string: String(repeating: "*", count: 65536), isBinary: false) + } + + // case 1.2.1 + func testCase9() { + sendMessage(string: "", isBinary: true) + } + + // case 1.2.2 + func testCase10() { + sendMessage(string: String(repeating: "*", count: 125), isBinary: true) + } + + // case 1.2.3 + func testCase11() { + sendMessage(string: String(repeating: "*", count: 126), isBinary: true) + } + + // case 1.2.4 + func testCase12() { + sendMessage(string: String(repeating: "*", count: 127), isBinary: true) + } + + // case 1.2.5 + func testCase13() { + sendMessage(string: String(repeating: "*", count: 128), isBinary: true) + } + + // case 1.2.6 + func testCase14() { + sendMessage(string: String(repeating: "*", count: 65535), isBinary: true) + } + + // case 1.2.7, 1.2.8 + func testCase15() { + sendMessage(string: String(repeating: "*", count: 65536), isBinary: true) + } + + //TODO: the rest of them. +} diff --git a/macOS/OpenVPN/Info.plist b/Vendors/spm/Starscream/Tests/Info.plist similarity index 61% rename from macOS/OpenVPN/Info.plist rename to Vendors/spm/Starscream/Tests/Info.plist index defd4aac..ba72822e 100644 --- a/macOS/OpenVPN/Info.plist +++ b/Vendors/spm/Starscream/Tests/Info.plist @@ -3,9 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - openvpn + en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,17 +13,12 @@ CFBundleName $(PRODUCT_NAME) CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) + BNDL CFBundleShortVersionString 1.0 + CFBundleSignature + ???? CFBundleVersion 1 - NSExtension - - NSExtensionPointIdentifier - com.apple.networkextension.packet-tunnel - NSExtensionPrincipalClass - $(PRODUCT_MODULE_NAME).PacketTunnelProvider - diff --git a/Vendors/spm/Starscream/Tests/MockServer.swift b/Vendors/spm/Starscream/Tests/MockServer.swift new file mode 100644 index 00000000..95187e89 --- /dev/null +++ b/Vendors/spm/Starscream/Tests/MockServer.swift @@ -0,0 +1,130 @@ +// +// MockServer.swift +// Starscream +// +// Created by Dalton Cherry on 1/29/19. +// Copyright © 2019 Vluxe. All rights reserved. +// + +import Foundation +@testable import Starscream + +public class MockConnection: Connection, HTTPServerDelegate, FramerEventClient, FrameCollectorDelegate { + let transport: MockTransport + private let httpHandler = FoundationHTTPServerHandler() + private let framer = WSFramer(isServer: true) + private let frameHandler = FrameCollector() + private var didUpgrade = false + public var onEvent: ((ConnectionEvent) -> Void)? + fileprivate weak var delegate: ConnectionDelegate? + + init(transport: MockTransport) { + self.transport = transport + httpHandler.register(delegate: self) + framer.register(delegate: self) + frameHandler.delegate = self + } + + func add(data: Data) { + if !didUpgrade { + httpHandler.parse(data: data) + } else { + framer.add(data: data) + } + } + + public func write(data: Data, opcode: FrameOpCode) { + let wsData = framer.createWriteFrame(opcode: opcode, payload: data, isCompressed: false) + transport.received(data: wsData) + } + + /// MARK: - HTTPServerDelegate + + public func didReceive(event: HTTPEvent) { + switch event { + case .success(let headers): + didUpgrade = true + //TODO: add headers and key check? + let response = httpHandler.createResponse(headers: [:]) + transport.received(data: response) + delegate?.didReceive(event: .connected(self, headers)) + onEvent?(.connected(headers)) + case .failure(let error): + onEvent?(.error(error)) + } + } + + /// MARK: - FrameCollectorDelegate + + public func frameProcessed(event: FrameEvent) { + switch event { + case .frame(let frame): + frameHandler.add(frame: frame) + case .error(let error): + onEvent?(.error(error)) + } + } + + public func didForm(event: FrameCollector.Event) { + switch event { + case .text(let string): + delegate?.didReceive(event: .text(self, string)) + onEvent?(.text(string)) + case .binary(let data): + delegate?.didReceive(event: .binary(self, data)) + onEvent?(.binary(data)) + case .pong(let data): + delegate?.didReceive(event: .pong(self, data)) + onEvent?(.pong(data)) + case .ping(let data): + delegate?.didReceive(event: .ping(self, data)) + onEvent?(.ping(data)) + case .closed(let reason, let code): + delegate?.didReceive(event: .disconnected(self, reason, code)) + onEvent?(.disconnected(reason, code)) + case .error(let error): + onEvent?(.error(error)) + } + } + + public func decompress(data: Data, isFinal: Bool) -> Data? { + return nil + } +} + + +public class MockServer: Server, ConnectionDelegate { + fileprivate var connections = [String: MockConnection]() + + public var onEvent: ((ServerEvent) -> Void)? + + public func start(address: String, port: UInt16) -> Error? { + return nil + } + + public func connect(transport: MockTransport) { + let conn = MockConnection(transport: transport) + conn.delegate = self + connections[transport.uuid] = conn + } + + public func disconnect(uuid: String) { +// guard let conn = connections[uuid] else { +// return +// } + //TODO: force disconnect + connections.removeValue(forKey: uuid) + } + + public func write(data: Data, uuid: String) { + guard let conn = connections[uuid] else { + return + } + conn.add(data: data) + } + + /// MARK: - MockConnectionDelegate + public func didReceive(event: ServerEvent) { + onEvent?(event) + } +} diff --git a/Vendors/spm/Starscream/Tests/MockTransport.swift b/Vendors/spm/Starscream/Tests/MockTransport.swift new file mode 100644 index 00000000..56993d55 --- /dev/null +++ b/Vendors/spm/Starscream/Tests/MockTransport.swift @@ -0,0 +1,65 @@ +// +// MockTransport.swift +// Starscream +// +// Created by Dalton Cherry on 1/28/19. +// Copyright © 2019 Vluxe. All rights reserved. +// + +import Foundation +@testable import Starscream + +public class MockTransport: Transport { + + public var usingTLS: Bool { + return false + } + private weak var delegate: TransportEventClient? + + private let id: String + weak var server: MockServer? + var uuid: String { + return id + } + + public init(server: MockServer) { + self.server = server + self.id = UUID().uuidString + } + + public func register(delegate: TransportEventClient) { + self.delegate = delegate + } + + public func connect(url: URL, timeout: Double, certificatePinning: CertificatePinning?) { + server?.connect(transport: self) + delegate?.connectionChanged(state: .connected) + } + + public func disconnect() { + server?.disconnect(uuid: uuid) + } + + public func write(data: Data, completion: @escaping ((Error?) -> ())) { + server?.write(data: data, uuid: uuid) + } + + public func received(data: Data) { + delegate?.connectionChanged(state: .receive(data)) + } + + public func getSecurityData() -> (SecTrust?, String?) { + return (nil, nil) + } +} + +public class MockSecurity: CertificatePinning, HeaderValidator { + + public func evaluateTrust(trust: SecTrust, domain: String?, completion: ((PinningState) -> ())) { + completion(.success) + } + + public func validate(headers: [String: String], key: String) -> Error? { + return nil + } +} diff --git a/Vendors/spm/Starscream/Tests/StarscreamTests/StarscreamTests.swift b/Vendors/spm/Starscream/Tests/StarscreamTests/StarscreamTests.swift new file mode 100644 index 00000000..b078f936 --- /dev/null +++ b/Vendors/spm/Starscream/Tests/StarscreamTests/StarscreamTests.swift @@ -0,0 +1,35 @@ +// +// StarscreamTests.swift +// StarscreamTests +// +// Created by Austin Cherry on 9/25/14. +// Copyright (c) 2014 Vluxe. All rights reserved. +// + +import XCTest + +class StarscreamTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + XCTAssert(true, "Pass") + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure() { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Vendors/spm/Starscream/build.sh b/Vendors/spm/Starscream/build.sh new file mode 100755 index 00000000..8f96b427 --- /dev/null +++ b/Vendors/spm/Starscream/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -o pipefail && xcodebuild -project Starscream.xcodeproj -scheme Starscream CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO build | xcpretty +swift build +pod repo update +pod lib lint --verbose --allow-warnings diff --git a/Vendors/spm/Starscream/examples/AutobahnTest/.gitignore b/Vendors/spm/Starscream/examples/AutobahnTest/.gitignore new file mode 100644 index 00000000..ad59cdc4 --- /dev/null +++ b/Vendors/spm/Starscream/examples/AutobahnTest/.gitignore @@ -0,0 +1,27 @@ +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? +# +# Pods/ + +# Xcode +.DS_Store +build +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +profile +*.moved-aside +DerivedData +.idea/ +*.hmap +*.xccheckout +*.xcodeproj/*.xcworkspace \ No newline at end of file diff --git a/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/AppDelegate.swift b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/AppDelegate.swift new file mode 100644 index 00000000..e8a252e6 --- /dev/null +++ b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// Autobahn +// +// Created by Dalton Cherry on 7/24/15. +// Copyright (c) 2015 vluxe. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Base.lproj/LaunchScreen.xib b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Base.lproj/LaunchScreen.xib new file mode 100644 index 00000000..0b8771f6 --- /dev/null +++ b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Base.lproj/LaunchScreen.xib @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Base.lproj/Main.storyboard b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Base.lproj/Main.storyboard new file mode 100644 index 00000000..c768e2eb --- /dev/null +++ b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Images.xcassets/AppIcon.appiconset/Contents.json b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Info.plist b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Info.plist new file mode 100644 index 00000000..4d851d95 --- /dev/null +++ b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.vluxe.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/ViewController.swift b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/ViewController.swift new file mode 100644 index 00000000..ce78e723 --- /dev/null +++ b/Vendors/spm/Starscream/examples/AutobahnTest/Autobahn/ViewController.swift @@ -0,0 +1,184 @@ +// +// ViewController.swift +// Autobahn +// +// Created by Dalton Cherry on 7/24/15. +// Copyright (c) 2015 vluxe. All rights reserved. +// + +import UIKit +import Starscream + +class ViewController: UIViewController { + + let host = "localhost:9001" + var socketArray = [WebSocket]() + var caseCount = 300 //starting cases + override func viewDidLoad() { + super.viewDidLoad() + getCaseCount() + //getTestInfo(1) + //runTest(304) + } + + func removeSocket(_ s: WebSocket?) { + guard let s = s else {return} + socketArray = socketArray.filter{$0 !== s} + } + + func getCaseCount() { + let req = URLRequest(url: URL(string: "ws://\(host)/getCaseCount")!) + let s = WebSocket(request: req) + socketArray.append(s) + s.onEvent = { [weak self] event in + switch event { + case .text(let string): + if let c = Int(string) { + print("number of cases is: \(c)") + self?.caseCount = c + } + case .disconnected(_, _): + self?.runTest(1) + self?.removeSocket(s) + default: + break + } + } + s.connect() + } + + func getTestInfo(_ caseNum: Int) { + let s = createSocket("getCaseInfo",caseNum) + socketArray.append(s) +// s.onText = { (text: String) in +// let data = text.dataUsingEncoding(NSUTF8StringEncoding) +// do { +// let resp: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data!, +// options: NSJSONReadingOptions()) +// if let dict = resp as? Dictionary { +// let num = dict["id"] +// let summary = dict["description"] +// if let n = num, let sum = summary { +// print("running case:\(caseNum) id:\(n) summary: \(sum)") +// } +// } +// } catch { +// print("error parsing the json") +// } + +// } + var once = false + s.onEvent = { [weak self] event in + switch event { + case .disconnected(_, _), .error(_): + if !once { + once = true + self?.runTest(caseNum) + } + self?.removeSocket(s) + default: + break + } + } + s.connect() + } + + func runTest(_ caseNum: Int) { + let s = createSocket("runCase",caseNum) + self.socketArray.append(s) + + var once = false + s.onEvent = { [weak self, weak s] event in + switch event { + case .disconnected(_, _), .error(_): + if !once { + once = true + print("case:\(caseNum) finished") + //self?.verifyTest(caseNum) //disabled since it slows down the tests + let nextCase = caseNum+1 + if nextCase <= (self?.caseCount)! { + self?.runTest(nextCase) + //self?.getTestInfo(nextCase) //disabled since it slows down the tests + } else { + self?.finishReports() + } + self?.removeSocket(s) + } + self?.removeSocket(s) + case .text(let string): + s?.write(string: string) + case .binary(let data): + s?.write(data: data) +// case .error(let error): +// print("got an error: \(error)") + default: + break + } + } + s.connect() + } + +// func verifyTest(_ caseNum: Int) { +// let s = createSocket("getCaseStatus",caseNum) +// self.socketArray.append(s) +// s.onText = { (text: String) in +// let data = text.data(using: String.Encoding.utf8) +// do { +// let resp: Any? = try JSONSerialization.jsonObject(with: data!, +// options: JSONSerialization.ReadingOptions()) +// if let dict = resp as? Dictionary { +// if let status = dict["behavior"] { +// if status == "OK" { +// print("SUCCESS: \(caseNum)") +// return +// } +// } +// print("FAILURE: \(caseNum)") +// } +// } catch { +// print("error parsing the json") +// } +// } +// var once = false +// s.onDisconnect = { [weak self, weak s] (error: Error?) in +// if !once { +// once = true +// let nextCase = caseNum+1 +// print("next test is: \(nextCase)") +// if nextCase <= (self?.caseCount)! { +// self?.getTestInfo(nextCase) +// } else { +// self?.finishReports() +// } +// } +// self?.removeSocket(s) +// } +// s.connect() +// } + + func finishReports() { + let s = createSocket("updateReports",0) + self.socketArray.append(s) + s.onEvent = { [weak self, weak s] event in + switch event { + case .disconnected(_, _): + print("finished all the tests!") + self?.removeSocket(s) + default: + break + } + } + s.connect() + } + + func createSocket(_ cmd: String, _ caseNum: Int) -> WebSocket { + let req = URLRequest(url: URL(string: "ws://\(host)\(buildPath(cmd,caseNum))")!) + //return WebSocket(request: req, compressionHandler: WSCompression()) + return WebSocket(request: req) + } + + func buildPath(_ cmd: String, _ caseNum: Int) -> String { + return "/\(cmd)?case=\(caseNum)&agent=Starscream" + } +} + diff --git a/Vendors/spm/Starscream/examples/SimpleTest/.gitignore b/Vendors/spm/Starscream/examples/SimpleTest/.gitignore new file mode 100644 index 00000000..ad59cdc4 --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/.gitignore @@ -0,0 +1,27 @@ +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? +# +# Pods/ + +# Xcode +.DS_Store +build +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +profile +*.moved-aside +DerivedData +.idea/ +*.hmap +*.xccheckout +*.xcodeproj/*.xcworkspace \ No newline at end of file diff --git a/Vendors/spm/Starscream/examples/SimpleTest/README.md b/Vendors/spm/Starscream/examples/SimpleTest/README.md new file mode 100644 index 00000000..69f494a5 --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/README.md @@ -0,0 +1,20 @@ +# Simple Test + +This is a very simple example on how to use Starscream. + +# Usage + +First make sure you have the gem dependencies of websocket server. + +``` +gem install em-websocket +gem install faker +``` + +Next simply run: + +``` +ruby ws-server.rb +``` + +After that, start and run the xCode project. Echo to your heart's desire. \ No newline at end of file diff --git a/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/AppDelegate.swift b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/AppDelegate.swift new file mode 100644 index 00000000..a454b4c1 --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/AppDelegate.swift @@ -0,0 +1,60 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// AppDelegate.swift +// SimpleTest +// +// Created by Dalton Cherry on 8/12/14. +// Copyright © 2014 Vluxe. All rights reserved. +// +// 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 UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Base.lproj/Main.storyboard b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Base.lproj/Main.storyboard new file mode 100644 index 00000000..4b831e75 --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Base.lproj/Main.storyboard @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Images.xcassets/AppIcon.appiconset/Contents.json b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..b7f3352e --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Images.xcassets/LaunchImage.launchimage/Contents.json b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 00000000..6f870a46 --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Info.plist b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Info.plist new file mode 100644 index 00000000..17711a68 --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/ViewController.swift b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/ViewController.swift new file mode 100644 index 00000000..6a6a4a36 --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/SimpleTest/ViewController.swift @@ -0,0 +1,112 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ViewController.swift +// SimpleTest +// +// Created by Dalton Cherry on 8/12/14. +// Copyright © 2014 Vluxe. All rights reserved. +// +// 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 UIKit +import Starscream + +class ViewController: UIViewController, WebSocketDelegate { + var socket: WebSocket! + var isConnected = false + let server = WebSocketServer() + + override func viewDidLoad() { + super.viewDidLoad() +// let err = server.start(address: "localhost", port: 8080) +// if err != nil { +// print("server didn't start!") +// } +// server.onEvent = { event in +// switch event { +// case .text(let conn, let string): +// let payload = string.data(using: .utf8)! +// conn.write(data: payload, opcode: .textFrame) +// default: +// break +// } +// } + //https://echo.websocket.org + var request = URLRequest(url: URL(string: "http://localhost:8080")!) //https://localhost:8080 + request.timeoutInterval = 5 + socket = WebSocket(request: request) + socket.delegate = self + socket.connect() + } + + // MARK: - WebSocketDelegate + func didReceive(event: WebSocketEvent, client: WebSocket) { + switch event { + case .connected(let headers): + isConnected = true + print("websocket is connected: \(headers)") + case .disconnected(let reason, let code): + isConnected = false + print("websocket is disconnected: \(reason) with code: \(code)") + case .text(let string): + print("Received text: \(string)") + case .binary(let data): + print("Received data: \(data.count)") + case .ping(_): + break + case .pong(_): + break + case .viabilityChanged(_): + break + case .reconnectSuggested(_): + break + case .cancelled: + isConnected = false + case .error(let error): + isConnected = false + handleError(error) + } + } + + func handleError(_ error: Error?) { + if let e = error as? WSError { + print("websocket encountered an error: \(e.message)") + } else if let e = error { + print("websocket encountered an error: \(e.localizedDescription)") + } else { + print("websocket encountered an error") + } + } + + // MARK: Write Text Action + + @IBAction func writeText(_ sender: UIBarButtonItem) { + socket.write(string: "hello there!") + } + + // MARK: Disconnect Action + + @IBAction func disconnect(_ sender: UIBarButtonItem) { + if isConnected { + sender.title = "Connect" + socket.disconnect() + } else { + sender.title = "Disconnect" + socket.connect() + } + } + +} + diff --git a/Vendors/spm/Starscream/examples/SimpleTest/ws-server.rb b/Vendors/spm/Starscream/examples/SimpleTest/ws-server.rb new file mode 100644 index 00000000..5177fe3e --- /dev/null +++ b/Vendors/spm/Starscream/examples/SimpleTest/ws-server.rb @@ -0,0 +1,25 @@ +require 'em-websocket' +require 'faker' + +EM.run { + EM::WebSocket.run(:host => "0.0.0.0", :port => 8080) do |ws| + ws.onopen { |handshake| + puts "WebSocket connection open" + puts "origin: #{handshake.origin}" + puts "headers: #{handshake.headers}" + + ws.send "Hello Client, you connected to #{handshake.path}" + } + + ws.onerror do |error| + puts "[error] #{error}" + end + + ws.onclose { puts "Connection closed" } + + ws.onmessage { |msg| + puts "message from client: #{msg}" + ws.send +Faker::Hacker.say_something_smart + } + end +} diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Podfile b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Podfile new file mode 100644 index 00000000..62f87cb4 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Podfile @@ -0,0 +1,11 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'WebSocketsOrgEcho' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + + # Pods for WebSocketsOrgEcho + + pod 'Starscream', :path => '../../' +end diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Podfile.lock b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Podfile.lock new file mode 100644 index 00000000..49f58e0c --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - Starscream (3.0.6) + +DEPENDENCIES: + - Starscream (from `../../`) + +EXTERNAL SOURCES: + Starscream: + :path: "../../" + +SPEC CHECKSUMS: + Starscream: 96cd79a6b7ef6a2ff2d00638c73bd195a5322586 + +PODFILE CHECKSUM: 96d91933fe13671aaa81af8a8675ff7698068845 + +COCOAPODS: 1.6.0.beta.1 diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Local Podspecs/Starscream.podspec.json b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Local Podspecs/Starscream.podspec.json new file mode 100644 index 00000000..514469bd --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Local Podspecs/Starscream.podspec.json @@ -0,0 +1,24 @@ +{ + "name": "Starscream", + "version": "3.0.6", + "summary": "A conforming WebSocket RFC 6455 client library in Swift.", + "homepage": "https://github.com/daltoniam/Starscream", + "license": "Apache License, Version 2.0", + "authors": { + "Dalton Cherry": "http://daltoniam.com", + "Austin Cherry": "http://austincherry.me" + }, + "source": { + "git": "https://github.com/daltoniam/Starscream.git", + "tag": "3.0.6" + }, + "social_media_url": "http://twitter.com/daltoniam", + "platforms": { + "ios": "8.0", + "osx": "10.10", + "tvos": "9.0", + "watchos": "2.0" + }, + "source_files": "Sources/**/*.swift", + "swift_version": "4.2" +} diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Manifest.lock b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Manifest.lock new file mode 100644 index 00000000..49f58e0c --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Manifest.lock @@ -0,0 +1,16 @@ +PODS: + - Starscream (3.0.6) + +DEPENDENCIES: + - Starscream (from `../../`) + +EXTERNAL SOURCES: + Starscream: + :path: "../../" + +SPEC CHECKSUMS: + Starscream: 96cd79a6b7ef6a2ff2d00638c73bd195a5322586 + +PODFILE CHECKSUM: 96d91933fe13671aaa81af8a8675ff7698068845 + +COCOAPODS: 1.6.0.beta.1 diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-Info.plist b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-Info.plist new file mode 100644 index 00000000..2243fe6e --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-acknowledgements.markdown b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-acknowledgements.markdown new file mode 100644 index 00000000..7d03b3d7 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-acknowledgements.markdown @@ -0,0 +1,182 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## Starscream + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + Copyright (c) 2014-2016 Dalton Cherry. + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. +Generated by CocoaPods - https://cocoapods.org diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-acknowledgements.plist b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-acknowledgements.plist new file mode 100644 index 00000000..7c82be47 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-acknowledgements.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-dummy.m b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-dummy.m new file mode 100644 index 00000000..fdcfa1ac --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_WebSocketsOrgEcho : NSObject +@end +@implementation PodsDummy_Pods_WebSocketsOrgEcho +@end diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-frameworks.sh b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-frameworks.sh new file mode 100755 index 00000000..53a9c475 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-frameworks.sh @@ -0,0 +1,158 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + if [ -r "$source" ]; then + # Copy the dSYM into a the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .framework.dSYM "$source")" + binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then + strip_invalid_archs "$binary" + fi + + if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + fi + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + STRIP_BINARY_RETVAL=0 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=1 +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-umbrella.h b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-umbrella.h new file mode 100644 index 00000000..04075b3a --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_WebSocketsOrgEchoVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_WebSocketsOrgEchoVersionString[]; + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.debug.xcconfig b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.debug.xcconfig new file mode 100644 index 00000000..6abce856 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.debug.xcconfig @@ -0,0 +1,11 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Starscream" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Starscream/Starscream.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Starscream" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.modulemap b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.modulemap new file mode 100644 index 00000000..ffcb5055 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.modulemap @@ -0,0 +1,6 @@ +framework module Pods_WebSocketsOrgEcho { + umbrella header "Pods-WebSocketsOrgEcho-umbrella.h" + + export * + module * { export * } +} diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.release.xcconfig b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.release.xcconfig new file mode 100644 index 00000000..6abce856 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Pods-WebSocketsOrgEcho/Pods-WebSocketsOrgEcho.release.xcconfig @@ -0,0 +1,11 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Starscream" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Starscream/Starscream.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Starscream" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-Info.plist b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-Info.plist new file mode 100644 index 00000000..e822e160 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.0.6 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-dummy.m b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-dummy.m new file mode 100644 index 00000000..94456b3b --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Starscream : NSObject +@end +@implementation PodsDummy_Starscream +@end diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-prefix.pch b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-prefix.pch new file mode 100644 index 00000000..beb2a244 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-umbrella.h b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-umbrella.h new file mode 100644 index 00000000..7bffee0b --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double StarscreamVersionNumber; +FOUNDATION_EXPORT const unsigned char StarscreamVersionString[]; + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream.modulemap b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream.modulemap new file mode 100644 index 00000000..2b909707 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream.modulemap @@ -0,0 +1,6 @@ +framework module Starscream { + umbrella header "Starscream-umbrella.h" + + export * + module * { export * } +} diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream.xcconfig b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream.xcconfig new file mode 100644 index 00000000..9cedac42 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/Pods/Target Support Files/Starscream/Starscream.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Starscream +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../.. +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho.xcworkspace/contents.xcworkspacedata b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..7722c6bb --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/AppDelegate.swift b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/AppDelegate.swift new file mode 100644 index 00000000..60a688a6 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/AppDelegate.swift @@ -0,0 +1,21 @@ +// +// AppDelegate.swift +// WebSocketsOrgEcho +// +// Created by Kristaps Grinbergs on 08/10/2018. +// Copyright © 2018 Starscream. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + return true + } + +} + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Assets.xcassets/AppIcon.appiconset/Contents.json b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Assets.xcassets/Contents.json b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Base.lproj/LaunchScreen.storyboard b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..bfa36129 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Base.lproj/Main.storyboard b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Base.lproj/Main.storyboard new file mode 100644 index 00000000..b13794d4 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Base.lproj/Main.storyboard @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Info.plist b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Info.plist new file mode 100644 index 00000000..16be3b68 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/URL+Extensions.swift b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/URL+Extensions.swift new file mode 100644 index 00000000..d6c37891 --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/URL+Extensions.swift @@ -0,0 +1,19 @@ +// +// URL+Extensions.swift +// Example +// +// Created by Kristaps Grinbergs on 08/10/2018. +// Copyright © 2018 Kristaps Grinbergs. All rights reserved. +// + +import Foundation + +extension URL { + init(staticString string: StaticString) { + guard let url = URL(string: "\(string)") else { + preconditionFailure("Invalid static URL string: \(string)") + } + + self = url + } +} diff --git a/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/ViewController.swift b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/ViewController.swift new file mode 100644 index 00000000..d40fa8bb --- /dev/null +++ b/Vendors/spm/Starscream/examples/WebSocketsOrgEcho/WebSocketsOrgEcho/ViewController.swift @@ -0,0 +1,42 @@ +// +// ViewController.swift +// WebSocketsOrgEcho +// +// Created by Kristaps Grinbergs on 08/10/2018. +// Copyright © 2018 Starscream. All rights reserved. +// + +import UIKit + +import Starscream + +class ViewController: UIViewController, WebSocketDelegate { + + var socket: WebSocket = WebSocket(url: URL(staticString: "wss://echo.websocket.org")) + + func websocketDidConnect(socket: WebSocketClient) { + print("websocketDidConnect") + } + + func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { + print("websocketDidDisconnect", error ?? "") + } + + func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { + print("websocketDidReceiveMessage", text) + } + + func websocketDidReceiveData(socket: WebSocketClient, data: Data) { + print("websocketDidReceiveData", data) + } + + override func viewDidLoad() { + super.viewDidLoad() + + socket.delegate = self + } + + @IBAction func connect(_ sender: Any) { + socket.connect() + } +} diff --git a/Vendors/spm/Starscream/fastlane/Fastfile b/Vendors/spm/Starscream/fastlane/Fastfile new file mode 100644 index 00000000..069321f9 --- /dev/null +++ b/Vendors/spm/Starscream/fastlane/Fastfile @@ -0,0 +1,28 @@ +default_platform(:ios) + +update_fastlane + +platform :ios do + desc "Deploy new version" + lane :release do + ensure_git_branch + version = version_get_podspec(path: "Starscream.podspec") + changelog = prompt(text: "Changelog: ", multi_line_end_keyword: "END") + + github_token = ENV['GITHUB_TOKEN'] + if !github_token || github_token.empty? + github_token = prompt(text: "Please enter your GitHub token: ") + end + + github_release = set_github_release( + repository_name: "daltoniam/Starscream", + api_token: github_token, + name: version, + tag_name: version, + description: changelog, + commitish: "master" + ) + sh("git fetch --tags") + pod_push(allow_warnings: true, verbose: true) + end +end diff --git a/Vendors/spm/Starscream/fastlane/README.md b/Vendors/spm/Starscream/fastlane/README.md new file mode 100644 index 00000000..58c0dc75 --- /dev/null +++ b/Vendors/spm/Starscream/fastlane/README.md @@ -0,0 +1,29 @@ +fastlane documentation +================ +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +``` +xcode-select --install +``` + +Install _fastlane_ using +``` +[sudo] gem install fastlane -NV +``` +or alternatively using `brew cask install fastlane` + +# Available Actions +## iOS +### ios release +``` +fastlane ios release +``` +Deploy new version + +---- + +This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). +The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/Vendors/spm/Starscream/release.sh b/Vendors/spm/Starscream/release.sh new file mode 100755 index 00000000..7e244db9 --- /dev/null +++ b/Vendors/spm/Starscream/release.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +bundle install +bundle exec fastlane release diff --git a/macOS/Constants/PrivadoConstants.swift b/macOS/Constants/PrivadoConstants.swift new file mode 100644 index 00000000..964bfa11 --- /dev/null +++ b/macOS/Constants/PrivadoConstants.swift @@ -0,0 +1,121 @@ +// +// PrivadoConstants.swift +// Privado +// +// Created by Juraldinio on 10/25/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +#if os(iOS) +import UIKit +#endif + +struct PrivadoConstants { + + struct Window { + static let sizeWidth: CGFloat = 340 + static let sizeHeight: CGFloat = 440 + } + + struct Keychain { + static let email = "privado.user" + static let passwd = "privado.password" + } + + struct Hive { + static let bundle = "io.privado.main.hive" + static let error = "io.privado.main.hive.error" + + static let preffixError = "E>>>" + static let preffixConsole = "$>>>" + + struct ErrorCode { + static let xpcNotConnected = 500 + static let xpcEmptyRemoteObject = 501 + + static let nothing = 1000 + + static let ovpnAlreadyConnected = 2000 + static let ovpnAlreadydisconnected = 2001 + + static let firewallStatus = 5000 + static let firewallStatusUndefined = 5001 + static let firewallStatusDiabled = 5002 + + static let firewallAlreadyEnabled = 5100 + static let firewallAlreadyDisabled = 5101 + static let firewallCreateConfig = 5102 + + } + } + + struct Launcher { + static let bundle = "io.privado.main.launcher" + static let notificationName = "Leon4Launcher" + } + + struct Application { + static let name = "PrivadoVPN" + static let bundle = "io.privado.main" + } + + struct OpenVPN { + static let tunnelIdentifier = "io.privado.ios.openvpn" + static let appGroupSuffix = "io.privado.ios" + } + + static let sessionKey = "SessionRecord" + static let databaseKey = "DatabaseKey" + + struct Teams { + static let development = "S9D2XZUZH2" + static let production = "4D7YV49724" + } + + // Salt for API + struct Session { + #if PRODUCTION + static let key = "9f994c466340e8f2ed60a99396fecb6a" + #else + static let key = "46dc8a3706ad0ef6be52cd0f8d1ad69f" + #endif + } + + struct Event { + + static let error = "Error" + static let hive = "Hive" + static let config = "Config" + static let navigate = "navigate" + static let loginItem = "LoginItem" + static let environment = "Environment" + static let user = "User" + + struct VPN { + static let ikev2 = "ikev2" + static let ovpn = "openvpn" + static let wireguard = "wireguard" + } + + struct ATTRIBUTES { + static let route = "Route" + static let enabled = "Enabled" + static let success = "Success" + static let isOn = "IsOn" + static let connect = "Connect" + static let server = "Server" + static let city = "City" + static let proto = "Protocol" + } + + struct STATISTIC { + static let prefix = "statistic" + static let start = "\(prefix).start" + static let login = "\(prefix).login" + static let connection = "\(prefix).connection" + static let settings = "\(prefix).settings" + static let errorConnection = "\(prefix).ErrorConnectExecute" + } + } +} diff --git a/macOS/OpenVPN/Sources/PacketTunnelProvider.swift b/macOS/OpenVPN/Sources/PacketTunnelProvider.swift deleted file mode 100644 index 07ba9d9d..00000000 --- a/macOS/OpenVPN/Sources/PacketTunnelProvider.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// PacketTunnelProvider.swift -// openvpn -// -// Created by Juraldinio on 2/5/20. -// Copyright © 2020 Privado LLC. All rights reserved. -// - -import NetworkExtension -import TunnelKit - -class PacketTunnelProvider: OpenVPNTunnelProvider { - - -} diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/Contents.json new file mode 100644 index 00000000..aaaa9389 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "ezgif.com-gif-maker-8.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "ezgif.com-gif-maker-7-2.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "ezgif.com-gif-maker-7-3.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "ezgif.com-gif-maker-7-4.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "ezgif.com-gif-maker-9.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "ezgif.com-gif-maker-7-5.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "ezgif.com-gif-maker-7-6.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "ezgif.com-gif-maker-7-7.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-7-8.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-7-9.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-7-10.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-11.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-7-11.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-10.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-7-12.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-7-13.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "ezgif.com-gif-maker-7-14.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "ezgif.com-gif-maker-7.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-10.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-10.png new file mode 100644 index 00000000..17aa44cd Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-10.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-11.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-11.png new file mode 100644 index 00000000..c5050e9b Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-11.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-10.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-10.png new file mode 100644 index 00000000..c2a48de6 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-10.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-11.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-11.png new file mode 100644 index 00000000..e7560dfb Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-11.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-12.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-12.png new file mode 100644 index 00000000..0a545f46 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-12.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-13.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-13.png new file mode 100644 index 00000000..8b887ea3 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-13.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-14.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-14.png new file mode 100644 index 00000000..c437e046 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-14.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-2.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-2.png new file mode 100644 index 00000000..86254c5e Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-2.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-3.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-3.png new file mode 100644 index 00000000..c5050e9b Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-3.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-4.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-4.png new file mode 100644 index 00000000..6e16a471 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-4.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-5.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-5.png new file mode 100644 index 00000000..e4f57e57 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-5.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-6.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-6.png new file mode 100644 index 00000000..e4f57e57 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-6.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-7.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-7.png new file mode 100644 index 00000000..b1e2dcac Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-7.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-8.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-8.png new file mode 100644 index 00000000..c0904e9d Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-8.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-9.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-9.png new file mode 100644 index 00000000..e7560dfb Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7-9.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7.png new file mode 100644 index 00000000..a8155c57 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-7.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-8.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-8.png new file mode 100644 index 00000000..e7560dfb Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-8.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-9.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-9.png new file mode 100644 index 00000000..17aa44cd Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.iOS.appiconset/ezgif.com-gif-maker-9.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/Contents.json new file mode 100644 index 00000000..1998318e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-2.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-3.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-11.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-5.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-6.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-10.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-9.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-8.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "ezgif.com-gif-maker-7.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-10.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-10.png new file mode 100644 index 00000000..d4aac583 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-10.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-11.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-11.png new file mode 100644 index 00000000..a5a578a5 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-11.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-2.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-2.png new file mode 100644 index 00000000..dda6ab47 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-2.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-3.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-3.png new file mode 100644 index 00000000..dda6ab47 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-3.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-5.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-5.png new file mode 100644 index 00000000..fab57dab Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-5.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-6.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-6.png new file mode 100644 index 00000000..d4aac583 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-6.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-7.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-7.png new file mode 100644 index 00000000..acdba1df Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-7.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-8.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-8.png new file mode 100644 index 00000000..8e728778 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-8.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-9.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-9.png new file mode 100644 index 00000000..8e728778 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker-9.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker.png b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker.png new file mode 100644 index 00000000..516bba9a Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/AppIcon.macOS.appiconset/ezgif.com-gif-maker.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Connection/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Connection/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/connection.lock.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.lock.imageset/Contents.json new file mode 100644 index 00000000..e5d3f828 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.lock.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "lock_red.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/connection.lock.imageset/lock_red.png b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.lock.imageset/lock_red.png new file mode 100644 index 00000000..fb0f5bd0 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.lock.imageset/lock_red.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/connection.off.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.off.imageset/Contents.json new file mode 100644 index 00000000..1b204310 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.off.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Off.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/connection.off.imageset/Off.png b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.off.imageset/Off.png new file mode 100644 index 00000000..9d8ea758 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.off.imageset/Off.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/connection.on.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.on.imageset/Contents.json new file mode 100644 index 00000000..09caa92e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.on.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "On.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/connection.on.imageset/On.png b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.on.imageset/On.png new file mode 100644 index 00000000..8a542848 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.on.imageset/On.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/connection.unlock.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.unlock.imageset/Contents.json new file mode 100644 index 00000000..c8de8152 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.unlock.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "lock_green.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Connection/connection.unlock.imageset/lock_green.png b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.unlock.imageset/lock_green.png new file mode 100644 index 00000000..de6463ef Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Connection/connection.unlock.imageset/lock_green.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AD.imageset/AD.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AD.imageset/AD.png new file mode 100644 index 00000000..b1fda7b7 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AD.imageset/AD.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AD.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AD.imageset/Contents.json new file mode 100644 index 00000000..2e30e55f --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AD.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "AD.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AE.imageset/AE.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AE.imageset/AE.png new file mode 100644 index 00000000..3d10a6fb Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AE.imageset/AE.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AE.imageset/Contents.json new file mode 100644 index 00000000..f67ff84a --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "AE.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AF.imageset/AF.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AF.imageset/AF.png new file mode 100644 index 00000000..e7d2596b Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AF.imageset/AF.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AF.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AF.imageset/Contents.json new file mode 100644 index 00000000..6241367c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AF.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "AF.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AL.imageset/AL.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AL.imageset/AL.png new file mode 100644 index 00000000..591d3938 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AL.imageset/AL.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AL.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AL.imageset/Contents.json new file mode 100644 index 00000000..ab76c1fd --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AL.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "AL.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AM.imageset/AM.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AM.imageset/AM.png new file mode 100644 index 00000000..31928665 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AM.imageset/AM.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AM.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AM.imageset/Contents.json new file mode 100644 index 00000000..4d2b32bf --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AM.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "AM.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AR.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AR.imageset/Contents.json new file mode 100644 index 00000000..ddc81107 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "argentina.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AR.imageset/argentina.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AR.imageset/argentina.png new file mode 100644 index 00000000..a99d5aea Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AR.imageset/argentina.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AT.imageset/Contents.json new file mode 100644 index 00000000..cd17da0c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "austria.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AT.imageset/austria.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AT.imageset/austria.png new file mode 100644 index 00000000..e7a05812 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AT.imageset/austria.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AU.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AU.imageset/Contents.json new file mode 100644 index 00000000..894e46c2 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AU.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "australia.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AU.imageset/australia.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AU.imageset/australia.png new file mode 100644 index 00000000..12acfdf1 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AU.imageset/australia.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AZ.imageset/AZ.png b/macOS/Privado/Assets/Assets.xcassets/Flags/AZ.imageset/AZ.png new file mode 100644 index 00000000..83bfd2ec Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/AZ.imageset/AZ.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/AZ.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/AZ.imageset/Contents.json new file mode 100644 index 00000000..2ddd20e2 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/AZ.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "AZ.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BA.imageset/BA.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BA.imageset/BA.png new file mode 100644 index 00000000..ad3d03c1 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BA.imageset/BA.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BA.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BA.imageset/Contents.json new file mode 100644 index 00000000..4f799ede --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BA.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BA.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BD.imageset/BD.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BD.imageset/BD.png new file mode 100644 index 00000000..77591588 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BD.imageset/BD.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BD.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BD.imageset/Contents.json new file mode 100644 index 00000000..4cfcd545 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BD.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BD.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BE.imageset/Contents.json new file mode 100644 index 00000000..054a531b --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "belgium.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BE.imageset/belgium.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BE.imageset/belgium.png new file mode 100644 index 00000000..efec6e15 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BE.imageset/belgium.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BG.imageset/BG.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BG.imageset/BG.png new file mode 100644 index 00000000..c59139df Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BG.imageset/BG.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BG.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BG.imageset/Contents.json new file mode 100644 index 00000000..6120de35 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BG.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BG.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BN.imageset/BN.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BN.imageset/BN.png new file mode 100644 index 00000000..570578a8 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BN.imageset/BN.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BN.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BN.imageset/Contents.json new file mode 100644 index 00000000..3c64d9f6 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BN.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BN.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BO.imageset/BO.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BO.imageset/BO.png new file mode 100644 index 00000000..2827b6d9 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BO.imageset/BO.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BO.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BO.imageset/Contents.json new file mode 100644 index 00000000..b3af48fa --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BO.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BO.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BR.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BR.imageset/Contents.json new file mode 100644 index 00000000..9d9d9899 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "brazil.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BR.imageset/brazil.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BR.imageset/brazil.png new file mode 100644 index 00000000..408cb94f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BR.imageset/brazil.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BS.imageset/BS.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BS.imageset/BS.png new file mode 100644 index 00000000..8f67069c Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BS.imageset/BS.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BS.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BS.imageset/Contents.json new file mode 100644 index 00000000..c7fea01a --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BS.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BS.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BT.imageset/BT.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BT.imageset/BT.png new file mode 100644 index 00000000..ba9c9ee4 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BT.imageset/BT.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BT.imageset/Contents.json new file mode 100644 index 00000000..70924755 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BT.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BY.imageset/BY.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BY.imageset/BY.png new file mode 100644 index 00000000..de51d540 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BY.imageset/BY.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BY.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BY.imageset/Contents.json new file mode 100644 index 00000000..e0db79c1 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BY.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BY.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BZ.imageset/BZ.png b/macOS/Privado/Assets/Assets.xcassets/Flags/BZ.imageset/BZ.png new file mode 100644 index 00000000..0b021317 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/BZ.imageset/BZ.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/BZ.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/BZ.imageset/Contents.json new file mode 100644 index 00000000..ea6b7a04 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/BZ.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BZ.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CA.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/CA.imageset/Contents.json new file mode 100644 index 00000000..aefb5969 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/CA.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "canada.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CA.imageset/canada.png b/macOS/Privado/Assets/Assets.xcassets/Flags/CA.imageset/canada.png new file mode 100644 index 00000000..62013b11 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/CA.imageset/canada.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CH.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/CH.imageset/Contents.json new file mode 100644 index 00000000..9ef25cc3 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/CH.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "switzerland.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CH.imageset/switzerland.png b/macOS/Privado/Assets/Assets.xcassets/Flags/CH.imageset/switzerland.png new file mode 100644 index 00000000..f14b783f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/CH.imageset/switzerland.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CL.imageset/CL.png b/macOS/Privado/Assets/Assets.xcassets/Flags/CL.imageset/CL.png new file mode 100644 index 00000000..ad25d82f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/CL.imageset/CL.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CL.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/CL.imageset/Contents.json new file mode 100644 index 00000000..a6e780d8 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/CL.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "CL.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CO.imageset/CO.png b/macOS/Privado/Assets/Assets.xcassets/Flags/CO.imageset/CO.png new file mode 100644 index 00000000..2e24e2d6 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/CO.imageset/CO.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CO.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/CO.imageset/Contents.json new file mode 100644 index 00000000..8f6d25f7 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/CO.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "CO.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CR.imageset/CR.png b/macOS/Privado/Assets/Assets.xcassets/Flags/CR.imageset/CR.png new file mode 100644 index 00000000..b58df61c Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/CR.imageset/CR.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CR.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/CR.imageset/Contents.json new file mode 100644 index 00000000..e69cd684 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/CR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "CR.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CY.imageset/CY.png b/macOS/Privado/Assets/Assets.xcassets/Flags/CY.imageset/CY.png new file mode 100644 index 00000000..46b6ebac Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/CY.imageset/CY.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CY.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/CY.imageset/Contents.json new file mode 100644 index 00000000..28b9837b --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/CY.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "CY.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CZ.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/CZ.imageset/Contents.json new file mode 100644 index 00000000..803c2f0d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/CZ.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "czech.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/CZ.imageset/czech.png b/macOS/Privado/Assets/Assets.xcassets/Flags/CZ.imageset/czech.png new file mode 100644 index 00000000..eef2b342 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/CZ.imageset/czech.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/DE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/DE.imageset/Contents.json new file mode 100644 index 00000000..43b5872d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/DE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "germany.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/DE.imageset/germany.png b/macOS/Privado/Assets/Assets.xcassets/Flags/DE.imageset/germany.png new file mode 100644 index 00000000..bbff10ca Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/DE.imageset/germany.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/DK.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/DK.imageset/Contents.json new file mode 100644 index 00000000..cc91b329 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/DK.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "denmark.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/DK.imageset/denmark.png b/macOS/Privado/Assets/Assets.xcassets/Flags/DK.imageset/denmark.png new file mode 100644 index 00000000..e197cf0a Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/DK.imageset/denmark.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/DZ.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/DZ.imageset/Contents.json new file mode 100644 index 00000000..9360af6f --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/DZ.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "DZ.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/DZ.imageset/DZ.png b/macOS/Privado/Assets/Assets.xcassets/Flags/DZ.imageset/DZ.png new file mode 100644 index 00000000..4ac066be Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/DZ.imageset/DZ.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/EC.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/EC.imageset/Contents.json new file mode 100644 index 00000000..90022b10 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/EC.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "EC.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/EC.imageset/EC.png b/macOS/Privado/Assets/Assets.xcassets/Flags/EC.imageset/EC.png new file mode 100644 index 00000000..05e6b48f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/EC.imageset/EC.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/EE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/EE.imageset/Contents.json new file mode 100644 index 00000000..de50a630 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/EE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "EE.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/EE.imageset/EE.png b/macOS/Privado/Assets/Assets.xcassets/Flags/EE.imageset/EE.png new file mode 100644 index 00000000..7e7f1111 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/EE.imageset/EE.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/EG.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/EG.imageset/Contents.json new file mode 100644 index 00000000..f426d3c6 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/EG.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "EG.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/EG.imageset/EG.png b/macOS/Privado/Assets/Assets.xcassets/Flags/EG.imageset/EG.png new file mode 100644 index 00000000..fde1cb9a Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/EG.imageset/EG.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ES.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/ES.imageset/Contents.json new file mode 100644 index 00000000..4889dbee --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/ES.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "spain.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ES.imageset/spain.png b/macOS/Privado/Assets/Assets.xcassets/Flags/ES.imageset/spain.png new file mode 100644 index 00000000..5c9f6e72 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/ES.imageset/spain.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ET.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/ET.imageset/Contents.json new file mode 100644 index 00000000..f804244a --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/ET.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ET.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ET.imageset/ET.png b/macOS/Privado/Assets/Assets.xcassets/Flags/ET.imageset/ET.png new file mode 100644 index 00000000..7fb32edd Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/ET.imageset/ET.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/FI.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/FI.imageset/Contents.json new file mode 100644 index 00000000..f1c174d6 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/FI.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "FI.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/FI.imageset/FI.png b/macOS/Privado/Assets/Assets.xcassets/Flags/FI.imageset/FI.png new file mode 100644 index 00000000..2fc16c39 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/FI.imageset/FI.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/FR.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/FR.imageset/Contents.json new file mode 100644 index 00000000..a2f37fbd --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/FR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "france.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/FR.imageset/france.png b/macOS/Privado/Assets/Assets.xcassets/Flags/FR.imageset/france.png new file mode 100644 index 00000000..97ac5a14 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/FR.imageset/france.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GB.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/GB.imageset/Contents.json new file mode 100644 index 00000000..e0cc177d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/GB.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "united-kingdom.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GB.imageset/united-kingdom.png b/macOS/Privado/Assets/Assets.xcassets/Flags/GB.imageset/united-kingdom.png new file mode 100644 index 00000000..09260b73 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/GB.imageset/united-kingdom.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GD.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/GD.imageset/Contents.json new file mode 100644 index 00000000..796048ca --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/GD.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "GD.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GD.imageset/GD.png b/macOS/Privado/Assets/Assets.xcassets/Flags/GD.imageset/GD.png new file mode 100644 index 00000000..f444fae3 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/GD.imageset/GD.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/GE.imageset/Contents.json new file mode 100644 index 00000000..07a10a6d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/GE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "GE.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GE.imageset/GE.png b/macOS/Privado/Assets/Assets.xcassets/Flags/GE.imageset/GE.png new file mode 100644 index 00000000..c910603e Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/GE.imageset/GE.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GR.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/GR.imageset/Contents.json new file mode 100644 index 00000000..08a36a84 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/GR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "GR.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GR.imageset/GR.png b/macOS/Privado/Assets/Assets.xcassets/Flags/GR.imageset/GR.png new file mode 100644 index 00000000..c07bee06 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/GR.imageset/GR.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/GT.imageset/Contents.json new file mode 100644 index 00000000..f0a80f07 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/GT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "GT.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/GT.imageset/GT.png b/macOS/Privado/Assets/Assets.xcassets/Flags/GT.imageset/GT.png new file mode 100644 index 00000000..3dfa101f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/GT.imageset/GT.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/HK.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/HK.imageset/Contents.json new file mode 100644 index 00000000..ef27c3b7 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/HK.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "hong-kong.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/HK.imageset/hong-kong.png b/macOS/Privado/Assets/Assets.xcassets/Flags/HK.imageset/hong-kong.png new file mode 100644 index 00000000..83d5650c Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/HK.imageset/hong-kong.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/HR.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/HR.imageset/Contents.json new file mode 100644 index 00000000..f4d15edd --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/HR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "HR.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/HR.imageset/HR.png b/macOS/Privado/Assets/Assets.xcassets/Flags/HR.imageset/HR.png new file mode 100644 index 00000000..966dcce0 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/HR.imageset/HR.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/HT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/HT.imageset/Contents.json new file mode 100644 index 00000000..1bf6e2ff --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/HT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "HT.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/HT.imageset/HT.png b/macOS/Privado/Assets/Assets.xcassets/Flags/HT.imageset/HT.png new file mode 100644 index 00000000..a4154975 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/HT.imageset/HT.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/HU.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/HU.imageset/Contents.json new file mode 100644 index 00000000..064bd66a --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/HU.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "HU.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/HU.imageset/HU.png b/macOS/Privado/Assets/Assets.xcassets/Flags/HU.imageset/HU.png new file mode 100644 index 00000000..0561ea49 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/HU.imageset/HU.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ID.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/ID.imageset/Contents.json new file mode 100644 index 00000000..dd7cd77f --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/ID.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ID.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ID.imageset/ID.png b/macOS/Privado/Assets/Assets.xcassets/Flags/ID.imageset/ID.png new file mode 100644 index 00000000..a10ba480 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/ID.imageset/ID.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/IE.imageset/Contents.json new file mode 100644 index 00000000..d428fa2f --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/IE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ireland.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IE.imageset/ireland.png b/macOS/Privado/Assets/Assets.xcassets/Flags/IE.imageset/ireland.png new file mode 100644 index 00000000..ff7f275d Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/IE.imageset/ireland.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IL.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/IL.imageset/Contents.json new file mode 100644 index 00000000..80d643ca --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/IL.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "IL.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IL.imageset/IL.png b/macOS/Privado/Assets/Assets.xcassets/Flags/IL.imageset/IL.png new file mode 100644 index 00000000..10c094fd Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/IL.imageset/IL.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IM.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/IM.imageset/Contents.json new file mode 100644 index 00000000..ef3e78bc --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/IM.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "IM.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IM.imageset/IM.png b/macOS/Privado/Assets/Assets.xcassets/Flags/IM.imageset/IM.png new file mode 100644 index 00000000..8d80e55e Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/IM.imageset/IM.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IN.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/IN.imageset/Contents.json new file mode 100644 index 00000000..e84ec67c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/IN.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "india.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IN.imageset/india.png b/macOS/Privado/Assets/Assets.xcassets/Flags/IN.imageset/india.png new file mode 100644 index 00000000..d6ff6395 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/IN.imageset/india.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IS.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/IS.imageset/Contents.json new file mode 100644 index 00000000..e86dca67 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/IS.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "IS.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IS.imageset/IS.png b/macOS/Privado/Assets/Assets.xcassets/Flags/IS.imageset/IS.png new file mode 100644 index 00000000..0f720ce9 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/IS.imageset/IS.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/IT.imageset/Contents.json new file mode 100644 index 00000000..251ecde4 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/IT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "IT.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/IT.imageset/IT.png b/macOS/Privado/Assets/Assets.xcassets/Flags/IT.imageset/IT.png new file mode 100644 index 00000000..c6da4e96 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/IT.imageset/IT.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/JM.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/JM.imageset/Contents.json new file mode 100644 index 00000000..68798794 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/JM.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "JM.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/JM.imageset/JM.png b/macOS/Privado/Assets/Assets.xcassets/Flags/JM.imageset/JM.png new file mode 100644 index 00000000..7bbef56f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/JM.imageset/JM.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/JP.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/JP.imageset/Contents.json new file mode 100644 index 00000000..70e34237 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/JP.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "japan.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/JP.imageset/japan.png b/macOS/Privado/Assets/Assets.xcassets/Flags/JP.imageset/japan.png new file mode 100644 index 00000000..b0442a61 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/JP.imageset/japan.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/KE.imageset/Contents.json new file mode 100644 index 00000000..248595c6 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/KE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "KE.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KE.imageset/KE.png b/macOS/Privado/Assets/Assets.xcassets/Flags/KE.imageset/KE.png new file mode 100644 index 00000000..05232ea2 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/KE.imageset/KE.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KG.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/KG.imageset/Contents.json new file mode 100644 index 00000000..06c54866 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/KG.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "KG.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KG.imageset/KG.png b/macOS/Privado/Assets/Assets.xcassets/Flags/KG.imageset/KG.png new file mode 100644 index 00000000..175eb05b Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/KG.imageset/KG.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KH.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/KH.imageset/Contents.json new file mode 100644 index 00000000..db8b9c97 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/KH.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "KH.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KH.imageset/KH.png b/macOS/Privado/Assets/Assets.xcassets/Flags/KH.imageset/KH.png new file mode 100644 index 00000000..1055aa11 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/KH.imageset/KH.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KR.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/KR.imageset/Contents.json new file mode 100644 index 00000000..4955a20f --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/KR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "KR.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KR.imageset/KR.png b/macOS/Privado/Assets/Assets.xcassets/Flags/KR.imageset/KR.png new file mode 100644 index 00000000..2243ac08 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/KR.imageset/KR.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KW.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/KW.imageset/Contents.json new file mode 100644 index 00000000..31246bac --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/KW.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "KW.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KW.imageset/KW.png b/macOS/Privado/Assets/Assets.xcassets/Flags/KW.imageset/KW.png new file mode 100644 index 00000000..ce3e0ee8 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/KW.imageset/KW.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KZ.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/KZ.imageset/Contents.json new file mode 100644 index 00000000..3ec7b060 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/KZ.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "KZ.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/KZ.imageset/KZ.png b/macOS/Privado/Assets/Assets.xcassets/Flags/KZ.imageset/KZ.png new file mode 100644 index 00000000..d7b32de3 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/KZ.imageset/KZ.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LA.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/LA.imageset/Contents.json new file mode 100644 index 00000000..2df3bdd8 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/LA.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "LA.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LA.imageset/LA.png b/macOS/Privado/Assets/Assets.xcassets/Flags/LA.imageset/LA.png new file mode 100644 index 00000000..f9ffb97a Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/LA.imageset/LA.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LI.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/LI.imageset/Contents.json new file mode 100644 index 00000000..585f6dbb --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/LI.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "LI.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LI.imageset/LI.png b/macOS/Privado/Assets/Assets.xcassets/Flags/LI.imageset/LI.png new file mode 100644 index 00000000..3ce22040 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/LI.imageset/LI.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LK.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/LK.imageset/Contents.json new file mode 100644 index 00000000..3ed8341e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/LK.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "LK.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LK.imageset/LK.png b/macOS/Privado/Assets/Assets.xcassets/Flags/LK.imageset/LK.png new file mode 100644 index 00000000..f46afa6b Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/LK.imageset/LK.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/LT.imageset/Contents.json new file mode 100644 index 00000000..96b0cde2 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/LT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "LT.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LT.imageset/LT.png b/macOS/Privado/Assets/Assets.xcassets/Flags/LT.imageset/LT.png new file mode 100644 index 00000000..281ff9db Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/LT.imageset/LT.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LU.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/LU.imageset/Contents.json new file mode 100644 index 00000000..47fff09b --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/LU.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "luxembourg.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LU.imageset/luxembourg.png b/macOS/Privado/Assets/Assets.xcassets/Flags/LU.imageset/luxembourg.png new file mode 100644 index 00000000..b5a7fd75 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/LU.imageset/luxembourg.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LV.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/LV.imageset/Contents.json new file mode 100644 index 00000000..57b25e30 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/LV.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "LV.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/LV.imageset/LV.png b/macOS/Privado/Assets/Assets.xcassets/Flags/LV.imageset/LV.png new file mode 100644 index 00000000..fd4fa4da Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/LV.imageset/LV.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MC.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MC.imageset/Contents.json new file mode 100644 index 00000000..7a77a965 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MC.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MC.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MC.imageset/MC.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MC.imageset/MC.png new file mode 100644 index 00000000..acbe64cb Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MC.imageset/MC.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MD.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MD.imageset/Contents.json new file mode 100644 index 00000000..90bbdea2 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MD.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MD.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MD.imageset/MD.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MD.imageset/MD.png new file mode 100644 index 00000000..f0c43d1f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MD.imageset/MD.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ME.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/ME.imageset/Contents.json new file mode 100644 index 00000000..8e0c3550 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/ME.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ME.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ME.imageset/ME.png b/macOS/Privado/Assets/Assets.xcassets/Flags/ME.imageset/ME.png new file mode 100644 index 00000000..f624904b Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/ME.imageset/ME.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MK.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MK.imageset/Contents.json new file mode 100644 index 00000000..706c8cb5 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MK.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MK.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MK.imageset/MK.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MK.imageset/MK.png new file mode 100644 index 00000000..c4a795ab Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MK.imageset/MK.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MM.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MM.imageset/Contents.json new file mode 100644 index 00000000..43c5283e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MM.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MM.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MM.imageset/MM.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MM.imageset/MM.png new file mode 100644 index 00000000..20903999 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MM.imageset/MM.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MN.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MN.imageset/Contents.json new file mode 100644 index 00000000..54bcba43 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MN.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MN.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MN.imageset/MN.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MN.imageset/MN.png new file mode 100644 index 00000000..edc15f11 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MN.imageset/MN.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MO.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MO.imageset/Contents.json new file mode 100644 index 00000000..377a1b02 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MO.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MO.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MO.imageset/MO.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MO.imageset/MO.png new file mode 100644 index 00000000..e031e53a Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MO.imageset/MO.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MT.imageset/Contents.json new file mode 100644 index 00000000..989bd695 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MT.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MT.imageset/MT.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MT.imageset/MT.png new file mode 100644 index 00000000..9070accd Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MT.imageset/MT.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MX.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MX.imageset/Contents.json new file mode 100644 index 00000000..03be9a21 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MX.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MX.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MX.imageset/MX.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MX.imageset/MX.png new file mode 100644 index 00000000..49edc996 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MX.imageset/MX.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MY.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/MY.imageset/Contents.json new file mode 100644 index 00000000..765906a9 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/MY.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MY.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/MY.imageset/MY.png b/macOS/Privado/Assets/Assets.xcassets/Flags/MY.imageset/MY.png new file mode 100644 index 00000000..1261459c Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/MY.imageset/MY.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NG.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/NG.imageset/Contents.json new file mode 100644 index 00000000..b86e9c9c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/NG.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "NG.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NG.imageset/NG.png b/macOS/Privado/Assets/Assets.xcassets/Flags/NG.imageset/NG.png new file mode 100644 index 00000000..ee5b09df Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/NG.imageset/NG.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NL.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/NL.imageset/Contents.json new file mode 100644 index 00000000..9ff5a4ce --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/NL.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "netherlands.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NL.imageset/netherlands.png b/macOS/Privado/Assets/Assets.xcassets/Flags/NL.imageset/netherlands.png new file mode 100644 index 00000000..902b26d0 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/NL.imageset/netherlands.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NO.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/NO.imageset/Contents.json new file mode 100644 index 00000000..c87150b5 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/NO.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "norway.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NO.imageset/norway.png b/macOS/Privado/Assets/Assets.xcassets/Flags/NO.imageset/norway.png new file mode 100644 index 00000000..b4d2803a Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/NO.imageset/norway.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NP.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/NP.imageset/Contents.json new file mode 100644 index 00000000..20c65992 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/NP.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "NP.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NP.imageset/NP.png b/macOS/Privado/Assets/Assets.xcassets/Flags/NP.imageset/NP.png new file mode 100644 index 00000000..abd9828d Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/NP.imageset/NP.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NZ.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/NZ.imageset/Contents.json new file mode 100644 index 00000000..e8757c5e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/NZ.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "new-zealand.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/NZ.imageset/new-zealand.png b/macOS/Privado/Assets/Assets.xcassets/Flags/NZ.imageset/new-zealand.png new file mode 100644 index 00000000..ba1e0f56 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/NZ.imageset/new-zealand.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PA.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/PA.imageset/Contents.json new file mode 100644 index 00000000..2364ae64 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/PA.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "PA.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PA.imageset/PA.png b/macOS/Privado/Assets/Assets.xcassets/Flags/PA.imageset/PA.png new file mode 100644 index 00000000..5a9023d5 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/PA.imageset/PA.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/PE.imageset/Contents.json new file mode 100644 index 00000000..3150ebe9 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/PE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "PE.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PE.imageset/PE.png b/macOS/Privado/Assets/Assets.xcassets/Flags/PE.imageset/PE.png new file mode 100644 index 00000000..d31a5d14 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/PE.imageset/PE.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PG.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/PG.imageset/Contents.json new file mode 100644 index 00000000..3944c226 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/PG.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "PG.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PG.imageset/PG.png b/macOS/Privado/Assets/Assets.xcassets/Flags/PG.imageset/PG.png new file mode 100644 index 00000000..706188cb Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/PG.imageset/PG.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PH.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/PH.imageset/Contents.json new file mode 100644 index 00000000..1198e4cf --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/PH.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "PH.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PH.imageset/PH.png b/macOS/Privado/Assets/Assets.xcassets/Flags/PH.imageset/PH.png new file mode 100644 index 00000000..c0e1014f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/PH.imageset/PH.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PK.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/PK.imageset/Contents.json new file mode 100644 index 00000000..f1670f3f --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/PK.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "PK.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PK.imageset/PK.png b/macOS/Privado/Assets/Assets.xcassets/Flags/PK.imageset/PK.png new file mode 100644 index 00000000..81153c73 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/PK.imageset/PK.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PL.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/PL.imageset/Contents.json new file mode 100644 index 00000000..461e7669 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/PL.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "poland.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PL.imageset/poland.png b/macOS/Privado/Assets/Assets.xcassets/Flags/PL.imageset/poland.png new file mode 100644 index 00000000..579acea6 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/PL.imageset/poland.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/PT.imageset/Contents.json new file mode 100644 index 00000000..54ca9835 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/PT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "PT.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/PT.imageset/PT.png b/macOS/Privado/Assets/Assets.xcassets/Flags/PT.imageset/PT.png new file mode 100644 index 00000000..ac53ba1c Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/PT.imageset/PT.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/QA.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/QA.imageset/Contents.json new file mode 100644 index 00000000..16d56f55 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/QA.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "QA.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/QA.imageset/QA.png b/macOS/Privado/Assets/Assets.xcassets/Flags/QA.imageset/QA.png new file mode 100644 index 00000000..1065aca3 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/QA.imageset/QA.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/RO.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/RO.imageset/Contents.json new file mode 100644 index 00000000..f16db588 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/RO.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "RO.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/RO.imageset/RO.png b/macOS/Privado/Assets/Assets.xcassets/Flags/RO.imageset/RO.png new file mode 100644 index 00000000..52890e39 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/RO.imageset/RO.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/RS.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/RS.imageset/Contents.json new file mode 100644 index 00000000..dbbdcb85 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/RS.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "serbia.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/RS.imageset/serbia.png b/macOS/Privado/Assets/Assets.xcassets/Flags/RS.imageset/serbia.png new file mode 100644 index 00000000..12e1fc61 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/RS.imageset/serbia.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/RU.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/RU.imageset/Contents.json new file mode 100644 index 00000000..740bb078 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/RU.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "RU.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/RU.imageset/RU.png b/macOS/Privado/Assets/Assets.xcassets/Flags/RU.imageset/RU.png new file mode 100644 index 00000000..7ea0b451 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/RU.imageset/RU.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/SE.imageset/Contents.json new file mode 100644 index 00000000..59025f41 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/SE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "sweden.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SE.imageset/sweden.png b/macOS/Privado/Assets/Assets.xcassets/Flags/SE.imageset/sweden.png new file mode 100644 index 00000000..d4a2b4c8 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/SE.imageset/sweden.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SG.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/SG.imageset/Contents.json new file mode 100644 index 00000000..43018934 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/SG.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "singapore.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SG.imageset/singapore.png b/macOS/Privado/Assets/Assets.xcassets/Flags/SG.imageset/singapore.png new file mode 100644 index 00000000..557bfcb0 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/SG.imageset/singapore.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SI.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/SI.imageset/Contents.json new file mode 100644 index 00000000..e717c42e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/SI.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SI.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SI.imageset/SI.png b/macOS/Privado/Assets/Assets.xcassets/Flags/SI.imageset/SI.png new file mode 100644 index 00000000..1771fc39 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/SI.imageset/SI.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SK.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/SK.imageset/Contents.json new file mode 100644 index 00000000..cf1edf0b --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/SK.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SK.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SK.imageset/SK.png b/macOS/Privado/Assets/Assets.xcassets/Flags/SK.imageset/SK.png new file mode 100644 index 00000000..08d619ae Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/SK.imageset/SK.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SV.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/SV.imageset/Contents.json new file mode 100644 index 00000000..50d660f4 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/SV.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SV.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/SV.imageset/SV.png b/macOS/Privado/Assets/Assets.xcassets/Flags/SV.imageset/SV.png new file mode 100644 index 00000000..9bdd3af4 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/SV.imageset/SV.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TH.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/TH.imageset/Contents.json new file mode 100644 index 00000000..407b737f --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/TH.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "TH.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TH.imageset/TH.png b/macOS/Privado/Assets/Assets.xcassets/Flags/TH.imageset/TH.png new file mode 100644 index 00000000..f56fd54c Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/TH.imageset/TH.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TJ.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/TJ.imageset/Contents.json new file mode 100644 index 00000000..d1fefd06 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/TJ.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "TJ.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TJ.imageset/TJ.png b/macOS/Privado/Assets/Assets.xcassets/Flags/TJ.imageset/TJ.png new file mode 100644 index 00000000..deab7f8f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/TJ.imageset/TJ.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TM.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/TM.imageset/Contents.json new file mode 100644 index 00000000..1766e0bb --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/TM.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "TM.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TM.imageset/TM.png b/macOS/Privado/Assets/Assets.xcassets/Flags/TM.imageset/TM.png new file mode 100644 index 00000000..ffd5afef Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/TM.imageset/TM.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TR.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/TR.imageset/Contents.json new file mode 100644 index 00000000..728677fc --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/TR.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "TR.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TR.imageset/TR.png b/macOS/Privado/Assets/Assets.xcassets/Flags/TR.imageset/TR.png new file mode 100644 index 00000000..3631ab5f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/TR.imageset/TR.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TT.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/TT.imageset/Contents.json new file mode 100644 index 00000000..9259c4dc --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/TT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "TT.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TT.imageset/TT.png b/macOS/Privado/Assets/Assets.xcassets/Flags/TT.imageset/TT.png new file mode 100644 index 00000000..da29ece3 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/TT.imageset/TT.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TW.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/TW.imageset/Contents.json new file mode 100644 index 00000000..dc2cae54 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/TW.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "TW.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/TW.imageset/TW.png b/macOS/Privado/Assets/Assets.xcassets/Flags/TW.imageset/TW.png new file mode 100644 index 00000000..0ca2e5b5 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/TW.imageset/TW.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/UA.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/UA.imageset/Contents.json new file mode 100644 index 00000000..1f558c22 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/UA.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ukraine.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/UA.imageset/ukraine.png b/macOS/Privado/Assets/Assets.xcassets/Flags/UA.imageset/ukraine.png new file mode 100644 index 00000000..9152ff53 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/UA.imageset/ukraine.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/UK.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/UK.imageset/Contents.json new file mode 100644 index 00000000..bcbde56d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/UK.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "united-kingdom.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/UK.imageset/united-kingdom.png b/macOS/Privado/Assets/Assets.xcassets/Flags/UK.imageset/united-kingdom.png new file mode 100644 index 00000000..09260b73 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/UK.imageset/united-kingdom.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/US.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/US.imageset/Contents.json new file mode 100644 index 00000000..be1633d2 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/US.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "united-states.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/US.imageset/united-states.png b/macOS/Privado/Assets/Assets.xcassets/Flags/US.imageset/united-states.png new file mode 100644 index 00000000..467b0a74 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/US.imageset/united-states.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/UY.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/UY.imageset/Contents.json new file mode 100644 index 00000000..3425260d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/UY.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "UY.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/UY.imageset/UY.png b/macOS/Privado/Assets/Assets.xcassets/Flags/UY.imageset/UY.png new file mode 100644 index 00000000..5c418c76 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/UY.imageset/UY.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/UZ.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/UZ.imageset/Contents.json new file mode 100644 index 00000000..a625241e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/UZ.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "UZ.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/UZ.imageset/UZ.png b/macOS/Privado/Assets/Assets.xcassets/Flags/UZ.imageset/UZ.png new file mode 100644 index 00000000..002c3d07 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/UZ.imageset/UZ.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/VE.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/VE.imageset/Contents.json new file mode 100644 index 00000000..b227479c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/VE.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "VE.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/VE.imageset/VE.png b/macOS/Privado/Assets/Assets.xcassets/Flags/VE.imageset/VE.png new file mode 100644 index 00000000..2fd3a0c2 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/VE.imageset/VE.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/VN.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/VN.imageset/Contents.json new file mode 100644 index 00000000..4e587c02 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/VN.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "VN.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/VN.imageset/VN.png b/macOS/Privado/Assets/Assets.xcassets/Flags/VN.imageset/VN.png new file mode 100644 index 00000000..7c7b52cc Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/VN.imageset/VN.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ZA.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Flags/ZA.imageset/Contents.json new file mode 100644 index 00000000..8511bb64 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Flags/ZA.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "south-africa.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Flags/ZA.imageset/south-africa.png b/macOS/Privado/Assets/Assets.xcassets/Flags/ZA.imageset/south-africa.png new file mode 100644 index 00000000..79e691e9 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Flags/ZA.imageset/south-africa.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Login/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Login/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Login/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Login/logo.login.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Login/logo.login.imageset/Contents.json new file mode 100644 index 00000000..fb69a98e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Login/logo.login.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logo.login.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Login/logo.login.imageset/logo.login.png b/macOS/Privado/Assets/Assets.xcassets/Login/logo.login.imageset/logo.login.png new file mode 100644 index 00000000..055f937d Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Login/logo.login.imageset/logo.login.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Preference/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Preference/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/Contents.json new file mode 100644 index 00000000..2be12d32 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "account.png", + "idiom" : "iphone", + "scale" : "1x" + }, + { + "filename" : "account@2x.png", + "idiom" : "iphone", + "scale" : "2x" + }, + { + "filename" : "account@3x.png", + "idiom" : "iphone", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account.png new file mode 100644 index 00000000..98f6ca2a Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account@2x.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account@2x.png new file mode 100644 index 00000000..49570422 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account@2x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account@3x.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account@3x.png new file mode 100644 index 00000000..c42353aa Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_account.imageset/account@3x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Contents.json new file mode 100644 index 00000000..3b3d4a8d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Group 3@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 3@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 3@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@1x.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@1x.png new file mode 100644 index 00000000..0ae587d0 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@1x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@2x.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@2x.png new file mode 100644 index 00000000..d5402275 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@2x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@3x.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@3x.png new file mode 100644 index 00000000..d14a1200 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_arrow.imageset/Group 3@3x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/Contents.json new file mode 100644 index 00000000..852ae1fa --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "correct_1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "correct_2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "correct.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct.png new file mode 100644 index 00000000..94753fac Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct_1.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct_1.png new file mode 100644 index 00000000..0dd6b3db Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct_1.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct_2.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct_2.png new file mode 100644 index 00000000..5698c5ab Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_checkmark.imageset/correct_2.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/Contents.json new file mode 100644 index 00000000..a42caf1d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "general.png", + "idiom" : "iphone", + "scale" : "1x" + }, + { + "filename" : "general@2x.png", + "idiom" : "iphone", + "scale" : "2x" + }, + { + "filename" : "general@3x.png", + "idiom" : "iphone", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general.png new file mode 100644 index 00000000..9c404237 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general@2x.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general@2x.png new file mode 100644 index 00000000..14bb9b34 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general@2x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general@3x.png b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general@3x.png new file mode 100644 index 00000000..a29cbced Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Preference/preference_settings.imageset/general@3x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/bestLocation.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/bestLocation.imageset/Contents.json new file mode 100644 index 00000000..91499450 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/bestLocation.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "stars.svg.hi.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/bestLocation.imageset/stars.svg.hi.png b/macOS/Privado/Assets/Assets.xcassets/Serverlist/bestLocation.imageset/stars.svg.hi.png new file mode 100644 index 00000000..4027ddd1 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Serverlist/bestLocation.imageset/stars.svg.hi.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrow.imageset/07.png b/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrow.imageset/07.png new file mode 100644 index 00000000..48b0ca05 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrow.imageset/07.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrow.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrow.imageset/Contents.json new file mode 100644 index 00000000..1f826cab --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "07.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrowUp.imageset/07up.png b/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrowUp.imageset/07up.png new file mode 100644 index 00000000..56cfe3bf Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrowUp.imageset/07up.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrowUp.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrowUp.imageset/Contents.json new file mode 100644 index 00000000..dca4249e --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/countryArrowUp.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "07up.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/leftMenu.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/leftMenu.imageset/Contents.json new file mode 100644 index 00000000..ed5bc872 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/leftMenu.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Group.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/leftMenu.imageset/Group.png b/macOS/Privado/Assets/Assets.xcassets/Serverlist/leftMenu.imageset/Group.png new file mode 100644 index 00000000..b5446091 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Serverlist/leftMenu.imageset/Group.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/location_checkmark.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/location_checkmark.imageset/Contents.json new file mode 100644 index 00000000..fcea0f4a --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/location_checkmark.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "bi_check_circle_fill.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/location_checkmark.imageset/bi_check_circle_fill.png b/macOS/Privado/Assets/Assets.xcassets/Serverlist/location_checkmark.imageset/bi_check_circle_fill.png new file mode 100644 index 00000000..4e1db081 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Serverlist/location_checkmark.imageset/bi_check_circle_fill.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocation.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocation.imageset/Contents.json new file mode 100644 index 00000000..1af3f25c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocation.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "pin.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocation.imageset/pin.png b/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocation.imageset/pin.png new file mode 100644 index 00000000..3453f43e Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocation.imageset/pin.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocationHover.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocationHover.imageset/Contents.json new file mode 100644 index 00000000..8a6e51ae --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocationHover.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "pin_hover.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocationHover.imageset/pin_hover.png b/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocationHover.imageset/pin_hover.png new file mode 100644 index 00000000..959139cf Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Serverlist/pinLocationHover.imageset/pin_hover.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/questionMark.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Serverlist/questionMark.imageset/Contents.json new file mode 100644 index 00000000..3f1bb571 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Serverlist/questionMark.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "question-mark.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Serverlist/questionMark.imageset/question-mark.png b/macOS/Privado/Assets/Assets.xcassets/Serverlist/questionMark.imageset/question-mark.png new file mode 100644 index 00000000..187ad812 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Serverlist/questionMark.imageset/question-mark.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/Tray/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Tray/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Tray/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Tray/TrayIcon.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/Tray/TrayIcon.imageset/Contents.json new file mode 100644 index 00000000..1a679343 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/Tray/TrayIcon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Icon-White.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/macOS/Privado/Assets/Assets.xcassets/Tray/TrayIcon.imageset/Icon-White.png b/macOS/Privado/Assets/Assets.xcassets/Tray/TrayIcon.imageset/Icon-White.png new file mode 100644 index 00000000..526e0f9f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/Tray/TrayIcon.imageset/Icon-White.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/info.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/info.imageset/Contents.json new file mode 100644 index 00000000..6838cc34 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/info.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "WinClient_Resources_Images_InfoIcon.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/info.imageset/WinClient_Resources_Images_InfoIcon.png b/macOS/Privado/Assets/Assets.xcassets/info.imageset/WinClient_Resources_Images_InfoIcon.png new file mode 100644 index 00000000..c3014a2c Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/info.imageset/WinClient_Resources_Images_InfoIcon.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/map.background.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/map.background.imageset/Contents.json new file mode 100644 index 00000000..cbf995c5 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/map.background.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "map.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/map.background.imageset/map.png b/macOS/Privado/Assets/Assets.xcassets/map.background.imageset/map.png new file mode 100644 index 00000000..4f36195a Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/map.background.imageset/map.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Contents.json new file mode 100644 index 00000000..6a18c38d --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Group 3@1x.png", + "idiom" : "iphone", + "scale" : "1x" + }, + { + "filename" : "Group 3@2x.png", + "idiom" : "iphone", + "scale" : "2x" + }, + { + "filename" : "Group 3@3x.png", + "idiom" : "iphone", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@1x.png b/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@1x.png new file mode 100644 index 00000000..9b69245f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@1x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@2x.png b/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@2x.png new file mode 100644 index 00000000..7717622f Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@2x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@3x.png b/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@3x.png new file mode 100644 index 00000000..f0d5c0c3 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/navigation_back.imageset/Group 3@3x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_copy.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/navigation_copy.imageset/Contents.json new file mode 100644 index 00000000..9a33b75b --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/navigation_copy.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "iconfinder_files-pages-print-copy-papers_3209289.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_copy.imageset/iconfinder_files-pages-print-copy-papers_3209289.png b/macOS/Privado/Assets/Assets.xcassets/navigation_copy.imageset/iconfinder_files-pages-print-copy-papers_3209289.png new file mode 100644 index 00000000..97bdb69c Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/navigation_copy.imageset/iconfinder_files-pages-print-copy-papers_3209289.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/Contents.json b/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/Contents.json new file mode 100644 index 00000000..9f7c7112 --- /dev/null +++ b/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "loader.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "loader@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "loader@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader.png b/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader.png new file mode 100644 index 00000000..0c4ea5ba Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader@2x.png b/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader@2x.png new file mode 100644 index 00000000..c7228e8d Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader@2x.png differ diff --git a/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader@3x.png b/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader@3x.png new file mode 100644 index 00000000..c282b960 Binary files /dev/null and b/macOS/Privado/Assets/Assets.xcassets/navigation_loader.imageset/loader@3x.png differ diff --git a/macOS/Privado/Assets/Font/Roboto-Black.ttf b/macOS/Privado/Assets/Font/Roboto-Black.ttf new file mode 100644 index 00000000..2d452383 Binary files /dev/null and b/macOS/Privado/Assets/Font/Roboto-Black.ttf differ diff --git a/macOS/Privado/Assets/Font/Roboto-Medium.ttf b/macOS/Privado/Assets/Font/Roboto-Medium.ttf new file mode 100644 index 00000000..f714a514 Binary files /dev/null and b/macOS/Privado/Assets/Font/Roboto-Medium.ttf differ diff --git a/macOS/Privado/Assets/Font/Roboto-Regular.ttf b/macOS/Privado/Assets/Font/Roboto-Regular.ttf new file mode 100644 index 00000000..2b6392ff Binary files /dev/null and b/macOS/Privado/Assets/Font/Roboto-Regular.ttf differ diff --git a/macOS/Privado/Assets/Font/SF-Pro-Text-Black.otf b/macOS/Privado/Assets/Font/SF-Pro-Text-Black.otf new file mode 100755 index 00000000..6d9a8486 Binary files /dev/null and b/macOS/Privado/Assets/Font/SF-Pro-Text-Black.otf differ diff --git a/macOS/Privado/Assets/Font/SF-Pro-Text-Regular.otf b/macOS/Privado/Assets/Font/SF-Pro-Text-Regular.otf new file mode 100755 index 00000000..19fcae5d Binary files /dev/null and b/macOS/Privado/Assets/Font/SF-Pro-Text-Regular.otf differ diff --git a/macOS/Privado/Assets/Font/SF-Pro-Text-Semibold.otf b/macOS/Privado/Assets/Font/SF-Pro-Text-Semibold.otf new file mode 100755 index 00000000..752e4189 Binary files /dev/null and b/macOS/Privado/Assets/Font/SF-Pro-Text-Semibold.otf differ diff --git a/macOS/Privado/Assets/Localizable.strings b/macOS/Privado/Assets/Localizable.strings new file mode 100644 index 00000000..6a5d21c5 --- /dev/null +++ b/macOS/Privado/Assets/Localizable.strings @@ -0,0 +1,108 @@ +/* + English.strings + Cyberlock + + Created by Jura on 9/27/19. + Copyright © 2019 Omicronmedia. All rights reserved. +*/ +//NSLocalizedString("main.menu.logs", comment: "") + +/* Login window */ +"form.login.username.placeholder" = "Username"; +"form.login.password.placeholder" = "Password"; +"form.login.connect.title" = "LOG IN"; +"form.login.forgot.title" = "Forgot Password?"; +"form.login.signup.title" = "Sign Up"; +"form.login.error.undefined" = "Undefined error"; +"form.login.error.requireUsername" = "Require username"; +"form.login.error.requirePassword" = "Require password"; +"form.login.error.requireBoth" = "Require username and password"; +"form.login.error.network" = "Network error"; +"form.login.error.decoding" = "Something went wrong,\nplease try again later"; +/* TrayIcon Menu */ +"tray.menu.account" = "Account"; +"tray.menu.options" = "Options"; +"tray.menu.protocols" = "Protocols"; +"tray.menu.logs" = "Logs"; +"tray.menu.about" = "About"; +"tray.menu.connect" = "Connect"; +"tray.menu.disconnect" = "Disconnect"; +"tray.menu.quit" = "Quit"; +/* Main Menu */ +"main.menu.hide" = "Hide"; +"main.menu.update" = "Update"; +"main.menu.quit" = "Quit"; +/* Connection */ +"connection.status.connected" = "Connected"; +"connection.status.connecting" = "Connecting..."; +"connection.status.disconnecting" = "Disconnecting..."; +"connection.status.disconnected" = "Click to connect"; +"connection.status.error" = "Error while connect"; +"connection.status.reconnect" = "Initialize connection..."; +"connection.status.authFailed" = "Auth failed"; +"connection.status.requireGrantRights" = "Require grant rights"; +"connection.killswitch.text" = "Kill Switch"; +"connection.killswitch.help.title" = "Kill Switch"; +"connection.killswitch.help.description" = "Suspends internet access to all applications\nand browsers if you lose connection to the\nPrivadoVPN servers."; +/* Geolocation */ +"geolocation.error.killswitch" = "Kill Switch is active"; +"geolocation.error.receive" = "Error receiving location"; +/* Server locations */ +"serverlist.location.best" = "Best location"; +"serverlist.location.virtual" = "Choose Virtual Location"; +"serverlist.location.my" = "Your Virtual Location:"; +/* Protection */ +"protection.protected" = "Your IP Address"; +"protection.unprotected" = "Your IP Address"; +/* Preferences */ +"preference.identifier.preference" = "Preference"; +"preference.identifier.enabled" = "Enabled"; +"preference.identifier.disabled" = "Disabled"; +"preference.identifier.settings" = "Settings"; +"preference.identifier.account" = "Account"; +"preference.identifier.options" = "Options"; +"preference.identifier.general" = "General"; +"preference.identifier.protocols" = "Protocols"; +"preference.identifier.logs" = "Logs"; +"preference.identifier.about" = "About"; +"preference.identifier.autoconnect" = "Auto Connect"; +"preference.identifier.openVPN" = "OpenVPN"; +"preference.identifier.openVPNPort" = "Port"; +"preference.identifier.openVPNProtocol" = "Protocol"; +"preference.identifier.diagnostics" = "Diagnostics"; +/* Logs preferences*/ +"preference.logging.copy"="Copy logs"; +/* Authentication preferences*/ +"preference.authentication.username"="Username: "; +"preference.authentication.expiredAt"="Subscription: Expired at "; +"preference.authentication.expiredAtShort"="Expired at "; +"preference.authentication.expirationInSingle"="Subscription: Expires on "; +"preference.authentication.expirationInSingleShort"="Expires on "; +"preference.authentication.expirationInFirst"="Subscription: Expires in "; +"preference.authentication.expirationInFirstShort"="Expires in "; +"preference.authentication.expirationInSecond"=" days on "; +"preference.authentication.signout"=" Sign Out "; +"preference.authentication.termsOfService" = "Terms of Service"; +"preference.authentication.privacyPolicy" = "Privacy Policy"; +/* Options preferences*/ +"preference.options.killSwitch.title" = "Kill Switch"; +"preference.options.killSwitch.description" = "Suspend all open internet applications and browsers if you get disconnected from the PrivadoVPN client."; +"preference.options.autoStart.title" = "Auto Start"; +"preference.options.autoStart.description" = "Automatically launch the PrivadoVPN app when your computer starts."; +"preference.options.autoConnect.title" = "Auto Connect"; +"preference.options.autoConnect.description" = "Automatically connect to your preffered server as soon as your app launches."; +/* Protocol preferences*/ +"preference.protocol.auto.title" = "Automatic"; +"preference.protocol.auto.description" = "Let PrivadoVPN automatically choose the best protocol."; +"preference.protocol.ikev2.title" = "IKEv2"; +"preference.protocol.ikev2.description" = "IKEv2 provides stable security and is faster than other protocols, but is only supported on limited platforms."; +"preference.protocol.ikev2.description_ios" = "Secure your device with the fastest connection speeds."; +"preference.protocol.openVPN.title" = "OpenVPN"; +"preference.protocol.openVPN.description_ios" = "Protect your privacy with the highest level of encryption."; +"preference.protocol.openVPN.description" = "Slow but configurable, OpenVPN provides strong security and is a solid choice when other protocols are not available due to firwall restrictions."; +"preference.protocol.openVPN.description.disabled" = "OpenVPN currently available on macOS 10.14 and higher."; +"preference.protocol.openVPN.options.protocol.title" = "Protocol"; +"preference.protocol.openVPN.options.port.title" = "Port"; + +"preference.protocol.wireguard.title" = "WireGuard"; +"preference.protocol.wireguard.description" = "Wireguard is a strong balance between speed and security. A newer and more experimental VPN server, Wireguard offers the best speed and established connections."; diff --git a/macOS/Privado/Common/Categories/Array+Extension.swift b/macOS/Privado/Common/Categories/Array+Extension.swift new file mode 100644 index 00000000..0c2d394d --- /dev/null +++ b/macOS/Privado/Common/Categories/Array+Extension.swift @@ -0,0 +1,16 @@ +// +// Array+Extension.swift +// Cyberlock +// +// Created by Jura on 8/28/19. +// + +import Foundation + +public extension Array { + + subscript(safe index: Index) -> Element? { + return (self.startIndex.." + } + + private func string(forInfoDictionaryKey key: String) -> String? { + // `object(forInfoDictionaryKey:)` prefers localized info dictionary over the regular one automatically + return object(forInfoDictionaryKey: key) as? String + } + +} diff --git a/macOS/Privado/Common/Categories/Data+Extension.swift b/macOS/Privado/Common/Categories/Data+Extension.swift new file mode 100644 index 00000000..a487b51a --- /dev/null +++ b/macOS/Privado/Common/Categories/Data+Extension.swift @@ -0,0 +1,24 @@ +// +// Data+Extension.swift +// Privado +// +// Created by Jura on 11/14/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public extension Data { + + init?(randomOfLength length: Int) { + var bytes = [UInt8](repeating: 0, count: length) + let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes) + if status == errSecSuccess { + self.init(bytes) + } else { + // throw an error + return nil + } + } + +} diff --git a/macOS/Privado/Common/Categories/NSEvent+Extension.swift b/macOS/Privado/Common/Categories/NSEvent+Extension.swift new file mode 100644 index 00000000..4b469c5b --- /dev/null +++ b/macOS/Privado/Common/Categories/NSEvent+Extension.swift @@ -0,0 +1,50 @@ +// +// NSEvent+Extension.swift +// Cyberlock +// +// Created by Jura on 9/10/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +extension NSEvent { + + static let userInteractionEvents: [NSEvent.EventType] = { + var events: [NSEvent.EventType] = [ + .leftMouseDown, + .leftMouseUp, + .rightMouseDown, + .rightMouseUp, + .leftMouseDragged, + .rightMouseDragged, + .keyDown, + .keyUp, + .scrollWheel, + .tabletPoint, + .otherMouseDown, + .otherMouseUp, + .otherMouseDragged, + .gesture, + .magnify, + .swipe, + .rotate, + .beginGesture, + .endGesture, + .smartMagnify, + .quickLook, + .directTouch + ] + + if #available(macOS 10.10.3, *) { + events.append(.pressure) + } + + return events + }() + + var isUserInteraction: Bool { + return NSEvent.userInteractionEvents.contains(type) + } +} diff --git a/macOS/Privado/Common/Categories/NSImage+Extension.swift b/macOS/Privado/Common/Categories/NSImage+Extension.swift new file mode 100644 index 00000000..2057ed05 --- /dev/null +++ b/macOS/Privado/Common/Categories/NSImage+Extension.swift @@ -0,0 +1,32 @@ +// +// NSImage+Extension.swift +// Privado +// +// Created by Juraldinio on 11/28/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +extension NSImage { + + func imageWithTintColor(tintColor: NSColor) -> NSImage { + + if self.isTemplate == false { + return self + } + + guard let image = self.copy() as? NSImage else { return self } + image.lockFocus() + + tintColor.set() + NSRect(x: 0, y: 0, width: image.size.width, height: image.size.height).fill(using: .sourceAtop) + + image.unlockFocus() + image.isTemplate = false + + return image + } + +} diff --git a/macOS/Privado/Common/Categories/NSView+Extension.swift b/macOS/Privado/Common/Categories/NSView+Extension.swift new file mode 100644 index 00000000..e1b104f2 --- /dev/null +++ b/macOS/Privado/Common/Categories/NSView+Extension.swift @@ -0,0 +1,45 @@ +// +// NSView+Extension.swift +// Cyberlock +// +// Created by Jura on 9/10/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +extension NSView { + + @discardableResult + func constrainToSuperviewBounds() -> [NSLayoutConstraint] { + + guard let superview = self.superview else { preconditionFailure("superview has to be set first") } + + var result = [NSLayoutConstraint]() + result.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", + options: .directionLeadingToTrailing, + metrics: nil, + views: ["subview": self])) + result.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", + options: .directionLeadingToTrailing, + metrics: nil, + views: ["subview": self])) + self.translatesAutoresizingMaskIntoConstraints = false + superview.addConstraints(result) + + return result + } + + var backgroundColor: NSColor? { + get { + guard let layer = layer, let backgroundColor = layer.backgroundColor else { return nil } + return NSColor(cgColor: backgroundColor) + } + set { + wantsLayer = true + layer?.backgroundColor = newValue?.cgColor + } + } + +} diff --git a/macOS/Privado/Common/Categories/Optional+Extension.swift b/macOS/Privado/Common/Categories/Optional+Extension.swift new file mode 100644 index 00000000..c16b1034 --- /dev/null +++ b/macOS/Privado/Common/Categories/Optional+Extension.swift @@ -0,0 +1,20 @@ +// +// Optional+Extension.swift +// Cyberlock +// +// Created by Jura on 8/13/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +extension Optional { + + public var isExist: Bool { + if case .some = self { + return true + } + return false + } + +} diff --git a/macOS/Privado/Common/Categories/Result+Extension.swift b/macOS/Privado/Common/Categories/Result+Extension.swift new file mode 100644 index 00000000..6e77dca7 --- /dev/null +++ b/macOS/Privado/Common/Categories/Result+Extension.swift @@ -0,0 +1,106 @@ +// +// Result+Extension.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public typealias Status = Result + +public extension Result where Success == Void { + static var succeeded: Result { return Result.success(()) } +} + +@discardableResult +public func catchToResult(_ f: () throws -> T) -> Result { + do { + return .success(try f()) + } catch { + return .failure(error) + } +} + +@discardableResult +public func catchToResult(_ f: () throws -> Void) -> Status { + do { + return .success(try f()) + } catch { + return .failure(error) + } +} + +public func convertToTuple(_ result: Result) -> (T?, Error?) { + switch result { + case .success(let x): + return (x, nil) + case .failure(let error): + return (nil, error) + } +} + +public extension Result { + + @discardableResult + func onFail(_ f: (Failure) -> Void) -> Result { + if case .failure(let error) = self { + f(error) + } + return self + } + + @discardableResult + func onSuccess(_ f: (Success) -> Void) -> Result { + if case .success(let val) = self { + f(val) + } + return self + } + + func flatMap(_ f: (Success) -> U) -> Result { + switch self { + case .success(let val): return .success(f(val)) + case .failure(let error): return .failure(error) + } + } + + func flatMap(_ f: (Success) throws -> U) -> Result { + switch self { + case .success(let val): + return catchToResult { try f(val) } + case .failure(let error): + return .failure(error) + } + } + + func flatMap(_ f: (Success) -> Result) -> Result { + switch self { + case .success(let val): return f(val) + case .failure(let error): return .failure(error) + } + } + + var optional: Success? { + switch self { + case .success(let val): return .some(val) + case .failure: return .none + } + } + + var isSuccess: Bool { + switch self { + case .success: return true + case .failure: return false + } + } + + var maybeError: Error? { + switch self { + case .success: return .none + case .failure(let error): return .some(error) + } + } + +} diff --git a/macOS/Privado/Common/Categories/String+Extension.swift b/macOS/Privado/Common/Categories/String+Extension.swift new file mode 100644 index 00000000..0d4757f9 --- /dev/null +++ b/macOS/Privado/Common/Categories/String+Extension.swift @@ -0,0 +1,34 @@ +// +// String+Extension.swift +// PrivadoVPN +// +// Created by Juraldinio on 7/15/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +extension String { + + func replace(_ pattern: String, replacement: String) throws -> String { + + let regex = try NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) + return regex.stringByReplacingMatches( + in: self, + options: [.withTransparentBounds], + range: NSRange(location: 0, length: self.count), + withTemplate: replacement + ) + } + + func substring(using range: NSRange) -> String? { + let intersection = NSIntersectionRange(NSRange(location: 0, length: self.count), range) + guard intersection.location >= 0 + , intersection.length > 0 else { + return nil + } + let start = self.index(self.startIndex, offsetBy: intersection.location) + let end = self.index(start, offsetBy: intersection.length) + return String(self[start.. URL? { + let domainPath: FileManager.SearchPathDirectory = .documentDirectory + let urls = FileManager.default.urls(for: domainPath, in: .userDomainMask) + guard let url = urls.first else { return nil } + + let directory = url.appendingPathComponent(Environment.bundleName) + try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false, attributes: nil) + + let file = directory.appendingPathComponent(UUID().uuidString) + if FileManager.default.fileExists(atPath: file.absoluteString) { + try? FileManager.default.removeItem(at: file) + } + + return file + } + + func remove() { + if FileManager.default.fileExists(atPath: self.absoluteString) { + try? FileManager.default.removeItem(at: self) + } + } +} diff --git a/macOS/Privado/Common/Comet/CometLogger.swift b/macOS/Privado/Common/Comet/CometLogger.swift new file mode 100644 index 00000000..f6f58786 --- /dev/null +++ b/macOS/Privado/Common/Comet/CometLogger.swift @@ -0,0 +1,95 @@ +// +// CometLogger.swift +// Privado +// +// Created by Juraldinio on 11/12/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class CometLogger: CometLoggerProtocol { + + var level: CometLogLevel = .info + + private(set) var loggers = [CometLoggerProtocol]() + + static let shared = CometLogger() + + private init() { } + + func add(logger: CometLoggerProtocol) { + self.loggers.append(logger) + } + + func add(loggers: [CometLoggerProtocol]) { + self.loggers.append(contentsOf: loggers) + } + + func clear() { + self.loggers.removeAll() + } + + // MARK: - CometLoggerProtocol + + func setAdditionalObject(with value: Any, forKey key: String) { + self.loggers + .forEach { $0.setAdditionalObject(with: value, forKey: key) } + } + +} + +extension CometLogger: ErrorCometLoggerProtocol { + + func record(error: Error) { + self.loggers + .compactMap { $0 as? ErrorCometLoggerProtocol } + .forEach { $0.record(error: error) } + } + + func record(error: Error, userInfo: [String: Any]?, secured: [String: Any]?) { + self.loggers + .compactMap { $0 as? ErrorCometLoggerProtocol } + .forEach { $0.record(error: error, userInfo: userInfo, secured: (self.level == .debug ? secured : nil) ) } + } +} + +extension CometLogger: EventCometLoggerProtocol { + + func event(name: String, attributes: [String: Any], secured: [String: Any]?) { + self.loggers + .compactMap { $0 as? EventCometLoggerProtocol } + .forEach { $0.event(name: name, attributes: attributes, secured: (self.level == .debug ? secured : nil)) } + } +} + +extension CometLogger: NetworkCometLoggerProtocol { + func record(request: URLRequest) { + self.loggers + .compactMap { $0 as? NetworkCometLoggerProtocol } + .forEach { $0.record(request: request) } + } + + func record(response: URLResponse, data: Data?) { + self.loggers + .compactMap { $0 as? NetworkCometLoggerProtocol } + .forEach { $0.record(response: response, data: data) } + } +} + +extension CometLogger: VPNCometLoggerProtocol { + + func vpnLog(type: String, error: Error) { + self.loggers + .compactMap { $0 as? VPNCometLoggerProtocol } + .forEach { $0.vpnLog(type: type, error: error) } + } + + func vpnEvent(type: String, message: String, secured: String?) { + self.loggers + .compactMap { $0 as? VPNCometLoggerProtocol } + .forEach { $0.vpnEvent(type: type, message: message, secured: (self.level == .debug ? secured : nil)) } + } + + +} diff --git a/macOS/Privado/Common/Comet/CometProtocol.swift b/macOS/Privado/Common/Comet/CometProtocol.swift new file mode 100644 index 00000000..2e877402 --- /dev/null +++ b/macOS/Privado/Common/Comet/CometProtocol.swift @@ -0,0 +1,45 @@ +// +// CometProtocol.swift +// Privado +// +// Created by Juraldinio on 11/12/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +enum CometLogLevel { + case off + case error + case info + case debug +} + +protocol CometLoggerProtocol { + func setAdditionalObject(with value: Any, forKey key: String) +} + +protocol ErrorCometLoggerProtocol: CometLoggerProtocol { + func record(error: Error) + func record(error: Error, userInfo: [String: Any]?, secured: [String: Any]?) +} + +protocol EventCometLoggerProtocol: CometLoggerProtocol { + func event(name: String, attributes: [String: Any], secured: [String: Any]?) +} + +protocol NetworkCometLoggerProtocol: CometLoggerProtocol { + func record(request: URLRequest) + func record(response: URLResponse, data: Data?) +} + +protocol VPNCometLoggerProtocol: CometLoggerProtocol { + func vpnLog(type: String, error: Error) + func vpnEvent(type: String, message: String, secured: String?) +} + +// + +extension CometLoggerProtocol { + func setAdditionalObject(with value: Any, forKey key: String) { } +} diff --git a/macOS/Privado/Common/Comet/Loggers/AppCenterLogger.swift b/macOS/Privado/Common/Comet/Loggers/AppCenterLogger.swift new file mode 100644 index 00000000..60d3f04d --- /dev/null +++ b/macOS/Privado/Common/Comet/Loggers/AppCenterLogger.swift @@ -0,0 +1,54 @@ +// +// AppCenterLogger.swift +// Privado +// +// Created by Jura on 3/13/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation +import AppCenter +import AppCenterAnalytics +import AppCenterCrashes + +final class AppCenterLogger: EventCometLoggerProtocol { + + init() { + #if os(macOS) + #if INTERNAL + MSAppCenter.start("430b7e48-a17b-475a-89e6-0f2d0ffb5e43", withServices: [MSAnalytics.self, MSCrashes.self]) + #else + MSAppCenter.start("85703fa1-c9fc-4f84-9373-605c11f310bf", withServices: [MSAnalytics.self, MSCrashes.self]) + #endif + #else + MSAppCenter.start("ebaa632a-cee6-462b-a321-bb086b5e4cef", withServices: [MSAnalytics.self, MSCrashes.self]) + #endif + } + + func event(name: String, attributes: [String: Any], secured: [String: Any]?) { + + guard name.contains(PrivadoConstants.Event.STATISTIC.prefix) else { return } + + let eventName: String? + switch name { + case PrivadoConstants.Event.STATISTIC.login: + eventName = "Login" + case PrivadoConstants.Event.STATISTIC.start: + eventName = "AppStartup" + case PrivadoConstants.Event.STATISTIC.connection: + eventName = "Connection" + case PrivadoConstants.Event.STATISTIC.settings: + eventName = "SaveSettings" + case PrivadoConstants.Event.STATISTIC.errorConnection: + eventName = "ErrorConnectExecute" + default: + eventName = nil + } + + guard let event = eventName else { return } + + MSAnalytics.trackEvent(event, + withProperties: attributes.mapValues({ value -> String in String(describing: value) })) + + } +} diff --git a/macOS/Privado/Common/Comet/Loggers/EventLogger.swift b/macOS/Privado/Common/Comet/Loggers/EventLogger.swift new file mode 100644 index 00000000..317930c2 --- /dev/null +++ b/macOS/Privado/Common/Comet/Loggers/EventLogger.swift @@ -0,0 +1,50 @@ +// +// EventLogger.swift +// Privado +// +// Created by Juraldinio on 1/21/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +final class EventLogger: EventCometLoggerProtocol { + + func event(name: String, attributes: [String: Any], secured: [String: Any]?) { + + guard !name.contains(PrivadoConstants.Event.STATISTIC.prefix) else { return } + + let payloadDict = attributes.reduce(secured ?? [:]) { acc, elem in + var acc = acc + acc[elem.0] = elem.1 + return acc + } + + let payload = payloadDict.compactMap { (key, value) -> String? in + guard let dic = value as? [String: Any] else { + return "\(key) = \(value)" + } + return "[" + dic.compactMap { (ikey, ivalue) -> String? in + return "\(ikey) = \(ivalue)" + }.joined(separator: "; ") + "]" + }.joined(separator: "; ") + + self.save(tag: "Event", label: name, payload: payload) + } + + private func save(tag: String, label: String, payload: String) { + + guard let coordinator = DatabaseAnswer.loggingDB else { return } + + let context = coordinator.createContext() + + let record: LogRecord = context.create() + record.tag = "Event" + record.label = label + record.payload = payload + + context.save() + coordinator.save() + } + +} diff --git a/macOS/Privado/Common/Comet/Loggers/NetworkLogger.swift b/macOS/Privado/Common/Comet/Loggers/NetworkLogger.swift new file mode 100644 index 00000000..c75d29df --- /dev/null +++ b/macOS/Privado/Common/Comet/Loggers/NetworkLogger.swift @@ -0,0 +1,64 @@ +// +// NetworkLogger.swift +// Privado +// +// Created by Juraldinio on 11/13/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class NetworkLogger: NetworkCometLoggerProtocol { + + private enum Constants { + static let tag = "NETWORK" + static let request = "REQ" + static let response = "RESP" + } + + func record(request: URLRequest) { + guard let url = request.url else { return } + self.save(tag: Constants.tag, label: Constants.request, payload: url.absoluteString) + } + + func record(response: URLResponse, data: Data?) { + guard let httpResponse = response as? HTTPURLResponse else { + return + } + + let value: String + #if PRODUCTION + value = "-" + #else + if let data = data + , let converted = String(data: data, encoding: .utf8) { + value = converted + } else { + value = "" + } + #endif + + let payload = "(\(httpResponse.statusCode))\n\(value)" + self.save(tag: Constants.tag, label: Constants.response, payload: payload) + } + + // MARK: - Private + + private func save(tag: String, label: String, payload: String) { + + guard let coordinator = DatabaseAnswer.loggingDB else { return } + + let context = coordinator.createContext() + let log: LogRecord = context.create() + + log.uuid = UUID().uuidString + log.tag = tag + log.label = label + log.date = Date() + log.payload = payload + + context.save() + coordinator.save() + } + +} diff --git a/macOS/Privado/Common/Comet/Loggers/SentryLogger.swift b/macOS/Privado/Common/Comet/Loggers/SentryLogger.swift new file mode 100644 index 00000000..974aea90 --- /dev/null +++ b/macOS/Privado/Common/Comet/Loggers/SentryLogger.swift @@ -0,0 +1,47 @@ +// +// SentryLogger.swift +// Privado +// +// Created by Juraldinio on 11/13/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import Sentry + +final class SentryLogger: ErrorCometLoggerProtocol { + + private enum Constants { + #if os(iOS) + static let dsn = "https://168a9c7234b34859981b49262d022f30@sentry.privado.io/10" + #else + static let dsn = "https://87da46d8f6ba4d029a047dd45669d94b@sentry.privado.io/3" + #endif + } + + init() { + + SentrySDK.start { options in + options.dsn = Constants.dsn + options.logLevel = .verbose + } + } + + // MARK: - ErrorCometLoggerProtocol + + func record(error: Error) { + self.record(error: error, userInfo: nil, secured: nil) + } + + func record(error: Error, userInfo: [String: Any]?, secured: [String: Any]?) { + + let event = Event(level: .error) + event.message = error.localizedDescription + + if let info = userInfo { + event.extra = info + } + + SentrySDK.capture(event: event) + } +} diff --git a/macOS/Privado/Common/Comet/Loggers/VPNLogger.swift b/macOS/Privado/Common/Comet/Loggers/VPNLogger.swift new file mode 100644 index 00000000..5a8bc490 --- /dev/null +++ b/macOS/Privado/Common/Comet/Loggers/VPNLogger.swift @@ -0,0 +1,44 @@ +// +// VPNLogger.swift +// Privado +// +// Created by Juraldinio on 11/20/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class VPNLogger: VPNCometLoggerProtocol { + + private enum Constants { + static let tag = "VPN" + } + + func vpnLog(type: String, error: Error) { + self.save(tag: Constants.tag, label: type, payload: error.localizedDescription) + } + + func vpnEvent(type: String, message: String, secured: String?) { + self.save(tag: Constants.tag, label: type, payload: message + (secured ?? "")) + } + + // MARK: - Private + + private func save(tag: String, label: String, payload: String) { + + guard let coordinator = DatabaseAnswer.loggingDB else { return } + + let context = coordinator.createContext() + let log: LogRecord = context.create() + + log.uuid = UUID().uuidString + log.tag = tag + log.label = label + log.date = Date() + log.payload = payload + + context.save() + coordinator.save() + } + +} diff --git a/macOS/Privado/Common/CountryResolver.swift b/macOS/Privado/Common/CountryResolver.swift new file mode 100644 index 00000000..140734df --- /dev/null +++ b/macOS/Privado/Common/CountryResolver.swift @@ -0,0 +1,144 @@ +// +// CountryResolver.swift +// Privado +// +// Created by Juraldinio on 1/4/20. +// Copyright © 2020 Omicronmedia. All rights reserved. +// + +import Foundation + +final class CountryResolver { + + typealias Code = String + typealias Name = String + + static func code(from name: Name) -> Code { + let result = self.сodeNames.first { _, value -> Bool in + return value == name + } + guard let (key, _) = result else { return name } + return key + } + + static func name(from code: Code) -> Name { + guard let name = self.сodeNames[code] else { return code } + return name + } + + private static let сodeNames: [Code: Name] = + ["AD": "Andorra", + "AE": "United Arab Emirates", + "AF": "Afghanistan", + "AL": "Albania", + "AM": "Armenia", + "AT": "Austria", + "AR": "Argentina", + "AU": "Australia", + "AZ": "Azerbaijan", + "BA": "Bosnia and Herzegovina", + "BD": "Bangladesh", + "BE": "Belgium", + "BG": "Bulgaria", + "BN": "Brunei", + "BO": "Bolivia", + "BR": "Brazil", + "BS": "The Bahamas", + "BT": "Bhutan", + "BY": "Belarus", + "BZ": "Belize", + "CA": "Canada", + "CH": "Switzerland", + "CL": "Chilie", + "CO": "Colombia", + "CR": "Costa-Rica", + "CY": "Cyprus", + "CZ": "Czechia", + "DE": "Germany", + "DK": "Denmark", + "DZ": "Algeria", + "EC": "Ecuador", + "EE": "Estonia", + "EG": "Egypt", + "ES": "Spain", + "ET": "Ethiopia", + "FI": "Finland", + "FR": "France", + "GB": "United Kingdom", + "GD": "Grenada", + "GE": "Georgia", + "GR": "Greece", + "GT": "Guatemala", + "HK": "Hong Kong", + "HR": "Croatia", + "HT": "Haiti", + "HU": "Hungary", + "ID": "Indonesia", + "IE": "Ireland", + "IL": "Israel", + "IM": "Isle of Man", + "IN": "India", + "IS": "Iceland", + "IT": "Italy", + "JM": "Jamaica", + "JP": "Japan", + "KE": "Kenya", + "KG": "Kyrgyzstan", + "KH": "Cambodia", + "KR": "South Korea", + "KW": "Kuwait", + "KZ": "Kazakhstan", + "LA": "Laos", + "LI": "Liechtenstein", + "LK": "Sri Lanka", + "LT": "Lithuania", + "LU": "Luxembourg", + "LV": "Latvia", + "MC": "Monaco", + "MD": "Moldova", + "ME": "Montenegro", + "MK": "North Macedonia", + "MM": "Myanmar", + "MN": "Mongolia", + "MO": "Macau", + "MT": "Malta", + "MX": "Mexico", + "MY": "Malaysia", + "NG": "Nigeria", + "NL": "Netherlands", + "NO": "Norway", + "NP": "Nepal", + "NZ": "New Zealand", + "PA": "Panama", + "PE": "Peru", + "PG": "Papua New Guinea", + "PH": "Philippines", + "PK": "Pakistan", + "PL": "Poland", + "PT": "Portugal", + "QA": "Qatar", + "RO": "Romania", + "RS": "Serbia", + "RU": "Russia", + "SE": "Sweden", + "SG": "Singapore", + "SI": "Slovenia", + "SK": "Slovakia", + "SV": "El Salvador", + "TH": "Thailand", + "TJ": "Tajikistan", + "TM": "Turkmenistan", + "TR": "Turkey", + "TT": "Trinidad and Tobago", + "TW": "Taiwan", + "UA": "Ukraine", + "UK": "United Kingdom", + "US": "USA", + "UY": "Uruguay", + "UZ": "Uzbekistan", + "VE": "Venezuela", + "VN": "Vietnam", + "ZA": "South Africa" + ] + +} diff --git a/macOS/Privado/Common/Database/CoreData/CoreDataContext.swift b/macOS/Privado/Common/Database/CoreData/CoreDataContext.swift new file mode 100644 index 00000000..f7c78eff --- /dev/null +++ b/macOS/Privado/Common/Database/CoreData/CoreDataContext.swift @@ -0,0 +1,69 @@ +// +// CoreDataContext.swift +// Privado +// +// Created by Juraldinio on 10/7/20. +// Copyright © 2020 Omicronmedia. All rights reserved. +// + +import Foundation +import CoreData + +public struct CoreDataContext { + + let managedObjectContext: NSManagedObjectContext + + init(context: NSManagedObjectContext) { + self.managedObjectContext = context + } + + public func create() -> EntityType { + return EntityType(context: self.managedObjectContext) + } + + public lazy var observer = CoreDataObserver(context: self.managedObjectContext) + + public func fetch(_ request: NSFetchRequest) -> [EntityType] { + return (try? self.managedObjectContext.fetch(request)) ?? [] + } + + public func delete(_ request: NSManagedObject) { + self.managedObjectContext.delete(request) + } + + + public func save(sync: Bool = true, forced: Bool = false) { + + guard self.managedObjectContext.hasChanges else { return } + + if sync { + self.saveSync(context: self.managedObjectContext) + } else { + self.saveAsync(context: self.managedObjectContext) + } + + } + + private func saveSync(context: NSManagedObjectContext) { + + context.performAndWait { + do { + try context.save() + } catch { + print(error) + } + } + } + + private func saveAsync(context: NSManagedObjectContext) { + + context.perform { + do { + try context.save() + } catch { + print(error) + } + } + } + +} diff --git a/macOS/Privado/Common/Database/CoreData/CoreDataCoordinator.swift b/macOS/Privado/Common/Database/CoreData/CoreDataCoordinator.swift new file mode 100644 index 00000000..f111ad5a --- /dev/null +++ b/macOS/Privado/Common/Database/CoreData/CoreDataCoordinator.swift @@ -0,0 +1,218 @@ +// +// CoreDataCoordinator.swift +// Privado +// +// Created by Juraldinio on 10/7/20. +// Copyright © 2020 Omicronmedia. All rights reserved. +// + +import Foundation +import CoreData + +final public class CoreDataCoordinator { + + public enum CoreDataError: Error { + /// Can't find momd + case find(model: String, bundle: Bundle?) + /// Can't extract model name + case initialize(url: URL) + /// Unable to load NSManagedObjectModel + case load(model: URL) + /// Unable to Add Persistent Store + case store(model: String) + /// Unknown error + case unknown(error: Error) + } + + public typealias CompletionClosure = (Result) -> Void + + // MARK: - Properties + + public let modelName: String + + // MARK: - Core Data Stack + + private let model: NSManagedObjectModel + private let persistentStoreCoordinator: NSPersistentStoreCoordinator + + private lazy var privateManagedObjectContext: NSManagedObjectContext = { + + let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator + return managedObjectContext + }() + + // MARK: - Initialization + + private init(url: URL) throws { + + let pathExtension = url.pathExtension + guard let name = try? url.lastPathComponent.replace(pathExtension, replacement: "") else { + throw CoreDataError.initialize(url: url) + } + + guard let model = NSManagedObjectModel(contentsOf: url) else { + throw CoreDataError.load(model: url) + } + + self.modelName = name + self.model = model + self.persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model) + } + + private convenience init(name: String, bundle: Bundle? = nil) throws { + + let fileExtension = "momd" + + if let bundle = bundle + , let url = bundle.url(forResource: name, withExtension: fileExtension) { + try self.init(url: url) + } else if let url = Bundle.main.url(forResource: name, withExtension: fileExtension) { + try self.init(url: url) + } else { + throw CoreDataError.find(model: name, bundle: bundle) + } + } + + // MARK: - Methods + + public lazy var mainContext: CoreDataContext = { + + let main = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) + main.parent = self.privateManagedObjectContext + + return CoreDataContext(context: main) + }() + + public func createContext() -> CoreDataContext { + + let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + context.parent = self.mainContext.managedObjectContext + + return CoreDataContext(context: context) + } + + public func save() { + + self.mainContext.managedObjectContext.performAndWait { + do { + if self.mainContext.managedObjectContext.hasChanges { + try self.mainContext.managedObjectContext.save() + } + } catch { + print("Unable to Save Changes of Main Managed Object Context") + print("\(error), \(error.localizedDescription)") + } + } + + self.privateManagedObjectContext.perform { + do { + if self.privateManagedObjectContext.hasChanges { + try self.privateManagedObjectContext.save() + } + } catch { + print("Unable to Save Changes of Private Managed Object Context") + print("\(error), \(error.localizedDescription)") + } + } + + } + + // MARK: - Static + + public static func create(url modelURL: URL, async: Bool = true, completion: CompletionClosure?) -> CoreDataCoordinator? { + do { + let coordinator = try CoreDataCoordinator(url: modelURL) + Self.setup(coordinator: coordinator, async: async, completion: completion) + return coordinator + } catch let error as CoreDataError { + completion?(.failure(error)) + } catch { + completion?(.failure(.unknown(error: error))) + } + + return nil + } + + public static func create(name modelName: String, using bundle: Bundle? = nil, async: Bool = true, completion: CompletionClosure?) -> CoreDataCoordinator? { + do { + let coordinator = try CoreDataCoordinator(name: modelName, bundle: bundle) + Self.setup(coordinator: coordinator, async: async, completion: completion) + return coordinator + } catch let error as CoreDataError { + completion?(.failure(error)) + } catch { + completion?(.failure(.unknown(error: error))) + } + + return nil + } + + private static func setup(coordinator: CoreDataCoordinator, async: Bool, completion: CompletionClosure?) { + + let storeCoordinator = coordinator.persistentStoreCoordinator + + let fileManager = FileManager.default + let storeName = "\(coordinator.modelName).sqlite" + + let documentsDirectoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first + let persistentStoreURL = documentsDirectoryURL?.appendingPathComponent(storeName) + + if async { + + DispatchQueue.global(qos: .userInitiated).async { + + var coreDataError: CoreDataError? + do { + let options = [ + NSMigratePersistentStoresAutomaticallyOption: true, + NSInferMappingModelAutomaticallyOption: true + ] + + // Add Persistent Store - may be long operation! + try storeCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, + configurationName: nil, + at: persistentStoreURL, + options: options) + + } catch { + coreDataError = .store(model: coordinator.modelName) + } + + DispatchQueue.main.async { + if let error = coreDataError { + completion?(.failure(error)) + } else { + completion?(.success(coordinator)) + } + + } + } + } else { + + var coreDataError: CoreDataError? + do { + let options = [ + NSMigratePersistentStoresAutomaticallyOption: true, + NSInferMappingModelAutomaticallyOption: true + ] + + // Add Persistent Store - may be long operation! + try storeCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, + configurationName: nil, + at: persistentStoreURL, + options: options) + + } catch { + coreDataError = .store(model: coordinator.modelName) + } + + if let error = coreDataError { + completion?(.failure(error)) + } else { + completion?(.success(coordinator)) + } + } + } + +} diff --git a/macOS/Privado/Common/Database/CoreData/CoreDataObserver.swift b/macOS/Privado/Common/Database/CoreData/CoreDataObserver.swift new file mode 100644 index 00000000..68fc712d --- /dev/null +++ b/macOS/Privado/Common/Database/CoreData/CoreDataObserver.swift @@ -0,0 +1,157 @@ +// +// CoreDataObserver.swift +// Privado +// +// Created by Juraldinio on 10/8/20. +// Copyright © 2020 Omicronmedia. All rights reserved. +// + +import Foundation +import CoreData + +final public class CoreDataObserver { + + public typealias CompletionClosure = (NSManagedObject, CoreDataObserver.State) -> Void + public typealias ChangeClosure = (_ notification: Notification, _ changedObjects: [CoreDataObserver.ChangeType]) -> Void + + public struct State: OptionSet { + + public let rawValue: Int + public init(rawValue: Int) { self.rawValue = rawValue } + + public static let inserted = State(rawValue: 1 << 0) + public static let updated = State(rawValue: 1 << 1) + public static let deleted = State(rawValue: 1 << 2) + public static let refreshed = State(rawValue: 1 << 3) + public static let all: State = [inserted, updated, deleted, refreshed] + } + + public enum ChangeType { + + case updated(NSManagedObject) + case refreshed(NSManagedObject) + case inserted(NSManagedObject) + case deleted(NSManagedObject) + + public func managedObject() -> NSManagedObject { + switch self { + case let .updated(value): return value + case let .inserted(value): return value + case let .refreshed(value): return value + case let .deleted(value): return value + } + } + } + + struct Action { + let state: State + let completionBlock: CompletionClosure + } + + private let context: NSManagedObjectContext + private var notificationObserver: NSObjectProtocol? + + private var actions = [NSManagedObjectID: [Action]]() + + public var enabled = true + public var changed: ChangeClosure? + + // MARK: - Init + + init(context: NSManagedObjectContext) { + + self.context = context + + self.notificationObserver = NotificationCenter + .default + .addObserver(forName: NSNotification.Name.NSManagedObjectContextObjectsDidChange, object: context, queue: nil) { [weak self] notification in + self?.handleContextObjectDidChangeNotification(notification: notification) + } + } + + deinit { + guard let observer = self.notificationObserver else { return } + NotificationCenter.default.removeObserver(observer) + } + + // MARK: - Public + + public func addObserver(object: NSManagedObject, state: CoreDataObserver.State = CoreDataObserver.State.all, completionBlock: @escaping CompletionClosure) { + + let action = Action(state: state, completionBlock: completionBlock) + if var actions = self.actions[object.objectID] { + actions.append(action) + self.actions[object.objectID] = actions + } else { + self.actions[object.objectID] = [action] + } + + } + + public func removeObserver(object: NSManagedObject, forState state: CoreDataObserver.State = CoreDataObserver.State.all) { + + if state == State.all { + self.actions.removeValue(forKey: object.objectID) + } else if let actions = self.actions[object.objectID] { + self.actions[object.objectID] = actions.filter { !$0.state.contains(state) } + } + } + + public func removeAllObservers() { + self.actions.removeAll() + } + + // MARK: - Private + + private func handleContextObjectDidChangeNotification(notification: Notification) { + + guard enabled + , let context = notification.object as? NSManagedObjectContext + , self.context == context + , let userInfo = notification.userInfo else { + return + } + + let insertedObjectsSet = userInfo[NSInsertedObjectsKey] as? Set ?? Set() + let updatedObjectsSet = userInfo[NSUpdatedObjectsKey] as? Set ?? Set() + let deletedObjectsSet = userInfo[NSDeletedObjectsKey] as? Set ?? Set() + let refreshedObjectsSet = userInfo[NSRefreshedObjectsKey] as? Set ?? Set() + + var combinedObjectChanges = insertedObjectsSet.map { ChangeType.inserted($0) } + combinedObjectChanges += updatedObjectsSet.map { ChangeType.updated($0) } + combinedObjectChanges += deletedObjectsSet.map { ChangeType.deleted($0) } + combinedObjectChanges += refreshedObjectsSet.map { ChangeType.refreshed($0) } + + self.changed?(notification, combinedObjectChanges) + + let combinedSet = insertedObjectsSet + .union(updatedObjectsSet) + .union(deletedObjectsSet) + + let allObjectIDs = Array(self.actions.keys) + let filteredObjects = combinedSet.filter { allObjectIDs.contains($0.objectID) } + + filteredObjects.forEach { object in + + guard let actions = self.actions[object.objectID] else { return } + + actions.forEach { action in + if action.state.contains(.inserted) + , insertedObjectsSet.contains(object) { + action.completionBlock(object, .inserted) + } else if action.state.contains(.updated) + , updatedObjectsSet.contains(object) { + action.completionBlock(object, .updated) + } else if action.state.contains(.deleted) + , deletedObjectsSet.contains(object) { + action.completionBlock(object, .deleted) + } else if action.state.contains(.refreshed) + , refreshedObjectsSet.contains(object) { + action.completionBlock(object, .refreshed) + } + } + } + + } + +} diff --git a/macOS/Privado/Common/Database/DatabaseAnswer.swift b/macOS/Privado/Common/Database/DatabaseAnswer.swift new file mode 100644 index 00000000..257614e5 --- /dev/null +++ b/macOS/Privado/Common/Database/DatabaseAnswer.swift @@ -0,0 +1,187 @@ +// +// DatabaseAnswer.swift +// Privado +// +// Created by Juraldinio on 11/13/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import CoreData + +final class DatabaseAnswer { + + private enum Constants { + static let maxLogRecords = 100 + static let encryptionKey = "DatabaseEncryptionKey" + } + + // MARK: - Methods + + private static var encryptionKey: Data { + + let bytesCount = 64 + let keychain = KeychainSwift() + if let data = keychain.getData(Constants.encryptionKey) { + return data + } + + if let data = Data(randomOfLength: bytesCount) { + keychain.set(data, forKey: Constants.encryptionKey) + return data + } + + if let data = UUID().uuidString.data(using: .utf8) { + keychain.set(data, forKey: Constants.encryptionKey) + return data + } + + return Data(repeating: 8, count: bytesCount) + + } + + private static var loggerUrl: URL? { + #if os(iOS) + let domainPath: FileManager.SearchPathDirectory = .documentDirectory + #else + let domainPath: FileManager.SearchPathDirectory = .applicationSupportDirectory + #endif + + let urls = FileManager.default.urls(for: domainPath, in: .userDomainMask) + guard let url = urls.first + , let weekday = Calendar.current.dateComponents([.weekday], from: Date()).weekday else { + return nil + } + + let directory = url.appendingPathComponent(Environment.bundleName) + try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false, attributes: nil) + + let file = directory.appendingPathComponent("pl0\(weekday).realm") + if !FileManager.default.fileExists(atPath: file.path) { + FileManager.default.createFile(atPath: file.path, contents: nil, attributes: nil) + } + + return file + } + + private static var commonUrl: URL? { + #if os(iOS) + let domainPath: FileManager.SearchPathDirectory = .documentDirectory + #else + let domainPath: FileManager.SearchPathDirectory = .applicationSupportDirectory + #endif + + let urls = FileManager.default.urls(for: domainPath, in: .userDomainMask) + guard let url = urls.first else { return nil } + + let directory = url.appendingPathComponent(Environment.bundleName) + try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false, attributes: nil) + + let file = directory.appendingPathComponent("psett.realm") + if !FileManager.default.fileExists(atPath: file.path) { + FileManager.default.createFile(atPath: file.path, contents: nil, attributes: nil) + } + + return file + } + + private static func createLogging(allowDelete: Bool = false) -> CoreDataCoordinator? { + + guard let url = self.loggerUrl else { return nil } + + let coordintor = CoreDataCoordinator.create(url: url, async: false, completion: nil) + + guard let context = coordintor?.createContext() else { return coordintor } + + let request: NSFetchRequest = LogRecord.fetchRequest() + + do { + let _ = context.fetch(request) + //if logs.cou + } catch { + print(error) + } + + /* + .create(forEntityName: <#T##String#>) + + if let db = realm { + + let count = db.objects(LogRecord.self).count + if count > Constants.maxLogRecords { + + do { + try db.write { + db.delete(db.objects(LogRecord.self)) + } + } catch { + print(error) + } + + } + + }*/ + + return coordintor + } + + private static func createCommon(allowDelete: Bool = false) -> CoreDataCoordinator? { + + /*var config = Realm.Configuration() + config.fileURL = self.commonUrl + #if PRODUCTION + config.encryptionKey = self.encryptionKey + #endif + config.readOnly = false + config.objectTypes = [Settings.self] + config.deleteRealmIfMigrationNeeded = true // In future we need create migration mechansm + + let realm: Realm? + do { + realm = try Realm(configuration: config) + } catch { + if allowDelete + , self.remove(at: self.commonUrl) { + return self.createCommon(allowDelete: false) + } + print(error) + realm = nil + } + + return realm*/ + + return nil + } + + private static func remove(at url: URL?) -> Bool { + guard let pathUrl = url else { return false } + + guard FileManager.default.fileExists(atPath: pathUrl.path) + , FileManager.default.isDeletableFile(atPath: pathUrl.path) else { + return false + } + + try? FileManager.default.removeItem(atPath: pathUrl.path) + return true + } + + static var loggingDB: CoreDataCoordinator? = { + return DatabaseAnswer.createLogging(allowDelete: true) + }() + + static var commonDB: CoreDataCoordinator? = { + return DatabaseAnswer.createCommon(allowDelete: true) + }() + + // TEMPORARY! Next we need migration! + static func clearCommon() { + + return + /*guard let url = self.commonUrl else { return } + try? FileManager.default.removeItem(at: url) + + guard let loggerUrl = self.loggerUrl else { return } + try? FileManager.default.removeItem(at: loggerUrl)*/ + } + +} diff --git a/macOS/Privado/Common/Environment.swift b/macOS/Privado/Common/Environment.swift new file mode 100644 index 00000000..57383870 --- /dev/null +++ b/macOS/Privado/Common/Environment.swift @@ -0,0 +1,42 @@ +// +// Environment.swift +// Privado +// +// Created by Juraldinio on 11/14/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +struct Environment { + + #if DEBUG + static let developmentTeam = PrivadoConstants.Teams.development + #else + static let developmentTeam = PrivadoConstants.Teams.production + #endif + + static let environment = ProcessInfo.processInfo.environment + static let arguments = ProcessInfo.processInfo.arguments + static let hostName = ProcessInfo.processInfo.hostName + static let processName = ProcessInfo.processInfo.processName + static let globallyUniqueString = ProcessInfo.processInfo.globallyUniqueString + + static let operationSystem = ProcessInfo.processInfo.operatingSystemVersionString + static let currentUUID = UUID().uuidString + static let bundleName = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "Privado" + + static let shortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0" + static let bundleVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1" + + static let router: NetworkRouter = { + NetworkRouter.addPlugin(ErrorNetworkPlugin()) + #if PRODUCTION + let router = NetworkRouter(environment: WLVPNEnvironment.production) + #else + let router = NetworkRouter(environment: WLVPNEnvironment.development) + #endif + router.customHeaders = ["User-Agent": "App: \(Environment.shortVersion) (\(Environment.bundleVersion)), macOS: \(Environment.operationSystem)"] + return router + }() +} diff --git a/macOS/Privado/Common/Haversine.swift b/macOS/Privado/Common/Haversine.swift new file mode 100644 index 00000000..0fac515a --- /dev/null +++ b/macOS/Privado/Common/Haversine.swift @@ -0,0 +1,60 @@ +// +// Haversine.swift +// Privado +// +// Created by Juraldinio on 12/7/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class Haversine { + + private init() {} + + enum Units { + case miles + case kilometers + case knots + } + + typealias Location = (latitude: Double, longitude: Double) + typealias Degree = Double + typealias Radian = Double + typealias Distance = Double + + private static func radians(from degree: Degree) -> Double { + return degree * .pi / 180 + } + + private static func degree(from radian: Radian) -> Double { + return radian * 180.0 / .pi + } + + static func distance(from location: Location, to toLocation: Location, unit: Units = .kilometers) -> Distance { + let thetta = location.longitude - toLocation.longitude + + var distance = + sin(radians(from: location.latitude)) + * sin(radians(from: toLocation.latitude)) + + cos(radians(from: location.latitude)) + * cos(radians(from: toLocation.latitude)) + * cos(radians(from: thetta)) + + distance = acos(distance) + distance = degree(from: distance) + distance = distance * 60 * 1.1515 + + switch unit { + case .kilometers: + distance *= 1.609344 + case .knots: + distance *= 0.8684 + case .miles: + break + } + + return distance + } + +} diff --git a/macOS/Privado/Common/Inception/AppDelegate+Inception.swift b/macOS/Privado/Common/Inception/AppDelegate+Inception.swift new file mode 100644 index 00000000..e9e0a36c --- /dev/null +++ b/macOS/Privado/Common/Inception/AppDelegate+Inception.swift @@ -0,0 +1,25 @@ +// +// ApplicationDelegate+Inception.swift +// Privado +// +// Created by Jura on 10/10/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +#if os(macOS) +import AppKit +#elseif os(iOS) +import UIKit +#endif + +func openApplicationRoute(_ route: Route) { + DispatchQueue.main.async { + #if os(macOS) + guard let appDelegateInput = NSApplication.shared.delegate as? ApplicationDelegateInput else { return } + #elseif os(iOS) + guard let appDelegateInput = UIApplication.shared.delegate as? ApplicationDelegateInput else { return } + #endif + appDelegateInput.openRoute(route) + } +} diff --git a/macOS/Privado/Common/Inception/Inception.swift b/macOS/Privado/Common/Inception/Inception.swift new file mode 100644 index 00000000..b3cd7e45 --- /dev/null +++ b/macOS/Privado/Common/Inception/Inception.swift @@ -0,0 +1,37 @@ +// +// Inception.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/18/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +final class Inception { + + // Credentials passed from url + private struct Credentials: Decodable { + let user: String + let pass: String + } + + // Example prototype + static func handle(url: URL) -> Route? { + + let base64 = "base64" + + guard let host = url.host + , host.contains(base64) else { + return nil + } + + let encoded = host.replacingOccurrences(of: base64, with: "") + guard let data = Data(base64Encoded: encoded) + , let credentials = try? JSONDecoder().decode(Credentials.self, from: data) else { + return nil + } + + return .login(type: .authenticate(credentials.user, credentials.pass)) + } +} diff --git a/macOS/Privado/Common/Inception/Route.swift b/macOS/Privado/Common/Inception/Route.swift new file mode 100644 index 00000000..e49249b3 --- /dev/null +++ b/macOS/Privado/Common/Inception/Route.swift @@ -0,0 +1,117 @@ +// +// Route.swift +// Privado +// +// Created by Jura on 10/9/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +enum Route: CustomStringConvertible { + + case hive(type: Hive) + case killSwitch + case login(type: Login) + case servers + case connect + case disconnect + case preference(tab: Preference?) + case update + case hide + case quit + case navigate(url: URL) +///just pop top Coordinator + case back + + enum Hive: CustomStringConvertible { + case check + case install + + var description: String { + switch self { + case .check: return "check" + case .install: return "install" + } + } + } + + enum Login: CustomStringConvertible, Equatable { + case clear + case refresh + case authorize + case restore + case create + case confirm + case signout + case authenticate(String, String) + + // MARK: - CustomStringConvertible + + var description: String { + switch self { + case .clear: return "clear" + case .refresh: return "refresh" + case .authorize: return "authorize" + case .restore: return "restore" + case .create: return "create" + case .confirm: return "confirm" + case .signout: return "signout" + case .authenticate: return "authenticate" + } + } + + // MARK: - Equatable + + static func == (lhs: Self, rhs: Self) -> Bool { + switch (lhs, rhs) { + case (.clear, .clear), + (.refresh, .refresh), + (.authorize, .authorize), + (.restore, .restore), + (.create, .create), + (.confirm, .confirm), + (.signout, .signout): + return true + case let (.authenticate(llogin, lpass), .authenticate(rlogin, rpass)): + return llogin == rlogin && lpass == rpass + default: + return false + } + } + } + + enum Preference: CustomStringConvertible { + case account + case options + case `protocol` + case logs + + var description: String { + switch self { + case .account: return "account" + case .options: return "options" + case .`protocol`: return "protocol" + case .logs: return "logs" + } + } + } + + var description: String { + switch self { + case let .hive(type: type): return "hive(\(type)" + case .killSwitch: return "killSwitch" + case let .login(type: type): return "login(\(type))" + case .servers: return "servers" + case .connect: return "connect" + case .disconnect: return "disconnect" + case let .preference(tab: tab): return "preference(\(String(describing: tab)))" + case .update: return "update" + case .hide: return "hide" + case .quit: return "quit" + case let .navigate(url: url): return "navigate: \(url)" + case .back: return "back" + } + } + +} diff --git a/macOS/Privado/Common/Keychain/KeychainSwift.swift b/macOS/Privado/Common/Keychain/KeychainSwift.swift new file mode 100644 index 00000000..716b4a98 --- /dev/null +++ b/macOS/Privado/Common/Keychain/KeychainSwift.swift @@ -0,0 +1,343 @@ +import Security +import Foundation + +/** + +A collection of helper functions for saving text and data in the keychain. + +*/ +open class KeychainSwift { + + var lastQueryParameters: [String: Any]? // Used by the unit tests + + /// Contains result code from the last operation. Value is noErr (0) for a successful result. + open var lastResultCode: OSStatus = noErr + + var keyPrefix = "" // Can be useful in test. + + /** + + Specify an access group that will be used to access keychain items. Access groups can be used to share keychain items between applications. When access group value is nil all application access groups are being accessed. Access group name is used by all functions: set, get, delete and clear. + + */ + open var accessGroup: String? + + + /** + + Specifies whether the items can be synchronized with other devices through iCloud. Setting this property to true will + add the item to other devices with the `set` method and obtain synchronizable items with the `get` command. Deleting synchronizable items will remove them from all devices. In order for keychain synchronization to work the user must enable "Keychain" in iCloud settings. + + Does not work on macOS. + + */ + open var synchronizable: Bool = false + + private let lock = NSLock() + + + /// Instantiate a KeychainSwift object + public init() { } + + /** + + - parameter keyPrefix: a prefix that is added before the key in get/set methods. Note that `clear` method still clears everything from the Keychain. + + */ + public init(keyPrefix: String) { + self.keyPrefix = keyPrefix + } + + /** + + Stores the text value in the keychain item under the given key. + + - parameter key: Key under which the text value is stored in the keychain. + - parameter value: Text string to be written to the keychain. + - parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. + + - returns: True if the text was successfully written to the keychain. + + */ + @discardableResult + open func set(_ value: String, forKey key: String, withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { + + if let value = value.data(using: String.Encoding.utf8) { + return set(value, forKey: key, withAccess: access) + } + + return false + } + + /** + + Stores the data in the keychain item under the given key. + + - parameter key: Key under which the data is stored in the keychain. + - parameter value: Data to be written to the keychain. + - parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. + + - returns: True if the text was successfully written to the keychain. + + */ + @discardableResult + open func set(_ value: Data, forKey key: String, withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { + + // The lock prevents the code to be run simultaneously + // from multiple threads which may result in crashing + lock.lock() + defer { lock.unlock() } + + deleteNoLock(key) // Delete any existing key before saving it + + let accessible = access?.value ?? KeychainSwiftAccessOptions.defaultOption.value + + let prefixedKey = keyWithPrefix(key) + + var query: [String: Any] = [ + KeychainSwiftConstants.klass: kSecClassGenericPassword, + KeychainSwiftConstants.attrAccount: prefixedKey, + KeychainSwiftConstants.valueData: value, + KeychainSwiftConstants.accessible: accessible + ] + + query = addAccessGroupWhenPresent(query) + query = addSynchronizableIfRequired(query, addingItems: true) + lastQueryParameters = query + + lastResultCode = SecItemAdd(query as CFDictionary, nil) + + return lastResultCode == noErr + } + + /** + + Stores the boolean value in the keychain item under the given key. + + - parameter key: Key under which the value is stored in the keychain. + - parameter value: Boolean to be written to the keychain. + - parameter withAccess: Value that indicates when your app needs access to the value in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. + + - returns: True if the value was successfully written to the keychain. + + */ + @discardableResult + open func set(_ value: Bool, forKey key: String, withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { + + let bytes: [UInt8] = value ? [1] : [0] + let data = Data(bytes) + + return set(data, forKey: key, withAccess: access) + } + + /** + + Retrieves the text value from the keychain that corresponds to the given key. + + - parameter key: The key that is used to read the keychain item. + - returns: The text value from the keychain. Returns nil if unable to read the item. + + */ + open func get(_ key: String) -> String? { + if let data = getData(key) { + + if let currentString = String(data: data, encoding: .utf8) { + return currentString + } + + lastResultCode = -67853 // errSecInvalidEncoding + } + + return nil + } + + /** + + Retrieves the data from the keychain that corresponds to the given key. + + - parameter key: The key that is used to read the keychain item. + - parameter asReference: If true, returns the data as reference (needed for things like NEVPNProtocol). + - returns: The text value from the keychain. Returns nil if unable to read the item. + + */ + open func getData(_ key: String, asReference: Bool = false) -> Data? { + // The lock prevents the code to be run simultaneously + // from multiple threads which may result in crashing + lock.lock() + defer { lock.unlock() } + + let prefixedKey = keyWithPrefix(key) + + var query: [String: Any] = [ + KeychainSwiftConstants.klass: kSecClassGenericPassword, + KeychainSwiftConstants.attrAccount: prefixedKey, + KeychainSwiftConstants.matchLimit: kSecMatchLimitOne + ] + + if asReference { + query[KeychainSwiftConstants.returnReference] = kCFBooleanTrue + } else { + query[KeychainSwiftConstants.returnData] = kCFBooleanTrue + } + + query = addAccessGroupWhenPresent(query) + query = addSynchronizableIfRequired(query, addingItems: false) + lastQueryParameters = query + + var result: AnyObject? + + lastResultCode = withUnsafeMutablePointer(to: &result) { + SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) + } + + if lastResultCode == noErr { + return result as? Data + } + + return nil + } + + /** + + Retrieves the boolean value from the keychain that corresponds to the given key. + + - parameter key: The key that is used to read the keychain item. + - returns: The boolean value from the keychain. Returns nil if unable to read the item. + + */ + open func getBool(_ key: String) -> Bool? { + guard let data = getData(key) else { return nil } + guard let firstBit = data.first else { return nil } + return firstBit == 1 + } + + /** + + Deletes the single keychain item specified by the key. + + - parameter key: The key that is used to delete the keychain item. + - returns: True if the item was successfully deleted. + + */ + @discardableResult + open func delete(_ key: String) -> Bool { + // The lock prevents the code to be run simultaneously + // from multiple threads which may result in crashing + lock.lock() + defer { lock.unlock() } + + return deleteNoLock(key) + } + + /** + Return all keys from keychain + + - returns: An string array with all keys from the keychain. + + */ + public var allKeys: [String] { + var query: [String: Any] = [ + KeychainSwiftConstants.klass: kSecClassGenericPassword, + KeychainSwiftConstants.returnData: true, + KeychainSwiftConstants.returnAttributes: true, + KeychainSwiftConstants.returnReference: true, + KeychainSwiftConstants.matchLimit: KeychainSwiftConstants.secMatchLimitAll + ] + + query = addAccessGroupWhenPresent(query) + query = addSynchronizableIfRequired(query, addingItems: false) + + var result: AnyObject? + + let lastResultCode = withUnsafeMutablePointer(to: &result) { + SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) + } + + if lastResultCode == noErr { + return (result as? [[String: Any]])?.compactMap { + $0[KeychainSwiftConstants.attrAccount] as? String } ?? [] + } + + return [] + } + + /** + + Same as `delete` but is only accessed internally, since it is not thread safe. + + - parameter key: The key that is used to delete the keychain item. + - returns: True if the item was successfully deleted. + + */ + @discardableResult + func deleteNoLock(_ key: String) -> Bool { + let prefixedKey = keyWithPrefix(key) + + var query: [String: Any] = [ + KeychainSwiftConstants.klass: kSecClassGenericPassword, + KeychainSwiftConstants.attrAccount: prefixedKey + ] + + query = addAccessGroupWhenPresent(query) + query = addSynchronizableIfRequired(query, addingItems: false) + lastQueryParameters = query + + lastResultCode = SecItemDelete(query as CFDictionary) + + return lastResultCode == noErr + } + + /** + + Deletes all Keychain items used by the app. Note that this method deletes all items regardless of the prefix settings used for initializing the class. + + - returns: True if the keychain items were successfully deleted. + + */ + @discardableResult + open func clear() -> Bool { + // The lock prevents the code to be run simultaneously + // from multiple threads which may result in crashing + lock.lock() + defer { lock.unlock() } + + var query: [String: Any] = [kSecClass as String: kSecClassGenericPassword] + query = addAccessGroupWhenPresent(query) + query = addSynchronizableIfRequired(query, addingItems: false) + lastQueryParameters = query + + lastResultCode = SecItemDelete(query as CFDictionary) + + return lastResultCode == noErr + } + + /// Returns the key with currently set prefix. + func keyWithPrefix(_ key: String) -> String { + return "\(keyPrefix)\(key)" + } + + func addAccessGroupWhenPresent(_ items: [String: Any]) -> [String: Any] { + guard let accessGroup = accessGroup else { return items } + + var result: [String: Any] = items + result[KeychainSwiftConstants.accessGroup] = accessGroup + return result + } + + /** + + Adds kSecAttrSynchronizable: kSecAttrSynchronizableAny` item to the dictionary when the `synchronizable` property is true. + + - parameter items: The dictionary where the kSecAttrSynchronizable items will be added when requested. + - parameter addingItems: Use `true` when the dictionary will be used with `SecItemAdd` method (adding a keychain item). For getting and deleting items, use `false`. + + - returns: the dictionary with kSecAttrSynchronizable item added if it was requested. Otherwise, it returns the original dictionary. + + */ + func addSynchronizableIfRequired(_ items: [String: Any], addingItems: Bool) -> [String: Any] { + if !synchronizable { return items } + var result: [String: Any] = items + result[KeychainSwiftConstants.attrSynchronizable] = addingItems == true ? true : kSecAttrSynchronizableAny + return result + } +} diff --git a/macOS/Privado/Common/Keychain/KeychainSwiftAccessOptions.swift b/macOS/Privado/Common/Keychain/KeychainSwiftAccessOptions.swift new file mode 100644 index 00000000..b6ee3fb2 --- /dev/null +++ b/macOS/Privado/Common/Keychain/KeychainSwiftAccessOptions.swift @@ -0,0 +1,83 @@ +import Security + +/** + +These options are used to determine when a keychain item should be readable. The default value is AccessibleWhenUnlocked. + +*/ +public enum KeychainSwiftAccessOptions { + + /** + + The data in the keychain item can be accessed only while the device is unlocked by the user. + + This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute migrate to a new device when using encrypted backups. + + This is the default value for keychain items added without explicitly setting an accessibility constant. + + */ + case accessibleWhenUnlocked + + /** + + The data in the keychain item can be accessed only while the device is unlocked by the user. + + This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present. + + */ + case accessibleWhenUnlockedThisDeviceOnly + + /** + + The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user. + + After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups. + + */ + case accessibleAfterFirstUnlock + + /** + + The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user. + + After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present. + + */ + case accessibleAfterFirstUnlockThisDeviceOnly + + /** + + The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device. + + This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute never migrate to a new device. After a backup is restored to a new device, these items are missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode causes all items in this class to be deleted. + + */ + case accessibleWhenPasscodeSetThisDeviceOnly + + static var defaultOption: KeychainSwiftAccessOptions { + return .accessibleWhenUnlocked + } + + var value: String { + switch self { + case .accessibleWhenUnlocked: + return toString(kSecAttrAccessibleWhenUnlocked) + + case .accessibleWhenUnlockedThisDeviceOnly: + return toString(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) + + case .accessibleAfterFirstUnlock: + return toString(kSecAttrAccessibleAfterFirstUnlock) + + case .accessibleAfterFirstUnlockThisDeviceOnly: + return toString(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) + + case .accessibleWhenPasscodeSetThisDeviceOnly: + return toString(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) + } + } + + func toString(_ value: CFString) -> String { + return KeychainSwiftConstants.toString(value) + } +} diff --git a/macOS/Privado/Common/Keychain/KeychainSwiftConstants.swift b/macOS/Privado/Common/Keychain/KeychainSwiftConstants.swift new file mode 100644 index 00000000..fe264745 --- /dev/null +++ b/macOS/Privado/Common/Keychain/KeychainSwiftConstants.swift @@ -0,0 +1,46 @@ +import Foundation +import Security + +/// Constants used by the library +public struct KeychainSwiftConstants { + /// Specifies a Keychain access group. Used for sharing Keychain items between apps. + public static var accessGroup: String { return toString(kSecAttrAccessGroup) } + + /** + + A value that indicates when your app needs access to the data in a keychain item. The default value is AccessibleWhenUnlocked. For a list of possible values, see KeychainSwiftAccessOptions. + + */ + public static var accessible: String { return toString(kSecAttrAccessible) } + + /// Used for specifying a String key when setting/getting a Keychain value. + public static var attrAccount: String { return toString(kSecAttrAccount) } + + /// Used for specifying synchronization of keychain items between devices. + public static var attrSynchronizable: String { return toString(kSecAttrSynchronizable) } + + /// An item class key used to construct a Keychain search dictionary. + public static var klass: String { return toString(kSecClass) } + + /// Specifies the number of values returned from the keychain. The library only supports single values. + public static var matchLimit: String { return toString(kSecMatchLimit) } + + /// A return data type used to get the data from the Keychain. + public static var returnData: String { return toString(kSecReturnData) } + + /// Used for specifying a value when setting a Keychain value. + public static var valueData: String { return toString(kSecValueData) } + + /// Used for returning a reference to the data from the keychain + public static var returnReference: String { return toString(kSecReturnPersistentRef) } + + /// A key whose value is a Boolean indicating whether or not to return item attributes + public static var returnAttributes: String { return toString(kSecReturnAttributes) } + + /// A value that corresponds to matching an unlimited number of items + public static var secMatchLimitAll: String { return toString(kSecMatchLimitAll) } + + static func toString(_ value: CFString) -> String { + return value as String + } +} diff --git a/macOS/Privado/Common/Monads/Either.swift b/macOS/Privado/Common/Monads/Either.swift new file mode 100644 index 00000000..eb9a2d74 --- /dev/null +++ b/macOS/Privado/Common/Monads/Either.swift @@ -0,0 +1,45 @@ +// +// Either.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public enum Either: Decodable { + case firstType(T) + case secondType(U) + + public func unwrap() -> Any { + switch self { + case .firstType(let objectOfTypeT): return objectOfTypeT + case .secondType(let objectOfTypeU): return objectOfTypeU + } + } + + public func map(firstTypeTransform: (T) -> V, secondTypeTransform: (U) -> V) -> V { + switch self { + case .firstType(let value): + return firstTypeTransform(value) + case .secondType(let value): + return secondTypeTransform(value) + } + } + + public init(from decoder: Decoder) throws { + if let value = try? T(from: decoder) { + self = .firstType(value) + } else if let value = try? U(from: decoder) { + self = .secondType(value) + } else { + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: + "Cannot decode \(T.self) or \(U.self)") + throw DecodingError.dataCorrupted(context) + } + } + +} diff --git a/macOS/Privado/Common/Monads/Emitter.swift b/macOS/Privado/Common/Monads/Emitter.swift new file mode 100644 index 00000000..a1af2871 --- /dev/null +++ b/macOS/Privado/Common/Monads/Emitter.swift @@ -0,0 +1,29 @@ +// +// Emitter.swift +// Cyberlock +// +// Created by Jura on 8/29/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public typealias ShouldContinueReceiveNotifications = Bool + +final public class Emitter { + + public typealias ReactionClosure = (T) -> ShouldContinueReceiveNotifications + var reactions: [ReactionClosure] = [] + + public init() {} + + public func addReaction(_ reaction: @escaping ReactionClosure) { + self.reactions.append(reaction) + } + + public func invoke(_ object: T) { + self.reactions = self.reactions.filter { + $0(object) + } + } +} diff --git a/macOS/Privado/Common/Monads/Future.swift b/macOS/Privado/Common/Monads/Future.swift new file mode 100644 index 00000000..3e290505 --- /dev/null +++ b/macOS/Privado/Common/Monads/Future.swift @@ -0,0 +1,95 @@ +// +// Future.swift +// Privado +// +// Created by Juraldinio on 6/23/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +public final class Future { + + private let serialQueue = DispatchQueue(label: "Future-\(UUID().uuidString)") + + public typealias Callback = (Result) -> Void + + private var callbacks: [Callback] = [] + private var cached: Result? + + // MARK: - Public + + public init(compute: (@escaping Callback) -> Void) { + compute(self.send) + } + + public convenience init(value: T) { + self.init(compute: { $0(.success(value)) }) + } + + public convenience init(error: Error) { + self.init(compute: { $0(.failure(error)) }) + } + + @discardableResult + public func onResult(_ callback: @escaping Callback) -> Future { + if let result = self.cached { + callback(result) + } else { + self.serialQueue.sync { + self.callbacks.append(callback) + } + } + + return self + } + + @discardableResult + public func map(_ success: @escaping (T) -> Future, failure: @escaping (Error) -> Future) -> Future { + return Future { completion in + self.onResult { result in + switch result { + case .success(let value): + success(value).onResult(completion) + case .failure(let error): + failure(error).onResult(completion) + } + } + } + } + + @discardableResult + public func flatMap(_ transform: @escaping (T) -> Future) -> Future { + return Future { completion in + self.onResult { result in + switch result { + case .success(let value): + transform(value).onResult(completion) + case .failure(let error): + completion(.failure(error)) + } + } + } + } + + // MARK: - Private + + private func send(result: Result) { + assert(!self.cached.isExist) + self.cached = result + + let callbacks = self.callbacks + self.serialQueue.sync { + self.callbacks = [] + } + + callbacks.forEach { $0(result) } + } + +} + +extension Future where T == Void { + public convenience init() { + self.init(compute: { $0(.success(())) }) + } +} diff --git a/macOS/Privado/Common/Monads/Operations.swift b/macOS/Privado/Common/Monads/Operations.swift new file mode 100644 index 00000000..df3cca6e --- /dev/null +++ b/macOS/Privado/Common/Monads/Operations.swift @@ -0,0 +1,53 @@ +// +// Operations.swift +// Cyberlock +// +// Created by Jura on 8/14/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +precedencegroup MonadicPrecedence { + associativity: left + higherThan: BitwiseShiftPrecedence +} + +infix operator >>- : MonadicPrecedence + +@inline(__always) +@discardableResult +public func >>-(a: T?, f: (T) throws -> U?) rethrows -> U? { + switch a { + case .some(let x): + return try f(x) + case .none: + return nil + } +} + +// MARK: <<< / >>> + +precedencegroup FunctionApplicationPrecedenceLeft { + lowerThan: AssignmentPrecedence + associativity: left +} + +infix operator >>> : FunctionApplicationPrecedenceLeft + +@inline(__always) +public func >>>(x: T, f: (T) throws -> U) rethrows -> U { + return try f(x) +} + +precedencegroup FunctionApplicationPrecedenceRight { + lowerThan: AssignmentPrecedence + associativity: right +} + +infix operator <<< : FunctionApplicationPrecedenceRight + +@inline(__always) +public func <<<(f: (T) throws -> U, x: T) rethrows -> U { + return try f(x) +} diff --git a/macOS/Privado/Common/Networking/Encoding/JSONParameterEncoder.swift b/macOS/Privado/Common/Networking/Encoding/JSONParameterEncoder.swift new file mode 100644 index 00000000..bf5b2547 --- /dev/null +++ b/macOS/Privado/Common/Networking/Encoding/JSONParameterEncoder.swift @@ -0,0 +1,26 @@ +// +// JSONParameterEncoder.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public struct JSONParameterEncoder: ParameterEncoder { + public static func encode(parameters: HTTPParameters, for request: inout URLRequest) throws { + + guard let jsonData = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) else { + throw NetworkRouterError.encoding(params: parameters) + } + + request.httpBody = jsonData + if request.value(forHTTPHeaderField: "Content-Type").isExist { + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + } else { + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + } + + } +} diff --git a/macOS/Privado/Common/Networking/Encoding/URLParameterEncoder.swift b/macOS/Privado/Common/Networking/Encoding/URLParameterEncoder.swift new file mode 100644 index 00000000..8bb9b948 --- /dev/null +++ b/macOS/Privado/Common/Networking/Encoding/URLParameterEncoder.swift @@ -0,0 +1,26 @@ +// +// URLParameterEncoder.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public struct URLParameterEncoder: ParameterEncoder { + public static func encode(parameters: HTTPParameters, for request: inout URLRequest) throws { + + guard let url = request.url + , var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + throw NetworkRouterError.invalidUrl + } + + urlComponents.queryItems = parameters.map { + URLQueryItem(name: $0.key, value: "\($0.value)".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)) + } + + request.url = urlComponents.url + + } +} diff --git a/macOS/Privado/Common/Networking/Models/Response.swift b/macOS/Privado/Common/Networking/Models/Response.swift new file mode 100644 index 00000000..b02efb53 --- /dev/null +++ b/macOS/Privado/Common/Networking/Models/Response.swift @@ -0,0 +1,56 @@ +// +// Response.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public struct Response { + + public let statusCode: Int? + + public let data: Data + + public let response: URLResponse? + + public var request: URLRequest + + #if DEBUG + public var curlString: NSString { + return self.request.curlString + } + #endif + +} + +// MARK: - cURL +internal extension URLRequest { + + var curlString: NSString { + var result = "curl -k " + + if let method = httpMethod { + result += "-X \(method) \\\n" + } + + if let headers = allHTTPHeaderFields { + for (header, value) in headers { + result += "-H \"\(header): \(value)\" \\\n" + } + } + + if let body = httpBody, !body.isEmpty, let string = String(data: body, encoding: .utf8), !string.isEmpty { + result += "-d '\(string)' \\\n" + } + + if let url = url { + result += "'\(url.absoluteString)'" + } + + return result as NSString + } + +} diff --git a/macOS/Privado/Common/Networking/Models/ResponseResult.swift b/macOS/Privado/Common/Networking/Models/ResponseResult.swift new file mode 100644 index 00000000..6663d5f2 --- /dev/null +++ b/macOS/Privado/Common/Networking/Models/ResponseResult.swift @@ -0,0 +1,37 @@ +// +// ResponseResult.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +let decodingContext = CodingUserInfoKey(rawValue: "keyPath")! + +internal struct ResponseResult: Decodable { + + let result: T + + struct ResponseResultKey: CodingKey { + var stringValue: String + init(stringValue: String) { + self.stringValue = stringValue + } + var intValue: Int? { return nil } + init?(intValue: Int) { return nil } + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: ResponseResultKey.self) + guard let keyPath = decoder.userInfo[decodingContext] as? String + , let matchedKey = container.allKeys.filter({ $0.stringValue == keyPath }).first else { + throw DecodingError.dataCorruptedError(forKey: ResponseResultKey.init(stringValue: decodingContext.rawValue), + in: container, + debugDescription: "Keypath \(decodingContext.rawValue) not found") + } + self.result = try container.decode(T.self, forKey: matchedKey) + } + +} diff --git a/macOS/Privado/Common/Networking/Router/NetworkEnvironment.swift b/macOS/Privado/Common/Networking/Router/NetworkEnvironment.swift new file mode 100644 index 00000000..3818ad97 --- /dev/null +++ b/macOS/Privado/Common/Networking/Router/NetworkEnvironment.swift @@ -0,0 +1,13 @@ +// +// NetworkEnvironment.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public protocol NetworkEnvironment { + var host: String { get } +} diff --git a/macOS/Privado/Common/Networking/Router/NetworkPluginType.swift b/macOS/Privado/Common/Networking/Router/NetworkPluginType.swift new file mode 100644 index 00000000..62942ef2 --- /dev/null +++ b/macOS/Privado/Common/Networking/Router/NetworkPluginType.swift @@ -0,0 +1,40 @@ +// +// NetworkPluginType.swift +// Cyberlock +// +// Created by Jura on 9/9/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public protocol NetworkPluginType { + + typealias NetworkShouldCancel = Bool + typealias RequestType = URLRequest + typealias ResultResponseType = Result + + var name: String? { get } + + func willTransmit(_ request: URLRequest, service: ServiceType) -> NetworkShouldCancel + + func didReceive(_ response: URLResponse, data: Data?, service: ServiceType) -> NetworkShouldCancel + + func prepareTransmit(_ request: RequestType, service: ServiceType) -> RequestType? + + func prepareReceive(_ result: ResultResponseType, service: ServiceType) -> ResultResponseType? +} + +// MARK: - Default NetworkPluginType Methods Realization +public extension NetworkPluginType { + + var name: String? { return nil } + + func willTransmit(_ request: URLRequest, service: ServiceType) -> NetworkShouldCancel { return false } + + func didReceive(_ response: URLResponse, service: ServiceType) -> NetworkShouldCancel { return false } + + func prepareTransmit(_ request: RequestType, service: ServiceType) -> RequestType? { return request } + + func prepareReceive(_ result: ResultResponseType, service: ServiceType) -> ResultResponseType? { return result } +} diff --git a/macOS/Privado/Common/Networking/Router/NetworkRouter+Internal.swift b/macOS/Privado/Common/Networking/Router/NetworkRouter+Internal.swift new file mode 100644 index 00000000..98813821 --- /dev/null +++ b/macOS/Privado/Common/Networking/Router/NetworkRouter+Internal.swift @@ -0,0 +1,88 @@ +// +// NetworkRouter+Internal.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +extension NetworkRouter { + + func buildRequest(_ service: ServiceType, env: NetworkEnvironment?) throws -> URLRequest { + let url = try self.baseURL(for: service, env: env) + let reqURL = service.path.isEmpty ? url : url.appendingPathComponent(service.path) + var request = URLRequest(url: reqURL, + cachePolicy: .useProtocolCachePolicy, + timeoutInterval: service.timeout) + request.httpMethod = "\(service.httpMethod)" + + switch service.task { + case .request: break + case .requestParameters(parameters: let parameters, parameterEncoding: .json): + try self.configureParameters(body: parameters, query: nil, request: &request) + case .requestParameters(parameters: let parameters, parameterEncoding: .query): + try self.configureParameters(body: nil, query: parameters, request: &request) + case let .requestCompositeParameters(bodyParameters: bodyParameters, urlParameters: parameters): + try self.configureParameters(body: bodyParameters, query: parameters, request: &request) + case .custom(preparation: let preparation): + try preparation(&request) + } + + var headers = HTTPHeaders() + if let serviceHeaders = service.headers { headers.merge(serviceHeaders) { (current, _) in current } } + if let customHeaders = self.customHeaders { headers.merge(customHeaders) { (current, _) in current } } + + if !headers.isEmpty { + headers.forEach { (key, value) in + request.value(forHTTPHeaderField: key).isExist + ? request.setValue(value, forHTTPHeaderField: key) + : request.addValue(value, forHTTPHeaderField: key) + } + } + + return request + } + + private func baseURL(for service: ServiceType, env: NetworkEnvironment?) throws -> URL { + + guard let environment = env else { + if case let .custom(url) = service.baseURL { + return url + } + throw NetworkRouterError.invalidEnvironment + } + + let path: String + switch service.baseURL { + case .apiService: + path = "https://\(environment.host)/" + case .passportService, + .notifyService, + .statService: + path = "" + case .custom(let url): + return url + } + + guard let url = URL(string: path) else { + throw NetworkRouterError.invalidUrl + } + + return url + } + + private func configureParameters(body: HTTPParameters?, query: HTTPParameters?, request: inout URLRequest) throws { + + if let params = query { + try URLParameterEncoder.encode(parameters: params, for: &request) + } + + if let params = body { + try JSONParameterEncoder.encode(parameters: params, for: &request) + } + + } + +} diff --git a/macOS/Privado/Common/Networking/Router/NetworkRouter+Plugins.swift b/macOS/Privado/Common/Networking/Router/NetworkRouter+Plugins.swift new file mode 100644 index 00000000..c0ee94fa --- /dev/null +++ b/macOS/Privado/Common/Networking/Router/NetworkRouter+Plugins.swift @@ -0,0 +1,79 @@ +// +// NetworkRouter+Plugins.swift +// Cyberlock +// +// Created by Jura on 9/9/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +extension NetworkRouter { + + static func networkShouldProcess(priority: [NetworkPluginType], secondary: [NetworkPluginType], service: ServiceType, data: T) -> Result { + + let predicate = { (plugin: NetworkPluginType) -> Bool in + switch data { + case let value as URLRequest: + return plugin.willTransmit(value, service: service) + case let value as NetworkPluginType.ResultResponseType: + if value.isSuccess + , let response = value.optional?.response { + return plugin.didReceive(response, data: value.optional?.data, service: service) + } else { + return false + } + default: + return false + } + } + + if let plugin = priority.first(where: predicate) { return Result.failure(.cancelledByPlugin(name: plugin.name)) } + if let plugin = secondary.first(where: predicate) { return Result.failure(.cancelledByPlugin(name: plugin.name)) } + + return Result.success(data) + } + + static func networkModifyRequest(priority: [NetworkPluginType], secondary: [NetworkPluginType], service: ServiceType, request: URLRequest) -> Result { + var modified: NetworkPluginType.RequestType? + let reduceAction = { (request: URLRequest, plugin: NetworkPluginType) -> NetworkPluginType.RequestType in + guard let modified = plugin.prepareTransmit(request, service: service) else { + throw NetworkRouterError.preventedByPlugin(name: plugin.name) + } + return modified + } + + do { + modified = try priority.reduce(request, reduceAction) + modified = try secondary.reduce(request, reduceAction) + } catch { + return Result.failure(error) + } + + guard let result = modified else { return Result.failure(NetworkRouterError.cancelled) } + + return Result.success(result) + } + + static func networkModifyResponse(priority: [NetworkPluginType], secondary: [NetworkPluginType], service: ServiceType, result: NetworkPluginType.ResultResponseType) -> NetworkPluginType.ResultResponseType? { + + var modified: NetworkPluginType.ResultResponseType? + let reduceAction = { (result: NetworkPluginType.ResultResponseType, plugin: NetworkPluginType) -> NetworkPluginType.ResultResponseType in + guard let modified = plugin.prepareReceive(result, service: service) else { + throw NetworkRouterError.preventedByPlugin(name: plugin.name) + } + return modified + } + + do { + modified = try priority.reduce(result, reduceAction) + modified = try secondary.reduce(result, reduceAction) + } catch { + return Result.failure(error) + } + + return modified + + } + +} diff --git a/macOS/Privado/Common/Networking/Router/NetworkRouter.swift b/macOS/Privado/Common/Networking/Router/NetworkRouter.swift new file mode 100644 index 00000000..aa681c4f --- /dev/null +++ b/macOS/Privado/Common/Networking/Router/NetworkRouter.swift @@ -0,0 +1,189 @@ +// +// NetworkRouter.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public typealias NetworkResultCompletionClosure = (_ result: Result) -> Void + +public typealias NetworkResultDecodableCompletionClosure = (_ result: Result<(T, Response), Error>) -> Void + +public enum StubBehavior { + case never + case immediate + case delayed(seconds: TimeInterval) +} + +let responseContext = CodingUserInfoKey(rawValue: "response")! + +final public class NetworkRouter { + + private static var globalPlugins: [NetworkPluginType] = [] + + public var customHeaders: HTTPHeaders? + + private let environment: NetworkEnvironment? + private let plugins: [NetworkPluginType] + private var requests = [CancellationToken: Cancellable]() + + public init(environment: NetworkEnvironment?, plugins: [NetworkPluginType] = []) { + self.environment = environment + self.plugins = plugins + } + + convenience public init() { + self.init(environment: nil) + } + + public static func addPlugin(_ plugin: NetworkPluginType) { + NetworkRouter.globalPlugins.append(plugin) + } + + @discardableResult + public func request(_ service: ServiceType, stubBehavior: StubBehavior = .never, completion: @escaping NetworkResultCompletionClosure) -> CancellationToken? { + let mainThreadCompletion = { result in + DispatchQueue.main.async { completion(result) } + } + return self.sendRequest(service, stubBehavior: stubBehavior, completion: mainThreadCompletion) + } + + @discardableResult + public func requestDecodable(_ decodableType: T.Type, keyPath: String? = nil, service: ServiceType, stubBehavior: StubBehavior = .never, completion: @escaping NetworkResultDecodableCompletionClosure) -> CancellationToken? { + + return self.sendRequest(service, stubBehavior: stubBehavior) { result in + + result.flatMap { response -> (T, Response) in + let decoder = JSONDecoder() + decoder.userInfo[responseContext] = response + if let keyPath = keyPath, keyPath.count > 0 { + decoder.userInfo[decodingContext] = keyPath + let decoded = try decoder.decode(ResponseResult.self, from: response.data) + return (decoded.result, response) + } else { + let decoded = try decoder.decode(decodableType, from: response.data) + return (decoded, response) + } + } >>> { result in + DispatchQueue.main.async { completion(result) } + } + + } + + } + + public func cancel(_ token: CancellationToken) { + guard let cancellable = self.requests[token] else { return } + synced(self) { + self.requests.removeValue(forKey: token) + } + cancellable.cancel() + } + + public func cancelAllRequests() { + let cancellables = self.requests.values + synced(self) { + self.requests.removeAll() + } + cancellables.forEach { $0.cancel() } + } + + // swiftlint:disable function_body_length + private func sendRequest(_ service: ServiceType, stubBehavior: StubBehavior, completion: @escaping NetworkResultCompletionClosure) -> CancellationToken? { + + let request: URLRequest + do { + request = try self.buildRequest(service, env: self.environment) + } catch { + completion(Result.failure(NetworkRouterError.invalidRequest)) + return nil + } + + let result: Result = NetworkRouter.networkShouldProcess(priority: NetworkRouter.globalPlugins, + secondary: self.plugins, + service: service, + data: request) + + if let error = result.maybeError { + completion(Result.failure(error)) + return nil + } + + let modified: Result = NetworkRouter.networkModifyRequest(priority: NetworkRouter.globalPlugins, + secondary: self.plugins, + service: service, + request: request) + + guard let netRequest = modified.optional else { + if let error = modified.maybeError { + completion(Result.failure(error)) + } else { + completion(Result.failure(NetworkRouterError.cancelled)) + } + return nil + } + + let cancellable = NetworkTask(request: netRequest) { [weak self] token, result in + + guard let self = self else { + completion(Result.failure(NetworkRouterError.cancelled)) + return + } + + synced(self) { + self.requests.removeValue(forKey: token) + } + + guard let modified: Result = NetworkRouter.networkModifyResponse(priority: self.plugins, + secondary: NetworkRouter.globalPlugins, + service: service, + result: result) else { + completion(Result.failure(NetworkRouterError.cancelled)) + return + } + + let processed = NetworkRouter.networkShouldProcess(priority: self.plugins, secondary: NetworkRouter.globalPlugins, service: service, data: modified) + + if let error = processed.maybeError { + completion(Result.failure(error)) + return + } + + guard let netResult = processed.optional else { + if let error = processed.maybeError { + completion(Result.failure(error)) + } else { + completion(Result.failure(NetworkRouterError.cancelled)) + } + return + } + + completion(netResult) + + } + + synced(self) { + self.requests[cancellable.token] = cancellable + } + + cancellable.start(with: stubBehavior, sampleResponse: service.sampleResponse) + + return cancellable.token + } + // swiftlint:enable function_body_length + +} + +// MARK: - Synchronization + +private func synced(_ lock: Any?, closure: () -> Void) { + guard let lock = lock else { + return + } + objc_sync_enter(lock) + closure() + objc_sync_exit(lock) +} diff --git a/macOS/Privado/Common/Networking/Router/NetworkRouterError.swift b/macOS/Privado/Common/Networking/Router/NetworkRouterError.swift new file mode 100644 index 00000000..eb6731e4 --- /dev/null +++ b/macOS/Privado/Common/Networking/Router/NetworkRouterError.swift @@ -0,0 +1,19 @@ +// +// NetworkRouterError.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public enum NetworkRouterError: Error { + case invalidEnvironment + case invalidUrl + case encoding(params: HTTPParameters) + case invalidRequest + case cancelled + case cancelledByPlugin(name: String?) + case preventedByPlugin(name: String?) +} diff --git a/macOS/Privado/Common/Networking/Router/NetworkTask.swift b/macOS/Privado/Common/Networking/Router/NetworkTask.swift new file mode 100644 index 00000000..a2a31c30 --- /dev/null +++ b/macOS/Privado/Common/Networking/Router/NetworkTask.swift @@ -0,0 +1,126 @@ +// +// NetworkTask.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public typealias CancellationToken = String + +internal typealias NetworkingTaskCompletionClosure = (_ token: CancellationToken, _ result: Result) -> Void + +public protocol Cancellable { + var token: CancellationToken { get } + func cancel() +} + +final class NetworkTask: Cancellable { + + private let request: URLRequest + private let completion: NetworkingTaskCompletionClosure + + private var task: URLSessionDataTask? + + init(request: URLRequest, completion: @escaping NetworkingTaskCompletionClosure) { + self.request = request + self.completion = completion + self.token = UUID().uuidString + } + + func start(with behaviour: StubBehavior, sampleResponse: Data?) { + switch behaviour { + case .never: self.performRequest() + case .immediate: self.perform(with: sampleResponse, after: 0) + case let .delayed(seconds: seconds): self.perform(with: sampleResponse, after: seconds) + } + } + + // MARK: - Cancellable + + let token: CancellationToken + + func cancel() { + self.task?.cancel() + self.task = nil + } + + // MARK: - + + // MARK: - Private + + private func performRequest() { + + let config = URLSessionConfiguration.default + config.timeoutIntervalForRequest = 1 + config.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData + config.urlCache = nil + + let session = URLSession(configuration: config) + let task = session.dataTask(with: self.request) { [weak self] (data, response, error) in + guard let self = self else { return } + guard let task = self.task else { + self.completion(self.token, Result.failure(NetworkRouterError.cancelled)) + return + } + task.cancel() + self.task = nil + let result = NetworkTask.convertResponseToResult(response as? HTTPURLResponse, data: data, error: error, request: self.request) + self.completion(self.token, result) + } + self.task = task + task.resume() + } + + private func perform(with sampleData: Data?, after seconds: TimeInterval) { + + let completionHandler = { [weak self] in + guard let self = self else { return } + let result = NetworkTask.convertResponseToResult(nil, data: sampleData, error: nil, request: self.request) + self.completion(self.token, result) + } + + guard seconds > 0 else { + completionHandler() + return + } + + DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + seconds, execute: completionHandler) + } + + private static func convertResponseToResult(_ response: HTTPURLResponse?, data: Data?, error: Error?, request: URLRequest) -> Result { + + let taskResponse = Response(statusCode: response?.statusCode ?? 200, + data: data ?? Data(), + response: response, + request: request) + + return Result.success(taskResponse) + /* + switch (data, error) { + case (.some, .none): + return self.errorFromServerResponse(tossResponse) + >>- { self.fail(with: $0, response: tossResponse) } + ?? .success(tossResponse) + case let (_, error?): + guard (error as NSError).domain != NSURLErrorDomain else { + return self.fail(with: error, response: tossResponse) + } + guard let errorFromResponse = self.errorFromServerResponse(tossResponse) else { + let generalError = NSError.tossSpecificError(with: (error as NSError).code, + response: tossResponse, + localizedDescription: (error as NSError).localizedDescription) + return self.fail(with: generalError, response: tossResponse) + } + return self.fail(with: errorFromResponse, response: tossResponse) + default: + let error = NSError.tossSpecificError(with: .unknown, response: tossResponse) + return self.fail(with: error, response: tossResponse) + } + */ + } + + +} diff --git a/macOS/Privado/Common/Networking/Service/ServiceType.swift b/macOS/Privado/Common/Networking/Service/ServiceType.swift new file mode 100644 index 00000000..f82427f3 --- /dev/null +++ b/macOS/Privado/Common/Networking/Service/ServiceType.swift @@ -0,0 +1,92 @@ +// +// ServiceType.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public typealias HTTPHeaders = [String: String] + +public protocol ServiceType { + + var baseURL: BaseURLOptions { get } + + var path: String { get } + + var httpMethod: HTTPMethod { get } + + var headers: HTTPHeaders? { get } + + var task: Task { get } + + var timeout: TimeInterval { get } + + var responseFormat: ResponseFormat { get } + + var shouldPropagateErrorAfterCancelling: Bool { get } + + var sampleResponse: Data? { get } +} + +public enum BaseURLOptions { + + case apiService + + case notifyService + + case passportService + + case statService + + case custom(_: URL) + +} + +public enum HTTPMethod: CustomStringConvertible { + case get + case post + + public var description: String { + switch self { + case .get: return "GET" + case .post: return "POST" + } + } +} + +public enum ResponseFormat { + case json + case image + case rawData + case custom +} + +public extension ServiceType { + + var httpMethod: HTTPMethod { + return .get + } + + var headers: HTTPHeaders? { + return nil + } + + var timeout: TimeInterval { + return 30.0 + } + + var responseFormat: ResponseFormat { + return .json + } + + var shouldPropagateErrorAfterCancelling: Bool { + return true + } + + var sampleResponse: Data? { + return nil + } +} diff --git a/macOS/Privado/Common/Networking/Service/Task.swift b/macOS/Privado/Common/Networking/Service/Task.swift new file mode 100644 index 00000000..bb548453 --- /dev/null +++ b/macOS/Privado/Common/Networking/Service/Task.swift @@ -0,0 +1,34 @@ +// +// Task.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +public typealias HTTPParameters = [String: Any] + +public protocol ParameterEncoder { + static func encode(parameters: HTTPParameters, for request: inout URLRequest) throws +} + +public typealias TaskRequestPreparation = (_ request: inout URLRequest) throws -> Void + +public enum Task { + + case request + + case requestParameters(parameters: HTTPParameters, parameterEncoding: ParameterEncoding) + + case requestCompositeParameters(bodyParameters: HTTPParameters, urlParameters: HTTPParameters) + + case custom(preparation: TaskRequestPreparation) + +} + +public enum ParameterEncoding { + case query + case json +} diff --git a/macOS/Privado/Common/SMJobLess/CodesignCheck.swift b/macOS/Privado/Common/SMJobLess/CodesignCheck.swift new file mode 100644 index 00000000..2f80a54a --- /dev/null +++ b/macOS/Privado/Common/SMJobLess/CodesignCheck.swift @@ -0,0 +1,106 @@ +// +// CodesignCheck.swift +// SMJobLess +// +// Created by Jura on 3/12/20. +// Copyright © 2020 Juraldinio. All rights reserved. +// + +import Foundation +import Security + +enum CodesignCheckError: Error { + case message(String) +} + +struct CodesignCheck { + + // MARK: - Compare + + public static func codeSigningMatches(pid: pid_t) throws -> Bool { + return try self.codeSigningCertificatesForSelf() == self.codeSigningCertificates(forPID: pid) + } + + // MARK: - Public + + public static func codeSigningCertificatesForSelf() throws -> [SecCertificate] { + guard let secStaticCode = try secStaticCodeSelf() else { return [] } + return try codeSigningCertificates(forStaticCode: secStaticCode) + } + + public static func codeSigningCertificates(forPID pid: pid_t) throws -> [SecCertificate] { + guard let secStaticCode = try secStaticCode(forPID: pid) else { return [] } + return try codeSigningCertificates(forStaticCode: secStaticCode) + } + + public static func codeSigningCertificates(forURL url: URL) throws -> [SecCertificate] { + guard let secStaticCode = try secStaticCode(forURL: url) else { return [] } + return try codeSigningCertificates(forStaticCode: secStaticCode) + } + + // MARK: - Private + + private static func executeSecFunction(_ secFunction: () -> (OSStatus) ) throws { + let osStatus = secFunction() + guard osStatus == errSecSuccess else { + throw CodesignCheckError.message(String(describing: SecCopyErrorMessageString(osStatus, nil))) + } + } + + private static func secStaticCodeSelf() throws -> SecStaticCode? { + var secCodeSelf: SecCode? + try executeSecFunction { SecCodeCopySelf(SecCSFlags(rawValue: 0), &secCodeSelf) } + guard let secCode = secCodeSelf else { + throw CodesignCheckError.message("SecCode returned empty from SecCodeCopySelf") + } + return try secStaticCode(forSecCode: secCode) + } + + private static func secStaticCode(forPID pid: pid_t) throws -> SecStaticCode? { + var secCodePID: SecCode? + try executeSecFunction { SecCodeCopyGuestWithAttributes(nil, [kSecGuestAttributePid: pid] as CFDictionary, [], &secCodePID) } + guard let secCode = secCodePID else { + throw CodesignCheckError.message("SecCode returned empty from SecCodeCopyGuestWithAttributes") + } + return try secStaticCode(forSecCode: secCode) + } + + private static func secStaticCode(forURL url: URL) throws -> SecStaticCode? { + var secStaticCodePath: SecStaticCode? + try executeSecFunction { SecStaticCodeCreateWithPath(url as CFURL, [], &secStaticCodePath) } + guard let secStaticCode = secStaticCodePath else { + throw CodesignCheckError.message("SecStaticCode returned empty from SecStaticCodeCreateWithPath") + } + return secStaticCode + } + + private static func secStaticCode(forSecCode secCode: SecCode) throws -> SecStaticCode? { + var secStaticCodeCopy: SecStaticCode? + try executeSecFunction { SecCodeCopyStaticCode(secCode, [], &secStaticCodeCopy) } + guard let secStaticCode = secStaticCodeCopy else { + throw CodesignCheckError.message("SecStaticCode returned empty from SecCodeCopyStaticCode") + } + return secStaticCode + } + + private static func isValid(secStaticCode: SecStaticCode) throws { + try executeSecFunction { SecStaticCodeCheckValidity(secStaticCode, SecCSFlags(rawValue: kSecCSDoNotValidateResources | kSecCSCheckNestedCode), nil) } + } + + private static func secCodeInfo(forStaticCode secStaticCode: SecStaticCode) throws -> [String: Any]? { + try isValid(secStaticCode: secStaticCode) + var secCodeInfoCFDict: CFDictionary? + try executeSecFunction { SecCodeCopySigningInformation(secStaticCode, SecCSFlags(rawValue: kSecCSSigningInformation), &secCodeInfoCFDict) } + guard let secCodeInfo = secCodeInfoCFDict as? [String: Any] else { + throw CodesignCheckError.message("CFDictionary returned empty from SecCodeCopySigningInformation") + } + return secCodeInfo + } + + private static func codeSigningCertificates(forStaticCode secStaticCode: SecStaticCode) throws -> [SecCertificate] { + guard + let secCodeInfo = try secCodeInfo(forStaticCode: secStaticCode), + let secCertificates = secCodeInfo[kSecCodeInfoCertificates as String] as? [SecCertificate] else { return [] } + return secCertificates + } +} diff --git a/macOS/Privado/Common/SMJobLess/HiveAuthorization.swift b/macOS/Privado/Common/SMJobLess/HiveAuthorization.swift new file mode 100644 index 00000000..63762183 --- /dev/null +++ b/macOS/Privado/Common/SMJobLess/HiveAuthorization.swift @@ -0,0 +1,71 @@ +// +// HiveAuthorization.swift +// main +// +// Created by Jura on 3/12/20. +// Copyright © 2020 Juraldinio. All rights reserved. +// + +import Foundation +import ServiceManagement + +final class HiveAuthorization { + + enum HiveAuthorizationError: Error { + case message(String) + } + + // MARK: - Interface + + static func install(executable label: String) -> Bool { + + // Install and activate the helper inside our application bundle to disk. + var cfError: Unmanaged? + var authItem = AuthorizationItem(name: kSMRightBlessPrivilegedHelper, + valueLength: 0, + value: UnsafeMutableRawPointer(bitPattern: 0), + flags: 0) + + var authRights = AuthorizationRights(count: 1, + items: &authItem) + + let success: Bool + do { + if let authRef = try self.authorizationRef(&authRights, nil, [.interactionAllowed, .extendRights, .preAuthorize]) { + success = SMJobBless(kSMDomainSystemLaunchd, + label as CFString, + authRef, + &cfError) + } else { + success = false + } + } catch { + success = false + } + + if let error = cfError?.takeRetainedValue() { + return false + } + + return success + + } + + // MARK: - Private + + private static func authorizationRef(_ rights: UnsafePointer?, + _ environment: UnsafePointer?, + _ flags: AuthorizationFlags) throws -> AuthorizationRef? { + var authRef: AuthorizationRef? + try executeAuthorizationFunction { AuthorizationCreate(rights, environment, flags, &authRef) } + return authRef + } + + private static func executeAuthorizationFunction(_ authorizationFunction: () -> (OSStatus) ) throws { + let osStatus = authorizationFunction() + guard osStatus == errAuthorizationSuccess else { + throw HiveAuthorizationError.message(String(describing: SecCopyErrorMessageString(osStatus, nil))) + } + } + +} diff --git a/macOS/Privado/Common/View/FlatButton/FlatButton.swift b/macOS/Privado/Common/View/FlatButton/FlatButton.swift new file mode 100644 index 00000000..d7d921a2 --- /dev/null +++ b/macOS/Privado/Common/View/FlatButton/FlatButton.swift @@ -0,0 +1,411 @@ +// +// FlatButton.swift +// Privado +// +// Created by Jura on 11/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class FlatButton: NSButton, CALayerDelegate { + + /// Restore button state when mouse up. + var momentary = false { + didSet { + self.animateColor(with: self.state) + } + } + + /// The animation duration from NSOffState to NSOnState. + var onAnimateDuration: TimeInterval = 0.0 + + /// The animation duration from NSOnState to NSOffState. + var offAnimateDuration: TimeInterval = 0.0 + + /// Button's border corner radius. + var cornerRadius: CGFloat = 0.0 { + didSet { + self.layer?.cornerRadius = self.cornerRadius + } + } + + /// Button's border width. + var borderWidth: CGFloat = 0.0 { + didSet { + self.layer?.borderWidth = self.borderWidth + } + } + + /// Button's spacint between image and title. + var spacing: CGFloat = 0.0 { + didSet { + self.setupImageLayer() + self.setupTitleLayer() + } + } + + /// Button's border color when state off. + var borderNormalColor: NSColor? { + didSet { + self.animateColor(with: self.state) + } + } + + /// Button's border color when state on. + var borderHighlightColor: NSColor? { + didSet { + self.animateColor(with: self.state) + } + } + + /// Button's background color when state off. + var backgroundNormalColor: NSColor? { + didSet { + self.animateColor(with: self.state) + } + } + + /// Button's background color when state on. + var backgroundHighlightColor: NSColor? { + didSet { + self.animateColor(with: self.state) + } + } + + /// Button's image color when state off. + var imageNormalColor: NSColor? { + didSet { + self.animateColor(with: self.state) + } + } + + /// Button's image color when state on. + var imageHighlightColor: NSColor? { + didSet { + self.animateColor(with: self.state) + } + } + + /// Button's title color when state off. + var titleNormalColor: NSColor? { + didSet { + self.animateColor(with: self.state) + } + } + + /// Button's title color when state on. + var titleHighlightColor: NSColor? { + didSet { + self.animateColor(with: self.state) + } + } + + private lazy var imageLayer: CAShapeLayer = { + let layer = CAShapeLayer() + layer.delegate = self + return layer + }() + private lazy var titleLayer: CATextLayer = { + let layer = CATextLayer() + layer.delegate = self + return layer + }() + private var mouseDown = false + + // MARK: - Initializers + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + + self.setup() + self.setupImageLayer() + self.setupTitleLayer() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Private + + private func setup() { + // Setup layer + self.translatesAutoresizingMaskIntoConstraints = false + self.wantsLayer = true + self.layer?.masksToBounds = true + self.layer?.delegate = self + self.layer?.backgroundColor = NSColor.red.cgColor + self.alphaValue = self.isEnabled ? 1.0 : 0.5 + } + + // swiftlint:disable function_body_length + private func setupImageLayer() { + // Ignore image layer if has no image or imagePosition equal to NSNoImage + guard let image = self.image + , self.imagePosition != .noImage else { + self.imageLayer.removeFromSuperlayer() + return + } + + let buttonSize = self.frame.size + let imageSize = image.size + let titleSize: CGSize + if let font = self.font { + titleSize = self.title.size(withAttributes: [.font: font]) + } else { + titleSize = self.title.size(withAttributes: nil) + } + + + var x: CGFloat = 0.0 // Image's origin x + var y: CGFloat = 0.0 // Image's origin y + + // Caculate the image's and title's position depends on button's imagePosition and imageHugsTitle property + switch self.imagePosition { + case .noImage: + return + case .imageOnly: + x = (buttonSize.width - imageSize.width) / 2.0 + y = (buttonSize.height - imageSize.height) / 2.0 + case .imageOverlaps: + x = (buttonSize.width - imageSize.width) / 2.0 + y = (buttonSize.height - imageSize.height) / 2.0 + case .imageLeading, + .imageLeft: + x = self.imageHugsTitle + ? (buttonSize.width - imageSize.width - titleSize.width) / 2.0 - self.spacing + : self.spacing + y = (buttonSize.height - imageSize.height) / 2.0 + case .imageTrailing, + .imageRight: + x = self.imageHugsTitle + ? (buttonSize.width - imageSize.width + titleSize.width) / 2.0 + self.spacing + : buttonSize.width - imageSize.width - self.spacing + y = (buttonSize.height - imageSize.height) / 2.0 + case .imageAbove: + x = (buttonSize.width - imageSize.width) / 2.0 + y = self.imageHugsTitle + ? (buttonSize.height - imageSize.height - titleSize.height) / 2.0 - self.spacing + : self.spacing + case .imageBelow: + x = (buttonSize.width - imageSize.width) / 2.0 + y = self.imageHugsTitle + ? (buttonSize.height - imageSize.height + titleSize.height) / 2.0 + self.spacing + : buttonSize.height - imageSize.height - self.spacing + default: + break + } + + // Setup image layer + self.imageLayer.frame = self.bounds + + self.imageLayer.mask = { + let layer = CALayer() + var rect = NSRect(x: round(x), y: round(y), width: imageSize.width, height: imageSize.height) + layer.frame = rect + layer.contents = image.cgImage(forProposedRect: &rect, context: nil, hints: nil) + return layer + }() + + self.layer?.addSublayer(self.imageLayer) + } + // swiftlint:enable function_body_length + + // swiftlint:disable function_body_length + private func setupTitleLayer() { + + // Ignore title layer if has no title or imagePosition equal to NSImageOnly + if self.title.isEmpty || self.imagePosition == .imageOnly { + self.titleLayer.removeFromSuperlayer() + return + } + + guard let font = self.font else { return } + + let buttonSize = self.frame.size + let imageSize = self.image?.size ?? .zero + let titleSize = self.title.size(withAttributes: [.font: font]) + + var x: CGFloat = 0.0 // Title's origin x + var y: CGFloat = 0.0 // Title's origin y + + // Caculate the image's and title's position depends on button's imagePosition and imageHugsTitle property + switch self.imagePosition { + case .imageOnly: + return + case .noImage: + x = (buttonSize.width - titleSize.width) / 2.0 + y = (buttonSize.height - titleSize.height) / 2.0 + case .imageOverlaps: + x = (buttonSize.width - titleSize.width) / 2.0 + y = (buttonSize.height - titleSize.height) / 2.0 + case .imageLeading, + .imageLeft: + x = self.imageHugsTitle + ? (buttonSize.width + imageSize.width - titleSize.width) / 2.0 + self.spacing + : buttonSize.width - titleSize.width - self.spacing + y = (buttonSize.height - titleSize.height) / 2.0 + case .imageTrailing, + .imageRight: + x = self.imageHugsTitle + ? (buttonSize.width - imageSize.width - titleSize.width) / 2.0 - self.spacing + : self.spacing + y = (buttonSize.height - titleSize.height) / 2.0 + case .imageAbove: + x = (buttonSize.width - titleSize.width) / 2.0 + y = self.imageHugsTitle + ? (buttonSize.height + imageSize.height - titleSize.height) / 2.0 + self.spacing + : buttonSize.height - titleSize.height - self.spacing + case .imageBelow: + y = self.imageHugsTitle + ? (buttonSize.height - imageSize.height - titleSize.height) / 2.0 - self.spacing + : self.spacing + x = (buttonSize.width - titleSize.width) / 2.0 + default: + break + } + + // Setup title layer + self.titleLayer.frame = NSRect(x: round(x), y: round(y), width: ceil(titleSize.width), height: ceil(titleSize.height)) + self.titleLayer.string = self.title + self.titleLayer.font = font + self.titleLayer.fontSize = font.pointSize + + self.layer?.addSublayer(self.titleLayer) + } + // swiftlint:enable function_body_length + + // MARK: - Drawing method + + override func draw(_ dirtyRect: NSRect) { + } + + // MARK: - Lifecycle + + // MARK: - Animation method + + func removeAllAnimations() { + self.layer?.removeAllAnimations() + self.layer?.sublayers?.forEach { + $0.removeAllAnimations() + } + } + + func animateColor(with state: NSCell.StateValue) { + self.removeAllAnimations() + + let duration = state == .on ? self.onAnimateDuration : self.offAnimateDuration + let borderColor = state == .on ? self.borderHighlightColor : self.borderNormalColor + let backgroundColor = state == .on ? self.backgroundHighlightColor : self.backgroundNormalColor + let titleColor = state == .on ? self.titleHighlightColor : self.titleNormalColor + let imageColor = state == .on ? self.imageHighlightColor : self.imageNormalColor + + self.animate(layer: self.layer, color: borderColor, keyPath: "borderColor", duration: duration) + self.animate(layer: self.layer, color: backgroundColor, keyPath: "backgroundColor", duration: duration) + self.animate(layer: self.imageLayer, color: imageColor, keyPath: "backgroundColor", duration: duration) + self.animate(layer: self.titleLayer, color: titleColor, keyPath: "foregroundColor", duration: duration) + } + + func animate(layer: CALayer?, color: NSColor?, keyPath: String, duration: TimeInterval) { + + guard let currentLayer = layer + , let newColor = color else { + return + } + + // swiftlint:disable force_cast + let oldColor = currentLayer.value(forKeyPath: keyPath) as! CGColor + // swiftlint:enable force_cast + + guard oldColor != newColor.cgColor else { return } + + let animation = CABasicAnimation(keyPath: keyPath) + animation.fromValue = currentLayer.value(forKeyPath: keyPath) + animation.toValue = newColor.cgColor + animation.duration = duration + animation.isRemovedOnCompletion = false + + currentLayer.add(animation, forKey: keyPath) + currentLayer.setValue(newColor.cgColor, forKey: keyPath) + } + + // MARK: - Event Response + + override func hitTest(_ point: NSPoint) -> NSView? { + return self.isEnabled + ? super.hitTest(point) + : nil + } + + override func mouseDown(with event: NSEvent) { + guard self.isEnabled else { return } + self.mouseDown = true + self.state = self.state == .on ? .off : . on + } + + override func mouseEntered(with event: NSEvent) { + guard self.mouseDown else { return } + self.state = self.state == .on ? .off : .on + } + + override func mouseExited(with event: NSEvent) { + guard self.mouseDown else { return } + self.mouseDown = false + self.state = self.state == .on ? .off : .on + } + + override func mouseUp(with event: NSEvent) { + guard self.mouseDown else { return } + self.mouseDown = false + if self.momentary { + self.state = (self.state == .on) ? .off : .on + } + + if let selector = self.action { + NSApp.sendAction(selector, to: self.target, from: self) + } + + } + + // MARK: - Properties + + override var frame: NSRect { + didSet { + self.setupTitleLayer() + } + } + + override var font: NSFont? { + didSet { + self.setupTitleLayer() + } + } + + override var title: String { + didSet { + self.setupTitleLayer() + } + } + + override var image: NSImage? { + didSet { + self.setupImageLayer() + } + } + + override var state: NSControl.StateValue { + didSet { + self.animateColor(with: self.state) + } + } + + override var imagePosition: NSControl.ImagePosition { + didSet { + self.setupImageLayer() + self.setupTitleLayer() + } + } + +} diff --git a/macOS/Privado/Common/View/HyperlinkTextField.swift b/macOS/Privado/Common/View/HyperlinkTextField.swift new file mode 100644 index 00000000..ae76a32f --- /dev/null +++ b/macOS/Privado/Common/View/HyperlinkTextField.swift @@ -0,0 +1,64 @@ +// +// HyperlinkTextField.swift +// Cyberlock +// +// Created by Juraldinio on 8/31/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +protocol HyperlinkTextFieldDelegate: AnyObject { + func hyperlinkClicked(target: HyperlinkTextField) +} + +final class HyperlinkTextField: NSTextField { + + private struct Constants { + static let linkColor = NSColor(calibratedRed: 173 / 255, green: 179 / 255, blue: 210 / 255, alpha: 0.8) + } + + weak var hyperlinkDelegate: HyperlinkTextFieldDelegate? + + // MARK: - Initializers + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + + self.isBezeled = false + self.drawsBackground = false + self.isEditable = false + self.isSelectable = false + self.translatesAutoresizingMaskIntoConstraints = false + self.focusRingType = .none + if let font = self.font { + self.font = NSFont(name: font.fontName, size: 12.0) + } + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - + + func show(hyper link: String, with url: URL?, using color: NSColor? = nil) { + let textAttributes = [ + NSAttributedString.Key.foregroundColor: color ?? Constants.linkColor + ] + self.attributedStringValue = NSAttributedString(string: link, attributes: textAttributes) + } + + override func mouseDown(with event: NSEvent) { + self.hyperlinkDelegate?.hyperlinkClicked(target: self) + } + + /// Always display a pointing-hand cursor + override func resetCursorRects() { + super.resetCursorRects() + self.addCursorRect(self.bounds, cursor: NSCursor.pointingHand) + } + +} diff --git a/macOS/Privado/Common/View/ImageView.swift b/macOS/Privado/Common/View/ImageView.swift new file mode 100644 index 00000000..7eeaa900 --- /dev/null +++ b/macOS/Privado/Common/View/ImageView.swift @@ -0,0 +1,122 @@ +// +// ImageView.swift +// Cyberlock +// +// Created by Juraldinio on 9/1/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class ImageView: NSImageView { + + typealias TrackingMouseOverClosure = (Bool) -> Void + + private var trackingArea: NSTrackingArea? + + var roundImage = false + + var imageUrl: URL? { + didSet { + guard let url = self.imageUrl else { + self.image = nil + return + } + + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + guard let image = NSImage(contentsOf: url) else { return } + DispatchQueue.main.async { + self?.image = image + } + } + + } + } + + var useHandCursor = false { + didSet { + self.window?.invalidateCursorRects(for: self) + } + } + + var mouseOverTracker: TrackingMouseOverClosure? { + didSet { + self.setupTrackingArea(enable: self.mouseOverTracker.isExist) + } + } + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + self.setup() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setup() { + self.setContentCompressionResistancePriority(.windowSizeStayPut, for: .horizontal) + self.setContentCompressionResistancePriority(.windowSizeStayPut, for: .vertical) + self.imageScaling = .scaleProportionallyUpOrDown + self.translatesAutoresizingMaskIntoConstraints = false + self.wantsLayer = true + } + + private func setupTrackingArea(enable: Bool) { + + if let trackingArea = self.trackingArea { + self.removeTrackingArea(trackingArea) + } + + guard enable else { return } + + let area = NSTrackingArea.init(rect: self.bounds, + options: [NSTrackingArea.Options.mouseEnteredAndExited, NSTrackingArea.Options.activeAlways], + owner: self, + userInfo: nil) + self.trackingArea = area + self.addTrackingArea(area) + } + + // MARK: - LifeCycle + + override func layout() { + super.layout() + + self.setupTrackingArea(enable: self.mouseOverTracker.isExist) + + // Round image + guard self.roundImage else { return } + + let radius = self.bounds.width / 2.0 + self.layer?.cornerRadius = radius + } + + override func resetCursorRects() { + super.resetCursorRects() + guard self.useHandCursor else { return } + self.addCursorRect(self.bounds, cursor: NSCursor.pointingHand) + } + + override func mouseDown(with event: NSEvent) { + guard self.useHandCursor + , let action = self.action else { + super.mouseDown(with: event) + return + } + NSApp.sendAction(action, to: self.target, from: self) + } + + override func mouseEntered(with event: NSEvent) { + super.mouseEntered(with: event) + self.mouseOverTracker?(true) + } + + override func mouseExited(with event: NSEvent) { + super.mouseExited(with: event) + self.mouseOverTracker?(false) + } + +} diff --git a/macOS/Privado/Common/View/NavigationController/EmptyViewController.swift b/macOS/Privado/Common/View/NavigationController/EmptyViewController.swift new file mode 100644 index 00000000..a483aed0 --- /dev/null +++ b/macOS/Privado/Common/View/NavigationController/EmptyViewController.swift @@ -0,0 +1,76 @@ +// +// EmptyViewController.swift +// PrivadoVPN +// +// Created by Juraldinio on 7/30/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation +import AppKit + +final class EmptyViewController: NSViewController { + + private enum Constants { + static let inputBrightness = "inputBrightness" + static let filterName = "CIColorControls" + + } + + // MARK: - Lyfecycle + + override func loadView() { + let view = NSView(frame: .zero) + view.autoresizesSubviews = true + view.translatesAutoresizingMaskIntoConstraints = false + view.wantsLayer = true + view.layer?.backgroundColor = NSColor(red: 21 / 255, green: 25 / 255, blue: 53 / 255, alpha: 1.0).cgColor + self.view = view + } + + override func viewDidLoad() { + super.viewDidLoad() + self.configureUI() + } + + // MARK: - UI + + private func configureUI() { + + NSLayoutConstraint.activate([ + self.view.widthAnchor.constraint(equalToConstant: PrivadoConstants.Window.sizeWidth), + self.view.heightAnchor.constraint(equalToConstant: PrivadoConstants.Window.sizeHeight) + ]) + + // Background image + let image = NSImage(imageLiteralResourceName: "map.background") + let imageView = ImageView(image: image) + self.view.addSubview(imageView) + let space: CGFloat = -100 + NSLayoutConstraint.activate([ + imageView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: space), + imageView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -space), + imageView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: space), + imageView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -space) + ]) + + let progressIndicator = NSProgressIndicator(frame: .zero) + progressIndicator.style = .spinning + if let lighten = CIFilter(name: Constants.filterName) { + lighten.setDefaults() + lighten.setValue(1, forKey: Constants.inputBrightness) + progressIndicator.contentFilters = [lighten] + } + progressIndicator.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(progressIndicator) + NSLayoutConstraint.activate([ + progressIndicator.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + progressIndicator.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), + progressIndicator.heightAnchor.constraint(equalToConstant: 30), + progressIndicator.widthAnchor.constraint(equalToConstant: 30) + ]) + + progressIndicator.startAnimation(self) + + } +} diff --git a/macOS/Privado/Common/View/NavigationController/JSNavigationBarController.swift b/macOS/Privado/Common/View/NavigationController/JSNavigationBarController.swift new file mode 100644 index 00000000..47013163 --- /dev/null +++ b/macOS/Privado/Common/View/NavigationController/JSNavigationBarController.swift @@ -0,0 +1,101 @@ +// +// JSNavigationBarController.swift +// JSNavigationController +// + +import AppKit + +final class JSNavigationBarController: JSViewControllersStackManager { + var viewControllers: [NSViewController] = [] + weak var contentView: NSView? + + // MARK: - Initializers + public init(view: NSView?) { + contentView = view + } + + // MARK: - Default animations + func defaultPushAnimation() -> AnimationBlock { + return { [weak self] (_, _) in + let containerViewBounds = self?.contentView?.bounds ?? .zero + + let slideToLeftTransform = CATransform3DMakeTranslation(-containerViewBounds.width / 2, 0, 0) + let slideToLeftAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform)) + slideToLeftAnimation.fromValue = NSValue(caTransform3D: CATransform3DIdentity) + slideToLeftAnimation.toValue = NSValue(caTransform3D: slideToLeftTransform) + slideToLeftAnimation.duration = 0.25 + slideToLeftAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + slideToLeftAnimation.fillMode = CAMediaTimingFillMode.forwards + slideToLeftAnimation.isRemovedOnCompletion = false + + let slideFromRightTransform = CATransform3DMakeTranslation(containerViewBounds.width / 2, 0, 0) + let slideFromRightAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform)) + slideFromRightAnimation.fromValue = NSValue(caTransform3D: slideFromRightTransform) + slideFromRightAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity) + slideFromRightAnimation.duration = 0.25 + slideFromRightAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + slideFromRightAnimation.fillMode = CAMediaTimingFillMode.forwards + slideFromRightAnimation.isRemovedOnCompletion = false + + let fadeInAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity)) + fadeInAnimation.fromValue = 0.0 + fadeInAnimation.toValue = 1.0 + fadeInAnimation.duration = 0.25 + fadeInAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + fadeInAnimation.fillMode = CAMediaTimingFillMode.forwards + fadeInAnimation.isRemovedOnCompletion = false + + let fadeOutAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity)) + fadeOutAnimation.fromValue = 1.0 + fadeOutAnimation.toValue = 0.0 + fadeOutAnimation.duration = 0.25 + fadeOutAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + fadeOutAnimation.fillMode = CAMediaTimingFillMode.forwards + fadeOutAnimation.isRemovedOnCompletion = false + + return ([slideToLeftAnimation, fadeOutAnimation], [slideFromRightAnimation, fadeInAnimation]) + } + } + + func defaultPopAnimation() -> AnimationBlock { + return { [weak self] (_, _) in + let containerViewBounds = self?.contentView?.bounds ?? .zero + + let slideToRightTransform = CATransform3DMakeTranslation(-containerViewBounds.width / 2, 0, 0) + let slideToRightAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform)) + slideToRightAnimation.fromValue = NSValue(caTransform3D: slideToRightTransform) + slideToRightAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity) + slideToRightAnimation.duration = 0.25 + slideToRightAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + slideToRightAnimation.fillMode = CAMediaTimingFillMode.forwards + slideToRightAnimation.isRemovedOnCompletion = false + + let slideToRightFromCenterTransform = CATransform3DMakeTranslation(containerViewBounds.width / 2, 0, 0) + let slideToRightFromCenterAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform)) + slideToRightFromCenterAnimation.fromValue = NSValue(caTransform3D: CATransform3DIdentity) + slideToRightFromCenterAnimation.toValue = NSValue(caTransform3D: slideToRightFromCenterTransform) + slideToRightFromCenterAnimation.duration = 0.35 + slideToRightFromCenterAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + slideToRightFromCenterAnimation.fillMode = CAMediaTimingFillMode.forwards + slideToRightFromCenterAnimation.isRemovedOnCompletion = false + + let fadeInAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity)) + fadeInAnimation.fromValue = 0.0 + fadeInAnimation.toValue = 1.0 + fadeInAnimation.duration = 0.25 + fadeInAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + fadeInAnimation.fillMode = CAMediaTimingFillMode.forwards + fadeInAnimation.isRemovedOnCompletion = false + + let fadeOutAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity)) + fadeOutAnimation.fromValue = 1.0 + fadeOutAnimation.toValue = 0.0 + fadeOutAnimation.duration = 0.25 + fadeOutAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + fadeOutAnimation.fillMode = CAMediaTimingFillMode.forwards + fadeOutAnimation.isRemovedOnCompletion = false + + return ([slideToRightFromCenterAnimation, fadeOutAnimation], [slideToRightAnimation, fadeInAnimation]) + } + } +} diff --git a/macOS/Privado/Common/View/NavigationController/JSNavigationBarViewControllerProvider.swift b/macOS/Privado/Common/View/NavigationController/JSNavigationBarViewControllerProvider.swift new file mode 100644 index 00000000..0d918e44 --- /dev/null +++ b/macOS/Privado/Common/View/NavigationController/JSNavigationBarViewControllerProvider.swift @@ -0,0 +1,11 @@ +// +// JSNavigationBarViewControllerProvider.swift +// JSNavigationController +// + +import AppKit + +protocol JSNavigationBarViewControllerProvider: class { + var navigationController: JSNavigationController? { get set } + func navigationBarViewController() -> NSViewController +} diff --git a/macOS/Privado/Common/View/NavigationController/JSNavigationController.swift b/macOS/Privado/Common/View/NavigationController/JSNavigationController.swift new file mode 100644 index 00000000..33f9ce5f --- /dev/null +++ b/macOS/Privado/Common/View/NavigationController/JSNavigationController.swift @@ -0,0 +1,266 @@ +// +// JSNavigationController.swift +// JSNavigationController +// + +import AppKit + +final class JSNavigationController: NSViewController, JSViewControllersStackManager { + + private struct Constants { + static let segueTemplatesKey = "segueTemplates" + static let segueIdentifierKey = "identifier" + static let segueIdentifier = "rootViewController" + } + + weak var contentView: NSView? + var viewControllers: [NSViewController] = [] + var navigationBarController: JSNavigationBarController? + weak var delegate: JSNavigationControllerDelegate? + + // MARK: - Creating Navigation Controllers + init(rootViewController: NSViewController, contentView: NSView, navigationBarView: NSView?) { + self.contentView = contentView + self.navigationBarController = JSNavigationBarController(view: navigationBarView) + super.init(nibName: nil, bundle: nil) + self.push(viewController: rootViewController, animated: false) + } + + init(viewControllers: [NSViewController], contentView: NSView, navigationBarView: NSView) { + self.contentView = contentView + self.navigationBarController = JSNavigationBarController(view: navigationBarView) + super.init(nibName: nil, bundle: nil) + self.set(viewControllers: viewControllers, animated: false) + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + } + + // MARK: - View Lifecycle + override func loadView() { + if self.nibName.isExist { + super.loadView() + } else { + let v = NSView(frame: .zero) + v.translatesAutoresizingMaskIntoConstraints = false + v.wantsLayer = true + self.view = v + } + } + + override func viewDidAppear() { + super.viewDidAppear() + + guard self.nibName != nil else { return } + guard let segues = self.value(forKey: Constants.segueTemplatesKey) as? [NSObject] else { return } // Undocumented + + for segue in segues { + if let id = segue.value(forKey: Constants.segueIdentifierKey) as? String { + self.performSegue(withIdentifier: id, sender: self) + } + } + } + + // MARK: - Pushing + func push(viewController: NSViewController, contentAnimation: AnimationBlock?, navigationBarAnimation: AnimationBlock?) { + guard !Set(self.viewControllers).contains(viewController) else { return } + + self.viewControllers.append(viewController) + self.delegate?.navigationController(self, willShowViewController: viewController, animated: (contentAnimation != nil)) + + // Remove old view + if let previousViewController = self.previousViewController + , contentAnimation == nil { + previousViewController.view.removeFromSuperview() + } + + // Add the new view + self.contentView?.addSubview(viewController.view, positioned: .above, relativeTo: self.previousViewController?.view) + + // NavigationBar + if let vc = viewController as? JSNavigationBarViewControllerProvider { + vc.navigationController = self + self.navigationBarController?.push(viewController: vc.navigationBarViewController(), animation: navigationBarAnimation) + } else { + self.navigationBarController?.push(viewController: EmptyViewController(), animation: navigationBarAnimation) + } + + if let contentAnimation = contentAnimation { + CATransaction.begin() + CATransaction.setCompletionBlock { [weak self] in + self?.previousViewController?.view.removeFromSuperview() + self?.previousViewController?.view.layer?.removeAllAnimations() + self?.delegate?.navigationController(self!, didShowViewController: viewController, animated: true) + } + self.animatePush(contentAnimation) + CATransaction.commit() + } else { + self.delegate?.navigationController(self, didShowViewController: viewController, animated: false) + } + } + + func push(viewController: NSViewController, animation: AnimationBlock?) { + let navBarAnimation: AnimationBlock? = animation != nil ? navigationBarController?.defaultPushAnimation() : nil + self.push(viewController: viewController, contentAnimation: animation, navigationBarAnimation: navBarAnimation) + } + + func push(viewController: NSViewController, animated: Bool) { + if animated { + self.push(viewController: viewController, animation: self.defaultPushAnimation()) + } else { + self.push(viewController: viewController, animation: nil) + } + } + + // MARK: - Popping + func pop(toViewController viewController: NSViewController, contentAnimation: AnimationBlock?, navigationBarAnimation: AnimationBlock?) { + guard Set(self.viewControllers).contains(viewController) else { return } + guard let rootViewController = self.viewControllers.first else { return } + guard let topViewController = self.topViewController else { return } + guard topViewController != rootViewController else { return } + + self.delegate?.navigationController(self, willShowViewController: viewController, animated: (contentAnimation != nil)) + + let viewControllerPosition = self.viewControllers.firstIndex(of: viewController) + + // Add the new view + self.contentView?.addSubview(viewController.view, positioned: .below, relativeTo: topViewController.view) + + // NavigationBar + if let vc = viewController as? JSNavigationBarViewControllerProvider { + self.navigationBarController?.pop(toViewController: vc.navigationBarViewController(), animation: navigationBarAnimation) + } + + if let contentAnimation = contentAnimation { + CATransaction.begin() + CATransaction.setCompletionBlock { [unowned self] in + self.topViewController?.view.removeFromSuperview() + self.topViewController?.view.layer?.removeAllAnimations() + let range = (viewControllerPosition! + 1).. AnimationBlock { + return { [weak self] (_, _) in + let containerViewBounds = self?.contentView?.bounds ?? .zero + + let slideToLeftTransform = CATransform3DMakeTranslation(-containerViewBounds.width / 2, 0, 0) + let slideToLeftAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform)) + slideToLeftAnimation.fromValue = NSValue(caTransform3D: CATransform3DIdentity) + slideToLeftAnimation.toValue = NSValue(caTransform3D: slideToLeftTransform) + slideToLeftAnimation.duration = 0.25 + slideToLeftAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + slideToLeftAnimation.fillMode = CAMediaTimingFillMode.forwards + slideToLeftAnimation.isRemovedOnCompletion = false + + let slideFromRightTransform = CATransform3DMakeTranslation(containerViewBounds.width, 0, 0) + let slideFromRightAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform)) + slideFromRightAnimation.fromValue = NSValue(caTransform3D: slideFromRightTransform) + slideFromRightAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity) + slideFromRightAnimation.duration = 0.25 + slideFromRightAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + slideFromRightAnimation.fillMode = CAMediaTimingFillMode.forwards + slideFromRightAnimation.isRemovedOnCompletion = false + + return ([slideToLeftAnimation], [slideFromRightAnimation]) + } + } + + func defaultPopAnimation() -> AnimationBlock { + return { [weak self] (_, _) in + let containerViewBounds = self?.contentView?.bounds ?? .zero + + let slideToRightTransform = CATransform3DMakeTranslation(-containerViewBounds.width / 2, 0, 0) + let slideToRightAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform)) + slideToRightAnimation.fromValue = NSValue(caTransform3D: slideToRightTransform) + slideToRightAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity) + slideToRightAnimation.duration = 0.25 + slideToRightAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + slideToRightAnimation.fillMode = CAMediaTimingFillMode.forwards + slideToRightAnimation.isRemovedOnCompletion = false + + let slideToRightFromCenterTransform = CATransform3DMakeTranslation(containerViewBounds.width, 0, 0) + let slideToRightFromCenterAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform)) + slideToRightFromCenterAnimation.fromValue = NSValue(caTransform3D: CATransform3DIdentity) + slideToRightFromCenterAnimation.toValue = NSValue(caTransform3D: slideToRightFromCenterTransform) + slideToRightFromCenterAnimation.duration = 0.25 + slideToRightFromCenterAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + slideToRightFromCenterAnimation.fillMode = CAMediaTimingFillMode.forwards + slideToRightFromCenterAnimation.isRemovedOnCompletion = false + + return ([slideToRightFromCenterAnimation], [slideToRightAnimation]) + } + } + + // MARK: - Storyboard + override func prepare(for segue: NSStoryboardSegue, sender: Any?) { + guard segue.identifier == Constants.segueIdentifier else { return } + guard let destinationController = segue.destinationController as? NSViewController else { return } + + self.push(viewController: destinationController, animated: false) + } +} diff --git a/macOS/Privado/Common/View/NavigationController/JSNavigationControllerDelegate.swift b/macOS/Privado/Common/View/NavigationController/JSNavigationControllerDelegate.swift new file mode 100644 index 00000000..3cdde0c9 --- /dev/null +++ b/macOS/Privado/Common/View/NavigationController/JSNavigationControllerDelegate.swift @@ -0,0 +1,11 @@ +// +// JSNavigationControllerDelegate.swift +// JSNavigationController +// + +import AppKit + +protocol JSNavigationControllerDelegate: class { + func navigationController(_ navigationController: JSNavigationController, willShowViewController viewController: NSViewController, animated: Bool) + func navigationController(_ navigationController: JSNavigationController, didShowViewController viewController: NSViewController, animated: Bool) +} diff --git a/macOS/Privado/Common/View/NavigationController/JSNavigationControllerSegue.swift b/macOS/Privado/Common/View/NavigationController/JSNavigationControllerSegue.swift new file mode 100644 index 00000000..faa76945 --- /dev/null +++ b/macOS/Privado/Common/View/NavigationController/JSNavigationControllerSegue.swift @@ -0,0 +1,12 @@ +// +// JSRootViewControllerSegue.swift +// JSNavigationController +// + +import AppKit + +class JSNavigationControllerSegue: NSStoryboardSegue { + override func perform() { + return + } +} diff --git a/macOS/Privado/Common/View/NavigationController/JSViewController.swift b/macOS/Privado/Common/View/NavigationController/JSViewController.swift new file mode 100644 index 00000000..10a347b5 --- /dev/null +++ b/macOS/Privado/Common/View/NavigationController/JSViewController.swift @@ -0,0 +1,64 @@ +// +// JSViewController.swift +// JSNavigationController +// + +import AppKit + +class JSViewController: NSViewController, JSNavigationBarViewControllerProvider { + + private struct Constants { + static let segueTemplatesKey = "segueTemplates" + static let segueIdentifierKey = "identifier" + static let navigationControllerPushIdentifier = "navigationControllerPush" + static let navigationBarViewControllerIdentifier = "navigationBarViewController" + } + + private(set) var destinationViewController: NSViewController? + private(set) var destinationViewControllers: [String: NSViewController] = [:] + var navigationBarVC: NSViewController? + weak var navigationController: JSNavigationController? + + func navigationBarViewController() -> NSViewController { + guard let navigationBarVC = self.navigationBarVC else { fatalError("You must set the navigationBar view controller") } + return navigationBarVC + } + + // MARK: - View Lifecycle + override func awakeFromNib() { + if type(of: self).instancesRespond(to: #selector(NSViewController.awakeFromNib)) { + super.awakeFromNib() + } + self.setupSegues() + } + + // MARK: - Segues + private func setupSegues() { + guard let segues = self.value(forKey: Constants.segueTemplatesKey) as? [NSObject] else { return } + for segue in segues { + if let id = segue.value(forKey: Constants.segueIdentifierKey) as? String { + self.performSegue(withIdentifier: id, sender: self) + } + } + } + + override func prepare(for segue: NSStoryboardSegue, sender: Any?) { + + guard let segueIdentifier = segue.identifier else { return } + + switch segueIdentifier { + case Constants.navigationBarViewControllerIdentifier: + self.navigationBarVC = segue.destinationController as? NSViewController + default: + if segueIdentifier.contains(Constants.navigationControllerPushIdentifier) { + if segueIdentifier.count > Constants.navigationControllerPushIdentifier.count && segueIdentifier.contains("#") { + if let key = segueIdentifier.split(separator: "#").map({ String($0) }).last { + self.destinationViewControllers[key] = segue.destinationController as? NSViewController + } + } else { + self.destinationViewController = segue.destinationController as? NSViewController + } + } + } + } +} diff --git a/macOS/Privado/Common/View/NavigationController/JSViewControllersStackManager.swift b/macOS/Privado/Common/View/NavigationController/JSViewControllersStackManager.swift new file mode 100644 index 00000000..0ba3b0ae --- /dev/null +++ b/macOS/Privado/Common/View/NavigationController/JSViewControllersStackManager.swift @@ -0,0 +1,257 @@ +// +// JSViewControllersStackController.swift +// JSNavigationController +// + +import AppKit + +typealias AnimationBlock = (_ fromView: NSView?, _ toView: NSView?) -> (fromViewAnimations: [CAAnimation], toViewAnimations: [CAAnimation]) + +protocol JSViewControllersStackManager: AnyObject { + /// The view in which views will be pushed. + var contentView: NSView? { get set } + /// The view controllers currently on the navigation stack. + var viewControllers: [NSViewController] { get set } + /// The view controller at the top of the navigation stack. + var topViewController: NSViewController? { get } + /// The view controller above the top view controller. Nil if the top view controller is the root view controller. + var previousViewController: NSViewController? { get } + + /** + Replaces the view controllers currently managed by the navigation controller with the specified items. + + - parameter viewControllers: The view controllers to place in the stack. + The front-to-back order of the controllers in this array represents the new bottom-to-top order of the controllers in the navigation stack. + Thus, the last item added to the array becomes the top item of the navigation stack. + - parameter animated: If true, animate the pushing or popping of the top view controller. If false, replace the view controllers without any animations. + */ + func set(viewControllers: [NSViewController], animated: Bool) + // MARK: - Pushing + /** + Pushes a view controller onto the receiver’s stack and updates the display. + + - parameter viewController: The view controller to push onto the stack. + If the view controller is already on the navigation stack, this method does nothing. + - parameter animation: The animation block to apply during the transition. Specify nil if you do not want the transition to be animated. + */ + func push(viewController: NSViewController, animation: AnimationBlock?) + /** + Pushes a view controller onto the receiver’s stack and updates the display. + + - parameter viewController: The view controller to push onto the stack. + If the view controller is already on the navigation stack, this method does nothing. + - parameter animated: Specify true to animate the transition or false if you do not want the transition to be animated. + You might specify false if you are setting up the navigation controller at launch time.. + */ + func push(viewController: NSViewController, animated: Bool) + // MARK: - Popping + /** + Pops the top view controller from the navigation stack and updates the display. + + - parameter animation: The animation block to apply during the transition. Specify nil if you do not want the transition to be animated. + */ + func popViewController(animation: AnimationBlock?) + /** + Pops the top view controller from the navigation stack and updates the display. + + - parameter animated: Specify true to animate the transition or false if you do not want the transition to be animated. + */ + func popViewController(animated: Bool) + /** + Pops view controllers until the specified view controller is at the top of the navigation stack. + + - parameter viewController: The view controller that you want to be at the top of the stack. + Does nothing if this view controller is not on the navigation stack. + - parameter animation: The animation block to apply during the transition. Specify nil if you do not want the transition to be animated. + */ + func pop(toViewController viewController: NSViewController, animation: AnimationBlock?) + /** + Pops view controllers until the specified view controller is at the top of the navigation stack. + + - parameter viewController: The view controller that you want to be at the top of the stack. + - parameter animated: Specify true to animate the transition or false if you do not want the transition to be animated. + */ + func pop(toViewController viewController: NSViewController, animated: Bool) + /** + Pops all the view controllers on the stack except the root view controller and updates the display. + + - parameter animation: The animation block to apply during the transition. Specify nil if you do not want the transition to be animated. + */ + func popToRootViewController(animation: AnimationBlock?) + /** + Pops all the view controllers on the stack except the root view controller and updates the display. + + - parameter animated: Specify true to animate the transition or false if you do not want the transition to be animated. + */ + func popToRootViewController(animated: Bool) + + // MARK: - Animating + func animatePush(_ animation: AnimationBlock) + func animatePop(toView view: NSView?, animation: AnimationBlock) + func defaultPushAnimation() -> AnimationBlock + func defaultPopAnimation() -> AnimationBlock +} + +// MARK: - +extension JSViewControllersStackManager { + + var topViewController: NSViewController? { + return self.viewControllers.last + } + + var previousViewController: NSViewController? { + return self.viewControllers[safe: self.viewControllers.count - 2] + } + + func set(viewControllers: [NSViewController], animated: Bool) { + + guard !viewControllers.isEmpty else { return } + + if animated { + if let lastViewController = viewControllers.last { + if self.viewControllers.contains(lastViewController) && lastViewController != self.topViewController { + self.pop(toViewController: lastViewController, animated: true) + } else { + self.push(viewController: lastViewController, animated: true) + } + } + } else { + if let lastViewController = viewControllers.last { + self.push(viewController: lastViewController, animated: false) + } + self.viewControllers = viewControllers + } + } + + func push(viewController: NSViewController, animation: AnimationBlock?) { + + guard !Set(self.viewControllers).contains(viewController) else { return } + + self.viewControllers.append(viewController) + + // Remove old view + if let previousViewController = self.previousViewController + , !animation.isExist { + previousViewController.view.removeFromSuperview() + } + + // Add the new view + self.contentView?.addSubview(viewController.view, positioned: .above, relativeTo: self.previousViewController?.view) + + if let animation = animation { + CATransaction.begin() + CATransaction.setCompletionBlock { [weak self] in + self?.previousViewController?.view.removeFromSuperview() + self?.previousViewController?.view.layer?.removeAllAnimations() + } + self.animatePush(animation) + CATransaction.commit() + } + } + + func push(viewController: NSViewController, animated: Bool) { + if animated { + self.push(viewController: viewController, animation: self.defaultPushAnimation()) + } else { + self.push(viewController: viewController, animation: nil) + } + } + + // MARK: - Popping + func popViewController(animation: AnimationBlock?) { + guard let previousViewController = self.previousViewController else { return } // You can't pop the root view controller + self.pop(toViewController: previousViewController, animation: animation) + } + + func popViewController(animated: Bool) { + if animated { + self.popViewController(animation: self.defaultPopAnimation()) + } else { + self.popViewController(animation: nil) + } + } + + func pop(toViewController viewController: NSViewController, animation: AnimationBlock?) { + + guard Set(self.viewControllers).contains(viewController) + , let rootViewController = self.viewControllers.first + , let topViewController = self.topViewController + , topViewController != rootViewController else { + return + } + + let viewControllerPosition = self.viewControllers.firstIndex(of: viewController) + + // Add the new view + self.contentView?.addSubview(viewController.view, positioned: .below, relativeTo: topViewController.view) + + if let animation = animation { + CATransaction.begin() + CATransaction.setCompletionBlock { [unowned self] in + self.topViewController?.view.removeFromSuperview() + self.topViewController?.view.layer?.removeAllAnimations() + let range = (viewControllerPosition! + 1).. 1) + } + + private func restoreWindowPosition() { + guard let window = self.window + , let screenContainingWindow = window.screen else { + return + } + + window.setFrameOrigin(CGPoint( + x: screenContainingWindow.visibleFrame.midX - window.frame.width / 2, + y: screenContainingWindow.visibleFrame.midY - window.frame.height / 2 + )) + window.setFrameUsingName(.preferences) + window.setFrameAutosaveName(.preferences) + } +} diff --git a/macOS/Privado/Common/View/Preferences/View/Subviews/PreferencesTabViewController.swift b/macOS/Privado/Common/View/Preferences/View/Subviews/PreferencesTabViewController.swift new file mode 100644 index 00000000..6e1bed68 --- /dev/null +++ b/macOS/Privado/Common/View/Preferences/View/Subviews/PreferencesTabViewController.swift @@ -0,0 +1,253 @@ +// +// PreferencesTabViewController.swift +// Cyberlock +// +// Created by Jura on 9/11/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class PreferencesTabViewController: NSViewController, PreferencesStyleControllerOutput { + + private var activeTabIndex: Int? + + private var preferencePanes = [PreferencePane]() + + private var styleController: PreferencesStyleControllerInput? + + private var isKeepingWindowCentered: Bool { + return self.styleController?.isKeepingWindowCentered ?? false + } + + private var toolbarItemIdentifiers: [NSToolbarItem.Identifier] { + return self.styleController?.toolbarItemIdentifiers ?? [] + } + + var preferencePanesCount: Int { + return preferencePanes.count + } + + var window: NSWindow? { + return view.window + } + + var isAnimated = true + + // MARK: - Lifecycle + + override func loadView() { + self.view = NSView() + self.view.translatesAutoresizingMaskIntoConstraints = false + } + + func configure(preferencePanes: [PreferencePane], style: PreferencesStyle) { + + self.preferencePanes = preferencePanes + self.children = preferencePanes + + let toolbar = NSToolbar(identifier: "PreferencesToolbar") + toolbar.allowsUserCustomization = false + toolbar.showsBaselineSeparator = true + toolbar.displayMode = .iconAndLabel + toolbar.delegate = self + + let styleController: PreferencesStyleControllerInput + switch style { + case .segmentedControl: + styleController = SegmentedControlStyleViewController(preferencePanes: preferencePanes) + case .toolbarItems: + styleController = ToolbarItemStyleViewController( + preferencePanes: preferencePanes, + toolbar: toolbar, + centerToolbarItems: false + ) + } + + styleController.output = self + self.styleController = styleController + + // Called last so that `preferencesStyleController` can be asked for items + self.window?.toolbar = toolbar + } + + func activateTab(preferencePane: PreferencePane, animated: Bool) { + self.activateTab(preferenceIdentifier: preferencePane.preferencePaneIdentifier, animated: animated) + } + + func restoreInitialTab() { + if !self.activeTabIndex.isExist { + self.activateTab(index: 0, animated: false) + } + } + + private func updateWindowTitle(tabIndex: Int) { + self.window?.title = { + if !self.preferencePanes.isEmpty { + return self.preferencePanes[tabIndex].preferencePaneTitle + } else { + let preferences = "Settings" + let appName = Bundle.main.appName + return "\(appName) \(preferences)" + } + }() + } + + /// Cached constraints that pin childViewController views to the content view + private var activeChildViewConstraints = [NSLayoutConstraint]() + + private func immediatelyDisplayTab(index: Int) { + guard let viewController = self.preferencePanes[safe: index] else { return } + self.view.addSubview(viewController.view) + self.activeChildViewConstraints = viewController.view.constrainToSuperviewBounds() + self.setWindowFrame(for: viewController, animated: false) + } + + private func animateTabTransition(index: Int, animated: Bool) { + guard let activeTab = self.activeTabIndex else { + assertionFailure("animateTabTransition called before a tab was displayed; transition only works from one tab to another") + self.immediatelyDisplayTab(index: index) + return + } + + let fromViewController = self.preferencePanes[activeTab] + let toViewController = self.preferencePanes[index] + + // View controller animations only work on macOS 10.14 and newer + let options: NSViewController.TransitionOptions + if #available(macOS 10.14, *) { + options = animated && self.isAnimated ? [.crossfade] : [] + } else { + options = [] + } + + self.view.removeConstraints(self.activeChildViewConstraints) + + self.transition( + from: fromViewController, + to: toViewController, + options: options + ) { + self.activeChildViewConstraints = toViewController.view.constrainToSuperviewBounds() + } + } + + override func transition(from fromViewController: NSViewController, + to toViewController: NSViewController, + options: NSViewController.TransitionOptions = [], + completionHandler completion: (() -> Void)? = nil) { + let isAnimated = options + .intersection([ + .crossfade, + .slideUp, + .slideDown, + .slideForward, + .slideBackward, + .slideLeft, + .slideRight + ]) + .isEmpty == false + + guard isAnimated else { + super.transition(from: fromViewController, + to: toViewController, + options: options, + completionHandler: completion) + return + } + + NSAnimationContext.runAnimationGroup({ context in + context.allowsImplicitAnimation = true + context.duration = 0.25 + context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + self.setWindowFrame(for: toViewController, animated: true) + + super.transition( + from: fromViewController, + to: toViewController, + options: options, + completionHandler: completion + ) + }, completionHandler: nil) + + } + + private func setWindowFrame(for viewController: NSViewController, animated: Bool = false) { + + guard let window = self.window else { + preconditionFailure() + } + + let contentSize = viewController.view.fittingSize + + let newWindowSize = window.frameRect(forContentRect: CGRect(origin: .zero, size: contentSize)).size + + var frame = window.frame + frame.origin.y += frame.height - newWindowSize.height + frame.size = newWindowSize + + if self.isKeepingWindowCentered { + let horizontalDiff = (window.frame.width - newWindowSize.width) / 2 + frame.origin.x += horizontalDiff + } + + let animatableWindow = animated ? window.animator() : window + animatableWindow.setFrame(frame, display: false) + } + + // MARK: - PreferencesStyleControllerOutput + + func activateTab(preferenceIdentifier: PreferencePane.Identifier, animated: Bool) { + guard let index = (self.preferencePanes.firstIndex { $0.preferencePaneIdentifier == preferenceIdentifier }) else { + return self.activateTab(index: 0, animated: animated) + } + + self.activateTab(index: index, animated: animated) + } + + func activateTab(index: Int, animated: Bool) { + defer { + self.activeTabIndex = index + self.styleController?.selectTab(index: index) + self.updateWindowTitle(tabIndex: index) + } + + if self.activeTabIndex.isExist { + guard index != self.activeTabIndex else { + return + } + self.animateTabTransition(index: index, animated: animated) + } else { + self.immediatelyDisplayTab(index: index) + } + } + +} + +extension PreferencesTabViewController: NSToolbarDelegate { + + func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + return toolbarItemIdentifiers + } + + func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + return toolbarItemIdentifiers + } + + func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + return toolbarItemIdentifiers + } + + public func toolbar( + _ toolbar: NSToolbar, + itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, + willBeInsertedIntoToolbar flag: Bool + ) -> NSToolbarItem? { + guard itemIdentifier != .flexibleSpace else { + return nil + } + + return self.styleController?.toolbarItem(preferenceIdentifier: PreferencePane.Identifier(fromToolbarItemIdentifier: itemIdentifier)) + } +} diff --git a/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/PreferencesStyle.swift b/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/PreferencesStyle.swift new file mode 100644 index 00000000..84bc9c52 --- /dev/null +++ b/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/PreferencesStyle.swift @@ -0,0 +1,14 @@ +// +// PreferencesStyle.swift +// Cyberlock +// +// Created by Jura on 9/11/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +enum PreferencesStyle { + case toolbarItems + case segmentedControl +} diff --git a/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/PreferencesStyleController.swift b/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/PreferencesStyleController.swift new file mode 100644 index 00000000..39920689 --- /dev/null +++ b/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/PreferencesStyleController.swift @@ -0,0 +1,26 @@ +// +// PreferencesStyleController.swift +// Cyberlock +// +// Created by Jura on 9/11/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +protocol PreferencesStyleControllerInput: AnyObject { + + var output: PreferencesStyleControllerOutput? { get set } + var isKeepingWindowCentered: Bool { get } + var toolbarItemIdentifiers: [NSToolbarItem.Identifier] { get } + + func toolbarItem(preferenceIdentifier: PreferencePane.Identifier) -> NSToolbarItem? + func selectTab(index: Int) +} + +protocol PreferencesStyleControllerOutput: AnyObject { + + func activateTab(preferenceIdentifier: PreferencePane.Identifier, animated: Bool) + func activateTab(index: Int, animated: Bool) +} diff --git a/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/SegmentedControlStyleViewController.swift b/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/SegmentedControlStyleViewController.swift new file mode 100644 index 00000000..e6d87e21 --- /dev/null +++ b/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/SegmentedControlStyleViewController.swift @@ -0,0 +1,153 @@ +// +// SegmentedControlStyleViewController.swift +// Cyberlock +// +// Created by Jura on 9/11/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class SegmentedControlStyleViewController: NSViewController, PreferencesStyleControllerInput { + + private lazy var segmentedControl = self.createSegmentedControl(preferencePanes: self.preferencePanes) + private var preferencePanes: [PreferencePane] + + // MARK: - Init + + required init(preferencePanes: [PreferencePane]) { + self.preferencePanes = preferencePanes + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + self.view = self.segmentedControl + } + + @objc + private func segmentedControlAction(_ control: NSSegmentedControl) { + self.output?.activateTab(index: control.selectedSegment, animated: true) + } + + @objc + private func segmentedControlMenuAction(_ menuItem: NSMenuItem) { + self.output?.activateTab(index: menuItem.tag, animated: true) + } + + // MARK: - PreferencesStyleControllerInput + + weak var output: PreferencesStyleControllerOutput? + let isKeepingWindowCentered = true + let toolbarItemIdentifiers: [NSToolbarItem.Identifier] = [ + .flexibleSpace, + .toolbarSegmentedControlItem, + .flexibleSpace + ] + + func toolbarItem(preferenceIdentifier: PreferencePane.Identifier) -> NSToolbarItem? { + let toolbarItemIdentifier = preferenceIdentifier.toolbarItemIdentifier + precondition(toolbarItemIdentifier == .toolbarSegmentedControlItem) + + // When the segments outgrow the window, we need to provide a group of + // NSToolbarItems with custom menu item labels and action handling for the + // context menu that pops up at the right edge of the window. + let toolbarItemGroup = NSToolbarItemGroup(itemIdentifier: toolbarItemIdentifier) + toolbarItemGroup.view = self.segmentedControl + toolbarItemGroup.subitems = self.preferencePanes + .enumerated() + .map { index, preferenceable -> NSToolbarItem in + let item = NSToolbarItem(itemIdentifier: .init("segment-\(preferenceable.preferencePaneTitle)")) + item.label = preferenceable.preferencePaneTitle + + let menuItem = NSMenuItem( + title: preferenceable.preferencePaneTitle, + action: #selector(self.segmentedControlMenuAction), + keyEquivalent: "" + ) + menuItem.tag = index + menuItem.target = self + item.menuFormRepresentation = menuItem + + return item + } + + return toolbarItemGroup + } + + func selectTab(index: Int) { + self.segmentedControl.selectedSegment = index + } + + private func createSegmentedControl(preferencePanes: [PreferencePane]) -> NSSegmentedControl { + + let segmentedControl = NSSegmentedControl() + segmentedControl.segmentCount = preferencePanes.count + segmentedControl.segmentStyle = .texturedSquare + segmentedControl.target = self + segmentedControl.action = #selector(self.segmentedControlAction) + segmentedControl.identifier = .toolbarSegmentedControl + + if let cell = segmentedControl.cell as? NSSegmentedCell { + cell.controlSize = .regular + cell.trackingMode = .selectOne + } + + let segmentSize: CGSize = { + let insets = CGSize(width: 36, height: 12) + var maxSize = CGSize.zero + + preferencePanes.forEach { pane in + let title = pane.preferencePaneTitle + let titleSize = title.size( + withAttributes: [ + .font: NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .regular)) + ] + ) + + maxSize = CGSize( + width: max(titleSize.width, maxSize.width), + height: max(titleSize.height, maxSize.height) + ) + } + + return CGSize( + width: maxSize.width + insets.width, + height: maxSize.height + insets.height + ) + }() + + let segmentBorderWidth = CGFloat(preferencePanes.count) + 1 + let segmentWidth = segmentSize.width * CGFloat(preferencePanes.count) + segmentBorderWidth + let segmentHeight = segmentSize.height + segmentedControl.frame = CGRect(x: 0, y: 0, width: segmentWidth, height: segmentHeight) + + preferencePanes.enumerated().forEach { (index, pane) in + segmentedControl.setLabel(pane.preferencePaneTitle, forSegment: index) + segmentedControl.setWidth(segmentSize.width, forSegment: index) + if let cell = segmentedControl.cell as? NSSegmentedCell { + cell.setTag(index, forSegment: index) + } + } + + return segmentedControl + } + +} + +extension NSToolbarItem.Identifier { + static var toolbarSegmentedControlItem: NSToolbarItem.Identifier { + return NSToolbarItem.Identifier("toolbarSegmentedControlItem") + } +} + +extension NSUserInterfaceItemIdentifier { + static var toolbarSegmentedControl: NSUserInterfaceItemIdentifier { + return NSUserInterfaceItemIdentifier("toolbarSegmentedControl") + } +} diff --git a/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/ToolbarItemStyleViewController.swift b/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/ToolbarItemStyleViewController.swift new file mode 100644 index 00000000..1673e10c --- /dev/null +++ b/macOS/Privado/Common/View/Preferences/View/Subviews/StyleControllers/ToolbarItemStyleViewController.swift @@ -0,0 +1,71 @@ +// +// ToolbarItemStyleViewController.swift +// Cyberlock +// +// Created by Jura on 9/11/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class ToolbarItemStyleViewController: NSObject, PreferencesStyleControllerInput { + + private let toolbar: NSToolbar + private let preferencePanes: [PreferencePane] + + // MARK: - Init + + init(preferencePanes: [PreferencePane], toolbar: NSToolbar, centerToolbarItems: Bool) { + self.preferencePanes = preferencePanes + self.toolbar = toolbar + self.isKeepingWindowCentered = centerToolbarItems + } + + // MARK: - Private + + @objc + private func toolbarItemSelected(_ toolbarItem: NSToolbarItem) { + self.output?.activateTab( + preferenceIdentifier: PreferencePane.Identifier(fromToolbarItemIdentifier: toolbarItem.itemIdentifier), + animated: true + ) + } + + // MARK: - PreferencesStyleControllerInput + + weak var output: PreferencesStyleControllerOutput? + private(set) var isKeepingWindowCentered: Bool + + var toolbarItemIdentifiers: [NSToolbarItem.Identifier] { + + var toolbarItemIdentifiers = [NSToolbarItem.Identifier]() + + if self.isKeepingWindowCentered { toolbarItemIdentifiers.append(.flexibleSpace) } + + self.preferencePanes.forEach { toolbarItemIdentifiers.append($0.toolbarItemIdentifier) } + + if self.isKeepingWindowCentered { toolbarItemIdentifiers.append(.flexibleSpace) } + + return toolbarItemIdentifiers + } + + func toolbarItem(preferenceIdentifier: PreferencePane.Identifier) -> NSToolbarItem? { + + guard let preference = (self.preferencePanes.first { $0.preferencePaneIdentifier == preferenceIdentifier }) else { + preconditionFailure() + } + + let toolbarItem = NSToolbarItem(itemIdentifier: preferenceIdentifier.toolbarItemIdentifier) + toolbarItem.label = preference.preferencePaneTitle + toolbarItem.image = preference.toolbarItemIcon + toolbarItem.target = self + toolbarItem.action = #selector(self.toolbarItemSelected) + return toolbarItem + } + + func selectTab(index: Int) { + self.toolbar.selectedItemIdentifier = preferencePanes[index].toolbarItemIdentifier + } + +} diff --git a/macOS/Privado/Common/View/Preferences/View/Subviews/UserInteractionPausableWindow.swift b/macOS/Privado/Common/View/Preferences/View/Subviews/UserInteractionPausableWindow.swift new file mode 100644 index 00000000..a64f4a29 --- /dev/null +++ b/macOS/Privado/Common/View/Preferences/View/Subviews/UserInteractionPausableWindow.swift @@ -0,0 +1,36 @@ +// +// UserInteractionPausableWindow.swift +// Cyberlock +// +// Created by Jura on 9/11/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +/// A window that allows you to disable all user interactions via `isUserInteractionEnabled`. +/// +/// Used to avoid breaking animations when the user clicks too fast. Disable user interactions during +/// animations and you're set. +class UserInteractionPausableWindow: NSWindow { + + let isUserInteractionEnabled = true + + override func sendEvent(_ event: NSEvent) { + guard self.isUserInteractionEnabled || !event.isUserInteraction else { + return + } + + super.sendEvent(event) + } + + override func responds(to selector: Selector) -> Bool { + // Deactivate toolbar interactions from the Main Menu. + if selector == #selector(NSWindow.toggleToolbarShown(_:)) { + return false + } + + return super.responds(to: selector) + } +} diff --git a/macOS/Privado/Common/View/RoundedSecuredTextField/RoundedSecuredTextField.swift b/macOS/Privado/Common/View/RoundedSecuredTextField/RoundedSecuredTextField.swift new file mode 100644 index 00000000..0cac6e84 --- /dev/null +++ b/macOS/Privado/Common/View/RoundedSecuredTextField/RoundedSecuredTextField.swift @@ -0,0 +1,126 @@ +// +// RoundedSecuredTextField.swift +// Cyberlock +// +// Created by Jura on 8/31/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class RoundedSecuredTextField: NSSecureTextField, NSTextFieldDelegate { + + typealias EnterHandlerClosure = () -> Bool + + private struct Constants { + static let cornerRadius: CGFloat = 13 + static let placeholderColor = NSColor(calibratedRed: 173 / 255, green: 179 / 255, blue: 210 / 255, alpha: 0.5) + static let textColor = NSColor(calibratedRed: 173 / 255, green: 179 / 255, blue: 210 / 255, alpha: 1.0) + } + + // MARK: - Properties + + var enterHandler: EnterHandlerClosure? + + var coloredPlaceHolder: String? { + get { + return self.placeholderAttributedString?.string + } + set { + if let value = newValue + , let font = self.font { + let attrs = [NSAttributedString.Key.foregroundColor: Constants.placeholderColor, + NSAttributedString.Key.font: NSFont.systemFont(ofSize: font.pointSize)] + self.placeholderAttributedString = NSAttributedString(string: value, attributes: attrs) + } else { + self.placeholderAttributedString = nil + } + } + } + + // MARK: - Initializers + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + + self.isBezeled = false + self.drawsBackground = false + self.translatesAutoresizingMaskIntoConstraints = false + self.focusRingType = .none + self.textColor = Constants.textColor + self.delegate = self + if let font = self.font { + self.font = NSFont(name: font.fontName, size: 12.0) + } + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Methods + + func hightlight(with color: NSColor?) { + + guard let borderColor = color else { + self.layer?.borderWidth = 0.0 + return + } + + self.layer?.borderColor = borderColor.cgColor + self.layer?.borderWidth = 1.0 + self.layer?.cornerRadius = Constants.cornerRadius + + } + + + // MARK: - Lifecycle + + override class var cellClass: AnyClass? { + get { + return RoundedSecuredTextFieldCell.self + } + // swiftlint:disable unused_setter_value + set { } + // swiftlint:enable unused_setter_value + } + + override func viewDidMoveToWindow() { + if let textView = self.window?.fieldEditor(true, for: self) as? NSTextView { + textView.insertionPointColor = Constants.textColor + } + } + + override func draw(_ dirtyRect: NSRect) { + + let size = self.bounds.size + let blackOutlineFrame = NSRect(x: 0, y: 0, width: size.width, height: size.height - 1.0) + let color = NSColor(calibratedRed: 45 / 255, green: 51 / 255, blue: 82 / 255, alpha: 1.0) + + let gradient = NSGradient(colors: [color]) + let path = NSBezierPath(roundedRect: blackOutlineFrame, xRadius: 13, yRadius: 13) + gradient?.draw(in: path, angle: 90) + + super.draw(dirtyRect) + } + + // MARK: - NSTextFieldDelegate + + func controlTextDidChange(_ obj: Notification) { + self.hightlight(with: nil) + } + + func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { + + if commandSelector == #selector(NSResponder.insertNewline(_:)) { + + guard let handler = self.enterHandler else { return true } + return handler() + } + + return false + } + +} diff --git a/macOS/Privado/Common/View/RoundedSecuredTextField/RoundedSecuredTextFieldCell.swift b/macOS/Privado/Common/View/RoundedSecuredTextField/RoundedSecuredTextFieldCell.swift new file mode 100644 index 00000000..6867f47c --- /dev/null +++ b/macOS/Privado/Common/View/RoundedSecuredTextField/RoundedSecuredTextFieldCell.swift @@ -0,0 +1,43 @@ +// +// RoundedSecuredTextFieldCell.swift +// Cyberlock +// +// Created by Juraldinio on 8/31/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class RoundedSecuredTextFieldCell: NSSecureTextFieldCell { + + override var wraps: Bool { get { return false } set { } } + + override var isScrollable: Bool { get { return true } set { } } + + override func titleRect(forBounds rect: NSRect) -> NSRect { + var titleRect = super.titleRect(forBounds: rect) + let titleSize = self.attributedStringValue.size() + //Padding on left side + titleRect.origin.x = 10 + //Padding on right side + titleRect.origin.y += (titleRect.size.height - titleSize.height) / 2.0 - 3 + return titleRect + } + + override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { + let titleRect = self.titleRect(forBounds: cellFrame) + super.drawInterior(withFrame: titleRect, in: controlView) + } + + override func edit(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, event: NSEvent?) { + let titleRect = self.titleRect(forBounds: rect) + super.edit(withFrame: titleRect, in: controlView, editor: textObj, delegate: delegate, event: event) + } + + override func select(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, start selStart: Int, length selLength: Int) { + let titleRect = self.titleRect(forBounds: rect) + super.select(withFrame: titleRect, in: controlView, editor: textObj, delegate: delegate, start: selStart, length: selLength) + } + +} diff --git a/macOS/Privado/Common/View/RoundedTextField/RoundedTextField.swift b/macOS/Privado/Common/View/RoundedTextField/RoundedTextField.swift new file mode 100644 index 00000000..c1bd214b --- /dev/null +++ b/macOS/Privado/Common/View/RoundedTextField/RoundedTextField.swift @@ -0,0 +1,168 @@ +// +// RoundedTextField.swift +// Cyberlock +// +// Created by Jura on 8/31/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class RoundedTextField: NSTextField, NSTextFieldDelegate { + + private struct Constants { + static let cornerRadius: CGFloat = 13 + static let placeholderColor = NSColor(calibratedRed: 173 / 255, green: 179 / 255, blue: 210 / 255, alpha: 0.5) + static let textColor = NSColor(calibratedRed: 173 / 255, green: 179 / 255, blue: 210 / 255, alpha: 1.0) + } + + // MARK: - Properties + + var coloredPlaceHolder: String? { + get { + return self.placeholderAttributedString?.string + } + set { + if let value = newValue + , let font = self.font { + let attrs = [NSAttributedString.Key.foregroundColor: Constants.placeholderColor, + NSAttributedString.Key.font: NSFont.systemFont(ofSize: font.pointSize)] + self.placeholderAttributedString = NSAttributedString(string: value, attributes: attrs) + } else { + self.placeholderAttributedString = nil + } + } + } + + // MARK: - Initializers + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + + self.isBezeled = false + self.drawsBackground = false + self.translatesAutoresizingMaskIntoConstraints = false + self.focusRingType = .none + self.usesSingleLineMode = true + self.textColor = Constants.textColor + self.delegate = self + if let font = self.font { + self.font = NSFont(name: font.fontName, size: 14.0) + } + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Methods + + func hightlight(with color: NSColor?) { + + guard let borderColor = color else { + self.layer?.borderWidth = 0.0 + return + } + + self.layer?.borderColor = borderColor.cgColor + self.layer?.borderWidth = 1.0 + self.layer?.cornerRadius = Constants.cornerRadius + + } + + private func performEditingKeyEquivalent(with event: NSEvent) -> Bool { + + guard event.type == NSEvent.EventType.keyDown else { return false } + + let commandKey = NSEvent.ModifierFlags.command.rawValue + let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue + + let key = event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue + switch key { + case commandKey: return self.handleCommandKey(character: event.charactersIgnoringModifiers) + case commandShiftKey: return self.handleShiftKey(character: event.charactersIgnoringModifiers) + default: + break + } + + return false + } + + private func handleCommandKey(character: String?) -> Bool { + + guard let character = character else { return false } + + switch character { + case "x": + if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true } + case "c": + if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true } + case "v": + if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true } + case "z": + if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true } + case "a": + if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self) { return true } + default: + break + } + + return false + } + + private func handleShiftKey(character: String?) -> Bool { + if character == "Z" { + if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true } + } + + return false + } + + // MARK: - Lifecycle + + override class var cellClass: AnyClass? { + get { + return RoundedTextFieldCell.self + } + // swiftlint:disable unused_setter_value + set { } + // swiftlint:enable unused_setter_value + } + + override func viewDidMoveToWindow() { + if let textView = self.window?.fieldEditor(true, for: self) as? NSTextView { + textView.insertionPointColor = Constants.textColor + textView.alignment = .center + } + } + + override func draw(_ dirtyRect: NSRect) { + + let size = dirtyRect.size + let blackOutlineFrame = NSRect(x: 0, y: 0, width: size.width, height: size.height - 1.0) + let color = NSColor(calibratedRed: 45 / 255, green: 51 / 255, blue: 82 / 255, alpha: 1.0) + + let gradient = NSGradient(colors: [color]) + let path = NSBezierPath(roundedRect: blackOutlineFrame, xRadius: Constants.cornerRadius, yRadius: Constants.cornerRadius) + gradient?.draw(in: path, angle: 90) + + super.draw(dirtyRect) + } + + override func performKeyEquivalent(with event: NSEvent) -> Bool { + if self.performEditingKeyEquivalent(with: event) { + return true + } + + return super.performKeyEquivalent(with: event) + } + + // MARK: - NSTextFieldDelegate + + func controlTextDidChange(_ obj: Notification) { + self.hightlight(with: nil) + } + +} diff --git a/macOS/Privado/Common/View/RoundedTextField/RoundedTextFieldCell.swift b/macOS/Privado/Common/View/RoundedTextField/RoundedTextFieldCell.swift new file mode 100644 index 00000000..5c3cc68e --- /dev/null +++ b/macOS/Privado/Common/View/RoundedTextField/RoundedTextFieldCell.swift @@ -0,0 +1,43 @@ +// +// RoundedTextFieldCell.swift +// Cyberlock +// +// Created by Juraldinio on 8/31/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +final class RoundedTextFieldCell: NSTextFieldCell { + + override var wraps: Bool { get { return false } set { } } + + override var isScrollable: Bool { get { return true } set { } } + + override func titleRect(forBounds rect: NSRect) -> NSRect { + var titleRect = super.titleRect(forBounds: rect) + let titleSize = self.attributedStringValue.size() + //Padding on left side + titleRect.origin.x = 10 + //Padding on right side + titleRect.origin.y += (titleRect.size.height - titleSize.height) / 2.0 - 3.5 + return titleRect + } + + override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { + let titleRect = self.titleRect(forBounds: cellFrame) + super.drawInterior(withFrame: titleRect, in: controlView) + } + + override func edit(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, event: NSEvent?) { + let titleRect = self.titleRect(forBounds: rect) + super.edit(withFrame: titleRect, in: controlView, editor: textObj, delegate: delegate, event: event) + } + + override func select(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, start selStart: Int, length selLength: Int) { + let titleRect = self.titleRect(forBounds: rect) + super.select(withFrame: titleRect, in: controlView, editor: textObj, delegate: delegate, start: selStart, length: selLength) + } + +} diff --git a/macOS/Privado/Common/View/SwitchToggle.swift b/macOS/Privado/Common/View/SwitchToggle.swift new file mode 100644 index 00000000..29239f43 --- /dev/null +++ b/macOS/Privado/Common/View/SwitchToggle.swift @@ -0,0 +1,237 @@ +// +// SwitchToggle.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/10/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation +import AppKit + +@IBDesignable +final class SwitchToggle: NSView { + + //================================================================================ + // MARK: Properties + //================================================================================ + fileprivate var height: CGFloat = 44 + fileprivate var width: CGFloat { return height + ( height * 0.6 ) } + + fileprivate var leftConstraint: NSLayoutConstraint? + fileprivate var heightConstraint: NSLayoutConstraint? + fileprivate var widthConstraint: NSLayoutConstraint? + + fileprivate let backVw: NSView = { + let view = NSView() + view.wantsLayer = true + view.layer?.masksToBounds = false + return view + }() + + fileprivate let circle: NSView = { + let view = NSView() + + let shadow = NSShadow() + shadow.shadowColor = NSColor.black.withAlphaComponent(0.4) + shadow.shadowOffset = CGSize(width: 0, height: -2) + shadow.shadowBlurRadius = 2 + + view.backgroundColor = .white + view.wantsLayer = true + view.shadow = shadow + view.layer?.borderWidth = 2 + view.layer?.borderColor = NSColor.white.cgColor + return view + }() + + fileprivate var _radius: CGFloat? + fileprivate var backRadius: CGFloat { + if let radius = _radius { return radius } + return height / 2 + } + + fileprivate var circleRadius: CGFloat { + if let radius = _radius { return radius - outlineWidth } + return ( height - ( outlineWidth*2 ) ) / 2 + } + + fileprivate var toggleSize: CGFloat { height - ( outlineWidth * 2 ) } + + //================================================================================ + // MARK: Callback + //================================================================================ + var callback: ((_ isOn: Bool) -> Void)? + + //================================================================================ + // MARK: Public Parameters + //================================================================================ + @IBInspectable + public var isOn: Bool = false { + didSet { animate() } + } + + /// Change the toggle border on and off + @IBInspectable + public var hasToggleBorder: Bool = true { + didSet { circle.layer?.borderWidth = hasToggleBorder ? toggleBorderWidth : 0 } + } + + /// Change the width of the outline border + @IBInspectable public var outlineWidth: CGFloat = 2 { + didSet { + backVw.layer?.borderWidth = outlineWidth + layoutSwitch(resetingLayout: true) + } + } + + /// Change the width of the border on the toggle + @IBInspectable public var toggleBorderWidth: CGFloat = 2 { + didSet { circle.layer?.borderWidth = hasToggleBorder ? toggleBorderWidth : 0 } + } + + /// Change the radius of the complete toggle + @IBInspectable public var radius: CGFloat { + get { + if let radius = _radius { return radius } + return ( height - ( outlineWidth * 2 ) ) / 2 + } + set { + _radius = newValue + layoutSwitch() + } + } + + + /// Change the color of the outline border + @IBInspectable public var outlineColor: NSColor = .lightGray { + didSet { backVw.layer?.borderColor = outlineColor.cgColor } + } + + /// Change the color of the fill when the toggle is on + @IBInspectable public var fillColor: NSColor = .lightGray { + didSet { if isOn { backVw.layer?.borderColor = fillColor.cgColor } } + } + + /// Change the color of the toggle center + @IBInspectable public var toggleColor: NSColor = .white { + didSet { circle.backgroundColor = toggleColor } + } + + /// Change the background color of the complete toggle (visible when switch is off) + @IBInspectable public var backColor: NSColor = .white { + didSet { backVw.backgroundColor = backColor } + } + + //================================================================================ + // MARK: Initialization + //================================================================================ + required init?(coder: NSCoder) { + super.init(coder: coder) + drawView() + } + + required init(height: CGFloat = 44) { + self.height = height + super.init(frame: .zero) + drawView() + } + + override func mouseDown(with event: NSEvent) { + let push = Double(outlineWidth + width) - Double(height) + NSAnimationContext.runAnimationGroup({ (context) in + context.duration = 0.3 + context.allowsImplicitAnimation = true + + let adjustment = (toggleSize/4) + widthConstraint?.isActive = false + widthConstraint = circle.widthAnchor.constraint(equalToConstant: toggleSize+adjustment) + widthConstraint?.isActive = true + + if isOn { + leftConstraint?.constant = CGFloat(push)-adjustment + } + animator().layoutSubtreeIfNeeded() + }) + } + + override func mouseUp(with event: NSEvent) { + isOn = !isOn + } + + //================================================================================ + // MARK: Helpers + //================================================================================ + fileprivate func drawView() { + backVw.backgroundColor = backColor + + addSubview(backVw) + backVw.translatesAutoresizingMaskIntoConstraints = false + backVw.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + backVw.widthAnchor.constraint(equalToConstant: width).isActive = true + backVw.heightAnchor.constraint(equalToConstant: height).isActive = true + + addSubview(circle) + circle.translatesAutoresizingMaskIntoConstraints = false + leftConstraint = circle.leftAnchor.constraint(equalTo: backVw.leftAnchor, constant: outlineWidth) + circle.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + widthConstraint = circle.widthAnchor.constraint(equalToConstant: height-(outlineWidth*2)) + heightConstraint = circle.heightAnchor.constraint(equalToConstant: height-(outlineWidth*2)) + + leftConstraint?.isActive = true + widthConstraint?.isActive = true + heightConstraint?.isActive = true + + translatesAutoresizingMaskIntoConstraints = false + rightAnchor.constraint(equalTo: backVw.rightAnchor).isActive = true + heightAnchor.constraint(equalToConstant: height).isActive = true + + layoutSwitch() + } + + fileprivate func layoutSwitch(resetingLayout: Bool = false) { + if resetingLayout { + leftConstraint?.constant = outlineWidth + + widthConstraint?.isActive = false + widthConstraint = circle.widthAnchor.constraint(equalToConstant: height-(outlineWidth*2)) + widthConstraint?.isActive = true + + heightConstraint?.isActive = false + heightConstraint = circle.heightAnchor.constraint(equalToConstant: height-(outlineWidth*2)) + heightConstraint?.isActive = true + layoutSubtreeIfNeeded() + } + + backVw.layer?.cornerRadius = backRadius + backVw.layer?.borderWidth = isOn ? (height/2) : outlineWidth + backVw.layer?.borderColor = outlineColor.cgColor + + circle.layer?.cornerRadius = circleRadius + } + + fileprivate func animate() { + acceptsTouchEvents = false + + let push = Double(outlineWidth + width) - Double(height) + + NSAnimationContext.runAnimationGroup({ (context) in + context.duration = 0.3 + context.allowsImplicitAnimation = true + + backVw.animator().layer?.borderWidth = isOn ? (height/2) : outlineWidth + backVw.animator().layer?.borderColor = isOn ? fillColor.cgColor : outlineColor.cgColor + + widthConstraint?.isActive = false + widthConstraint = circle.widthAnchor.constraint(equalToConstant: toggleSize) + widthConstraint?.isActive = true + + leftConstraint?.constant = isOn ? CGFloat(push) : outlineWidth + animator().layoutSubtreeIfNeeded() + }, completionHandler: { + self.acceptsTouchEvents = true + self.callback?(self.isOn) + }) + } + +} diff --git a/macOS/Privado/Common/View/View.swift b/macOS/Privado/Common/View/View.swift new file mode 100644 index 00000000..80e0949f --- /dev/null +++ b/macOS/Privado/Common/View/View.swift @@ -0,0 +1,60 @@ +// +// View.swift +// Cyberlock +// +// Created by Juraldinio on 9/1/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +class View: NSView { + + var useHandCursor = false { + didSet { + self.window?.invalidateCursorRects(for: self) + } + } + + weak var target: AnyObject? + var action: Selector? + + @available(*, unavailable) + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + init() { + super.init(frame: .zero) + setup() + } + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + setup() + } + + func setup() { + addSubviews() + addConstraints() + } + + func addSubviews() { } + func addConstraints() { } + + override func resetCursorRects() { + super.resetCursorRects() + guard self.useHandCursor else { return } + self.addCursorRect(self.bounds, cursor: NSCursor.pointingHand) + } + + override func mouseDown(with event: NSEvent) { + guard self.useHandCursor + , let action = self.action else { + return + } + NSApp.sendAction(action, to: self.target, from: self) + } + +} diff --git a/macOS/Privado/Common/View/ViewBuilder.swift b/macOS/Privado/Common/View/ViewBuilder.swift new file mode 100644 index 00000000..124026eb --- /dev/null +++ b/macOS/Privado/Common/View/ViewBuilder.swift @@ -0,0 +1,12 @@ +// +// ViewBuilder.swift +// Privado +// +// Created by Juraldinio on 11/5/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import AppKit + +typealias ViewModuleBuilderClosure = () -> NSView diff --git a/macOS/Privado/Core/Bayeux/BayeuxChannel.swift b/macOS/Privado/Core/Bayeux/BayeuxChannel.swift new file mode 100644 index 00000000..7c961989 --- /dev/null +++ b/macOS/Privado/Core/Bayeux/BayeuxChannel.swift @@ -0,0 +1,93 @@ +// +// BayeuxChannel.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/24/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +class BayeuxChannel { + + typealias ShouldReceiveNotifications = Bool + typealias ChannelMessageClosure = (Data) -> ShouldReceiveNotifications + typealias SentMessageClosure = () -> ShouldReceiveNotifications + + enum State { + case inactive + case activating + case active + } + + private struct Subscription: Decodable { + let clientId: String + let channel: String + let successful: Bool + let subscription: String + } + + let channel: String + private(set) var state: State + + weak var transport: (BayeuxTransport & BayeuxSystemTransport)? + private var messageReceived: ChannelMessageClosure? + private var messageSent: SentMessageClosure? + + init(channel: String, transport: (BayeuxTransport & BayeuxSystemTransport)?, sentClosure: SentMessageClosure? = nil, completion: ChannelMessageClosure? = nil) { + + self.channel = channel + self.transport = transport + self.messageReceived = completion + self.messageSent = sentClosure + + self.state = .inactive + } + + @discardableResult + func subscribe() -> Bool { + + guard let clientID = self.transport?.clientId + , self.state == .inactive else { + return false + } + + self.state = .activating + + let meta = """ + {"channel": "/meta/subscribe", "clientId": "\(clientID)", "subscription": "\(self.channel)"} + """ + + self.transport?.send(channel: self.channel, text: meta) + return true + } + + func message(data: Data) { + + if self.state == .activating + , self.handleSubscribing(data: data) { + self.state = .active + return + } + + guard let messageReceived = self.messageReceived else { return } + + let result = messageReceived(data) + if !result { self.messageReceived = nil } + } + + func sent() { + guard let sent = self.messageSent else { return } + let result = sent() + if !result { self.messageSent = nil } + } + + // MARK: - Private + + private func handleSubscribing(data: Data) -> Bool { + + guard let subscription = try? JSONDecoder().decode(Subscription.self, from: data) else { return false } + print(subscription) + return subscription.successful && subscription.subscription == self.channel + } +} diff --git a/macOS/Privado/Core/Bayeux/BayeuxClient.swift b/macOS/Privado/Core/Bayeux/BayeuxClient.swift new file mode 100644 index 00000000..39888b90 --- /dev/null +++ b/macOS/Privado/Core/Bayeux/BayeuxClient.swift @@ -0,0 +1,221 @@ +// +// BayeuxClient.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/24/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation +import Starscream + +enum BayeuxClientState { + case disconnected + case preparing + case connecting + case handshake + case connected + case error +} + +protocol BayeuxSystemTransport: AnyObject { + var clientId: String? { get } + func update(clientID: String) +} + +protocol BayeuxTransport: AnyObject { + func send(channel: String, data: Data) + func send(channel: String, text: String) +} + +final class BayeuxClient: WebSocketDelegate, BayeuxTransport, BayeuxSystemTransport { + + enum Constants { + static let handshake = "/meta/handshake" + static let connect = "/meta/connect" + static let subscribe = "/meta/subscribe" + } + + // For decode all types messages + private struct BayeuxMessage: Decodable { + let channel: String + } + + private(set) var clientID: String? + private var webSocket: WebSocket? + + let stateEmitter = Emitter() + private(set) var state: BayeuxClientState { + didSet { + let state = self.state + guard state != oldValue else { return } + + DispatchQueue.main.async { + self.stateEmitter.invoke(state) + } + } + } + + let url: URL + let autoReconnect: Bool + + private var channels = [String: BayeuxChannel]() + + // MARK: - Init + + init(url: URL, autoReconnect: Bool = false) { + + self.url = url + self.autoReconnect = autoReconnect + + self.state = .disconnected + self.systemChannels() + + self.stateEmitter.addReaction { [weak self] state in + switch state { + case .connecting: + self?.subscription(channel: Constants.handshake).subscribe() + case .handshake: + self?.subscription(channel: Constants.connect).subscribe() + + default: + break + } + return self.isExist + } + } + + // MARK: - Interface + + func connect() { + guard [.disconnected, .error].contains(self.state) + , !self.webSocket.isExist else { + return + } + self.state = .preparing + self.webSocket = self.createSocket(with: self.url) + self.webSocket?.connect() + } + + func disconnect() { + guard self.state != .disconnected else { return } + self.webSocket?.disconnect(closeCode: 0) + } + + func subscription(channel: String, closure: BayeuxChannel.ChannelMessageClosure? = nil) -> BayeuxChannel { + + if let channel = self.channels[channel] { + return channel + } + + let channel = BayeuxChannel(channel: channel, transport: self, completion: closure) + self.channels[channel.channel] = channel + + return channel + } + + // MARK: - Private + + private func createSocket(with url: URL) -> WebSocket { + let request = URLRequest(url: url) + + let webSocket = WebSocket(request: request) + webSocket.delegate = self + return webSocket + } + + private func handle(received data: Data) { + + guard let prepared = self.prepareData(data: data) + , let message = try? JSONDecoder().decode(BayeuxMessage.self, from: prepared) else { + return + } + + let channel = self.subscription(channel: message.channel) + channel.message(data: prepared) + } + + func prepareData(data: Data) -> Data? { + guard let str = String(data: data, encoding: .utf8) + , let message = str.substring(using: NSRange(location: 1, length: str.count - 2)) + , let msgData = message.data(using: .utf8) else { return nil } + return msgData + } + + private func systemChannels() { + let handshake = HandshakeSystemChannel(channel: Constants.handshake, transport: self) + self.channels[handshake.channel] = handshake + + let connect = ConnectSystemChannel(channel: Constants.connect, transport: self, sentClosure: { [weak self] in + if let self = self + , self.state == .handshake { + self.state = .connected + } + return self.isExist + }) + self.channels[connect.channel] = connect + } + + // MARK: - WebSocketDelegate + + func didReceive(event: WebSocketEvent, client: WebSocket) { + + switch event { + case let .connected(metainfo): + self.state = .connecting + print(metainfo) + case let .text(message): + //print(message) + if let data = message.data(using: .utf8) { + self.handle(received: data) + } + case let .binary(data): + self.handle(received: data) + case .cancelled: + self.webSocket = nil + self.state = .disconnected + //print("cancelled") + case let .disconnected(message, code): + self.webSocket = nil + self.state = .disconnected + print("disconnected with mesage: '\(message)' and code: \(code)") + case let .error(error): + self.state = .error + print(error ?? "") + default: + print(event) + } + + print("EVENT: \(event)") + } + + // MARK: - BayeuxTransport + + func send(channel: String, data: Data) { + + guard [.connected, .connecting, .handshake].contains(self.state) + , let webSocket = self.webSocket else { return } + + let bChannel = self.subscription(channel: channel) + webSocket.write(data: data) { + bChannel.sent() + } + } + + func send(channel: String, text: String) { + guard let data = text.data(using: .utf8) else { return } + self.send(channel: channel, data: data) + } + + // MARK: - BayeuxSystemTransport + + var clientId: String? { return self.clientID } + + func update(clientID: String) { + self.clientID = clientID + + if self.state == .connecting { + self.state = .handshake + } + } +} diff --git a/macOS/Privado/Core/Bayeux/System/ConnectSystemChannel.swift b/macOS/Privado/Core/Bayeux/System/ConnectSystemChannel.swift new file mode 100644 index 00000000..be46816d --- /dev/null +++ b/macOS/Privado/Core/Bayeux/System/ConnectSystemChannel.swift @@ -0,0 +1,26 @@ +// +// ConnectSystemChannel.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/25/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +final class ConnectSystemChannel: BayeuxChannel { + + override func subscribe() -> Bool { + + guard let clientID = self.transport?.clientId else { return false } + + let meta = """ + {"channel": "/meta/connect", "clientId": "\(clientID)", "connectionType": "websocket"} + """ + + self.transport?.send(channel: self.channel, text: meta) + return true + } + + override func message(data: Data) { } +} diff --git a/macOS/Privado/Core/Bayeux/System/HandshakeSystemChannel.swift b/macOS/Privado/Core/Bayeux/System/HandshakeSystemChannel.swift new file mode 100644 index 00000000..ffbb58f1 --- /dev/null +++ b/macOS/Privado/Core/Bayeux/System/HandshakeSystemChannel.swift @@ -0,0 +1,37 @@ +// +// HandshakeSystemChannel.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/25/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +final class HandshakeSystemChannel: BayeuxChannel { + + struct Handshake: Decodable { + let channel: String + let successful: Bool + let version: String + let supportedConnectionTypes: [String] + let clientId: String + // "advice":{"reconnect":"retry","interval":0,"timeout":45000} + } + + override func subscribe() -> Bool { + let meta = """ + {"channel": "/meta/handshake", "version": "1.0", "supportedConnectionTypes": ["long-polling", "websocket"]} + """ + + self.transport?.send(channel: self.channel, text: meta) + return true + } + + override func message(data: Data) { + + guard let handshake = try? JSONDecoder().decode(Handshake.self, from: data) else { return } + + self.transport?.update(clientID: handshake.clientId) + } +} diff --git a/macOS/Privado/Core/Database/Models/LogRecord.swift b/macOS/Privado/Core/Database/Models/LogRecord.swift new file mode 100644 index 00000000..f9f578b0 --- /dev/null +++ b/macOS/Privado/Core/Database/Models/LogRecord.swift @@ -0,0 +1,25 @@ +// +// LogRecord.swift +// PrivadoVPN +// +// Created by Juraldinio on 10/18/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation +import CoreData + +@objc(LogRecord) +public class LogRecord: NSManagedObject { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "LogRecord") + } + + @NSManaged public var date: Date + @NSManaged public var label: String + @NSManaged public var payload: String + @NSManaged public var tag: String + @NSManaged public var uuid: String + +} diff --git a/macOS/Privado/Core/Database/Models/Settings.swift b/macOS/Privado/Core/Database/Models/Settings.swift new file mode 100644 index 00000000..56a33937 --- /dev/null +++ b/macOS/Privado/Core/Database/Models/Settings.swift @@ -0,0 +1,71 @@ +// +// Settings.swift +// PrivadoVPN +// +// Created by Juraldinio on 10/18/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation +import CoreData + +@objc(Settings) +public class Settings: NSManagedObject { + + private enum Constants { + static let entityName = "Settings" + static let vpnType = "vpnType" + static let port = "port" + } + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: Constants.entityName) + } + + @NSManaged public var autostartEnabled: Bool + @NSManaged public var id: String? + @NSManaged public var killSwitchEnabled: Bool + @NSManaged public var lastUsedServer: String? + public var port: Int16? { + get { + self.willAccessValue(forKey: Constants.port) + defer { self.didAccessValue(forKey: Constants.port) } + + return self.primitiveValue(forKey: Constants.port) as? Int16 + } + set { + self.willChangeValue(forKey: Constants.port) + defer { self.didChangeValue(forKey: Constants.port) } + + guard let value = newValue else { + self.setPrimitiveValue(nil, forKey: Constants.port) + return + } + + self.setPrimitiveValue(value, forKey: Constants.port) + } + } + @NSManaged public var preferredServer: Int16 + @NSManaged public var proto: String? + @NSManaged public var scramble: Int16 + public var vpnType: Int16? { + get { + self.willAccessValue(forKey: Constants.vpnType) + defer { self.didAccessValue(forKey: Constants.vpnType) } + + return self.primitiveValue(forKey: Constants.vpnType) as? Int16 + } + set { + self.willChangeValue(forKey: Constants.vpnType) + defer { self.didChangeValue(forKey: Constants.vpnType) } + + guard let value = newValue else { + self.setPrimitiveValue(nil, forKey: Constants.vpnType) + return + } + + self.setPrimitiveValue(value, forKey: Constants.vpnType) + } + } + +} diff --git a/macOS/Privado/Core/Database/Privado.xcdatamodeld/Privado.xcdatamodel/contents b/macOS/Privado/Core/Database/Schema/Privado.xcdatamodeld/Privado.xcdatamodel/contents similarity index 90% rename from macOS/Privado/Core/Database/Privado.xcdatamodeld/Privado.xcdatamodel/contents rename to macOS/Privado/Core/Database/Schema/Privado.xcdatamodeld/Privado.xcdatamodel/contents index 4de93e21..fd9d9c7c 100644 --- a/macOS/Privado/Core/Database/Privado.xcdatamodeld/Privado.xcdatamodel/contents +++ b/macOS/Privado/Core/Database/Schema/Privado.xcdatamodeld/Privado.xcdatamodel/contents @@ -1,13 +1,13 @@ - + - + - + @@ -19,7 +19,7 @@ - - + + \ No newline at end of file diff --git a/macOS/Privado/Core/Geo/Models/GeoLocation.swift b/macOS/Privado/Core/Geo/Models/GeoLocation.swift new file mode 100644 index 00000000..2a48db46 --- /dev/null +++ b/macOS/Privado/Core/Geo/Models/GeoLocation.swift @@ -0,0 +1,27 @@ +// +// GeoIP.swift +// Cyberlock +// +// Created by Jura on 8/22/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +struct GeoLocation: Decodable { + + let country: String? + let city: String? + let ip: String + let lat: String + let long: String + + enum CodingKeys: String, CodingKey { + case country + case city + case ip + case lat = "latitude" + case long = "longitude" + } + +} diff --git a/macOS/Privado/Core/Geo/Services/GeoService.swift b/macOS/Privado/Core/Geo/Services/GeoService.swift new file mode 100644 index 00000000..4da74709 --- /dev/null +++ b/macOS/Privado/Core/Geo/Services/GeoService.swift @@ -0,0 +1,40 @@ +// +// GeoService.swift +// Cyberlock +// +// Created by Jura on 8/22/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +enum GeoService { + case current(token: String) + case ip(ip: String, token: String) +} + +extension GeoService: ServiceType { + + var baseURL: BaseURLOptions { return .apiService } + + var path: String { return "v1/ipgeo" } + + var task: Task { + switch self { + case .current: + return .request + case let .ip(ip: ip, token: _): + return .requestParameters(parameters: ["ip": ip], parameterEncoding: .query) + } + } + + var headers: HTTPHeaders? { + switch self { + case let .current(token: token): + return ["authorization": "Bearer \(token)"] + case let .ip(ip: _, token: token): + return ["authorization": "Bearer \(token)"] + } + } + +} diff --git a/macOS/Privado/Core/InterfaceAddress/InterfaceAddress.swift b/macOS/Privado/Core/InterfaceAddress/InterfaceAddress.swift new file mode 100644 index 00000000..cfe8f249 --- /dev/null +++ b/macOS/Privado/Core/InterfaceAddress/InterfaceAddress.swift @@ -0,0 +1,144 @@ +// +// InterfaceAddress.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/30/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +final class InterfaceAddress { + + final class Record { + + let name: String + private(set) var received: UInt64 + private(set) var sent: UInt64 + + let statistic = Emitter() + + init(name: String, received: UInt64, sent: UInt64) { + self.name = name + self.received = received + self.sent = sent + } + + fileprivate func update(received: UInt64, sent: UInt64) { + self.received = received + self.sent = sent + + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + self.statistic.invoke(self) + } + } + } + + static var instance = InterfaceAddress() + + // MARK: - Properties + + private var scheduler: Timer? + private var interfaceRecords = [Record]() + + var records: [Record] { + + if self.interfaceRecords.isEmpty { + self.interfaceRecords = self.refresh() + } + + return self.interfaceRecords + } + + func record(with interface: String) -> Record? { + return self.records.first(where: { $0.name == interface }) + } + + // MARK: - Init + + private init() { } + + // MARK: - + + func resume() { + + guard !self.scheduler.isExist else { return } + + let scheduler = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] _ in + self?.update() + } + self.scheduler = scheduler + } + + func pause() { + + if let scheduler = self.scheduler { + scheduler.invalidate() + scheduler.fire() + } + + self.scheduler = nil + } + + @discardableResult + func update() -> [Record] { + + var new = [Record]() + self.refresh().forEach { rec in + + guard let record = self.interfaceRecords.first(where: { $0.name == rec.name }) else { + new.append(rec) + return + } + + record.update(received: rec.received, sent: rec.sent) + } + + self.interfaceRecords.append(contentsOf: new) + return new + } + + // MARK: - Private + + private func refresh() -> [Record] { + + var interfaceAddresses: UnsafeMutablePointer? + guard getifaddrs(&interfaceAddresses) == 0 else { return [] } + + var pointer = interfaceAddresses + var records = [Record]() + while pointer.isExist { + + guard let current = pointer else { + pointer = nil + continue + } + + guard let record = InterfaceAddress.getInterfaceRecord(from: current) else { + pointer = current.pointee.ifa_next + continue + } + + records.append(record) + pointer = current.pointee.ifa_next + } + + freeifaddrs(interfaceAddresses) + + return records + } + + private static func getInterfaceRecord(from infoPointer: UnsafeMutablePointer) -> Record? { + + let addr = infoPointer.pointee.ifa_addr.pointee + guard addr.sa_family == UInt8(AF_LINK) else { return nil } + + let networkData = unsafeBitCast(infoPointer.pointee.ifa_data, to: UnsafeMutablePointer.self) + let output = UInt64(networkData.pointee.ifi_obytes) + let input = UInt64(networkData.pointee.ifi_ibytes) + + let name = String(cString: infoPointer.pointee.ifa_name) + return Record(name: name, received: input, sent: output) + } +} diff --git a/macOS/Privado/Core/Network/Default/PingService.swift b/macOS/Privado/Core/Network/Default/PingService.swift new file mode 100644 index 00000000..6af3fc34 --- /dev/null +++ b/macOS/Privado/Core/Network/Default/PingService.swift @@ -0,0 +1,35 @@ +// +// PingService.swift +// Cyberlock +// +// Created by Jura on 8/22/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +enum PingService { + case ping(token: String) +} + +extension PingService: ServiceType { + + var baseURL: BaseURLOptions { return .apiService } + + var path: String { + switch self { + case .ping: + return "v1/ping" + } + } + + var task: Task { return .request } + + var headers: HTTPHeaders? { + switch self { + case let .ping(token: token): + return ["content-type": "application/json", "authorization": "Bearer \(token)"] + } + } + +} diff --git a/macOS/Privado/Core/Network/Models/ErrorResponse.swift b/macOS/Privado/Core/Network/Models/ErrorResponse.swift new file mode 100644 index 00000000..f096a0ad --- /dev/null +++ b/macOS/Privado/Core/Network/Models/ErrorResponse.swift @@ -0,0 +1,21 @@ +// +// ErrorResponse.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +struct ErrorResponse: Decodable, Error { + + enum Constants { + static let undefined = 0 + static let invalidBearer = 1001 + static let decoding = 100_001 + } + + let code: Int + let reason: String +} diff --git a/macOS/Privado/Core/Network/Models/SuccessResponse.swift b/macOS/Privado/Core/Network/Models/SuccessResponse.swift new file mode 100644 index 00000000..9abfe7d2 --- /dev/null +++ b/macOS/Privado/Core/Network/Models/SuccessResponse.swift @@ -0,0 +1,13 @@ +// +// SuccessResponse.swift +// Cyberlock +// +// Created by Jura on 8/22/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +struct SuccessResponse: Decodable { + let success: Bool +} diff --git a/macOS/Privado/Core/Network/WLVPNEnvironment.swift b/macOS/Privado/Core/Network/WLVPNEnvironment.swift new file mode 100644 index 00000000..00e062ac --- /dev/null +++ b/macOS/Privado/Core/Network/WLVPNEnvironment.swift @@ -0,0 +1,22 @@ +// +// WLVPNEnvironment.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +enum WLVPNEnvironment: NetworkEnvironment { + + case production + case development + + var host: String { + switch self { + case .production: return "client-api.privado.io" + case .development: return "client-api-dev.privado.io" + } + } +} diff --git a/macOS/Privado/Core/NetworkPlugin/ErrorNetworkPlugin.swift b/macOS/Privado/Core/NetworkPlugin/ErrorNetworkPlugin.swift new file mode 100644 index 00000000..9a930800 --- /dev/null +++ b/macOS/Privado/Core/NetworkPlugin/ErrorNetworkPlugin.swift @@ -0,0 +1,32 @@ +// +// ErrorNetworkPlugin.swift +// Privado +// +// Created by Juraldinio on 1/29/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +final class ErrorNetworkPlugin: NetworkPluginType { + + let name = "ErrorNetworkPlugin" + var count = 0 + + func didReceive(_ response: URLResponse, data: Data?, service: ServiceType) -> NetworkShouldCancel { + + guard let responseData = data + , let error = try? JSONDecoder().decode(ErrorResponse.self, from: responseData) else { + return false + } + + if error.code == ErrorResponse.Constants.invalidBearer { + openApplicationRoute(.login(type: self.count % 2 == 0 ? .refresh : .clear)) + self.count += 1 + return true + } + + return false + } + +} diff --git a/macOS/Privado/Core/NetworkPlugin/NetworkLoggerPlugin.swift b/macOS/Privado/Core/NetworkPlugin/NetworkLoggerPlugin.swift new file mode 100644 index 00000000..368a0c29 --- /dev/null +++ b/macOS/Privado/Core/NetworkPlugin/NetworkLoggerPlugin.swift @@ -0,0 +1,31 @@ +// +// NetworkLoggerPlugin.swift +// Privado +// +// Created by Juraldinio on 11/15/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class NetworkLoggerPlugin: NetworkPluginType { + + private let logger: NetworkCometLoggerProtocol + + init(logger: NetworkCometLoggerProtocol) { + self.logger = logger + } + + // MARK: - NetworkPluginType + + func willTransmit(_ request: URLRequest, service: ServiceType) -> NetworkShouldCancel { + self.logger.record(request: request) + return false + } + + func didReceive(_ response: URLResponse, data: Data?, service: ServiceType) -> NetworkShouldCancel { + self.logger.record(response: response, data: data) + return false + } + +} diff --git a/macOS/Privado/Core/Servers/Models/Protocol.swift b/macOS/Privado/Core/Servers/Models/Protocol.swift new file mode 100644 index 00000000..4811afcc --- /dev/null +++ b/macOS/Privado/Core/Servers/Models/Protocol.swift @@ -0,0 +1,36 @@ +// +// Protocol.swift +// Cyberlock +// +// Created by Jura on 8/22/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class Protocol: Codable { + + let id: Int + let name: String + let cipher: String? + let port: Int + let capacity: Int + let socketType: String + let scrambleEnabled: Bool + let scrambleWord: String + + enum CodingKeys: String, CodingKey { + case id + case name + case cipher + case port + case capacity + case socketType = "protocol" + case scrambleEnabled = "scramble_enabled" + case scrambleWord = "scramble_word" + } +} + +final class VPNProtocols: Decodable { + let protocols: [Protocol] +} diff --git a/macOS/Privado/Core/Servers/Models/Servers.swift b/macOS/Privado/Core/Servers/Models/Servers.swift new file mode 100644 index 00000000..53f6d1a9 --- /dev/null +++ b/macOS/Privado/Core/Servers/Models/Servers.swift @@ -0,0 +1,116 @@ +// +// Servers.swift +// Cyberlock +// +// Created by Jura on 8/22/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +struct VPNServers: Decodable { + var servers: [Server] + var geoLocation: GeoLocation? + + enum CodingKeys: String, CodingKey { + case servers + case geoLocation = "geodata" + } +} + +final class Server: Codable, Equatable { + + let id: String + let city: String + let country: String + let icon: String + let ip: String + let maintenance: Bool + let name: String + let pop: String + let capacity: Int + let scheduedMaintenance: Int + let latitude: Double + let longitude: Double + let protocols: [Protocol] + + enum CodingKeys: String, CodingKey { + case id + case name + case pop + case city + case country + case capacity + case icon + case ip = "ip_address" + case maintenance + case scheduedMaintenance = "scheduled_maintenance" + case latitude + case longitude + case protocols + } + + // MARK: - Methods + + /*func capacity(using type: VPNType) -> Int { + self.protocols + .filter { $0.name == type.description } + .sorted { $0.capacity > $1.capacity } + .last?.capacity ?? 0 + } + + func capacity(using type: VPNType, socketType: String, cipher: String, port: Int) -> Int { + self.protocols + .filter { $0.name == type.description } + .filter { $0.socketType == socketType } + .filter { $0.socketType == socketType } + .filter { $0.cipher == cipher } + .filter { $0.port == port } + .sorted { $0.capacity > $1.capacity } + .last?.capacity ?? 0 + }*/ + + // MARK: - Init + + public init(from decoder: Decoder) throws { + + let container = try decoder.container(keyedBy: CodingKeys.self) + + if let id = try? container.decode(String.self, forKey: .id) { + self.id = id + } else { + self.id = UUID().uuidString + } + + self.city = try container.decode(String.self, forKey: .city) + self.country = try container.decode(String.self, forKey: .country) + self.icon = try container.decode(String.self, forKey: .icon) + self.ip = try container.decode(String.self, forKey: .ip) + self.name = try container.decode(String.self, forKey: .name) + self.pop = try container.decode(String.self, forKey: .pop) + self.capacity = try container.decode(Int.self, forKey: .capacity) + + self.maintenance = try container.decode(Bool.self, forKey: .maintenance) + self.scheduedMaintenance = try container.decode(Int.self, forKey: .scheduedMaintenance) + self.latitude = try container.decode(Double.self, forKey: .latitude) + self.longitude = try container.decode(Double.self, forKey: .longitude) + + self.protocols = try container.decode([Protocol].self, forKey: .protocols) + } + + // MARK: - Equatable + + static func == (lhs: Server, rhs: Server) -> Bool { + return lhs.city == rhs.city + && lhs.country == rhs.country + && lhs.icon == rhs.icon + && lhs.ip == rhs.ip + && lhs.maintenance == rhs.maintenance + && lhs.name == rhs.name + && lhs.pop == rhs.pop + && lhs.capacity == rhs.capacity + && lhs.scheduedMaintenance == rhs.scheduedMaintenance + && lhs.latitude == rhs.latitude + && lhs.longitude == rhs.longitude + } +} diff --git a/macOS/Privado/Core/Servers/Service/ServerService.swift b/macOS/Privado/Core/Servers/Service/ServerService.swift new file mode 100644 index 00000000..168a6e29 --- /dev/null +++ b/macOS/Privado/Core/Servers/Service/ServerService.swift @@ -0,0 +1,69 @@ +// +// ServerService.swift +// Cyberlock +// +// Created by Jura on 8/22/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +/// Preffered server to autoconnect on startup. +enum PreferredServerType: Int16, CustomStringConvertible { + /// None. Autoconnect disabled. + case none + /// Calculated best nearest VPN server. + case best + /// Last used VPN server. + case last + /// Random calculated VPN server. + case random + + var description: String { + switch self { + case .none: return "none" + case .best: return "best" + case .last: return "last" + case .random: return "random" + } + } +} + +enum ServerService { + case protocols(token: String) + case servers(token: String, includeGeo: Bool) +} + +extension ServerService: ServiceType { + + var baseURL: BaseURLOptions { return .apiService } + + var path: String { + switch self { + case .protocols: + return "v1/protocols" + case .servers: + return "v1/servers" + } + } + + var task: Task { + switch self { + case .protocols: return .request + case let .servers(_, includeGeo: includeGeo): + return includeGeo + ? .requestParameters(parameters: ["includegeo": 1], parameterEncoding: .query) + : .request + } + } + + var headers: HTTPHeaders? { + switch self { + case let .protocols(token: token): + return ["authorization": "Bearer \(token)"] + case let .servers(token: token, includeGeo: _): + return ["authorization": "Bearer \(token)"] + } + } + +} diff --git a/macOS/Privado/Core/Session/Models/SessionRecord.swift b/macOS/Privado/Core/Session/Models/SessionRecord.swift new file mode 100644 index 00000000..5584e73e --- /dev/null +++ b/macOS/Privado/Core/Session/Models/SessionRecord.swift @@ -0,0 +1,53 @@ +// +// SessionRecord.swift +// Cyberlock +// +// Created by Jura on 8/29/19. +// + +import Foundation + +struct SessionRecord: Codable { + + var username: String? + + let accountType: Int64 + let accessExpireEpoch: Int64 + let subEpochEnd: Int + let token: TokenRecord + + var isValid: Bool { + return self.token.isValid + } + + enum CodingKeys: String, CodingKey { + case username + case accountType = "account_type" + case accessExpireEpoch = "access_expire_epoch" + case subEpochEnd = "sub_end_epoch" + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.username = try? container.decode(String.self, forKey: .username) + self.accountType = try container.decode(Int64.self, forKey: .accountType) + self.accessExpireEpoch = try container.decode(Int64.self, forKey: .accessExpireEpoch) + self.subEpochEnd = try container.decode(Int.self, forKey: .subEpochEnd) + + self.token = try TokenRecord(from: decoder) + } + + func encode(to encoder: Encoder) throws { + + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.username, forKey: .username) + try container.encode(self.accountType, forKey: .accountType) + try container.encode(self.accessExpireEpoch, forKey: .accessExpireEpoch) + try container.encode(self.subEpochEnd, forKey: .subEpochEnd) + + try self.token.encode(to: encoder) + } + +} diff --git a/macOS/Privado/Core/Session/Models/TokenRecord.swift b/macOS/Privado/Core/Session/Models/TokenRecord.swift new file mode 100644 index 00000000..a379471f --- /dev/null +++ b/macOS/Privado/Core/Session/Models/TokenRecord.swift @@ -0,0 +1,24 @@ +// +// TokenRecord.swift +// Cyberlock +// +// Created by Jura on 8/29/19. +// + +import Foundation + +struct TokenRecord: Codable { + + let value: String + let refreshToken: String + + var isValid: Bool { + return !self.value.isEmpty && !self.refreshToken.isEmpty + } + + enum CodingKeys: String, CodingKey { + case value = "access_token" + case refreshToken = "refresh_token" + } + +} diff --git a/macOS/Privado/Core/Session/Services/SessionService.swift b/macOS/Privado/Core/Session/Services/SessionService.swift new file mode 100644 index 00000000..520d3a1e --- /dev/null +++ b/macOS/Privado/Core/Session/Services/SessionService.swift @@ -0,0 +1,51 @@ +// +// SessionService.swift +// Cyberlock +// +// Created by Jura on 8/21/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +enum SessionService { + case login(username: String, password: String, key: String) + case refresh(token: String, refresh: String) +} + +extension SessionService: ServiceType { + + var baseURL: BaseURLOptions { return .apiService } + + var httpMethod: HTTPMethod { + return .post + } + + var path: String { + switch self { + case .login: + return "v1/login" + case .refresh: + return "v3/refresh" + } + } + + var task: Task { + switch self { + case let .login(username: username, password: password, key: key): + return .requestParameters(parameters: ["api_key": key, "username": username, "password": password], parameterEncoding: .json) + case let .refresh(token: _, refresh: refresh): + return .requestParameters(parameters: ["refresh_token": refresh], parameterEncoding: .json) + } + } + + var headers: HTTPHeaders? { + switch self { + case let .refresh(token: token, refresh: _): + return ["authorization": "Bearer \(token)"] + default: + return nil + } + } + +} diff --git a/macOS/Privado/Core/Session/Session.swift b/macOS/Privado/Core/Session/Session.swift new file mode 100644 index 00000000..5d1b4255 --- /dev/null +++ b/macOS/Privado/Core/Session/Session.swift @@ -0,0 +1,179 @@ +// +// Session.swift +// Cyberlock +// +// Created by Jura on 8/29/19. +// + +import Foundation + +typealias EmitterSessionRecordChangeType = (SessionRecord?) +typealias EmitterVPNChangeType = (VPNService?) +typealias EmitterVPNConnectionStateChangeType = (VPNConnectionState, VPNService?) +typealias EmitterLocationChangeType = (GeoLocation?) + +enum SessionError: Error { + case setupRequire +} + +final class Session { + + typealias AuthenticationCompletionClosure = (_ result: Result) -> Void + + private let router = Environment.router + private let key: String + private var cancellationToken: CancellationToken? + + let locationEmitter = Emitter() + let sessionEmitter = Emitter() + let vpnEmitter = Emitter() + let vpnConnectionStateEmitter = Emitter() + + private(set) var currentRecord: SessionRecord? { + didSet { + let record = self.currentRecord + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + self.sessionEmitter.invoke(record) + } + } + } + + var location: GeoLocation? { + didSet { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + self.locationEmitter.invoke(self.location) + } + } + } + + var currentVPN: VPNService? { + didSet { + + if let vpn = self.currentVPN { + + // Observe state changing! + vpn.stateEmitter.addReaction { [weak self, weak vpn] (state, service) -> ShouldContinueReceiveNotifications in + + guard let self = self + , let current = vpn else { + return false + } + + guard current.id == service.id else { return true } + + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + self.vpnConnectionStateEmitter.invoke((state, vpn)) + } + + return true + } + } + + DispatchQueue.main.async { [weak self] in + + guard let self = self else { return } + self.vpnConnectionStateEmitter.invoke((self.currentVPN?.state ?? .disconnected, self.currentVPN)) + + self.vpnEmitter.invoke(self.currentVPN) + + } + } + } + + var token: TokenRecord? { + return self.currentRecord?.token + } + + // MARK: - Static + + private static var instance: Session? + + @discardableResult + static func shared(with key: String? = nil) -> Session { + + if let session = self.instance { return session } + + let instance = Session(with: key ?? "") + self.instance = instance + return instance + } + + // MARK: - Init + + private init(with key: String) { + self.key = key + } + + // MARK: - Methods + + func login(using username: String, password: String, then completion: @escaping AuthenticationCompletionClosure) { + + guard !self.cancellationToken.isExist else { return } + + let service = SessionService.login(username: username, password: password, key: self.key) + self.cancellationToken = self.router.requestDecodable(Either.self, service: service) { [weak self] result in + + self?.cancellationToken = nil + + result + .onSuccess { (either, _) in + switch either { + case var .firstType(record): + record.username = username + completion(.success(record)) + case let .secondType(error): + completion(.failure(error)) + } + }.onFail { error in + + if error is DecodingError { + completion(.failure(ErrorResponse(code: ErrorResponse.Constants.decoding, reason: "\(error)"))) + } else { + completion(.failure(ErrorResponse(code: ErrorResponse.Constants.undefined, reason: "\(error)"))) + } + } + } + + } + + func refresh() { + + } + + @discardableResult + func save(_ sessionRecord: SessionRecord?) -> Bool { + let keys = KeychainSwift() + if let record = sessionRecord { + guard let encoded = try? JSONEncoder().encode(record) else { return false } + keys.set(encoded, forKey: PrivadoConstants.sessionKey) + } else { + keys.set(Data(), forKey: PrivadoConstants.sessionKey) + } + + self.currentRecord = sessionRecord + + return true + } + + @discardableResult + func restore() -> Bool { + + let keys = KeychainSwift() + + guard let data = keys.getData(PrivadoConstants.sessionKey) + , let record = try? JSONDecoder().decode(SessionRecord.self, from: data) else { + return false + } + + guard record.isValid else { + return false + } + + self.currentRecord = record + return true + } + +} diff --git a/macOS/Privado/Core/Settings/UserSettings.swift b/macOS/Privado/Core/Settings/UserSettings.swift new file mode 100644 index 00000000..6d78bcea --- /dev/null +++ b/macOS/Privado/Core/Settings/UserSettings.swift @@ -0,0 +1,213 @@ +// +// UserSettings.swift +// Privado +// +// Created by Jura on 11/14/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation +import CoreData + +final class UserSettings { + + // MARK: - Properties + + static var shared = UserSettings() + private var settings: Settings? + + let vpnTypeEmitter = Emitter() + let vpnConfigurationEmitter = Emitter() + let settingsEmitter = Emitter() + let killSwitchEmitter = Emitter() + + var lastUsedServer: String { + get { + self.settings?.lastUsedServer ?? "" + } + set { + guard let settings = self.settings else { return } + + settings.lastUsedServer = newValue + self.save(settings: settings) + } + } + + var autostartEnabled: Bool { + get { + self.settings?.autostartEnabled ?? false + } + set { + guard let settings = self.settings else { return } + + settings.autostartEnabled = newValue + self.save(settings: settings) + + self.settingsEmitter.invoke(settings) + } + } + + var preferredServer: PreferredServerType { + get { + return PreferredServerType(rawValue: self.settings?.preferredServer ?? 0) ?? .none + } + set { + guard newValue != self.preferredServer else { return } + + guard let settings = self.settings else { return } + + settings.preferredServer = newValue.rawValue + self.save(settings: settings) + } + } + + var killSwitchEnabled: Bool { + get { + self.settings?.killSwitchEnabled ?? false + } + set { + guard let settings = self.settings else { return } + + let changed = settings.killSwitchEnabled != newValue + + settings.killSwitchEnabled = newValue + self.save(settings: settings) + + if changed { self.killSwitchEmitter.invoke(newValue) } + + self.settingsEmitter.invoke(settings) + } + } + + var vpnType: VPNType? { + get { + guard let value = self.settings?.vpnType + , let type = VPNType(rawValue: value) else { + return nil + } + return type + } + set { + + guard newValue != self.vpnType else { return } + + guard let settings = self.settings else { return } + + if let value = newValue { + settings.vpnType = value.rawValue + } else { + settings.vpnType = nil + } + + self.save(settings: settings) + + guard let value = newValue else { return } + self.vpnTypeEmitter.invoke(value) + } + } + + var scramble: Bool { + get { + self.settings?.scramble == 1 + } + set { + + guard newValue != self.scramble else { return } + + guard let settings = self.settings else { return } + + settings.scramble = newValue ? 1 : 0 + self.save(settings: settings) + + self.vpnConfigurationEmitter.invoke(()) + } + } + + var proto: String { + get { + self.settings?.proto ?? "TCP" + } + set { + + guard newValue != self.proto else { return } + + guard let settings = self.settings else { return } + + settings.proto = newValue + self.save(settings: settings) + + self.vpnConfigurationEmitter.invoke(()) + } + } + + var port: Int16 { + get { + self.settings?.port ?? 1194 + } + set { + + guard newValue != self.port else { return } + + guard let settings = self.settings else { return } + + settings.port = newValue + self.save(settings: settings) + + self.vpnConfigurationEmitter.invoke(()) + } + } + + // MARK: - Initializers + + private init() { + self.restore() + } + + deinit { + self.settings = nil + } + + func clear() { + + guard let coordinator = DatabaseAnswer.commonDB else { return } + + let request: NSFetchRequest = Settings.fetchRequest() + let context = coordinator.createContext() + let settings: [Settings] = context.fetch(request) + + settings.forEach { + context.delete($0) + } + + context.save() + coordinator.save() + + self.settings = nil + self.restore() + } + + // MARK: - Private + + private func save(settings: Settings) { + + guard let coordinator = DatabaseAnswer.commonDB else { return } + coordinator.mainContext.save() + coordinator.save() + } + + private func restore() { + + guard let coordinator = DatabaseAnswer.commonDB else { return } + + if let settings: Settings = coordinator.mainContext.fetch(Settings.fetchRequest()).first { + self.settings = settings + } else { + let settings: Settings = coordinator.mainContext.create() + + coordinator.mainContext.save() + coordinator.save() + + self.settings = settings + } + } +} diff --git a/macOS/Privado/Core/vpn/VPNService.swift b/macOS/Privado/Core/vpn/VPNService.swift new file mode 100644 index 00000000..51bdbee8 --- /dev/null +++ b/macOS/Privado/Core/vpn/VPNService.swift @@ -0,0 +1,176 @@ +// +// VPNService.swift +// Privado +// +// Created by Juraldinio on 10/29/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +/// VPN protocol. +enum VPNType: Int16, CustomStringConvertible { + /// IKEv2. + case ikev2 = 0 + /// OpenVPN. + case openVPN + /// WireGuard. + case wireGuard + + var description: String { + switch self { + case .ikev2: return "ikev2" + case .openVPN: return "openvpn" + case .wireGuard: return "wireguard" + } + } +} + +/// IP Protocol. +enum VPNProtocolType { + case tcp + case udp +} + +/// VPN connection state. +enum VPNConnectionState: CustomStringConvertible { + case initialize + case disconnected + case prepare + case connecting + case connected + // manual disconnect or not + case disconnecting(Bool) + case error(VPNError) + + // MARK: - CustomStringConvertible + + var description: String { + switch self { + case .initialize: return "initialize" + case .prepare: return "prepare" + case .disconnected: return "disconnected" + case .connecting: return "connecting" + case .connected: return "connected" + case .disconnecting: return "disconnecting" + case let .error(error): return "error(\(error))" + } + } +} + +extension VPNConnectionState: Equatable { + static func == (lhs: VPNConnectionState, rhs: VPNConnectionState) -> Bool { + switch (lhs, rhs) { + case (initialize, initialize), + (prepare, prepare), + (disconnected, disconnected), + (connecting, connecting), + (connected, connected), + (disconnecting, disconnecting): + return true + case let (error(errorL), error(errorR)): + return errorL == errorR + default: + return false + } + } +} + +/// VPN Error. +enum VPNError: Error { + case undefined + case unsupportedConfiguration + case requireGrantsUpdate + case authFailed + case duplicateConfiguration + case notDisconnected + case loadConfiguration + case emptyConfiguration + case saveConfiguration + case message(String) + case extended(Error?) + case communication + case alreadyConnected +} + +extension VPNError: Equatable { + static func == (lhs: VPNError, rhs: VPNError) -> Bool { + switch (lhs, rhs) { + case (undefined, undefined), + (unsupportedConfiguration, unsupportedConfiguration), + (requireGrantsUpdate, requireGrantsUpdate), + (authFailed, authFailed), + (duplicateConfiguration, duplicateConfiguration), + (notDisconnected, notDisconnected), + (loadConfiguration, loadConfiguration), + (emptyConfiguration, emptyConfiguration), + (saveConfiguration, saveConfiguration), + (message, message), + (extended, extended), + (communication, communication), + (alreadyConnected, alreadyConnected): + return true + default: + return false + } + } +} + +protocol VPNConfiguration: CustomStringConvertible { } + +typealias EmitterVPNStateChangeType = (VPNConnectionState, VPNService) +typealias RemoveRecordCompletionClosure = (Error?) -> Void + +protocol VPNService: AnyObject { + + var id: String { get } + + var type: VPNType { get } + var state: VPNConnectionState { get } + + var stateEmitter: Emitter { get } + + var interface: String? { get } + var remoteAddress: String? { get } + + @discardableResult + func connect(using configuration: VPNConfiguration?) -> Bool + + @discardableResult + func disconnect() -> Bool + + func removeRecord(with completion: RemoveRecordCompletionClosure?) + + @discardableResult + func restoreConnection() -> Bool +} + +extension VPNService { + @discardableResult + func restoreConnection() -> Bool { + return false + } +} + +#if os(iOS) +class VPNServiceHelper { + static func isVPNActive() -> Bool { + let vpnProtocolsKeysIdentifiers = [ + "tap", "tun", "ppp", "ipsec", "utun" + ] + guard let cfDict = CFNetworkCopySystemProxySettings() else { return false } + let nsDict = cfDict.takeRetainedValue() as NSDictionary + guard let keys = nsDict["__SCOPED__"] as? NSDictionary, + let allKeys = keys.allKeys as? [String] else { return false } + + // Checking for tunneling protocols in the keys + for key in allKeys { + for protocolId in vpnProtocolsKeysIdentifiers + where key.starts(with: protocolId) { + return true + } + } + return false + } +} +#endif diff --git a/macOS/Privado/Core/xpc/Protocols.swift b/macOS/Privado/Core/xpc/Protocols.swift new file mode 100644 index 00000000..62f44ccd --- /dev/null +++ b/macOS/Privado/Core/xpc/Protocols.swift @@ -0,0 +1,123 @@ +import Foundation + +// MARK: - Shared types + +public enum HiveVPNType { + static let ikev2 = 0 + static let openVPN = 1 + static let wireGuard = 2 +} + +// MARK: - HardwarePort + +public final class HardwarePort: NSObject, NSSecureCoding { + + private(set) public var port: String + private(set) public var device: String + + public var isValid: Bool { !self.port.isEmpty && !self.device.isEmpty } + + // MARK: - Init + + init(port: String, device: String) { + self.port = port + self.device = device + } + + // MARK: - NSSecureCoding + + public static var supportsSecureCoding = true + + public func encode(with coder: NSCoder) { + coder.encode(self.port, forKey: "port") + coder.encode(self.device, forKey: "device") + } + + public required init?(coder: NSCoder) { + self.port = coder.decodeObject(of: NSString.self, forKey: "port") as String? ?? "" + self.device = coder.decodeObject(of: NSString.self, forKey: "device") as String? ?? "" + } +} + +public final class InterfaceConfiguration: NSObject, NSSecureCoding { + + private(set) public var device: String + private(set) public var inet: String + private(set) public var inet6: String + + public var isValid: Bool { !self.device.isEmpty && (!self.inet.isEmpty || !self.inet6.isEmpty) } + + // MARK: - Init + + init(device: String, inet: String, inet6: String) { + self.device = device + self.inet = inet + self.inet6 = inet6 + } + + // MARK: - NSSecureCoding + + public static var supportsSecureCoding = true + + public func encode(with coder: NSCoder) { + coder.encode(self.device, forKey: "device") + coder.encode(self.inet, forKey: "inet") + coder.encode(self.inet6, forKey: "inet6") + } + + public required init?(coder: NSCoder) { + self.device = coder.decodeObject(of: NSString.self, forKey: "device") as String? ?? "" + self.inet = coder.decodeObject(of: NSString.self, forKey: "inet") as String? ?? "" + self.inet6 = coder.decodeObject(of: NSString.self, forKey: "inet6") as String? ?? "" + } + +} + +// MARK: - Hive Intercomm Interfaces + +@objc(IntercommSystemType) +public protocol IntercommSystemType { + func updateRequired(short: String, bundle: String, completion: @escaping (Bool) -> Void) + func version(completion: @escaping (String, String) -> Void) + func ping(completion: @escaping () -> Void) + func ifconfig(completion: @escaping (String) -> Void) + func hardwarePorts(completion: @escaping ([HardwarePort]) -> Void) + func resolveDNS(name: String, completion: @escaping (String) -> Void) + func flushDNS(completion: @escaping (Bool) -> Void) + func currentDNS(hardware: [String], completion: @escaping ([String: [String]]) -> Void) + func changeDNS(servers: [String], hardware: String, completion: @escaping (Bool) -> Void) +} + +@objc(IntercommFirewallType) +public protocol IntercommFirewallType { + func firewallState(completion: @escaping (Bool, Error?) -> Void) + func firewallEnable(_ enable: Bool, completion: @escaping (Bool, Error?) -> Void) + func firewallActivate(rulesPath: String, validate: Bool, completion: @escaping (Bool, Error?) -> Void) + func firewallDeactivate(completion: @escaping (Bool, Error?) -> Void) + func firewallRulesSave(completion: @escaping (Bool) -> Void) + func firewallRulesRestore(completion: @escaping (Bool, Error?) -> Void) +} + +@objc(IntercommOpenVPNType) +public protocol IntercommOpenVPNType { + func connect(type: Int, ovpnPath: String, configPath: String, credentialsPath: String, completion: @escaping (Error?) -> Void) + func disconnect(completion: @escaping (Error?) -> Void) // sudo killall openvpn + func isConnected(completion: @escaping (Bool) -> Void) +} + +@objc(IntercommHiveType) +public protocol IntercommHiveType: IntercommSystemType, IntercommFirewallType, IntercommOpenVPNType { +} + +// MARK: - Client Intercomm Interfaces + +@objc(IntercommConsoleClientType) +public protocol IntercommConsoleClientType { + func log(message: String, completion: @escaping () -> Void) + func console(message: String, completion: @escaping () -> Void) + func error(message: String, errNo: Int, completion: @escaping () -> Void) +} + +@objc(IntercommHiveClientType) +public protocol IntercommHiveClientType: IntercommConsoleClientType { +} diff --git a/macOS/Privado/Flows/FlowCoordinator/FlowCoordinator.swift b/macOS/Privado/Flows/FlowCoordinator/FlowCoordinator.swift new file mode 100644 index 00000000..f9127466 --- /dev/null +++ b/macOS/Privado/Flows/FlowCoordinator/FlowCoordinator.swift @@ -0,0 +1,39 @@ +// +// FlowCoordinator.swift +// Cyberlock +// +// Created by Jura on 8/13/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +protocol CoordinatorProtocol: AnyObject { + + typealias CoordinatorCompletionClosure = () -> Void + + var currentCoordinator: CoordinatorProtocol? { get set } + + var previousCoordinator: CoordinatorProtocol? { get set } + + func start() + + func finish(animated: Bool, then completion: CoordinatorCompletionClosure?) + + func handleRoute(_ route: Route) + + func canHandleRoute(_ route: Route) -> Bool +} + +extension CoordinatorProtocol { + + func handleRoute(_ route: Route) { } + + func canHandleRoute(_ route: Route) -> Bool { return false } + + func start() { } + + func finish(animated: Bool, then completion: CoordinatorCompletionClosure?) { + completion?() + } +} diff --git a/macOS/Privado/Flows/LoginFlow/Module/Presenter/LoginPresenter.swift b/macOS/Privado/Flows/LoginFlow/Module/Presenter/LoginPresenter.swift new file mode 100644 index 00000000..7ad0016f --- /dev/null +++ b/macOS/Privado/Flows/LoginFlow/Module/Presenter/LoginPresenter.swift @@ -0,0 +1,99 @@ +// +// LoginPresenter.swift +// Privado +// +// Created by Juraldinio on 10/25/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class LoginPresenter: LoginControllerOutput, LoginControllerViewOutput { + + weak var viewInput: LoginControllerInput? + private let output: LoginModuleOutput? + private var loginType: Route.Login? { + didSet { + switch loginType { + case .authorize: + self.viewInput?.showContent(with: .authorize, output: self) + case .clear: + self.viewInput?.showContent(with: .authorizeManual("", ""), output: self) + case .restore: + self.viewInput?.showContent(with: .restore, output: self) + case let .authenticate(login, password): + self.viewInput?.showContent(with: .authorizeManual(login, password), output: self) + default: + break + } + } + } + + init(output: LoginModuleOutput?, type: Route.Login?) { + self.output = output + self.loginType = type + } + + // MARK: - LoginControllerOutput + + func viewIsReady() { + + //WARNING: - Explanation needed + var nextType = self.loginType + if let type = self.loginType { + switch type { + case .clear, + .authenticate: + Session.shared().save(nil) + + let keychain = KeychainSwift() + keychain.set("", forKey: PrivadoConstants.Keychain.email, withAccess: nil) + keychain.set("", forKey: PrivadoConstants.Keychain.passwd, withAccess: nil) + // + + nextType = type == .clear + ? .clear + : type + + case .refresh: + Session.shared().save(nil) + nextType = .authorize + default: + nextType = .authorize + } + } + + /*if Session.shared().restore() { + //self.loginSuccess() + //return + }*/ + + self.loginType = nextType + } + + // MARK: - LoginPresenterInput + + func loginSuccess() { + self.output?.navigate(to: .servers) + } + + func forgotPassword() { + guard let url = URL(string: "https://partners.privado.io/forgot-password") else { + return + } + self.output?.navigate(to: .navigate(url: url)) + } + + func createAccount() { + + } + + func restoreSuccess() { + self.loginType = .authorize + } + + func goBack() { + self.loginType = .authorize + } + +} diff --git a/macOS/Privado/Flows/LoginFlow/Module/View/LoginControllerView.swift b/macOS/Privado/Flows/LoginFlow/Module/View/LoginControllerView.swift new file mode 100644 index 00000000..26bff326 --- /dev/null +++ b/macOS/Privado/Flows/LoginFlow/Module/View/LoginControllerView.swift @@ -0,0 +1,29 @@ +// +// LoginControllerView.swift +// Privado +// +// Created by Juraldinio on 6/2/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +enum LoginControllerType { + case authorize + case authorizeManual(String, String) + case create + case resend + case verification + case restore +} + +protocol LoginControllerViewOutput: LoginViewModuleOutput { } + +protocol LoginControllerInput: AnyObject { + @discardableResult + func showContent(with type: LoginControllerType, output: LoginControllerViewOutput) -> Bool +} + +protocol LoginControllerOutput: AnyObject { + func viewIsReady() +} diff --git a/macOS/Privado/Flows/LoginFlow/Submodules/Login/Interactor/LoginViewInteractor.swift b/macOS/Privado/Flows/LoginFlow/Submodules/Login/Interactor/LoginViewInteractor.swift new file mode 100644 index 00000000..194a7bcb --- /dev/null +++ b/macOS/Privado/Flows/LoginFlow/Submodules/Login/Interactor/LoginViewInteractor.swift @@ -0,0 +1,95 @@ +// +// LoginViewInteractor.swift +// Cyberlock +// +// Created by Jura on 8/29/19. +// + +import Foundation + +enum LoginInteractorError: Error { + case undefined + case network + case decoding + case message(String) + case response(ErrorResponse) +} + +typealias LoginEmail = String +typealias LoginCompletion = (Result) -> Void +typealias RestoreCompletion = (_ user: String?, _ password: String?) -> Void + +protocol LoginViewInteractorInput: AnyObject { + + func login(using login: String, password: String, then completion: @escaping LoginCompletion) + func restore(with completion: @escaping RestoreCompletion) + func cancelRequest() +} + +final class LoginViewInteractor: LoginViewInteractorInput { + + private lazy var router = Environment.router + private var cancellationToken: CancellationToken? + + func login(using login: String, password: String, then completion: @escaping LoginCompletion) { + + Session.shared().login(using: login, password: password) { result in + + switch result { + case let .success(record): + Session.shared().save(record) + self.saveCredentials(login: login, password: password) + completion(.success(login)) + case let .failure(error): + print(error) + Session.shared().save(nil) + + CometLogger.shared.record(error: error) + + if let errorResponse = error as ErrorResponse? { + + guard errorResponse.code != ErrorResponse.Constants.decoding else { + completion(.failure(.decoding)) + return + } + completion(.failure(.response(errorResponse))) + + } else { + completion(.failure(.message(error.localizedDescription))) + } + + } + + } + + } + + func cancelRequest() { + guard let token = self.cancellationToken else { return } + self.cancellationToken = nil + self.router.cancel(token) + } + + func restore(with completion: @escaping RestoreCompletion) { + + let keychain = KeychainSwift() + + guard let login = keychain.get(PrivadoConstants.Keychain.email) + , let pwd = keychain.get(PrivadoConstants.Keychain.passwd) else { + completion(nil, nil) + return + } + + completion(login, pwd) + } + + // MARK: - Private + + private func saveCredentials(login: String, password: String) { + let keychain = KeychainSwift() + + keychain.set(login, forKey: PrivadoConstants.Keychain.email, withAccess: nil) + keychain.set(password, forKey: PrivadoConstants.Keychain.passwd, withAccess: nil) + } + +} diff --git a/macOS/Privado/Flows/LoginFlow/Submodules/Login/Presenter/LoginViewPresenter.swift b/macOS/Privado/Flows/LoginFlow/Submodules/Login/Presenter/LoginViewPresenter.swift new file mode 100644 index 00000000..86f258ea --- /dev/null +++ b/macOS/Privado/Flows/LoginFlow/Submodules/Login/Presenter/LoginViewPresenter.swift @@ -0,0 +1,110 @@ +// +// LoginViewPresenter.swift +// Cyberlock +// +// Created by Jura on 8/29/19. +// + +import Foundation + +final class LoginViewPresenter: LoginViewFormOutput { + + private static var allowRestore = true + + private weak var output: LoginViewModuleOutput? + weak var viewInput: LoginViewFormInput? + private let interactor: LoginViewInteractorInput + + private let type: LoginControllerType + + init(interactor: LoginViewInteractorInput, output: LoginViewModuleOutput?, type: LoginControllerType) { + self.interactor = interactor + self.output = output + self.type = type + } + + // MARK: - LoginViewOutput + + func login(with username: String?, password: String?) { + + guard let viewInput = self.viewInput else { return } + + guard let login = username else { + viewInput.showCommonError(LoginError.requireUsername) + return + } + + guard let password = password else { + viewInput.showCommonError(LoginError.requirePassword) + return + } + + viewInput.showLoader(true) + + self.interactor.login(using: login.trimmingCharacters(in: .whitespacesAndNewlines), + password: password.trimmingCharacters(in: .whitespacesAndNewlines)) { [weak self] result in + + self?.viewInput?.showLoader(false) + + result.flatMap { _ in + + CometLogger.shared.event(name: PrivadoConstants.Event.STATISTIC.login, + attributes: [PrivadoConstants.Event.ATTRIBUTES.success: true], + secured: nil) + + self?.output?.loginSuccess() + }.onFail { error in + + let lError: LoginError + switch error { + case .undefined: lError = .undefined + case .network: lError = .network + case .decoding: lError = .decoding + case .message(let msg): lError = .message(msg) + case .response(let errorResponse): lError = .message(errorResponse.reason) + //default: lError = .message(NSLocalizedString("form.login.error.undefined", comment: "")) + } + + CometLogger.shared.event(name: PrivadoConstants.Event.STATISTIC.login, + attributes: [PrivadoConstants.Event.ATTRIBUTES.success: false], + secured: nil) + + self?.viewInput?.showCommonError(lError) + } + + } + + } + + func viewIsReady() { + + switch self.type { + case .authorize where LoginViewPresenter.allowRestore: + LoginViewPresenter.allowRestore = false + + self.interactor.restore { [weak self] (user, password) in + self?.viewInput?.restored(user: user ?? "", password: password ?? "") + } + case let .authorizeManual(login, password): + self.viewInput?.restored(user: login, password: password) + + default: + break + } + } + + func viewWillDisappear() { + self.viewInput?.showLoader(false) + } + + func forgotPassword() { + self.output?.forgotPassword() + } + + func createAccount() { + self.output?.createAccount() + } + + // MARK: - Private + +} diff --git a/macOS/Privado/Flows/NotificationFlow/Module/Models/PrivadoNotification.swift b/macOS/Privado/Flows/NotificationFlow/Module/Models/PrivadoNotification.swift new file mode 100644 index 00000000..063c4745 --- /dev/null +++ b/macOS/Privado/Flows/NotificationFlow/Module/Models/PrivadoNotification.swift @@ -0,0 +1,93 @@ +// +// PrivadoNotification.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/25/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +struct PrivadoNotification: Decodable { + + struct Customer: Decodable { + let sku: String + let email: String + let endDate: String + let username: String + let dateAdded: String + let isPremium: Int + let startDate: String + let premiumDate: String + let serverGroup: String + let trafficLeft: UInt + let trafficTotal: UInt + let speedLimit: Int + let connectionsLimit: Int + + enum CodingKeys: String, CodingKey { + case sku + case email + case endDate = "end_date" + case username + case dateAdded = "date_added" + case isPremium = "is_premium" + case startDate = "start_date" + case premiumDate = "premium_date" + case serverGroup = "server_group" + case trafficLeft = "traffic_left_mb" + case trafficTotal = "traffic_total_mb" + case speedLimit = "speed_limit_mbps" + case connectionsLimit = "connections_limit" + } + } + + struct Payload: Decodable { + /// mandatory, body of the news, can be large string, follows MarkDown format + let body: String + /// email is optional + let email: String + /// optional, lists all actions that app needs to perform + let actions: [String] + /// optional, lists header/subject for the message + let subject: String + /// CTA button text + let callToActionText: String + let customer: Customer + /// number, from 0 lowest to 10+ highest + let priority: Int + /// Username is primary key identifying the customer, doesn't change, string, mandatory. + let username: String + /// unix-timestamp for message creation date&time + let created: UInt + /// unix-timestamp showing when the message should expire in the app (sticky message), can be empty + let expires: String + let textColor: String + let backgroundColor: String + let sentDate: String + + enum CodingKeys: String, CodingKey { + case body + case email + case actions + case subject + case callToActionText = "cta_text" + case customer + case priority + case username + case created = "created_at" + case expires = "expires_at" + case textColor = "cta_text_color" + case backgroundColor = "cta_text_bgcolor" + case sentDate = "sent_at" + } + } + + let channel: String + let payload: Payload + + enum CodingKeys: String, CodingKey { + case channel + case payload = "data" + } +} diff --git a/macOS/Privado/Flows/ServersFlow/Module/Presenter/ServersPresenter.swift b/macOS/Privado/Flows/ServersFlow/Module/Presenter/ServersPresenter.swift new file mode 100644 index 00000000..c47c6ecb --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Module/Presenter/ServersPresenter.swift @@ -0,0 +1,207 @@ +// +// ServersPresenter.swift +// Privado +// +// Created by Juraldinio on 11/19/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +protocol ServersPresenterInput: AnyObject { + var isReconnection: Bool { get } + + func vpnConnect() + func vpnDisconnect() +} + +final class ServersPresenter: ServersPresenterInput { + + private var current: Server? + private var requiredServer: Server? + + private(set) var isReconnection: Bool = false + + init(output: AnyObject?) { + + UserSettings.shared.vpnTypeEmitter.addReaction { [weak self] _ in + guard let self = self else { return false } + guard self.current != nil else { return true } + + self.switchService() + return true + } + + Session.shared().vpnConnectionStateEmitter.addReaction { [weak self] state, vpn -> ShouldContinueReceiveNotifications in + + print("Change state! New:\(state)") + + switch state { + case .disconnected: + if let server = self?.requiredServer { + DispatchQueue.main.asyncAfter(deadline: .now()+0.42) { + self?.requiredServer = nil + self?.connect(required: server) + } + } + default: + break + } + + return self.isExist + } + + #if os(macOS) + PrivadoHiveClient.shared().stateEmitter.addReaction { [weak self] state -> ShouldContinueReceiveNotifications in + guard let self = self else { return false } + + if state == .connected + , self.isReconnection + , let server = self.requiredServer { + self.connect(required: server) + } + + return true + } + #endif + + } + + private func connect(required: Server) { + DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in + self?.selected(server: required) + self?.vpnConnect() + } + } + + private func switchService() { + + let service: VPNService + switch UserSettings.shared.vpnType { + case .ikev2, + .none: service = IKEv2Service(with: CometLogger.shared) + case .openVPN: + #if SIMULATOR + service = IKEv2Service(with: CometLogger.shared) + #else + service = OpenVPNService(with: CometLogger.shared) + #endif + case .wireGuard: service = WireGuardService(with: CometLogger.shared) + } + + Session.shared().currentVPN = service + } +} + +extension ServersPresenter: LocationModuleOutput { } + +extension ServersPresenter: ServerListModuleOutput { + + func selected(server: Server) { + + if self.current == server { return } + + self.current = server + + self.switchService() + } + + func current(location: GeoLocation?) { + guard let location = location else { return } + Session.shared().location = location + } + + func reconnect(to server: Server) { + self.requiredServer = server + self.isReconnection = true + self.vpnDisconnect() + } +} + +extension ServersPresenter: ConnectionModuleBuilderOutput { + + //swiftlint:disable function_body_length + func vpnConnect() { + + self.isReconnection = false + + guard let server = self.current + , let service = Session.shared().currentVPN else { + return + } + + let keychain = KeychainSwift() + + guard let login = keychain.get(PrivadoConstants.Keychain.email) + , let pwd = keychain.get(PrivadoConstants.Keychain.passwd) else { + openApplicationRoute(.login(type: .clear)) + return + } + + let config: VPNConfiguration? + switch service.type { + case .ikev2, + .wireGuard: + config = IKEv2Configuration(username: login, + password: pwd, + shared: "", + city: server.city, + server: server.name, + serverIp: server.ip, + remoteIdentifier: "vpn.privado.io") + #if SIMULATOR + #else + case .openVPN: + if let proto = server.protocols + .filter({ $0.name == service.type.description }) + .filter({ $0.socketType.lowercased() == UserSettings.shared.proto.lowercased() }) + .filter({ $0.port == UserSettings.shared.port }).first { + config = OpenVPNConfiguration(username: login, + password: pwd, + city: server.city, + server: server.name, + serverIp: server.ip, + port: UInt16(proto.port), + socketType: proto.socketType) + } else { + config = OpenVPNConfiguration(username: login, + password: pwd, + city: server.city, + server: server.name, + serverIp: server.ip, + port: UInt16(1194), + socketType: "TCP") + } + #endif + default: + config = IKEv2Configuration(username: login, + password: pwd, + shared: "", + city: server.city, + server: server.name, + serverIp: server.ip, + remoteIdentifier: "vpn.privado.io") + + } + + CometLogger.shared.vpnEvent(type: PrivadoConstants.Event.config, message: "", secured: String(describing: config)) + + service.connect(using: config) + } + //swiftlint:enable function_body_length + + func vpnDisconnect() { + guard let service = Session.shared().currentVPN else { return } + service.disconnect() + } + + func vpnReinstall() { + + #if os(macOS) + self.requiredServer = self.current + self.isReconnection = true + PrivadoHiveClient.shared().forceUpdate() + openApplicationRoute(.hive(type: .install)) + #endif + } +} diff --git a/macOS/Privado/Flows/ServersFlow/Submodules/Connection/Presenter/ConnectionPresenter.swift b/macOS/Privado/Flows/ServersFlow/Submodules/Connection/Presenter/ConnectionPresenter.swift new file mode 100644 index 00000000..a3a35e4d --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Submodules/Connection/Presenter/ConnectionPresenter.swift @@ -0,0 +1,251 @@ +// +// ConnectionPresenter.swift +// Privado +// +// Created by Jura on 10/10/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +enum ConnectionViewColorState { + case connected + case progress + case disconnected + case error +} + +final class ConnectionPresenter: ConnectionViewOutput { + + private enum Constants { + static let imageConnected = "connection.on" + static let imageDisconnected = "connection.off" + // + static let statusSelectServer = "" + // + + } + + weak var viewInput: ConnectionViewInput? + + private weak var output: ConnectionModuleBuilderOutput? + private var connectionState: VPNConnectionState = .disconnected + + private var currentGeoLocationIP = "" + private var allowClick = false + + private var alreadyReinstalled = false + + //swiftlint:disable function_body_length + init(output: ConnectionModuleBuilderOutput?) { + + self.output = output + + Session.shared().vpnConnectionStateEmitter.addReaction { [weak self] (vpnState, _) -> ShouldContinueReceiveNotifications in + guard let self = self else { return false } + + guard self.connectionState != vpnState else { return true } + + let oldState = self.connectionState + self.connectionState = vpnState + + guard oldState != .initialize else { return true } + + self.allowClick = true + switch vpnState { + case .connecting, + .disconnecting: + self.alreadyReinstalled = false + self.allowClick = false + case let .error(error): + self.handleVPN(error: error) + case .connected: + CometLogger.shared.event(name: PrivadoConstants.Event.STATISTIC.connection, + attributes: [PrivadoConstants.Event.ATTRIBUTES.isOn: true], + secured: nil) + case .disconnected where [VPNConnectionState.connecting, VPNConnectionState.connected, + VPNConnectionState.disconnecting(true), VPNConnectionState.disconnecting(false)].contains(oldState): + self.currentGeoLocationIP = "" + + CometLogger.shared.event(name: PrivadoConstants.Event.STATISTIC.connection, + attributes: [PrivadoConstants.Event.ATTRIBUTES.isOn: false], + secured: nil) + + default: + break + } + + guard let view = self.viewInput else { return true } + self.update(view: view, using: vpnState, allowClick: self.allowClick) + + return true + } + + Session.shared().vpnEmitter.addReaction { [weak self] (_) -> ShouldContinueReceiveNotifications in + + guard let self = self else { return false } + + self.allowClick = self.connectionState == .disconnected || self.connectionState == .connected + + if let view = self.viewInput { + self.update(view: view, using: self.connectionState, allowClick: self.allowClick) + } + + return true + } + + Session.shared().locationEmitter.addReaction { [weak self] location -> ShouldContinueReceiveNotifications in + + guard let self = self else { return false } + + if let location = location { + self.allowClick = self.currentGeoLocationIP != location.ip || self.connectionState == .disconnected || self.connectionState == .connected + self.currentGeoLocationIP = location.ip + } else { + self.allowClick = self.connectionState == .disconnected || self.connectionState == .connected + } + + if let view = self.viewInput { + self.update(view: view, using: self.connectionState, allowClick: self.allowClick) + } + + return true + } + + UserSettings.shared.killSwitchEmitter.addReaction { [weak self] _ in + self?.currentGeoLocationIP = "" + return self.isExist + } + + } + //swiftlint:enable function_body_length + + // MARK: - Private + + private func handleVPN(error: VPNError) { + + switch error { + case .requireGrantsUpdate, + .communication: + self.allowClick = true + if !self.alreadyReinstalled { + self.alreadyReinstalled.toggle() + self.output?.vpnReinstall() + } + + default: + self.allowClick = true + } + + if let view = self.viewInput { + self.update(view: view, using: self.connectionState, allowClick: self.allowClick) + } + } + + //swiftlint:disable function_body_length + private func update(view: ConnectionViewInput, using state: VPNConnectionState, allowClick: Bool = false) { + + var clickAllowence = allowClick && !(self.output?.isReconnection ?? false) + + let text: String + if !(self.output?.isReconnection ?? false) { + switch state { + case .initialize: + text = "" + case .connected: + text = NSLocalizedString("connection.status.connected", comment: "") + case .disconnecting: + text = NSLocalizedString("connection.status.disconnecting", comment: "") + + case .prepare, + .connecting: + text = NSLocalizedString("connection.status.connecting", comment: "") + case .disconnected: + clickAllowence = UserSettings.shared.killSwitchEnabled ? true : clickAllowence + text = Session.shared().currentVPN.isExist + ? NSLocalizedString("connection.status.disconnected", comment: "") + : Constants.statusSelectServer + case let .error(vpnError): + switch vpnError { + case .authFailed: + text = NSLocalizedString("connection.status.authFailed", comment: "") + case .requireGrantsUpdate, + .communication: + text = NSLocalizedString("connection.status.requireGrantRights", comment: "") + + default: + text = NSLocalizedString("connection.status.error", comment: "") + } + } + } else { + text = NSLocalizedString("connection.status.reconnect", comment: "") + } + + switch state { + case .connected: + view.state(image: Constants.imageConnected, + text: text, + colorState: .connected, + allowClick: clickAllowence) + + case .initialize, + .prepare, + .connecting, + .disconnecting: + view.state(image: Constants.imageDisconnected, + text: text, + colorState: .progress, + allowClick: clickAllowence) + + case .disconnected: + view.state(image: Constants.imageDisconnected, + text: text, + colorState: .disconnected, + allowClick: clickAllowence) + + case let .error(vpnError): + let colorState: ConnectionViewColorState + switch vpnError { + case .requireGrantsUpdate, + .communication: + colorState = .progress + default: + colorState = .error + } + + view.state(image: Constants.imageDisconnected, + text: text, + colorState: colorState, + allowClick: clickAllowence) + return + } + + } + //swiftlint:enable function_body_length + + // MARK: - ConnectionViewOutput + + func isReady() { + guard let view = self.viewInput else { return } + self.update(view: view, using: self.connectionState) + } + + func connectionClicked() { + switch self.connectionState { + case .connected: + CometLogger.shared.event(name: PrivadoConstants.Event.user, + attributes: [PrivadoConstants.Event.ATTRIBUTES.connect: false], + secured: nil) + self.output?.vpnDisconnect() + case .disconnected, + .error: + CometLogger.shared.event(name: PrivadoConstants.Event.user, + attributes: [PrivadoConstants.Event.ATTRIBUTES.connect: true], + secured: nil) + self.output?.vpnConnect() + default: + break + } + } + +} diff --git a/macOS/Privado/Flows/ServersFlow/Submodules/List/Interactor/ServerListInteractor.swift b/macOS/Privado/Flows/ServersFlow/Submodules/List/Interactor/ServerListInteractor.swift new file mode 100644 index 00000000..8de6a9f6 --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Submodules/List/Interactor/ServerListInteractor.swift @@ -0,0 +1,118 @@ +// +// ServerListInteractor.swift +// Cyberlock +// +// Created by Jura on 8/27/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +struct ServerListInteractorResult { + let serverLocations: [ServerLocation] + let geoLocation: GeoLocation? + + static func empty() -> ServerListInteractorResult { + return ServerListInteractorResult(serverLocations: [], geoLocation: nil) + } +} + +protocol ServerListInteractorInput: AnyObject { + + func fetchServers(forced: Bool, then completion: @escaping (Result) -> Void) + func cancelRequest() + +} + +final class ServerListInteractor: ServerListInteractorInput { + + private lazy var router = Environment.router + private var cancellationToken: CancellationToken? + + private var servers = [Server]() + + func fetchServers(forced: Bool, then completion: @escaping (Result) -> Void) { + + if !forced { + let servers = self.restoreListCaches() + guard servers.isEmpty else { + completion(.success(ServerListInteractorResult(serverLocations: servers, geoLocation: nil))) + return + } + } + + let service = ServerService.servers(token: self.token, includeGeo: true) + + var requestToken: CancellationToken? + requestToken = self.router.requestDecodable(Either.self, service: service) { [weak self] result in + guard self?.cancellationToken == requestToken + , let self = self else { + return + } + self.cancellationToken = nil + result + .flatMap { + switch $0.0 { + case let .firstType(vpnServers): + return Result.success(vpnServers) + case let .secondType(error): + return Result.failure(error) + } + } + >>> self.updateListCaches(result:) + >>> completion + } + + self.cancellationToken = requestToken + + } + + func cancelRequest() { + guard let token = self.cancellationToken else { return } + self.cancellationToken = nil + self.router.cancel(token) + } + + // MARK: - Private + + private var token: String { + return Session.shared().currentRecord?.token.value ?? "" + } + + private func updateListCaches(result: Result) -> Result { + + guard let vpnServers = result.optional else { return Result.success(ServerListInteractorResult.empty()) } + + self.servers = vpnServers.servers + let list = self.restoreListCaches() + + return Result.success(ServerListInteractorResult(serverLocations: list, geoLocation: vpnServers.geoLocation)) + } + + private func restoreListCaches() -> [ServerLocation] { + var servers = [String: ServerLocation]() + var countries = [String]() + + self.servers + .forEach { server in + guard let location = servers[server.country] else { + countries.append(server.country) + servers[server.country] = ServerLocation(country: server.country, + cities: [server.city], + points: [server.city: [server]]) + return + } + + guard location.points[server.city].isExist else { + location.cities.append(server.city) + location.points[server.city] = [server] + return + } + + location.points[server.city] = [server] + } + + return countries.compactMap { servers[$0] } + } + +} diff --git a/macOS/Privado/Flows/ServersFlow/Submodules/List/Models/ServerLocation.swift b/macOS/Privado/Flows/ServersFlow/Submodules/List/Models/ServerLocation.swift new file mode 100644 index 00000000..6e4e576f --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Submodules/List/Models/ServerLocation.swift @@ -0,0 +1,47 @@ +// +// ServerLocation.swift +// Privado +// +// Created by Juraldinio on 12/3/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class ServerLocation { + + typealias CountryCode = String + typealias CityName = String + + let country: CountryCode + var cities: [CityName] + var points: [CityName: [Server]] + + init(country: CountryCode, cities: [CityName], points: [CityName: [Server]]) { + self.country = country + self.cities = cities + self.points = points + } + + func append(server: Server) { + guard var points = self.points[server.city] else { + self.cities.append(server.city) + self.points[server.city] = [server] + return + } + + points.append(server) + } + + func servers(in city: CityName) -> [Server] { + guard self.cities.contains(city) else { return [] } + guard let servers = self.points[city] else { return [] } + return servers + } + + func servers() -> [Server] { + return self.points + .flatMap { return $1 } + .compactMap { $0 } + } +} diff --git a/macOS/Privado/Flows/ServersFlow/Submodules/List/Models/ServerLocationViewModel.swift b/macOS/Privado/Flows/ServersFlow/Submodules/List/Models/ServerLocationViewModel.swift new file mode 100644 index 00000000..750794fa --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Submodules/List/Models/ServerLocationViewModel.swift @@ -0,0 +1,16 @@ +// +// ServerLocationViewModel.swift +// Privado +// +// Created by Juraldinio on 11/28/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +struct ServerLocationViewModel: ServerLocationModel { + let title: String + let type: ServerLocationViewType + let imageNamed: String? + let suptitle: String? +} diff --git a/macOS/Privado/Flows/ServersFlow/Submodules/List/Presenter/ServerListPresenter.swift b/macOS/Privado/Flows/ServersFlow/Submodules/List/Presenter/ServerListPresenter.swift new file mode 100644 index 00000000..78271f1d --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Submodules/List/Presenter/ServerListPresenter.swift @@ -0,0 +1,611 @@ +// +// ServerListPresenter.swift +// Cyberlock +// +// Created by Jura on 8/28/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +struct ServerLocationRecord { + let country: ServerLocation.CountryCode + let city: ServerLocation.CityName? + let servers: [Server]? +} + +final class ServerListPresenter { + + enum FindBetterserver: Error { + case better + } + + weak var viewInput: ServerListInput? + private weak var output: ServerListModuleOutput? + + private let interactor: ServerListInteractorInput + private var isLoading = false + + private var bestRecord: ServerLocationRecord? + private var serverRecords: [ServerLocationRecord]? + private var selectedRecord: ServerLocationRecord? + private var location: GeoLocation? + + private var switchRequired = false + + init(interactor: ServerListInteractorInput, output: ServerListModuleOutput?) { + self.interactor = interactor + self.output = output + + Session.shared().locationEmitter.addReaction { [weak self] location -> ShouldContinueReceiveNotifications in + self?.location = location + return self.isExist + } + + Session.shared().vpnConnectionStateEmitter.addReaction { [weak self] state, _ -> ShouldContinueReceiveNotifications in + + let clickAllow = !(self?.output?.isReconnection ?? false) + + if let self = self + , let viewInput = self.viewInput { + switch state { + case .disconnected: + viewInput.allowSelection(!self.switchRequired) + case .error, + .connected: + viewInput.allowSelection(true && clickAllow) + default: + viewInput.allowSelection(false) + } + } + + return self.isExist + } + } + + internal func retryFetch(canBeCancelled: Bool = true) { + self.interactor.cancelRequest() + self.isLoading = true + + self.interactor.fetchServers(forced: true) { [weak self] result in + guard let `self` = self else { return } + self.isLoading = false + result + .onSuccess(self.handleServerLoad) + .onFail(self.errorRiseWhileLoading) + } + } + + // MARK: - Private + + @discardableResult + private func calculateBestRecord(for records: [ServerLocationRecord]?, current location: GeoLocation?) -> ServerLocationRecord? { + + typealias BestRecord = (ServerLocationRecord?, Haversine.Distance) + + guard let records = records + , let location = location + , let latitude = Double(location.lat) + , let longitude = Double(location.long) else { return nil } + + let serversRecords = records.filter { $0.servers.isExist } + + let geoLocation = Haversine.Location(latitude: latitude, longitude: longitude) + let bestRecord = serversRecords.reduce(into: BestRecord(nil, 0)) { (result, record) in + + try? record.servers?.forEach { server in + let serverLocation = Haversine.Location(latitude: server.latitude, longitude: server.longitude) + let distance = Haversine.distance(from: geoLocation, to: serverLocation) + if result.1 == 0 || result.1 > distance { + result.0 = record + result.1 = distance + throw FindBetterserver.better + } + } + + } + + return bestRecord.0 + } + + private func searchRecord(for server: Server, in records: [ServerLocationRecord]?) -> ServerLocationRecord? { + guard let records = records else { return nil } + return records.first(where: { $0.servers?.contains(server) ?? false }) + } + + private func handleServerLoad(result: ServerListInteractorResult) { + + self.serverRecords = result.serverLocations + /*.sorted(by: { (location1, location2) -> Bool in + return CountryResolver.name(from: location1.country) < CountryResolver.name(from: location2.country) + })*/.reduce(into: []) { acc, location in + acc?.append(ServerLocationRecord(country: location.country, city: nil, servers: nil)) + let records = location.cities + .sorted(by: <) + .map { city in + ServerLocationRecord(country: location.country, city: city, servers: location.servers(in: city)) + } + acc?.append(contentsOf: records) + } + + // Update GEO + self.output?.current(location: result.geoLocation) + + // Try restore server and match to current list OR just select best server + self.bestRecord = self.calculateBestRecord(for: self.serverRecords, current: result.geoLocation) + + let preferred = UserSettings.shared.preferredServer + if !self.selectedRecord.isExist { + + var record: ServerLocationRecord? + var type: PreferredServerType + if preferred == .last + , let server = self.restoreServer() + , let found = self.searchRecord(for: server, in: self.serverRecords) { + record = found + type = .last + } else if preffered == .random { + type = .random + let randomIndex = Int.random(in: 0..<(self.serverRecords?.count ?? 0)) + if let searched = self.serverRecords?[randomIndex] { + if searched.city == nil { + record = self.serverRecords?[safe: randomIndex+1] ?? self.bestRecord + } + else { + record = searched + } + } + else { + record = nil + } + } else { + type = .best + record = self.bestRecord + } + + self.selectedRecord = record + self.updateView(isBest: type == .best) + } + + if let record = self.selectedRecord + , let server = self.bestServer(using: record) { + + self.output?.selected(server: server) + + if preferred != .none { + self.output?.reconnect(to: server) + } + } + } + + private func updateView(isBest: Bool) { + guard let selected = self.selectedRecord else { return } + DispatchQueue.main.async { + let city = selected.city.isExist ? selected.city ?? "" : "" + let title = "\(CountryResolver.name(from: selected.country)) \(city.isEmpty ? "" : "- " + city)" + #if os(iOS) + let model = ServerLocationViewModel(title: title, + type: isBest ? .best : .selected, + imageNamed: selected.country, + suptitle: isBest ? NSLocalizedString("serverlist.location.best", comment: "") : NSLocalizedString("serverlist.location.virtual", comment: "")) + #else + let model = ServerLocationViewModel(title: title, + type: isBest ? .best : .selected, + imageNamed: isBest ? nil : selected.country, + suptitle: isBest ? NSLocalizedString("serverlist.location.best", comment: "") : NSLocalizedString("serverlist.location.virtual", comment: "")) + #endif + self.viewInput?.reloadData(selected: model) + } + } + + private func errorRiseWhileLoading(error: Error) { + guard let error = error as? ErrorResponse + , error.code != NSURLErrorCancelled else { + return + } + self.viewInput?.showCommonError(error) + + openApplicationRoute(.login(type: .refresh)) + } + + private func bestServer(using record: ServerLocationRecord) -> Server? { + guard let servers = record.servers else { return nil } + + return servers + .sorted(by: { $0.capacity < $1.capacity })[safe: 0] + } + + // MARK: - Server serialization and restoring + + private func storeServer(_ server: Server) { + let encoder = JSONEncoder() + guard let data = try? encoder.encode(server) + , let value = String(data: data, encoding: .utf8) else { + return + } + + UserSettings.shared.lastUsedServer = value + } + + private func restoreServer() -> Server? { + let value = UserSettings.shared.lastUsedServer + let decoder = JSONDecoder() + guard let data = value.data(using: .utf8) + , let server = try? decoder.decode(Server.self, from: data) else { + return nil + } + + return server + } + + #if os(iOS) + private var groupCountries: [[ServerLocationModel]]? + #endif +} + +#if os(iOS) +// MARK: - ServerListOutput + +extension ServerListPresenter: ServerListOutput { + + func getCountryImageBest() -> String? { + guard let best = self.bestRecord else { + return nil + } + let countryCode = best.country + return countryCode + } + + private func prepareGroupCountries() { + let records = self.locations() + var groupped: [[ServerLocationModel]] = [[ServerLocationModel]()] + var currentCountry: ServerLocationModel? + var currentCountryRecords = [ServerLocationModel]() + let maxIndex = self.bestRecord != nil ? records.count+1 : records.count + currentCountryRecords.removeAll() + groupped.removeAll() + self.groupCountries?.removeAll() + for index in 0.. ServerLocationModel? { + self.prepareGroupCountries() + guard let group = self.groupCountries else { + return nil + } + let section = group[index] + return section[0] + } + + func countriesCount() -> Int { + self.prepareGroupCountries() + guard let group = self.groupCountries else { + return 0 + } + return group.count + } + + func isSelected(for countryIndex: Int, at index: Int) -> Bool { + guard let selected = self.selectedRecord else { + return false + } + let location = self.location(for: countryIndex, at: index) + let isEqual = location?.title == selected.city + + return isEqual + } + + func locationsCount(for countryIndex: Int) -> Int { + self.prepareGroupCountries() + guard let group = self.groupCountries else { + return 0 + } + let section = group[countryIndex] + return section.count-1 + } + + func location(for countryIndex: Int, at index: Int) -> ServerLocationModel? { + self.prepareGroupCountries() + guard let group = self.groupCountries else { + return nil + } + let section = group[countryIndex] + return section[index+1] + } + + func locationsCount() -> Int { + var count = 0 + + if self.bestRecord.isExist { + count += 1 + } + + if let records = self.serverRecords { + count += records.count + } + + return count + } + + func location(at index: Int) -> ServerLocationModel? { + + if index == 0 + , let record = self.bestRecord { + return ServerLocationViewModel(title: CountryResolver.name(from: record.country), + type: .best, + imageNamed: record.country, + suptitle: NSLocalizedString("serverlist.location.best", comment: "")) + } + + guard let record = self.serverRecords?[safe: index - (self.bestRecord.isExist ? 1 : 0)] else { return nil } + + if let city = record.city { + return ServerLocationViewModel(title: city, + type: .city, + imageNamed: nil, + suptitle: nil) + } + + return ServerLocationViewModel(title: CountryResolver.name(from: record.country), + type: .country, + imageNamed: record.country, + suptitle: nil) + } + + func locations() -> [ServerLocationModel] { + + guard let records = self.serverRecords + , let bestRecord = self.bestRecord else { + return [] + } + + let locations = records.map { record -> ServerLocationModel in + + guard let city = record.city else { + return ServerLocationViewModel(title: CountryResolver.name(from: record.country), + type: .country, + imageNamed: record.country, + suptitle: nil) + } + + return ServerLocationViewModel(title: city, + type: .city, + imageNamed: nil, + suptitle: nil) + } + + return [ServerLocationViewModel(title: CountryResolver.name(from: bestRecord.country), + type: .best, + imageNamed: bestRecord.country, + suptitle: NSLocalizedString("serverlist.location.best", comment: ""))] + locations + } + + func selectedLocation(model: ServerLocationModel) { + + guard let servers = self.serverRecords + , let best = self.bestRecord else { + return + } + + let isBest: Bool + let record: ServerLocationRecord + if CountryResolver.name(from: best.country) == model.title { + isBest = true + record = best + } else if let server = servers.filter({ $0.city == model.title }).first { + isBest = false + record = server + } else { + return + } + + self.selectedRecord = record + + self.prepareGroupCountries() + + if let server = self.bestServer(using: record) { + + if Session.shared().currentVPN?.state != .disconnected { + self.output?.reconnect(to: server) + } else { + self.output?.selected(server: server) + } + + self.storeServer(server) + + let city = record.city.isExist ? record.city ?? "" : "" + #if os(iOS) + let title = "\(CountryResolver.name(from: record.country)) \(city.isEmpty ? "" : "- " + city)" + #else + let title = "\(record.country) \(city.isEmpty ? "" : "- " + city)" + #endif + + let model = isBest + ? ServerLocationViewModel(title: title, + type: .best, + imageNamed: record.country, + suptitle: NSLocalizedString("serverlist.location.best", comment: "")) + : ServerLocationViewModel(title: title, + type: .selected, + imageNamed: record.country, + suptitle: NSLocalizedString("serverlist.location.virtual", comment: "")) + self.viewInput?.currentSelected(model: model) + } + } + + func viewIsReady() { + self.retryFetch() + } + + func viewWillDisappear() { + self.interactor.cancelRequest() + self.isLoading = false + } + +} +#else + +// MARK: - ServerListOutput + +extension ServerListPresenter: ServerListOutput { + + func locationsCount() -> Int { + var count = 0 + + if self.bestRecord.isExist { + count += 1 + } + + if let records = self.serverRecords { + count += records.count + } + + return count + } + + func location(at index: Int) -> ServerLocationModel? { + + if index == 0 + , let record = self.bestRecord { + return ServerLocationViewModel(title: CountryResolver.name(from: record.country), + type: .best, + imageNamed: nil, + suptitle: NSLocalizedString("serverlist.location.best", comment: "")) + } + + guard let record = self.serverRecords?[safe: index - (self.bestRecord.isExist ? 1 : 0)] else { return nil } + + if let city = record.city { + return ServerLocationViewModel(title: city, + type: .city, + imageNamed: nil, + suptitle: nil) + } + + return ServerLocationViewModel(title: CountryResolver.name(from: record.country), + type: .country, + imageNamed: record.country, + suptitle: nil) + } + + func locations() -> [ServerLocationModel] { + + guard let records = self.serverRecords + , let bestRecord = self.bestRecord else { + return [] + } + + let locations = records.map { record -> ServerLocationModel in + + guard let city = record.city else { + return ServerLocationViewModel(title: CountryResolver.name(from: record.country), + type: .country, + imageNamed: record.country, + suptitle: nil) + } + + return ServerLocationViewModel(title: city, + type: .city, + imageNamed: nil, + suptitle: nil) + } + + return [ServerLocationViewModel(title: CountryResolver.name(from: bestRecord.country), + type: .best, + imageNamed: nil, + suptitle: NSLocalizedString("serverlist.location.best", comment: ""))] + locations + } + + func selectedLocation(model: ServerLocationModel) { + + guard let servers = self.serverRecords + , let best = self.bestRecord else { + return + } + + let isBest: Bool + let record: ServerLocationRecord + if CountryResolver.name(from: best.country) == model.title { + isBest = true + record = best + } else if let server = servers.filter({ $0.city == model.title }).first { + isBest = false + record = server + } else { + return + } + + self.selectedRecord = record + + if let server = self.bestServer(using: record) { + + if Session.shared().currentVPN?.state != .disconnected { + self.output?.reconnect(to: server) + } else { + self.output?.selected(server: server) + } + + self.storeServer(server) + + let city = record.city.isExist ? record.city ?? "" : "" + let title = "\(record.country) \(city.isEmpty ? "" : "- " + city)" + + let model = isBest + ? ServerLocationViewModel(title: title, + type: .best, + imageNamed: nil, + suptitle: NSLocalizedString("serverlist.location.best", comment: "")) + : ServerLocationViewModel(title: title, + type: .selected, + imageNamed: record.country, + suptitle: NSLocalizedString("serverlist.location.virtual", comment: "")) + self.viewInput?.currentSelected(model: model) + } + } + + func viewIsReady() { + self.retryFetch() + } + + func viewWillDisappear() { + self.interactor.cancelRequest() + self.isLoading = false + } + +} + + +#endif diff --git a/macOS/Privado/Flows/ServersFlow/Submodules/Location/Interactor/LocationInteractor.swift b/macOS/Privado/Flows/ServersFlow/Submodules/Location/Interactor/LocationInteractor.swift new file mode 100644 index 00000000..14f9c885 --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Submodules/Location/Interactor/LocationInteractor.swift @@ -0,0 +1,60 @@ +// +// LocationInteractor.swift +// Cyberlock +// +// Created by Juraldinio on 9/2/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +typealias LocationInteractorCompletion = (_ result: Result) -> Void + +protocol LocationInteractorInput: AnyObject { + + func by(ip: String, then completion: @escaping LocationInteractorCompletion) + func current(then completion: @escaping LocationInteractorCompletion) + func cancelRequest() +} + +final class LocationInteractor: LocationInteractorInput { + + private lazy var router = Environment.router + private var cancellationToken: CancellationToken? + + // MARK: - LocationInteractorInput + + func by(ip: String, then completion: @escaping LocationInteractorCompletion) { + let service = GeoService.ip(ip: ip, token: self.token) + self.requires(with: service, then: completion) + } + + func current(then completion: @escaping LocationInteractorCompletion) { + let service = GeoService.current(token: self.token) + self.requires(with: service, then: completion) + } + + func cancelRequest() { + guard let token = self.cancellationToken else { return } + self.cancellationToken = nil + self.router.cancel(token) + }//let service = ServerService.servers(token: ) + + // MARK: - Private + + private var token: String { + return Session.shared().currentRecord?.token.value ?? "" + } + + private func requires(with service: ServiceType, then completion: @escaping LocationInteractorCompletion) { + self.cancellationToken = self.router.requestDecodable(GeoLocation.self, service: service) { [weak self] result in + self?.cancellationToken = nil + result + .flatMap { + return Result.success($0.0) + } + >>> completion + } + } + +} diff --git a/macOS/Privado/Flows/ServersFlow/Submodules/Location/Models/LocationError.swift b/macOS/Privado/Flows/ServersFlow/Submodules/Location/Models/LocationError.swift new file mode 100644 index 00000000..a0bab411 --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Submodules/Location/Models/LocationError.swift @@ -0,0 +1,14 @@ +// +// LocationError.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/4/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +enum LocationError: Error { + case common + case killswitch +} diff --git a/macOS/Privado/Flows/ServersFlow/Submodules/Location/Presenter/LocationPresenter.swift b/macOS/Privado/Flows/ServersFlow/Submodules/Location/Presenter/LocationPresenter.swift new file mode 100644 index 00000000..2bd5dbf2 --- /dev/null +++ b/macOS/Privado/Flows/ServersFlow/Submodules/Location/Presenter/LocationPresenter.swift @@ -0,0 +1,181 @@ +// +// LocationPresenter.swift +// Cyberlock +// +// Created by Juraldinio on 9/2/19. +// Copyright © 2019 Omicronmedia. All rights reserved. +// + +import Foundation + +final class LocationPresenter: LocationViewOutput { + + private enum Constants { + static let maxDelay: TimeInterval = 5 + } + + weak var viewInput: LocationViewInput? + private let interactor: LocationInteractorInput + private weak var output: LocationModuleOutput? + + private var ip = "" + private var connectionState: VPNConnectionState = .disconnected + private var killSwitchEnabled = false + + init(interactor: LocationInteractorInput, output: LocationModuleOutput?) { + + self.interactor = interactor + self.output = output + self.killSwitchEnabled = UserSettings.shared.killSwitchEnabled + + Session.shared().vpnConnectionStateEmitter.addReaction { [weak self] (vpnState, _) -> ShouldContinueReceiveNotifications in + guard let self = self else { return false } + + guard self.connectionState != vpnState else { return true } + + let oldState = self.connectionState + self.connectionState = vpnState + + #if os(iOS) + let delay: TimeInterval = 1 + #else + let delay: TimeInterval = 0 + #endif + + switch vpnState { + case .connected: + self.refreshCurrentLocation(for: self.connectionState, delay: delay) + case .disconnected: + if [.connecting, .connected].contains(oldState) { self.ip = "" } + self.refreshCurrentLocation(for: self.connectionState, delay: delay) + default: + break + } + + return true + } + + Session.shared().locationEmitter.addReaction { [weak self] location -> ShouldContinueReceiveNotifications in + guard let self = self else { return false } + + if let viewInput = self.viewInput + , let location = location { + self.updateView(viewInput, using: location, for: self.connectionState) + } + + return true + } + + UserSettings.shared.killSwitchEmitter.addReaction { [weak self] enabled in + + guard let self = self else { return false } + + let old = self.killSwitchEnabled + self.killSwitchEnabled = enabled + + guard old != self.killSwitchEnabled + , [.connected, .disconnected].contains(self.connectionState) else { + return true + } + + self.ip = "" + + self.refreshCurrentLocation(for: self.connectionState) + + return true + } + } + + // MARK: - LocationViewOutput + + func viewIsReady() { + self.viewInput?.showLoader(true) + } + + func viewWillDisappear() { + self.interactor.cancelRequest() + } + + // MARK: - Private + + private func updateView(_ viewInput: LocationViewInput, using geoLocation: GeoLocation, for state: VPNConnectionState) { + viewInput.showLoader(false) + viewInput.showLocation(with: nil, + protected: self.isConnected(state: state), + ip: geoLocation.ip) + } + + private func isConnected(state: VPNConnectionState) -> Bool { + return state == .connected + } + + private func refreshCurrentLocation(for state: VPNConnectionState, delay: TimeInterval = 0) { + + if state == .disconnected + , self.killSwitchEnabled + , let viewInput = self.viewInput { + + viewInput.showLoader(false) + viewInput.showCommonError(LocationError.killswitch) + return + } + + self.viewInput?.showLoader(true) + + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in + + guard let self = self + , self.connectionState == state else { + return + } + + self.interactor.current { [weak self] result in + guard let self = self + , self.connectionState == state else { + return + } + self.handleRefreshLocation(result: result, for: state, delay: delay) + } + } + } + + private func handleRefreshLocation(result: Result, for state: VPNConnectionState, delay: TimeInterval) { + + guard let viewInput = self.viewInput else { return } + + switch result { + case let .success(geoLocation): + + guard self.ip != geoLocation.ip + || self.connectionState == .disconnected else { + + if delay < Constants.maxDelay { + self.refreshCurrentLocation(for: state, delay: delay + 1) + } else { + viewInput.showLoader(false) + viewInput.showCommonError(nil) + } + return + } + + self.ip = geoLocation.ip + + self.output?.current(location: geoLocation) + + case let .failure(error): + + self.ip = "" + + if delay < Constants.maxDelay { + self.refreshCurrentLocation(for: state, delay: delay + 1) + } else { + viewInput.showLoader(false) + viewInput.showCommonError(error) + } + + self.output?.current(location: nil) + } + + } + +} diff --git a/macOS/Privado/Flows/TrafficFlow/Coordinator/TrafficCoordinator.swift b/macOS/Privado/Flows/TrafficFlow/Coordinator/TrafficCoordinator.swift new file mode 100644 index 00000000..ba0345cc --- /dev/null +++ b/macOS/Privado/Flows/TrafficFlow/Coordinator/TrafficCoordinator.swift @@ -0,0 +1,23 @@ +// +// TrafficCoordinator.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/31/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +final class TrafficCoordinator: CoordinatorProtocol { + + private var presenter: TrafficCounterPresenter? + + // MARK: - CoordinatorProtocol + + var currentCoordinator: CoordinatorProtocol? + var previousCoordinator: CoordinatorProtocol? + + func start() { + self.presenter = TrafficCounterPresenter() + } +} diff --git a/macOS/Privado/Flows/TrafficFlow/Module/Presenter/TrafficCounterPresenter.swift b/macOS/Privado/Flows/TrafficFlow/Module/Presenter/TrafficCounterPresenter.swift new file mode 100644 index 00000000..973a4245 --- /dev/null +++ b/macOS/Privado/Flows/TrafficFlow/Module/Presenter/TrafficCounterPresenter.swift @@ -0,0 +1,53 @@ +// +// TrafficCounterPresenter.swift +// PrivadoVPN +// +// Created by Juraldinio on 8/31/20. +// Copyright © 2020 Privado LLC. All rights reserved. +// + +import Foundation + +final class TrafficCounterPresenter { + + private var currentRecord: InterfaceAddress.Record? + private var requireCleanup = false + + init() { + + Session.shared().vpnConnectionStateEmitter.addReaction { [weak self] state, vpn in + + print("TrafficCounterPresenter: \(state)") + + guard let interface = vpn?.interface + , let record = InterfaceAddress.instance.record(with: interface) else { + return self.isExist + } + + print("TrafficCounterPresenter: \(interface)") + + switch state { + case .connected: + self?.requireCleanup = false + record.statistic.addReaction { record in + guard let self = self else { return false } + + // got new record data + print("interface: \(record.name) received: \(record.received)") + + return !self.requireCleanup + } + InterfaceAddress.instance.resume() + case .disconnected: + self?.requireCleanup = true + InterfaceAddress.instance.pause() + default: + break + } + + return self.isExist + } + + } + +} diff --git a/macOS/Privado/project.yml b/macOS/Privado/project.yml index ea65a0f1..512d8b77 100644 --- a/macOS/Privado/project.yml +++ b/macOS/Privado/project.yml @@ -9,6 +9,15 @@ attributes: packages: Starscream: path: ../../External/Vendors/spm/Starscream + Sentry: + url: https://github.com/getsentry/sentry-cocoa + majorVersion: 5.2.2 + AppCenterAnalytics: + url: https://github.com/microsoft/appcenter-sdk-apple.git + majorVersion: 3.3.4 + AppCenterCrashes: + url: https://github.com/microsoft/appcenter-sdk-apple.git + majorVersion: 3.3.4 targets: @@ -114,15 +123,10 @@ targets: dependencies: - framework: ../../External/Vendors/macOS/Sparkle/Sparkle.framework embed: true - - framework: ../../External/Vendors/macOS/Sentry/Sentry.framework - embed: true - - framework: ../../External/Vendors/macOS/AppCenter/AppCenter.framework - embed: true - - framework: ../../External/Vendors/macOS/AppCenter/AppCenterAnalytics.framework - embed: true - - framework: ../../External/Vendors/macOS/AppCenter/AppCenterCrashes.framework - embed: true - package: Starscream + - package: Sentry + - package: AppCenterAnalytics + - package: AppCenterCrashes preBuildScripts: - script: | if [ "$ENABLE_SWIFTLINT" == YES ] && which swiftlint >/dev/null ; then