From 929b494483bab5e6757d74fe8bf90be3ce0da1c1 Mon Sep 17 00:00:00 2001 From: Eitot Date: Tue, 7 Sep 2021 06:33:15 +0200 Subject: [PATCH] Enable sandboxing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: The system automatically migrates Vienna's library files to a sandbox container on launching Vienna with sandboxing enabled. This can be reversed by using the provided shell script, e.g. for development purposes or for downgrading to an earlier version of Vienna. The container-migration.plist file specifies the old and new locations for the migration. It should cover all of Vienna's directories and files, so that the user ideally ends up with a complete sandbox container. Some system-defined locations have to be changed to avoid duplication. For example, Apple moved the cookies storage from ~/Library/Cookies to ~/Library/HTTPStorages starting with macOS 11/Safari 14. Within sandbox containers however, ~/Library/Cookies is used. The automatic migration does not overwrite files. Therefore, a migration of ~/Library/HTTPStorages is attempted first. If that attempt is successful then the migration of ~/Library/Cookies should (silently) fail; otherwise ~/Library/Cookies is migrated instead. User preferences in ~/Library/Preferences are migrated automatically. User scripts are migrated from ~/Library/Scripts/Applications/Vienna to ~/Library/Application Scripts/ and a symlink is left at the former location; this also happens automatically. The shell script uses ditto to copy the directories. Ditto will merge directories rather than overwrite them, if the destination directory exists. It will, however, overwrite individual files. --- Vienna Tests/DirectoryMonitorTests.swift | 14 +- Vienna Tests/ExportTests.swift | 25 ++- .../NSFileManagerExtensionTests.swift | 9 +- Vienna.xcodeproj/project.pbxproj | 20 ++- ...t.entitlements => Deployment.entitlements} | 8 + ....entitlements => Development.entitlements} | 8 + Vienna/Info.plist | 2 +- Vienna/Resources/container-migration.plist | 39 +++++ .../SharedSupport/undo-container-migration.sh | 161 ++++++++++++++++++ .../Sources/Preferences window/Preferences.m | 4 +- Vienna/Sources/Shared/NSFileManager+Paths.h | 10 +- Vienna/Sources/Shared/NSFileManager+Paths.m | 18 +- 12 files changed, 263 insertions(+), 55 deletions(-) rename Vienna/{ViennaDeployment.entitlements => Deployment.entitlements} (74%) rename Vienna/{Vienna.entitlements => Development.entitlements} (75%) create mode 100644 Vienna/Resources/container-migration.plist create mode 100755 Vienna/SharedSupport/undo-container-migration.sh diff --git a/Vienna Tests/DirectoryMonitorTests.swift b/Vienna Tests/DirectoryMonitorTests.swift index 960c51112a..2be1a93223 100644 --- a/Vienna Tests/DirectoryMonitorTests.swift +++ b/Vienna Tests/DirectoryMonitorTests.swift @@ -44,31 +44,31 @@ class DirectoryMonitorTests: XCTestCase { var testExpectation: XCTestExpectation? - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() // Set the test expectation. testExpectation = expectation(description: "The monitor's event handler is called") // Create the temp directory. - XCTAssertNoThrow(try fileManager.createDirectory(at: tempDirectory, withIntermediateDirectories: false)) + tempDirectory = try fileManager.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: fileManager.applicationScriptsDirectory, create: true) // Create the monitor. monitor = DirectoryMonitor(directories: [tempDirectory]) } - override func tearDown() { + override func tearDownWithError() throws { // Deinitialize the monitor and reset the failsafe. monitor = nil hasHandlerBeenCalled = false // Delete the temp directory. - XCTAssertNoThrow(try fileManager.removeItem(at: tempDirectory)) + try fileManager.removeItem(at: tempDirectory) // Unset the test expectation. testExpectation = nil - super.tearDown() + try super.tearDownWithError() } // MARK: Test methods @@ -167,7 +167,7 @@ class DirectoryMonitorTests: XCTestCase { let fileManager = FileManager.default - let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString, isDirectory: true) + var tempDirectory: URL! var tempDirectoryItemCount: Int { return (try? fileManager.contentsOfDirectory(at: tempDirectory, includingPropertiesForKeys: nil))?.count ?? -1 diff --git a/Vienna Tests/ExportTests.swift b/Vienna Tests/ExportTests.swift index 319e7e7526..95ca64cab8 100644 --- a/Vienna Tests/ExportTests.swift +++ b/Vienna Tests/ExportTests.swift @@ -21,13 +21,18 @@ import XCTest class ExportTests: XCTestCase { + var tempURL: URL! + override func setUpWithError() throws { try super.setUpWithError() - // Put setup code here. This method is called before the invocation of each test method in the class. + + let downloadsDirectory = FileManager.default.downloadsDirectory + tempURL = try FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: downloadsDirectory, create: true) } override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. + try FileManager.default.removeItem(at: tempURL) + try super.tearDownWithError() } @@ -45,24 +50,16 @@ class ExportTests: XCTestCase { func testExportWithoutGroups() { // Test exporting feeds to opml file without groups let folders = self.foldersArray() - guard let tmpUrl = URL(string: "/tmp/vienna-test-nogroups.opml") else { - XCTAssertTrue(false) - fatalError("cannot happen") - } - - let countExported = Export.export(toFile: tmpUrl.absoluteString, from: folders, in: nil, withGroups: false) + let tmpUrl = tempURL.appendingPathComponent("vienna-test-nogroups.opml", isDirectory: false) + let countExported = Export.export(toFile: tmpUrl.path, from: folders, in: nil, withGroups: false) XCTAssertGreaterThan(countExported, 0, "Pass") } func testExportWithGroups() { // Test exporting feeds to opml file without groups let folders = self.foldersArray() - guard let tmpUrl = URL(string: "/tmp/vienna-test-groups.opml") else { - XCTAssertTrue(false) - fatalError("cannot happen") - } - - let countExported = Export.export(toFile: tmpUrl.absoluteString, from: folders, in: nil, withGroups: true) + let tmpUrl = tempURL.appendingPathComponent("vienna-test-groups.opml", isDirectory: false) + let countExported = Export.export(toFile: tmpUrl.path, from: folders, in: nil, withGroups: true) XCTAssertGreaterThan(countExported, 0, "Pass") } diff --git a/Vienna Tests/NSFileManagerExtensionTests.swift b/Vienna Tests/NSFileManagerExtensionTests.swift index 64b6bec4bd..e4e5d241b0 100644 --- a/Vienna Tests/NSFileManagerExtensionTests.swift +++ b/Vienna Tests/NSFileManagerExtensionTests.swift @@ -26,11 +26,14 @@ class NSFileManagerExtensionTests: XCTestCase { func testApplicationScriptsPath() throws { let result = FileManager.default.applicationScriptsDirectory - let fullPath = "\(homePath)/Library/Scripts/Applications/Vienna" + let userDirectory = try FileManager.default.url(for: .userDirectory, in: .localDomainMask, appropriateFor: nil, create: false) + .appendingPathComponent(NSUserName(), isDirectory: true) + let bundleID = try XCTUnwrap(bundleID) + let fullPath = "\(userDirectory.path)/Library/Application Scripts/\(bundleID)" XCTAssertEqual(result.path, fullPath) } - func testApplicationSupportPath() throws { + func testApplicationSupportPath() { let result = FileManager.default.applicationSupportDirectory let fullPath = "\(homePath)/Library/Application Support/Vienna" XCTAssertEqual(result.path, fullPath) @@ -43,7 +46,7 @@ class NSFileManagerExtensionTests: XCTestCase { XCTAssertEqual(result.path, fullPath) } - func testDownloadsPath() throws { + func testDownloadsPath() { let result = FileManager.default.downloadsDirectory let fullPath = "\(homePath)/Downloads" XCTAssertEqual(result.path, fullPath) diff --git a/Vienna.xcodeproj/project.pbxproj b/Vienna.xcodeproj/project.pbxproj index e2c739e06b..16266f8b0c 100644 --- a/Vienna.xcodeproj/project.pbxproj +++ b/Vienna.xcodeproj/project.pbxproj @@ -160,6 +160,7 @@ F6164C591E32A6660086261C /* DisclosureView.m in Sources */ = {isa = PBXBuildFile; fileRef = F6164C581E32A6660086261C /* DisclosureView.m */; }; F61CEA661F039E57009C878E /* ButtonToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61CEA651F039E56009C878E /* ButtonToolbarItem.swift */; }; F61CEA681F03F277009C878E /* PlugInToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61CEA671F03F277009C878E /* PlugInToolbarItem.swift */; }; + F6209C4423D3B87C00D5537D /* container-migration.plist in Resources */ = {isa = PBXBuildFile; fileRef = F6209C4323D3B87C00D5537D /* container-migration.plist */; }; F6209C5A23D3FFA700D5537D /* Vienna.sdef in Resources */ = {isa = PBXBuildFile; fileRef = F6209C5923D3FFA700D5537D /* Vienna.sdef */; }; F6257FF51E28485F0035E43C /* Downloads.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6257FF71E28485F0035E43C /* Downloads.xib */; }; F625801C1E2853B90035E43C /* ActivityViewer.xib in Resources */ = {isa = PBXBuildFile; fileRef = F625801E1E2853B90035E43C /* ActivityViewer.xib */; }; @@ -212,6 +213,7 @@ F6B355DB256025D3008CA1ED /* PreferenceTabViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6B355DA256025D3008CA1ED /* PreferenceTabViewItem.swift */; }; F6B3561525616313008CA1ED /* DownloadItem.m in Sources */ = {isa = PBXBuildFile; fileRef = F6B3561425616313008CA1ED /* DownloadItem.m */; }; F6B7BC3624A2A9A40051D76F /* FMDB in Frameworks */ = {isa = PBXBuildFile; productRef = F6B7BC3524A2A9A40051D76F /* FMDB */; }; + F6C9B03A26E6F3E0003C1058 /* undo-container-migration.sh in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = F668C10E26886CB6009AD505 /* undo-container-migration.sh */; }; F6C9DA70271BB3BB00FC3027 /* RSSFeed.m in Sources */ = {isa = PBXBuildFile; fileRef = F6C9DA6F271BB3BB00FC3027 /* RSSFeed.m */; }; F6C9DA73271BB55000FC3027 /* AtomFeed.m in Sources */ = {isa = PBXBuildFile; fileRef = F6C9DA72271BB55000FC3027 /* AtomFeed.m */; }; F6CE47131E7E3DCB0045EAD7 /* ActivityItem.m in Sources */ = {isa = PBXBuildFile; fileRef = F6CE47121E7E3DCB0045EAD7 /* ActivityItem.m */; }; @@ -261,6 +263,7 @@ files = ( B27CD00C1100E728001F3C83 /* Plugins in Copy Shared Support Files */, AA67F353089728AD008BBC37 /* Styles in Copy Shared Support Files */, + F6C9B03A26E6F3E0003C1058 /* undo-container-migration.sh in Copy Shared Support Files */, ); name = "Copy Shared Support Files"; runOnlyForDeploymentPostprocessing = 0; @@ -350,8 +353,8 @@ 3A7BD0DB1989AC7700E9444B /* VNAVerticallyCenteredTextFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VNAVerticallyCenteredTextFieldCell.h; sourceTree = ""; }; 3A7BD0DC1989AC7700E9444B /* VNAVerticallyCenteredTextFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VNAVerticallyCenteredTextFieldCell.m; sourceTree = ""; }; 3A8D9AE225A9DA4B0016F30F /* ArticleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleTests.swift; sourceTree = ""; }; - 3A932D8823BB999A009B8061 /* Vienna.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Vienna.entitlements; sourceTree = ""; }; - 3A932D8923BB999A009B8061 /* ViennaDeployment.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = ViennaDeployment.entitlements; sourceTree = ""; }; + 3A932D8823BB999A009B8061 /* Development.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Development.entitlements; sourceTree = ""; }; + 3A932D8923BB999A009B8061 /* Deployment.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Deployment.entitlements; sourceTree = ""; }; 3AC411A426BBFDFD004A8700 /* WebKitArticleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebKitArticleView.swift; sourceTree = ""; }; 3ADBA70C23DDAFCA00156722 /* Notarize.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = Notarize.sh; sourceTree = ""; }; 430C4ADB1661753F0079C9FC /* autorevision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autorevision.h; sourceTree = ""; }; @@ -511,6 +514,7 @@ F6167E6B209EFE370006E2C4 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pt; path = pt.lproj/Localizable.stringsdict; sourceTree = ""; }; F61CEA651F039E56009C878E /* ButtonToolbarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ButtonToolbarItem.swift; sourceTree = ""; }; F61CEA671F03F277009C878E /* PlugInToolbarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PlugInToolbarItem.swift; sourceTree = ""; }; + F6209C4323D3B87C00D5537D /* container-migration.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "container-migration.plist"; sourceTree = ""; }; F6209C5923D3FFA700D5537D /* Vienna.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; lineEnding = 0; path = Vienna.sdef; sourceTree = ""; usesTabs = 1; }; F62116A9209EFE9F00FAC2C2 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = ""; }; F6257FF61E28485F0035E43C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Downloads.xib; sourceTree = ""; }; @@ -685,6 +689,7 @@ F658FD8D209EFE5100896413 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = ""; }; F65F2313294E4B5200605F06 /* SeparatorPredicateEditorRowTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorPredicateEditorRowTemplate.swift; sourceTree = ""; }; F664A9A2209EFEBF003B0F7D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = ""; }; + F668C10E26886CB6009AD505 /* undo-container-migration.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = "undo-container-migration.sh"; sourceTree = ""; }; F66A7E542A019006002F5D5E /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Predicates.strings; sourceTree = ""; }; F672876624A2D20A0043432F /* UpdatePreferencesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdatePreferencesViewController.h; sourceTree = ""; }; F672876724A2D2170043432F /* UpdatePreferencesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdatePreferencesViewController.m; sourceTree = ""; }; @@ -1247,6 +1252,7 @@ F63DA1741F1CF04F007CBED4 /* Lists */, F63DA1721F1CEFC3007CBED4 /* Strings */, 03F33D951DF2A2F900B04FAF /* Assets.xcassets */, + F6209C4323D3B87C00D5537D /* container-migration.plist */, F6209C5923D3FFA700D5537D /* Vienna.sdef */, ); path = Resources; @@ -1278,6 +1284,7 @@ children = ( B27CCFFD1100E728001F3C83 /* Plugins */, AA67F32E089727FB008BBC37 /* Styles */, + F668C10E26886CB6009AD505 /* undo-container-migration.sh */, ); path = SharedSupport; sourceTree = ""; @@ -1408,8 +1415,8 @@ F63DA16E1F1CEC69007CBED4 /* Resources */, F63DA1751F1CF182007CBED4 /* SharedSupport */, 430C4AE0166175C20079C9FC /* Info.plist */, - 3A932D8823BB999A009B8061 /* Vienna.entitlements */, - 3A932D8923BB999A009B8061 /* ViennaDeployment.entitlements */, + 3A932D8823BB999A009B8061 /* Development.entitlements */, + 3A932D8923BB999A009B8061 /* Deployment.entitlements */, 3A6CC18623BBA49D0084ABEE /* Codesigning.xcconfig */, ); path = Vienna; @@ -1827,6 +1834,7 @@ F62580AA1E286A8C0035E43C /* Localizable.strings in Resources */, F6832F631F10C4C9007920D4 /* ExportAccessoryViewController.xib in Resources */, AACAEA3E0954E71100ACD502 /* DemoFeeds.plist in Resources */, + F6209C4423D3B87C00D5537D /* container-migration.plist in Resources */, F65D6D711E74619E00A30974 /* Credits.rtf in Resources */, F685D24C292460210097C0D3 /* Predicates.strings in Resources */, F69AE79A1F2215D600342640 /* InfoPlist.strings in Resources */, @@ -2941,7 +2949,7 @@ buildSettings = { APPLY_RULES_IN_COPY_FILES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_ENTITLEMENTS = Vienna/Vienna.entitlements; + CODE_SIGN_ENTITLEMENTS = Vienna/Development.entitlements; CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -2987,7 +2995,7 @@ buildSettings = { APPLY_RULES_IN_COPY_FILES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_ENTITLEMENTS = Vienna/ViennaDeployment.entitlements; + CODE_SIGN_ENTITLEMENTS = Vienna/Deployment.entitlements; COMBINE_HIDPI_IMAGES = YES; DEPLOYMENT_POSTPROCESSING = YES; ENABLE_HARDENED_RUNTIME = YES; diff --git a/Vienna/ViennaDeployment.entitlements b/Vienna/Deployment.entitlements similarity index 74% rename from Vienna/ViennaDeployment.entitlements rename to Vienna/Deployment.entitlements index 5d629032b2..8cbe34b981 100644 --- a/Vienna/ViennaDeployment.entitlements +++ b/Vienna/Deployment.entitlements @@ -2,10 +2,18 @@ + com.apple.security.app-sandbox + com.apple.security.automation.apple-events com.apple.security.files.bookmarks.app-scope + com.apple.security.files.downloads.read-write + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + com.apple.security.temporary-exception.mach-lookup.global-name $(PRODUCT_BUNDLE_IDENTIFIER)-spks diff --git a/Vienna/Vienna.entitlements b/Vienna/Development.entitlements similarity index 75% rename from Vienna/Vienna.entitlements rename to Vienna/Development.entitlements index e1cf5d5af4..0540f698bb 100644 --- a/Vienna/Vienna.entitlements +++ b/Vienna/Development.entitlements @@ -2,12 +2,20 @@ + com.apple.security.app-sandbox + com.apple.security.automation.apple-events com.apple.security.cs.disable-library-validation com.apple.security.files.bookmarks.app-scope + com.apple.security.files.downloads.read-write + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + com.apple.security.temporary-exception.mach-lookup.global-name $(PRODUCT_BUNDLE_IDENTIFIER)-spks diff --git a/Vienna/Info.plist b/Vienna/Info.plist index fa820940ef..eeda6a9e1c 100644 --- a/Vienna/Info.plist +++ b/Vienna/Info.plist @@ -141,7 +141,7 @@ SUEnableAutomaticChecks SUEnableInstallerLauncherService - + SUPublicEDKey nPwwT+poO5Kmi1NZkE5OveKB8lvQVY20N22E8jgxLCg= UTExportedTypeDeclarations diff --git a/Vienna/Resources/container-migration.plist b/Vienna/Resources/container-migration.plist new file mode 100644 index 0000000000..5cf0891e71 --- /dev/null +++ b/Vienna/Resources/container-migration.plist @@ -0,0 +1,39 @@ + + + + + MigrateScriptsForApplication + Vienna + Move + + + ${Caches}/${BundleId}/WebKit + ${Caches}/WebKit + + ${Caches}/${BundleId} + + ${ApplicationSupport}/Vienna/Sources + ${Caches}/${BundleId}/Sources + + ${ApplicationSupport}/Vienna + ${Library}/HTTPStorages/${BundleId} + + ${Library}/HTTPStorages/${BundleId}.binarycookies + ${Library}/Cookies/Cookies.binarycookies + + + ${Library}/Cookies/${BundleId}.binarycookies + ${Library}/Cookies/Cookies.binarycookies + + ${Library}/Saved Application State/${BundleId}.savedState + + ${Library}/WebKit/${BundleId} + ${Library}/WebKit + + + ${Library}/WebKit/Databases/___IndexedDB/${BundleId} + ${Library}/WebKit/Databases/___IndexedDB + + + + diff --git a/Vienna/SharedSupport/undo-container-migration.sh b/Vienna/SharedSupport/undo-container-migration.sh new file mode 100755 index 0000000000..575d9b670e --- /dev/null +++ b/Vienna/SharedSupport/undo-container-migration.sh @@ -0,0 +1,161 @@ +#!/bin/sh +# +# undo-container-migration.sh +# Vienna +# +# Copyright 2021-2022 Eitot +# +# 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 +# +# https://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. +# + +LIBRARY="$HOME/Library" +BUNDLE_ID='uk.co.opencommunity.vienna2' +CONTAINER="$LIBRARY/Containers/$BUNDLE_ID" +CONTAINER_LIBRARY="$CONTAINER/Data/Library" + +set -e + +if [ ! -d "$CONTAINER" ] +then + printf 'No sandbox container found for Vienna\n' >&2 + exit 1 +fi + +if [ -n "$(killall -s Vienna 2>/dev/null)" ] +then + printf 'Please quit Vienna and try again\n' >&2 + exit 1 +fi + +printf 'This script attempts to undo the container migration. Use this only to ' +printf 'revert to an older version of Vienna that does not support sandboxing. ' +printf 'Please make sure that Vienna is not opened during this process.\n' + +tput bold +printf 'Are you sure you want to do this? [y/N] ' +tput sgr0 +read -r +if ! printf '%s' "$REPLY" | grep -q -E '^[Yy]$' +then + exit 1 +fi + +# Configure ditto to use verbose mode and abort if encountering a fatal error. +alias ditto='ditto -v' +export DITTOABORT=1 + +# User scripts are located in ~/Library/Application Scripts/ instead +# of the sandbox container when sandboxing is enabled. Without sandboxing, they +# are located in ~/Library/Scripts/Applications/. A symlink was added +# at that location as a result of the automatic migration. +if [ -L "$LIBRARY/Scripts/Applications/Vienna" ] +then + unlink "$LIBRARY/Scripts/Applications/Vienna" \ + && printf 'Cleared %s\n' "$LIBRARY/Scripts/Applications/Vienna" +fi + +if [ -d "$LIBRARY/Application Scripts/$BUNDLE_ID" ] +then + ditto "$LIBRARY/Application Scripts/$BUNDLE_ID" \ + "$LIBRARY/Scripts/Applications/Vienna" + rm -r "$LIBRARY/Application Scripts/$BUNDLE_ID" \ + && printf 'Deleted %s\n' "$LIBRARY/Application Scripts/$BUNDLE_ID" +fi + +if [ -d "$CONTAINER_LIBRARY/WebKit/Databases/___IndexedDB" ] +then + ditto "$CONTAINER_LIBRARY/WebKit/Databases/___IndexedDB" \ + "$LIBRARY/WebKit/Databases/___IndexedDB/$BUNDLE_ID" + rm -r "$CONTAINER_LIBRARY/WebKit/Databases/___IndexedDB" \ + && printf 'Deleted %s\n' "$CONTAINER_LIBRARY/WebKit/Databases/___IndexedDB" +fi + +if [ -d "$CONTAINER_LIBRARY/WebKit" ] +then + ditto "$CONTAINER_LIBRARY/WebKit" "$LIBRARY/WebKit/$BUNDLE_ID" +fi + +if [ -d "$CONTAINER_LIBRARY/Saved Application State/$BUNDLE_ID.savedState" ] +then + ditto "$CONTAINER_LIBRARY/Saved Application State/$BUNDLE_ID.savedState" \ + "$LIBRARY/Saved Application State/$BUNDLE_ID.savedState" +fi + +# Preferences are not explicitely declared in the migration manifest file; the +# behaviour is implicit. +if [ -f "$CONTAINER_LIBRARY/Preferences/$BUNDLE_ID.plist" ] +then + ditto "$CONTAINER_LIBRARY/Preferences/$BUNDLE_ID.plist" \ + "$LIBRARY/Preferences/$BUNDLE_ID.plist" +fi + +# The location of cookies files of non-sandboxed applications changed from +# ~/Library/Cookies to ~/Library/HTTPStorages as of macOS 11 (and probably +# Safari 14). +MACOS_VERSION="$(sw_vers -productVersion)" +MACOS_MAJOR_VERSION="${MACOS_VERSION%%.*}" + +if [ "$MACOS_MAJOR_VERSION" -ge 11 ] || [ -d "$LIBRARY/HTTPStorages" ] +then + if [ -f "$CONTAINER_LIBRARY/Cookies/Cookies.binarycookies" ] + then + ditto "$CONTAINER_LIBRARY/Cookies/Cookies.binarycookies" \ + "$LIBRARY/HTTPStorages/$BUNDLE_ID.binarycookies" + fi +else + if [ -f "$CONTAINER_LIBRARY/Cookies/Cookies.binarycookies" ] + then + ditto "$CONTAINER_LIBRARY/Cookies/Cookies.binarycookies" \ + "$LIBRARY/Cookies/$BUNDLE_ID.binarycookies" + fi +fi + +if [ -d "$CONTAINER_LIBRARY/HTTPStorages/$BUNDLE_ID" ] +then + ditto "$CONTAINER_LIBRARY/HTTPStorages/$BUNDLE_ID" \ + "$LIBRARY/HTTPStorages/$BUNDLE_ID" +fi + +if [ -d "$CONTAINER_LIBRARY/Application Support/Vienna" ] +then + ditto "$CONTAINER_LIBRARY/Application Support/Vienna" \ + "$LIBRARY/Application Support/Vienna" +fi + +# The Sources subdirectory was moved from Library/Application Support to +# Library/Caches. +if [ -d "$CONTAINER_LIBRARY/Caches/$BUNDLE_ID/Sources" ] +then + ditto "$CONTAINER_LIBRARY/Caches/$BUNDLE_ID/Sources" \ + "$LIBRARY/Application Support/Vienna/Sources" + rm -r "$CONTAINER_LIBRARY/Caches/$BUNDLE_ID/Sources" \ + && printf 'Deleted %s\n' "$CONTAINER_LIBRARY/Caches/$BUNDLE_ID/Sources" +fi + +# The WebKit subdirectory in Library/Caches used to be located within the app's +# caches directory (Library/Caches//WebKit). +if [ -d "$CONTAINER_LIBRARY/Caches/WebKit" ] +then + ditto "$CONTAINER_LIBRARY/Caches/WebKit" \ + "$LIBRARY/Caches/$BUNDLE_ID/WebKit" + rm -r "$CONTAINER_LIBRARY/Caches/WebKit" \ + && printf 'Deleted %s\n' "$CONTAINER_LIBRARY/Caches/WebKit" +fi + +if [ -d "$CONTAINER_LIBRARY/Caches/$BUNDLE_ID" ] +then + ditto "$CONTAINER_LIBRARY/Caches/$BUNDLE_ID" "$LIBRARY/Caches/$BUNDLE_ID" +fi + +osascript -e "tell application \"Finder\" to delete POSIX file \"$CONTAINER\"" \ +1>/dev/null && printf 'Trashed %s\n' "$CONTAINER" diff --git a/Vienna/Sources/Preferences window/Preferences.m b/Vienna/Sources/Preferences window/Preferences.m index ee2338175c..a6d2fe4631 100644 --- a/Vienna/Sources/Preferences window/Preferences.m +++ b/Vienna/Sources/Preferences window/Preferences.m @@ -116,8 +116,8 @@ -(instancetype)init // Application-specific folder locations defaultDatabase = [userPrefs stringForKey:MAPref_DefaultDatabase]; NSFileManager *fileManager = NSFileManager.defaultManager; - NSString *appSupportPath = fileManager.vna_applicationSupportDirectory.path; - feedSourcesFolder = [appSupportPath stringByAppendingPathComponent:MA_FeedSourcesFolder_Name]; + NSString *cachesPath = fileManager.vna_cachesDirectory.path; + feedSourcesFolder = [cachesPath stringByAppendingPathComponent:MA_FeedSourcesFolder_Name]; // Load those settings that we cache. foldersTreeSortMethod = [self integerForKey:MAPref_AutoSortFoldersTree]; diff --git a/Vienna/Sources/Shared/NSFileManager+Paths.h b/Vienna/Sources/Shared/NSFileManager+Paths.h index cb22953d90..ec1e43939f 100644 --- a/Vienna/Sources/Shared/NSFileManager+Paths.h +++ b/Vienna/Sources/Shared/NSFileManager+Paths.h @@ -23,17 +23,17 @@ NS_ASSUME_NONNULL_BEGIN @interface NSFileManager (Paths) -/// The scripts directory for the current user (Library/Application Scripts or -/// Library/Scripts/Applications depending on whether sandboxing is enabled). +/// The scripts directory for Vienna for the current user +/// (Library/Application Scripts/). @property (readonly, nonatomic) NSURL *vna_applicationScriptsDirectory NS_SWIFT_NAME(applicationScriptsDirectory); -/// The application support directory for the current user (Library/Application -/// Support). +/// The application support directory for Vienna for the current user +/// (Library/Application Support/Vienna). @property (readonly, nonatomic) NSURL *vna_applicationSupportDirectory NS_SWIFT_NAME(applicationSupportDirectory); -/// The caches directory for the current user (Library/Caches). +/// The caches directory for the current user (Library/Caches/). @property (readonly, nonatomic) NSURL *vna_cachesDirectory NS_SWIFT_NAME(cachesDirectory); diff --git a/Vienna/Sources/Shared/NSFileManager+Paths.m b/Vienna/Sources/Shared/NSFileManager+Paths.m index fb8ba35864..c6605b501c 100644 --- a/Vienna/Sources/Shared/NSFileManager+Paths.m +++ b/Vienna/Sources/Shared/NSFileManager+Paths.m @@ -25,12 +25,6 @@ @implementation NSFileManager (Paths) -// The NSApplicationScriptsDirectory search path returns a subdirectory in the -// Library/Application Scripts directory. Vienna currently uses a subdirectory -// in Library/Scripts/Applications instead. The sandboxing migration performs -// an automatic migration to Library/Application Scripts/ -// and places a symlink at the old location. This code needs to be updated -// accordingly. - (NSURL *)vna_applicationScriptsDirectory { static NSURL *url = nil; if (url) { @@ -39,16 +33,11 @@ - (NSURL *)vna_applicationScriptsDirectory { NSFileManager *fileManager = NSFileManager.defaultManager; NSError *error = nil; - // Replace NSLibraryDirectory with NSApplicationScriptsDirectory for - // sandboxing. - url = [fileManager URLForDirectory:NSLibraryDirectory + url = [fileManager URLForDirectory:NSApplicationScriptsDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:&error]; - // Remove the following lines for sandboxing. - url = [url URLByAppendingPathComponent:@"Scripts/Applications/Vienna" - isDirectory:YES]; if (!url && error) { const char *function = __PRETTY_FUNCTION__; @@ -59,9 +48,6 @@ - (NSURL *)vna_applicationScriptsDirectory { return (id _Nonnull)url; } -// According to Apple's file-system programming guide, the bundle identifier -// should be used as the subdirectory name. However, Vienna presently uses the -// app name instead. This should be changed when Vienna migrates to sandboxing. - (NSURL *)vna_applicationSupportDirectory { static NSURL *url = nil; if (url) { @@ -75,8 +61,6 @@ - (NSURL *)vna_applicationSupportDirectory { appropriateForURL:nil create:NO error:&error]; - // For sandboxing, use NSBundle.mainBundle.bundleIdentifier instead of the - // application name (recommendation by Apple, but not required). url = [url URLByAppendingPathComponent:@"Vienna" isDirectory:YES];