Modify migration logic

Extracted bulk of migration logic out of TPCResourceManager into its own class named TPCSandboxMigration.
Some logic of the migration remained in a private category of TPCResourceManager as it makes sense given the methods are related to resources / file access.
The entire migration system has been modified to be completely modular. Everything relies on the unified `installation` argument to perform migration on any number of containers.
The container for Standard Release was renamed to conform to Apple's standards. No more warnings. The migrator includes an entry for migrating beta users which will be removed before this goes into stable.
This commit is contained in:
Michael 2024-07-07 11:44:16 -04:00
parent 0d0006eccf
commit e2356c09bc
15 changed files with 249 additions and 361 deletions

View File

@ -3,7 +3,7 @@
#include "Enabled Features.xcconfig"
TEXTUAL_BUNDLE_IDENTIFIER = com.codeux.apps.textual
TEXTUAL_GROUP_CONTAINER_IDENTIFIER = com.codeux.apps.textual.group
TEXTUAL_GROUP_CONTAINER_IDENTIFIER = 8482Q6EPL6.com.codeux.apps.textual
TEXTUAL_BUILD_SCHEME_TOKEN = debug
TEXTUAL_EXTENSION_BUILD_SCHEME = Debug

View File

@ -3,7 +3,7 @@
#include "Enabled Features.xcconfig"
TEXTUAL_BUNDLE_IDENTIFIER = com.codeux.apps.textual
TEXTUAL_GROUP_CONTAINER_IDENTIFIER = com.codeux.apps.textual.group
TEXTUAL_GROUP_CONTAINER_IDENTIFIER = 8482Q6EPL6.com.codeux.apps.textual
TEXTUAL_BUILD_SCHEME_TOKEN = devid
TEXTUAL_EXTENSION_BUILD_SCHEME = Release

View File

@ -60,7 +60,7 @@
#import "TPCPreferencesLocalPrivate.h"
#import "TPCPreferencesUserDefaults.h"
#import "TPCResourceManagerPrivate.h"
#import "TPCResourceManagerMigratePrivate.h"
#import "TPCSandboxMigrationPrivate.h"
#import "TPCThemeControllerPrivate.h"
#import "TXMenuControllerPrivate.h"
#import "TXWindowControllerPrivate.h"
@ -147,7 +147,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)_awakeFromNib
{
/* Migrate files and preferences */
[TPCResourceManager migrateResources];
[TPCSandboxMigration migrateResources];
/* Initialize preferences */
[TPCPreferences initPreferences];

View File

@ -39,7 +39,7 @@
NS_ASSUME_NONNULL_BEGIN
@interface TPCResourceManager (TPCResourceManagerMigrate)
@interface TPCSandboxMigration : NSObject
+ (void)migrateResources;
@end

View File

@ -42,7 +42,7 @@
#import "TPCPathInfo.h"
#import "TPCPreferencesUserDefaults.h"
#import "TPCPreferencesUserDefaultsLocal.h"
#import "TPCResourceManagerMigratePrivate.h"
#import "TPCSandboxMigrationPrivate.h"
NS_ASSUME_NONNULL_BEGIN
@ -72,17 +72,6 @@ NS_ASSUME_NONNULL_BEGIN
After migration is completed, the user will be asked if
they want to remove (delete / erase) the old contents.
*/
@interface TPCPathInfo (TPCResourceManagerMigrate)
@property (class, readonly, copy, nullable) NSURL *gcStandaloneClassicURL;
@property (class, readonly, copy, nullable) NSURL *gcMacAppStoreURL;
@property (class, readonly, copy, nullable) NSURL *extensionsStandaloneClassicURL;
@property (class, readonly, copy, nullable) NSURL *extensionsMacAppStoreURL;
@property (class, readonly, copy, nullable) NSURL *preferencesMacAppStoreURL;
@end
@interface TPCPreferencesUserDefaults ()
- (void)_migrateObject:(nullable id)value forKey:(NSString *)defaultName;
@end
/* Textual will import preferences from a Mac App Store purchase
if this is the first launch. This macro defines the maximum amount
@ -93,54 +82,79 @@ NS_ASSUME_NONNULL_BEGIN
/* Defaults key set after migration is performed. */
/* YES if migration was completed */
#define MigrationCompleteDefaultsKey @"TPCResourceManagerMigrate -> Migrated Resources"
#define MigrationCompleteDefaultsKey @"Sandbox Migration -> Migrated Resources"
/* Integer for the installation that was migrated */
/* nil value or zero result is possible even after migration
is complete because nothing might have been migrated. */
/* Migration is designed to be one shot. */
#define MigrationInstallationMigratedDefaultsKey @"TPCResourceManagerMigrate -> Installation Migrated"
#define MigrationInstallationMigratedDefaultsKey @"Sandbox Migration -> Installation Migrated"
/* Whether the user has dismissed the notification alert */
#define MigrationUserAcknowledgedDefaultsKey @"TPCResourceManagerMigrate -> User Acknowledged"
#define MigrationUserAcknowledgedDefaultsKey @"Sandbox Migration -> User Acknowledged"
/* Whether the user wants to delete old files */
#define MigrationUserPrefersPruningDefaultsKey @"TPCResourceManagerMigrate -> User Prefers Pruning Files"
#define MigrationUserPrefersPruningDefaultsKey @"Sandbox Migration -> User Prefers Pruning Files"
/* YES if there are no more extensions to prune
which means we can bypass all the directory scans. */
#define MigrationAllExtensionsPrunedDefaultsKey @"TPCResourceManagerMigrate -> All Extensions Pruned"
#define MigrationAllExtensionsPrunedDefaultsKey @"Sandbox Migration -> All Extensions Pruned"
/* An array of preference keys migrated */
#define MigrationKeysImportedDefaultsKey @"TPCResourceManagerMigrate -> Imported Keys"
#define MigrationKeysImportedDefaultsKey @"Sandbox Migration -> Imported Keys"
/* Result returned when performing migration of a specific installation. */
typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationResult)
typedef NS_ENUM(NSUInteger, TPCMigrateSandboxResult)
{
/* Migration was performed successfully */
TPCResourceManagerMigrationResultSuccess,
TPCMigrateSandboxResultSuccess,
/* Candidate for migration is not suitable.
For example, a Mac App Store installation was located
but the age of its preference file is out of range. */
TPCResourceManagerMigrationResultNotSuitable,
TPCMigrateSandboxResultNotSuitable,
/* An error occurred during migration. */
/* Errors are logged to console. */
TPCResourceManagerMigrationResultError
TPCMigrateSandboxResultError
};
/* The installation attempting migration / migrated */
typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
typedef NS_ENUM(NSUInteger, TPCMigrateSandboxInstallation)
{
/* Standalone Classic */
TPCResourceManagerMigrationInstallationStandaloneClassic = 100,
TPCMigrateSandboxInstallationStandaloneClassic = 100,
/* Mac App Store */
TPCResourceManagerMigrationInstallationMacAppStore = 200
TPCMigrateSandboxInstallationMacAppStore = 200,
/* Standalone Beta */
/* First beta of sandboxed Textual used a different group container.
We will now migrate beta users a 2nd time just for the fun of it. */
#warning TODO: Remove beta migration after we are confident most users are taken care of.
TPCMigrateSandboxInstallationStandaloneBeta = 300,
};
@implementation TPCResourceManager (TPCResourceManagerMigrate)
@interface TPCPathInfo (TPCSandboxMigration)
+ (nullable NSURL *)_groupContainerURLForInstallation:(TPCMigrateSandboxInstallation)installation;
+ (nullable NSURL *)_groupContainerPreferencesURLForInstallation:(TPCMigrateSandboxInstallation)installation;
+ (nullable NSURL *)_groupContainerExtensionsURLForInstallation:(TPCMigrateSandboxInstallation)installation;
@end
@interface TPCResourceManager (TPCSandoxMigration)
+ (nullable NSArray<NSURL *> *)_listOfExtensionsForInstallation:(TPCMigrateSandboxInstallation)installation;
+ (BOOL)_ageOfCurrentContainerIsRecent;
+ (NSTimeInterval)_modificationDateForMacAppStorePreferencesIsRecent;
+ (NSTimeInterval)_intervalSinceCreatedForURL:(NSURL *)url;
+ (NSTimeInterval)_intervalSinceLastModificationForURL:(NSURL *)url;
+ (BOOL)_URLIsSymbolicLink:(NSURL *)url;
@end
@interface TPCPreferencesUserDefaults ()
- (void)_migrateObject:(nullable id)value forKey:(NSString *)defaultName;
@end
@implementation TPCSandboxMigration
+ (void)migrateResources
{
@ -158,7 +172,7 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
/* Do not migrate if the age of the current group container is not recent.
The age is only going to be recent the launch it was created. */
if ([self _ageOfCurrentContainerIsRecent] == NO) {
if ([TPCResourceManager _ageOfCurrentContainerIsRecent] == NO) {
[self _setMigrationCompleteAndAcknowledged];
LogToConsole("Current group container was not created recently");
@ -166,49 +180,18 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return;
}
/* This code can probably be condensed. Not important enough. */
/* Migrate Standalone Classic? */
LogToConsole("Start: Migrating Standalone Classic installation");
/* The order of this array is the order of preference for migration.
If one migrates, then it stops there. */
/* I acknowledge creating a list of numbers just to enumerate them
is not efficient. This will be ran once. It's okay. :) */
NSArray *installations = @[@(TPCMigrateSandboxInstallationStandaloneBeta),
@(TPCMigrateSandboxInstallationStandaloneClassic),
@(TPCMigrateSandboxInstallationMacAppStore)];
TPCResourceManagerMigrationResult tryMigrateStandaloneClass = [self _migrateStandaloneClassic];
switch (tryMigrateStandaloneClass) {
case TPCResourceManagerMigrationResultSuccess:
[self _setMigrationCompleteForStandaloneClassic];
LogToConsole("End: Migrating Standalone Classic successful");
return;
case TPCResourceManagerMigrationResultError:
LogToConsole("End: Migrating Standalone Classic failed. Stopping all migration");
return;
case TPCResourceManagerMigrationResultNotSuitable:
LogToConsole("End: Migrating Standalone Classic failed. Installation is not suitable");
break; // Try Mac App Store next
}
/* Migrate Standalone Classic? */
LogToConsole("Start: Migrating Mac App Store installation");
TPCResourceManagerMigrationResult tryMigrateMacAppStore = [self _migrateMacAppStore];
switch (tryMigrateMacAppStore) {
case TPCResourceManagerMigrationResultSuccess:
[self _setMigrationCompleteForMacAppStore];
LogToConsole("End: Migrating Mac App Store successful");
return;
case TPCResourceManagerMigrationResultError:
LogToConsole("End: Migrating Mac App Store failed. Stopping all migration");
return;
case TPCResourceManagerMigrationResultNotSuitable:
LogToConsole("End: Migrating Mac App Store failed. Installation is not suitable");
break;
for (NSNumber *installationRef in installations) {
if ([self _migrateInstallationEntry:installationRef]) {
return; // Success
}
}
/* No other migration path */
@ -218,99 +201,90 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
#pragma mark -
#pragma mark Standalone Classic Migration
+ (TPCResourceManagerMigrationResult)_migrateStandaloneClassic /* YES on success */
+ (BOOL)_migrateInstallationEntry:(NSNumber *)installationRef
{
/* Bundle identifier did not change during non-sandbox -> sandbox transition
which means we create a new blank defaults object because sending the bundle
identifier for the current app to -initWithSuiteName: is not allowed. */
NSUserDefaults *standaloneDefaults =
[[NSUserDefaults alloc] initWithSuiteName:[self _defaultsSuiteNameForStandaloneClassic]];
NSParameterAssert(installationRef != nil);
if (standaloneDefaults == nil) {
LogToConsoleFault("NSUserDefaults object could not be created for standalone domain. "
"This should be impossible as the bundle identifier has not changed.");
TPCMigrateSandboxInstallation installation = installationRef.unsignedIntegerValue;
return TPCResourceManagerMigrationResultError;
NSString *description = [self _descriptionOfInstallation:installation];
LogToConsole("Start: Migrating [%@] installation", description);
TPCMigrateSandboxResult result = [self _migrateInstallation:installation];
switch (result) {
case TPCMigrateSandboxResultSuccess:
[self _setMigrationCompleteForInstallation:installation];
LogToConsole("End: Migrating [%@] successful", description);
return YES; // Stop further migration
case TPCMigrateSandboxResultError:
LogToConsole("End: Migrating [%@] failed. Stopping all migration", description);
return YES; // Stop further migration
case TPCMigrateSandboxResultNotSuitable:
LogToConsole("End: Migrating [%@] failed. Installation is not suitable", description);
return NO; // Allow further migration
}
}
+ (TPCMigrateSandboxResult)_migrateInstallation:(TPCMigrateSandboxInstallation)installation
{
/* Preflight checks */
BOOL isMacAppStore = (installation == TPCMigrateSandboxInstallationMacAppStore);
if (isMacAppStore && [TPCResourceManager _modificationDateForMacAppStorePreferencesIsRecent] == NO) {
LogToConsoleDebug("Migration of Mac App Store has stale preferences file");
return TPCMigrateSandboxResultNotSuitable;
}
NSString *suiteName = [self _defaultsSuiteNameForInstallation:installation];
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
if (defaults == nil) {
LogToConsole("NSUserDefaults object could not be created for [%@] domain: '%@'",
[self _descriptionOfInstallation:installation], ((suiteName) ?: @"<no suite name>"));
return TPCMigrateSandboxResultNotSuitable;
}
/* Import preference keys */
NSDictionary *preferences = standaloneDefaults.dictionaryRepresentation;
NSUInteger runCount = [preferences unsignedIntegerForKey:@"TXRunCount"];
if (runCount == 0) {
LogToConsoleError("Migration of Standalone Classic has zero run count");
return TPCResourceManagerMigrationResultNotSuitable;
}
/* Import preferences */
/* Import preferences before migrating group container that way if a
hard failure is encountered there, it wont undo the progress we made.
The user will want something rather than nothing. Especially when it
comes to their configuration. Custom content can be copied manually. */
NSArray *importedKeys = [self _importPreferences:preferences];
/* Migrate group container */
BOOL migrateContainer = [self _migrateGroupContainerContentsForStandaloneClassic];
if (migrateContainer == NO) {
return TPCResourceManagerMigrationResultError;
}
/* Finish */
[self _setListOfImportedKeys:importedKeys];
[self _notifyGroupContainerMigratedForStandaloneClassic];
return TPCResourceManagerMigrationResultSuccess;
}
+ (TPCResourceManagerMigrationResult)_migrateMacAppStore /* YES on success */
{
/* Preflight checks */
if ([self _modificationDateForMacAppStorePreferencesIsRecent] == NO) {
LogToConsoleDebug("Migration of Mac App Store has stale preferences file");
return TPCResourceManagerMigrationResultNotSuitable;
}
NSUserDefaults *appStoreDefaults =
[[NSUserDefaults alloc] initWithSuiteName:[self _defaultsSuiteNameForMacAppStore]];
if (appStoreDefaults == nil) {
LogToConsole("NSUserDefaults object could not be created for Mac App Store domain");
return TPCResourceManagerMigrationResultNotSuitable;
}
/* Import preference keys */
NSDictionary *preferences = appStoreDefaults.dictionaryRepresentation;
NSDictionary *preferences = defaults.dictionaryRepresentation;
NSUInteger runCount = [preferences unsignedIntegerForKey:@"TXRunCount"];
if (runCount == 0) {
LogToConsoleError("Migration of Mac App Store has zero run count");
LogToConsoleError("Migration of [%@]] has zero run count",
[self _descriptionOfInstallation:installation]);
return TPCResourceManagerMigrationResultNotSuitable;
return TPCMigrateSandboxResultNotSuitable;
}
/* Import preferences */
NSArray *importedKeys = [self _importPreferences:preferences];
/* Migrate group container */
BOOL migrateContainer = [self _migrateGroupContainerContentsForMacAppStore];
BOOL migrateContainer = [self _migrateGroupContainerContentsForInstallation:installation];
if (migrateContainer == NO) {
return TPCResourceManagerMigrationResultError;
return TPCMigrateSandboxResultError;
}
/* Finish */
[self _setListOfImportedKeys:importedKeys];
[self _notifyGroupContainerMigratedForMacAppStore];
[self _notifyGroupContainerMigratedForInstallation:installation];
return TPCResourceManagerMigrationResultSuccess;
return TPCMigrateSandboxResultSuccess;
}
+ (NSArray<NSString *> *)_importPreferences:(NSDictionary<NSString *, id> *)dict
@ -340,7 +314,7 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return [importedKeys copy];
}
+ (void)_removeImportedKeysForInstallation:(TPCResourceManagerMigrationInstallation)installation
+ (void)_removeImportedKeysForInstallation:(TPCMigrateSandboxInstallation)installation
{
LogToConsole("Start: Remove old preferences");
@ -382,16 +356,6 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
LogToConsole("End: Remove old preferences - Removed: %lu", listOfKeys.count);
}
+ (void)_removeImportedKeysForStandaloneClassic
{
[self _removeImportedKeysForInstallation:TPCResourceManagerMigrationInstallationStandaloneClassic];
}
+ (void)_removeImportedKeysForMacAppStore
{
[self _removeImportedKeysForInstallation:TPCResourceManagerMigrationInstallationMacAppStore];
}
+ (void)_setListOfImportedKeys:(nullable NSArray<NSString *> *)list
{
[RZUserDefaults() _migrateObject:list forKey:MigrationKeysImportedDefaultsKey];
@ -407,36 +371,31 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
[RZUserDefaults() _migrateObject:@(YES) forKey:MigrationCompleteDefaultsKey];
}
+ (void)_setMigrationCompleteForStandaloneClassic
{
[RZUserDefaults() _migrateObject:@(TPCResourceManagerMigrationInstallationStandaloneClassic) forKey:MigrationInstallationMigratedDefaultsKey];
[self _setMigrationComplete];
}
+ (void)_setMigrationCompleteForMacAppStore
{
[RZUserDefaults() _migrateObject:@(TPCResourceManagerMigrationInstallationMacAppStore) forKey:MigrationInstallationMigratedDefaultsKey];
[self _setMigrationComplete];
}
+ (void)_setMigrationCompleteAndAcknowledged
{
/* This method is called when there was nothing to migrate
so we set complete and pretend user acknowledged it. */
[self _setMigrationComplete];
[self _setUserAcknowledgedMigration];
}
+ (void)_setMigrationCompleteForInstallation:(TPCMigrateSandboxInstallation)installation
{
[RZUserDefaults() _migrateObject:@(installation) forKey:MigrationInstallationMigratedDefaultsKey];
[self _setMigrationComplete];
}
#pragma mark -
#pragma mark Group Container Migration
+ (BOOL)_migrateGroupContainerContentsForInstallation:(TPCResourceManagerMigrationInstallation)installation
+ (BOOL)_migrateGroupContainerContentsForInstallation:(TPCMigrateSandboxInstallation)installation
{
LogToConsole("Start: Migrate group container for '%@'",
[self _descriptionOfInstallation:installation]);
NSURL *oldLocation = [self _groupContainerURLForInstallation:installation];
NSURL *oldLocation = [TPCPathInfo _groupContainerURLForInstallation:installation];
if (oldLocation == nil) {
LogToConsoleError("Cannot migrate group container contents because of nil source location");
@ -470,17 +429,7 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return result;
}
+ (BOOL)_migrateGroupContainerContentsForStandaloneClassic
{
return [self _migrateGroupContainerContentsForInstallation:TPCResourceManagerMigrationInstallationStandaloneClassic];
}
+ (BOOL)_migrateGroupContainerContentsForMacAppStore
{
return [self _migrateGroupContainerContentsForInstallation:TPCResourceManagerMigrationInstallationMacAppStore];
}
+ (void)_notifyGroupContainerMigratedForInstallation:(TPCResourceManagerMigrationInstallation)installation
+ (void)_notifyGroupContainerMigratedForInstallation:(TPCMigrateSandboxInstallation)installation
{
LogToConsole("Notifying user that installation of type [%@] migration performed",
[self _descriptionOfInstallation:installation]);
@ -526,34 +475,15 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return; // Stop migration
}
TPCResourceManagerMigrationInstallation installation = [RZUserDefaults() unsignedIntegerForKey:MigrationInstallationMigratedDefaultsKey];
TPCMigrateSandboxInstallation installation = [RZUserDefaults() unsignedIntegerForKey:MigrationInstallationMigratedDefaultsKey];
/* Seeing as this value comes from outside and is manipulatable,
we use a switch statement to validate value instead of calling
directly into -_notifyGroupContainerMigratedForInstallation:
which assumes good faith for its arguments. */
switch (installation) {
case TPCResourceManagerMigrationInstallationStandaloneClassic:
[self _notifyGroupContainerMigratedForStandaloneClassic];
if ([self _isInstallationSupported:installation] == NO) {
[self _setUserAcknowledgedMigration];
break;
case TPCResourceManagerMigrationInstallationMacAppStore:
[self _notifyGroupContainerMigratedForMacAppStore];
break;
default:
break;
return; // Stop migration
}
}
+ (void)_notifyGroupContainerMigratedForStandaloneClassic
{
[self _notifyGroupContainerMigratedForInstallation:TPCResourceManagerMigrationInstallationStandaloneClassic];
}
+ (void)_notifyGroupContainerMigratedForMacAppStore
{
[self _notifyGroupContainerMigratedForInstallation:TPCResourceManagerMigrationInstallationMacAppStore];
[self _notifyGroupContainerMigratedForInstallation:installation];
}
+ (void)_setUserAcknowledgedMigration
@ -569,12 +499,12 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
#pragma mark -
#pragma mark Group Container Removal
+ (BOOL)_removeGroupContainerContentsForInstallation:(TPCResourceManagerMigrationInstallation)installation
+ (BOOL)_removeGroupContainerContentsForInstallation:(TPCMigrateSandboxInstallation)installation
{
LogToConsole("Start: Remove group container for '%@'",
[self _descriptionOfInstallation:installation]);
NSURL *gcLocation = [self _groupContainerURLForInstallation:installation];
NSURL *gcLocation = [TPCPathInfo _groupContainerURLForInstallation:installation];
if (gcLocation == nil) {
LogToConsoleError("Cannot remove group container contents because of nil location");
@ -584,7 +514,7 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
/* -_listOfExtensionsForInstallation: should only return nil on fatal errors.
It will not return nil for an extension folder that does not exist, or is empty. */
NSArray *oldExtensions = [self _listOfExtensionsForInstallation:installation];
NSArray *oldExtensions = [TPCResourceManager _listOfExtensionsForInstallation:installation];
if (gcLocation == nil) {
LogToConsoleError("Cannot remove group container contents because of nil extension list");
@ -604,20 +534,10 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return result;
}
+ (BOOL)_removeGroupContainerContentsForStandaloneClassic
{
return [self _removeGroupContainerContentsForInstallation:TPCResourceManagerMigrationInstallationStandaloneClassic];
}
+ (BOOL)_removeGroupContainerContentsForMacAppStore
{
return [self _removeGroupContainerContentsForInstallation:TPCResourceManagerMigrationInstallationMacAppStore];
}
#pragma mark -
#pragma mark Extension Pruning
+ (void)_pruneExtensionSymbolicLinksForInstallation:(TPCResourceManagerMigrationInstallation)installation
+ (void)_pruneExtensionSymbolicLinksForInstallation:(TPCMigrateSandboxInstallation)installation
{
/* When you create a copy of a bundle programmatically, macOS will move it to
quarantine. The user is then notified macOS can't verify that it isn't malware.
@ -634,7 +554,7 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
LogToConsole("Start: Pruning extensions for '%@'",
[self _descriptionOfInstallation:installation]);
NSArray *oldExtensions = [self _listOfExtensionsForInstallation:installation];
NSArray *oldExtensions = [TPCResourceManager _listOfExtensionsForInstallation:installation];
if (oldExtensions == nil) {
/* Helper method will describe error. */
@ -685,7 +605,7 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
created is near zero if not zero. This is already over engineered. */
BOOL pruned = NO;
if ([self _fileAtURLIsSymbolicLink:newExtension] == NO) {
if ([TPCResourceManager _URLIsSymbolicLink:newExtension] == NO) {
#ifdef DEBUG
LogToConsoleDebug("Pruning URL: '%@'", oldExtension);
#endif
@ -716,16 +636,6 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
numberRemaining, numberPruned);
}
+ (void)_pruneExtensionSymbolicLinksForStandaloneClassic
{
[self _pruneExtensionSymbolicLinksForInstallation:TPCResourceManagerMigrationInstallationStandaloneClassic];
}
+ (void)_pruneExtensionSymbolicLinksForMacAppStore
{
[self _pruneExtensionSymbolicLinksForInstallation:TPCResourceManagerMigrationInstallationMacAppStore];
}
+ (void)_pruneExtensionSymbolicLinksFromDefaults
{
BOOL doPrune = [RZUserDefaults() boolForKey:MigrationUserPrefersPruningDefaultsKey];
@ -740,20 +650,15 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return; // Stop pruning
}
TPCResourceManagerMigrationInstallation installation = [RZUserDefaults() unsignedIntegerForKey:MigrationInstallationMigratedDefaultsKey];
TPCMigrateSandboxInstallation installation = [RZUserDefaults() unsignedIntegerForKey:MigrationInstallationMigratedDefaultsKey];
switch (installation) {
case TPCResourceManagerMigrationInstallationStandaloneClassic:
[self _pruneExtensionSymbolicLinksForStandaloneClassic];
if ([self _isInstallationSupported:installation] == NO) {
[self _setAllExtensionSymbolicLinksPruned];
break;
case TPCResourceManagerMigrationInstallationMacAppStore:
[self _pruneExtensionSymbolicLinksForMacAppStore];
break;
default:
break;
return; // Stop pruning
}
[self _pruneExtensionSymbolicLinksForInstallation:installation];
}
+ (void)_setAllExtensionSymbolicLinksPruned
@ -764,92 +669,69 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
#pragma mark -
#pragma mark Utilities
+ (NSString *)_descriptionOfInstallation:(TPCResourceManagerMigrationInstallation)installation
+ (NSString *)_descriptionOfInstallation:(TPCMigrateSandboxInstallation)installation
{
/* This is used for logging so is not localized.
Localize is use changes. */
switch (installation) {
case TPCResourceManagerMigrationInstallationStandaloneClassic:
case TPCMigrateSandboxInstallationStandaloneClassic:
return @"Standalone Classic";
case TPCResourceManagerMigrationInstallationMacAppStore:
case TPCMigrateSandboxInstallationStandaloneBeta:
return @"Standalone Beta";
case TPCMigrateSandboxInstallationMacAppStore:
return @"Mac App Store";
default:
return @"<Unknown Installation>";
}
}
+ (nullable NSURL *)_groupContainerURLForInstallation:(TPCResourceManagerMigrationInstallation)installation
+ (nullable NSString *)_groupContainerIdentifierForInstallation:(TPCMigrateSandboxInstallation)installation
{
NSURL *gcLocation = nil;
switch (installation) {
case TPCResourceManagerMigrationInstallationStandaloneClassic:
gcLocation = [TPCPathInfo gcStandaloneClassicURL];
break;
case TPCResourceManagerMigrationInstallationMacAppStore:
gcLocation = [TPCPathInfo gcMacAppStoreURL];
break;
case TPCMigrateSandboxInstallationStandaloneClassic:
return @"com.codeux.apps.textual";
case TPCMigrateSandboxInstallationStandaloneBeta:
return @"com.codeux.apps.textual.group";
case TPCMigrateSandboxInstallationMacAppStore:
return @"8482Q6EPL6.com.codeux.irc.textual";
default:
return nil;
}
if (gcLocation == nil) {
LogToConsoleFault("Group container URL for installation [%@] is nil",
[self _descriptionOfInstallation:installation]);
}
return gcLocation;
}
+ (nullable NSURL *)_extensionsURLForInstallation:(TPCResourceManagerMigrationInstallation)installation
{
NSURL *gcLocation = nil;
switch (installation) {
case TPCResourceManagerMigrationInstallationStandaloneClassic:
gcLocation = [TPCPathInfo extensionsStandaloneClassicURL];
break;
case TPCResourceManagerMigrationInstallationMacAppStore:
gcLocation = [TPCPathInfo extensionsMacAppStoreURL];
break;
default:
return nil;
}
if (gcLocation == nil) {
LogToConsoleFault("Extensions URL for installation [%@] is nil",
[self _descriptionOfInstallation:installation]);
}
return gcLocation;
}
+ (nullable NSString *)_defaultsSuiteNameForInstallation:(TPCResourceManagerMigrationInstallation)installation
{
if (installation == TPCResourceManagerMigrationInstallationMacAppStore) {
return @"8482Q6EPL6.com.codeux.irc.textual";
}
return nil;
}
+ (nullable NSString *)_defaultsSuiteNameForStandaloneClassic
+ (nullable NSString *)_defaultsSuiteNameForInstallation:(TPCMigrateSandboxInstallation)installation
{
return [self _defaultsSuiteNameForInstallation:TPCResourceManagerMigrationInstallationStandaloneClassic];
switch (installation) {
case TPCMigrateSandboxInstallationStandaloneBeta:
return @"com.codeux.apps.textual.group";
case TPCMigrateSandboxInstallationMacAppStore:
return @"8482Q6EPL6.com.codeux.irc.textual";
default:
break;
}
return nil;
}
+ (nullable NSString *)_defaultsSuiteNameForMacAppStore
+ (BOOL)_isInstallationSupported:(TPCMigrateSandboxInstallation)installation
{
return [self _defaultsSuiteNameForInstallation:TPCResourceManagerMigrationInstallationMacAppStore];
return (installation == TPCMigrateSandboxInstallationStandaloneClassic ||
installation == TPCMigrateSandboxInstallationStandaloneBeta ||
installation == TPCMigrateSandboxInstallationMacAppStore);
}
+ (nullable NSArray<NSURL *> *)_listOfExtensionsForInstallation:(TPCResourceManagerMigrationInstallation)installation
@end
#pragma mark -
#pragma mark Resource Management
@implementation TPCResourceManager (TPCSandoxMigration)
+ (nullable NSArray<NSURL *> *)_listOfExtensionsForInstallation:(TPCMigrateSandboxInstallation)installation
{
NSURL *oldLocation = [self _extensionsURLForInstallation:installation];
NSURL *oldLocation = [TPCPathInfo _groupContainerExtensionsURLForInstallation:installation];
if (oldLocation == nil) {
LogToConsoleError("Cannot list extensions because of nil source location");
@ -886,30 +768,30 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return NO;
}
NSTimeInterval age = [self _ageOfFileAtURL:newLocation];
NSTimeInterval age = [self _intervalSinceCreatedForURL:newLocation];
/* macOS will create the group container the first time
we ask for its path. If the group container wasn't created
recently, then we have no reason to perform migration to it.
In theory, this could probably be narrowed down further as
the interval should be sub-second. */
return (age < 2.0);
return (age < 5.0);
}
+ (NSTimeInterval)_modificationDateForMacAppStorePreferencesIsRecent
{
NSURL *location = [TPCPathInfo preferencesMacAppStoreURL];
NSURL *location = [TPCPathInfo _groupContainerPreferencesURLForInstallation:TPCMigrateSandboxInstallationMacAppStore];
if (location == nil) {
return NO;
}
NSTimeInterval age = [self _intervalSinceFileAtURLLastModified:location];
NSTimeInterval age = [self _intervalSinceLastModificationForURL:location];
return (age >= 0 && age <= MaximumAgeOfStalePreferences);
}
+ (NSTimeInterval)_ageOfFileAtURL:(NSURL *)url
+ (NSTimeInterval)_intervalSinceCreatedForURL:(NSURL *)url
{
NSParameterAssert(url != nil);
@ -927,7 +809,7 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return age;
}
+ (NSTimeInterval)_intervalSinceFileAtURLLastModified:(NSURL *)url
+ (NSTimeInterval)_intervalSinceLastModificationForURL:(NSURL *)url
{
NSParameterAssert(url != nil);
@ -945,7 +827,7 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return age;
}
+ (BOOL)_fileAtURLIsSymbolicLink:(NSURL *)url
+ (BOOL)_URLIsSymbolicLink:(NSURL *)url
{
NSParameterAssert(url != nil);
@ -961,31 +843,45 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
#pragma mark -
#pragma mark Path Information
@implementation TPCPathInfo (TPCResourceManagerMigrate)
@implementation TPCPathInfo (TPCSandboxMigration)
+ (nullable NSURL *)gcStandaloneClassicURL
+ (nullable NSURL *)_groupContainerURLForInstallation:(TPCMigrateSandboxInstallation)installation
{
NSString *identifier = [TPCSandboxMigration _groupContainerIdentifierForInstallation:installation];
if (identifier == nil) {
return nil;
}
/* The reason we are not using -containerURLForSecurityApplicationGroupIdentifier: in this context is because
during testing, that method was returning ~/Library/Containers/com.codeux.apps.textual instead of the group
container location. I assume it's related to the fact the group identifier is same as the app's identifier.
This is not a make-or-break location in which hard coding will hurt it. */
// NSURL *baseURL = [RZFileManager() containerURLForSecurityApplicationGroupIdentifier:@"com.codeux.apps.textual"];
NSURL *baseURL = [[TPCPathInfo userHomeURL] URLByAppendingPathComponent:@"/Library/Group Containers/com.codeux.apps.textual/"];
identifier = [NSString localizedStringWithFormat:@"/Library/Group Containers/%@/", identifier];
NSURL *baseURL = [[TPCPathInfo userHomeURL] URLByAppendingPathComponent:identifier];
return baseURL;
}
+ (nullable NSURL *)gcMacAppStoreURL
+ (nullable NSURL *)_groupContainerPreferencesURLForInstallation:(TPCMigrateSandboxInstallation)installation
{
// NSURL *baseURL = [RZFileManager() containerURLForSecurityApplicationGroupIdentifier:@"8482Q6EPL6.com.codeux.irc.textual"];
NSURL *baseURL = [[TPCPathInfo userHomeURL] URLByAppendingPathComponent:@"/Library/Group Containers/8482Q6EPL6.com.codeux.irc.textual"];
NSString *identifier = [TPCSandboxMigration _groupContainerIdentifierForInstallation:installation];
if (identifier == nil) {
return nil;
}
identifier = [NSString localizedStringWithFormat:@"/Library/Group Containers/%1$@/Library/Preferences/%1$@.plist", identifier];
NSURL *baseURL = [[TPCPathInfo userHomeURL] URLByAppendingPathComponent:identifier];
return baseURL;
}
+ (nullable NSURL *)extensionsStandaloneClassicURL
+ (nullable NSURL *)_groupContainerExtensionsURLForInstallation:(TPCMigrateSandboxInstallation)installation
{
NSURL *sourceURL = self.gcStandaloneClassicURL;
NSURL *sourceURL = [self _groupContainerURLForInstallation:installation];
if (sourceURL == nil) {
return nil;
@ -994,28 +890,6 @@ typedef NS_ENUM(NSUInteger, TPCResourceManagerMigrationInstallation)
return [sourceURL URLByAppendingPathComponent:@"/Library/Application Support/Textual/Extensions/"];
}
+ (nullable NSURL *)extensionsMacAppStoreURL
{
NSURL *sourceURL = self.gcMacAppStoreURL;
if (sourceURL == nil) {
return nil;
}
return [sourceURL URLByAppendingPathComponent:@"/Library/Application Support/Textual/Extensions/"];
}
+ (nullable NSURL *)preferencesMacAppStoreURL
{
NSURL *sourceURL = self.gcMacAppStoreURL;
if (sourceURL == nil) {
return nil;
}
return [sourceURL URLByAppendingPathComponent:@"/Library/Preferences/8482Q6EPL6.com.codeux.irc.textual.plist"];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -8,6 +8,7 @@
<array>
<string>com.codeux.apps.textual</string>
<string>com.codeux.apps.textual.group</string>
<string>8482Q6EPL6.com.codeux.apps.textual</string>
<string>8482Q6EPL6.com.codeux.irc.textual</string>
</array>
<key>com.apple.security.automation.apple-events</key>

View File

@ -8,6 +8,7 @@
<array>
<string>com.codeux.apps.textual</string>
<string>com.codeux.apps.textual.group</string>
<string>8482Q6EPL6.com.codeux.apps.textual</string>
<string>8482Q6EPL6.com.codeux.irc.textual</string>
</array>
<key>com.apple.security.automation.apple-events</key>

View File

@ -52,17 +52,17 @@
<integer>0</integer>
<key>TVCLogControllerHistoricLogFileSavePath_v3</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Migrated Resources</key>
<key>Sandbox Migration -&gt; Migrated Resources</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Installation Migrated</key>
<key>Sandbox Migration -&gt; Installation Migrated</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; User Acknowledged</key>
<key>Sandbox Migration -&gt; User Acknowledged</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; User Prefers Pruning Files</key>
<key>Sandbox Migration -&gt; User Prefers Pruning Files</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; All Extensions Pruned</key>
<key>Sandbox Migration -&gt; All Extensions Pruned</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Imported Keys</key>
<key>Sandbox Migration -&gt; Imported Keys</key>
<integer>0</integer>
</dict>
</plist>

View File

@ -10,5 +10,17 @@
<integer>0</integer>
<key>__uniquePageGroupID-1.WebKit2ShouldRespectImageOrientation</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Migrated Resources</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Installation Migrated</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; User Acknowledged</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; User Prefers Pruning Files</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; All Extensions Pruned</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Imported Keys</key>
<integer>0</integer>
</dict>
</plist>

View File

@ -338,17 +338,17 @@
<integer>0</integer>
<key>TPCPreferences -&gt; Migration -&gt; World Controller Migrated (600)</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Migrated Resources</key>
<key>Sandbox Migration -&gt; Migrated Resources</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Installation Migrated</key>
<key>Sandbox Migration -&gt; Installation Migrated</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; User Acknowledged</key>
<key>Sandbox Migration -&gt; User Acknowledged</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; User Prefers Pruning Files</key>
<key>Sandbox Migration -&gt; User Prefers Pruning Files</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; All Extensions Pruned</key>
<key>Sandbox Migration -&gt; All Extensions Pruned</key>
<integer>0</integer>
<key>TPCResourceManagerMigrate -&gt; Imported Keys</key>
<key>Sandbox Migration -&gt; Imported Keys</key>
<integer>0</integer>
<key>THOPluginManager -&gt; Extras Installer Last Check for Update Payload</key>
<integer>0</integer>

View File

@ -4,7 +4,7 @@ set -e
export BUILD_PATH="/private/tmp/Textual-${RANDOM}"
export BUILD_PATH_SCRIPTS_STANDALONE="${BUILD_PATH}/Library/Application Scripts/com.codeux.apps.textual"
export BUILD_PATH_EXTENSIONS_STANDALONE="${BUILD_PATH}/Library/Group Containers/com.codeux.apps.textual.group/Library/Application Support/Textual/Extensions"
export BUILD_PATH_EXTENSIONS_STANDALONE="${BUILD_PATH}/Library/Group Containers/8482Q6EPL6.com.codeux.apps.textual/Library/Application Support/Textual/Extensions"
export CURRENT_DIRECTORY=$(cd `dirname $0` && pwd)

View File

@ -1210,10 +1210,10 @@
4C7D616C267DF466006AA911 /* TDCCommunityMovedDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C7D6169267DF466006AA911 /* TDCCommunityMovedDialog.xib */; };
4C7D616D267DF466006AA911 /* TDCCommunityMovedDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C7D6169267DF466006AA911 /* TDCCommunityMovedDialog.xib */; };
4C83B9121BA9BED500BD7718 /* Chat Filters.bundle in Copy Extensions */ = {isa = PBXBuildFile; fileRef = 4C83B9101BA9BEB800BD7718 /* Chat Filters.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
4C8878042C33706E0016DB98 /* TPCResourceManagerMigrate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C8878032C33706E0016DB98 /* TPCResourceManagerMigrate.m */; };
4C8878052C33706E0016DB98 /* TPCResourceManagerMigrate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C8878032C33706E0016DB98 /* TPCResourceManagerMigrate.m */; };
4C8878072C3371470016DB98 /* TPCResourceManagerMigratePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C8878062C3371470016DB98 /* TPCResourceManagerMigratePrivate.h */; };
4C8878082C3371470016DB98 /* TPCResourceManagerMigratePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C8878062C3371470016DB98 /* TPCResourceManagerMigratePrivate.h */; };
4C8878042C33706E0016DB98 /* TPCSandboxMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C8878032C33706E0016DB98 /* TPCSandboxMigration.m */; };
4C8878052C33706E0016DB98 /* TPCSandboxMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C8878032C33706E0016DB98 /* TPCSandboxMigration.m */; };
4C8878072C3371470016DB98 /* TPCSandboxMigrationPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C8878062C3371470016DB98 /* TPCSandboxMigrationPrivate.h */; };
4C8878082C3371470016DB98 /* TPCSandboxMigrationPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C8878062C3371470016DB98 /* TPCSandboxMigrationPrivate.h */; };
4C8F3FD82C31AF7700118AAF /* THOPluginManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C8F3FD62C31AF6E00118AAF /* THOPluginManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
4C8F3FD92C31AF7700118AAF /* THOPluginManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C8F3FD62C31AF6E00118AAF /* THOPluginManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
4C8F3FDC2C31B15B00118AAF /* THOPluginItemLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C8F3FDB2C31B15200118AAF /* THOPluginItemLogging.m */; };
@ -2040,8 +2040,8 @@
4C7D6164267DF44C006AA911 /* TDCCommunityMovedDialogPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TDCCommunityMovedDialogPrivate.h; sourceTree = "<group>"; };
4C7D616A267DF466006AA911 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/TDCCommunityMovedDialog.xib; sourceTree = "<group>"; };
4C83B9101BA9BEB800BD7718 /* Chat Filters.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = "Chat Filters.bundle"; path = "../../.tmp/SharedBuildProducts-Extensions/Chat Filters.bundle"; sourceTree = SOURCE_ROOT; };
4C8878032C33706E0016DB98 /* TPCResourceManagerMigrate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPCResourceManagerMigrate.m; sourceTree = "<group>"; };
4C8878062C3371470016DB98 /* TPCResourceManagerMigratePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TPCResourceManagerMigratePrivate.h; sourceTree = "<group>"; };
4C8878032C33706E0016DB98 /* TPCSandboxMigration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPCSandboxMigration.m; sourceTree = "<group>"; };
4C8878062C3371470016DB98 /* TPCSandboxMigrationPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TPCSandboxMigrationPrivate.h; sourceTree = "<group>"; };
4C8F2F5D1AAE6467007821CC /* EncryptionKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EncryptionKit.framework; path = "../../.tmp/SharedBuildProducts-Frameworks/EncryptionKit.framework"; sourceTree = SOURCE_ROOT; };
4C8F3FD62C31AF6E00118AAF /* THOPluginManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = THOPluginManager.h; sourceTree = "<group>"; };
4C8F3FDB2C31B15200118AAF /* THOPluginItemLogging.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = THOPluginItemLogging.m; sourceTree = "<group>"; };
@ -2401,7 +2401,7 @@
4C06E28920EC4F000055D09A /* TPCPreferencesUserDefaults.m */,
4C31511920EB673E00448776 /* TPCPreferencesUserDefaultsLocal.m */,
4C31511820EB673E00448776 /* TPCResourceManager.m */,
4C8878032C33706E0016DB98 /* TPCResourceManagerMigrate.m */,
4C8878032C33706E0016DB98 /* TPCSandboxMigration.m */,
);
path = Preferences;
sourceTree = "<group>";
@ -2769,7 +2769,7 @@
4C31521820EB673E00448776 /* TPCPreferencesLocalPrivate.h */,
4C06E26020EC4C560055D09A /* TPCPreferencesPrivate.h */,
4C06E26120EC4C560055D09A /* TPCPreferencesUserDefaultsPrivate.h */,
4C8878062C3371470016DB98 /* TPCResourceManagerMigratePrivate.h */,
4C8878062C3371470016DB98 /* TPCSandboxMigrationPrivate.h */,
4C31522520EB673E00448776 /* TPCResourceManagerPrivate.h */,
4C31521020EB673E00448776 /* TPCThemeControllerPrivate.h */,
4C3151D620EB673E00448776 /* TPCThemePrivate.h */,
@ -3653,7 +3653,7 @@
4C31578D20EB6D0600448776 /* IRCUserNicknameColorStyleGeneratorPrivate.h in Headers */,
4C3157B820EB6D0600448776 /* TDCPreferencesNotificationConfigurationPrivate.h in Headers */,
4C31580620EB6D0600448776 /* TXMenuControllerPrivate.h in Headers */,
4C8878082C3371470016DB98 /* TPCResourceManagerMigratePrivate.h in Headers */,
4C8878082C3371470016DB98 /* TPCSandboxMigrationPrivate.h in Headers */,
4C31558920EB6CB300448776 /* TVCChannelSelectionViewController.h in Headers */,
4C31577C20EB6D0600448776 /* IRCChannelModePrivate.h in Headers */,
4C3157F420EB6D0600448776 /* TVCMainWindowTextViewAppearancePrivate.h in Headers */,
@ -3923,7 +3923,7 @@
4C3156FA20EB6D0500448776 /* IRCUserNicknameColorStyleGeneratorPrivate.h in Headers */,
4C31572520EB6D0500448776 /* TDCPreferencesNotificationConfigurationPrivate.h in Headers */,
4C31577320EB6D0500448776 /* TXMenuControllerPrivate.h in Headers */,
4C8878072C3371470016DB98 /* TPCResourceManagerMigratePrivate.h in Headers */,
4C8878072C3371470016DB98 /* TPCSandboxMigrationPrivate.h in Headers */,
4C3155E020EB6CB400448776 /* TVCChannelSelectionViewController.h in Headers */,
4C3156E920EB6D0500448776 /* IRCChannelModePrivate.h in Headers */,
4C31576120EB6D0500448776 /* TVCMainWindowTextViewAppearancePrivate.h in Headers */,
@ -4755,7 +4755,7 @@
4C06E5D720EC553A0055D09A /* IRCSendingMessage.m in Sources */,
4C06E5D820EC553A0055D09A /* IRCServer.m in Sources */,
4C06E5D920EC553A0055D09A /* IRCTimerCommand.m in Sources */,
4C8878052C33706E0016DB98 /* TPCResourceManagerMigrate.m in Sources */,
4C8878052C33706E0016DB98 /* TPCSandboxMigration.m in Sources */,
4C06E5DA20EC553A0055D09A /* IRCTreeItem.m in Sources */,
4C06E5DB20EC553A0055D09A /* IRCWorld.m in Sources */,
4C06E5DD20EC553A0055D09A /* IRCUserRelations.m in Sources */,
@ -4942,7 +4942,7 @@
4C06E69020EC55B90055D09A /* IRCSendingMessage.m in Sources */,
4C06E69120EC55B90055D09A /* IRCServer.m in Sources */,
4C06E69220EC55B90055D09A /* IRCTimerCommand.m in Sources */,
4C8878042C33706E0016DB98 /* TPCResourceManagerMigrate.m in Sources */,
4C8878042C33706E0016DB98 /* TPCSandboxMigration.m in Sources */,
4C06E69320EC55B90055D09A /* IRCTreeItem.m in Sources */,
4C06E69420EC55B90055D09A /* IRCWorld.m in Sources */,
4C06E69620EC55B90055D09A /* IRCUserRelations.m in Sources */,

View File

@ -6,7 +6,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>com.codeux.apps.textual.group</string>
<string>8482Q6EPL6.com.codeux.apps.textual</string>
</array>
</dict>
</plist>

View File

@ -6,7 +6,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>com.codeux.apps.textual.group</string>
<string>8482Q6EPL6.com.codeux.apps.textual</string>
</array>
<key>com.apple.security.network.client</key>
<true/>