Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/duplicati/duplicati.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKenneth Skovhede <kenneth@hexad.dk>2019-10-29 01:21:50 +0300
committerKenneth Skovhede <kenneth@hexad.dk>2019-10-29 01:21:50 +0300
commit22a4aec31220422afa6771d08930e9f0894858c6 (patch)
tree7c6a5a6ffee3f2d5d954181fed38784ec1f4c193 /Installer
parentda0b1eefa42cb527b9e7a7b26f78eb196f47c1a4 (diff)
Ported `run-with-mono.sh` bash script to Objective C version to work better with MacOS Catalina security features
Diffstat (limited to 'Installer')
-rw-r--r--Installer/OSX/Duplicati-commandline-launcher7
-rw-r--r--Installer/OSX/Duplicati-server-launcher7
-rw-r--r--Installer/OSX/Duplicati-trayicon-launcher7
-rwxr-xr-xInstaller/OSX/launchers/compile.sh10
-rw-r--r--Installer/OSX/launchers/duplicati-cli.m13
-rw-r--r--Installer/OSX/launchers/duplicati-server.m13
-rw-r--r--Installer/OSX/launchers/duplicati.m13
-rw-r--r--Installer/OSX/launchers/run-with-mono.h8
-rw-r--r--Installer/OSX/launchers/run-with-mono.m162
-rw-r--r--Installer/OSX/make-dmg.sh15
-rw-r--r--Installer/OSX/run-with-mono.sh106
11 files changed, 228 insertions, 133 deletions
diff --git a/Installer/OSX/Duplicati-commandline-launcher b/Installer/OSX/Duplicati-commandline-launcher
deleted file mode 100644
index 03146d9ff..000000000
--- a/Installer/OSX/Duplicati-commandline-launcher
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-
-export APP_NAME="Duplicati CommandLine"
-export ASSEMBLY="Duplicati.CommandLine.exe"
-
-bash "${SCRIPT_DIR}/run-with-mono.sh" "$@" \ No newline at end of file
diff --git a/Installer/OSX/Duplicati-server-launcher b/Installer/OSX/Duplicati-server-launcher
deleted file mode 100644
index bb2ccbca2..000000000
--- a/Installer/OSX/Duplicati-server-launcher
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-
-export APP_NAME="Duplicati Server"
-export ASSEMBLY="Duplicati.Server.exe"
-
-bash "${SCRIPT_DIR}/run-with-mono.sh" "$@"
diff --git a/Installer/OSX/Duplicati-trayicon-launcher b/Installer/OSX/Duplicati-trayicon-launcher
deleted file mode 100644
index d1385bacf..000000000
--- a/Installer/OSX/Duplicati-trayicon-launcher
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-
-export APP_NAME="Duplicati"
-export ASSEMBLY="Duplicati.GUI.TrayIcon.exe"
-
-bash "${SCRIPT_DIR}/run-with-mono.sh" "$@" \ No newline at end of file
diff --git a/Installer/OSX/launchers/compile.sh b/Installer/OSX/launchers/compile.sh
new file mode 100755
index 000000000..6a6e42dc8
--- /dev/null
+++ b/Installer/OSX/launchers/compile.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# -fobjc-arc: enables ARC
+# -fmodules: enables modules so you can import with `@import AppKit;`
+# -mmacosx-version-min=10.6: support older OS X versions, this might increase the binary size
+
+if [ ! -d "bin" ]; then mkdir bin; fi
+
+clang run-with-mono.m duplicati.m -fobjc-arc -fmodules -mmacosx-version-min=10.6 -o bin/duplicati
+clang run-with-mono.m duplicati-cli.m -fobjc-arc -fmodules -mmacosx-version-min=10.6 -o bin/duplicati-cli
+clang run-with-mono.m duplicati-server.m -fobjc-arc -fmodules -mmacosx-version-min=10.6 -o bin/duplicati-server \ No newline at end of file
diff --git a/Installer/OSX/launchers/duplicati-cli.m b/Installer/OSX/launchers/duplicati-cli.m
new file mode 100644
index 000000000..434bd0f9f
--- /dev/null
+++ b/Installer/OSX/launchers/duplicati-cli.m
@@ -0,0 +1,13 @@
+#import "run-with-mono.h"
+
+NSString * const ASSEMBLY = @"Duplicati.CommandLine.exe";
+NSString * const APP_NAME = @"Duplicati.CommandLine";
+int const MONO_VERSION_MAJOR = 4;
+int const MONO_VERSION_MINOR = 0;
+
+int main() {
+ @autoreleasepool {
+ return [RunWithMono runAssemblyWithMono:APP_NAME assembly:ASSEMBLY major:MONO_VERSION_MAJOR minor:MONO_VERSION_MINOR];
+ }
+}
+
diff --git a/Installer/OSX/launchers/duplicati-server.m b/Installer/OSX/launchers/duplicati-server.m
new file mode 100644
index 000000000..f4d81ef42
--- /dev/null
+++ b/Installer/OSX/launchers/duplicati-server.m
@@ -0,0 +1,13 @@
+#import "run-with-mono.h"
+
+NSString * const ASSEMBLY = @"Duplicati.Server.exe";
+NSString * const APP_NAME = @"Duplicati.Server";
+int const MONO_VERSION_MAJOR = 4;
+int const MONO_VERSION_MINOR = 0;
+
+int main() {
+ @autoreleasepool {
+ return [RunWithMono runAssemblyWithMono:APP_NAME assembly:ASSEMBLY major:MONO_VERSION_MAJOR minor:MONO_VERSION_MINOR];
+ }
+}
+
diff --git a/Installer/OSX/launchers/duplicati.m b/Installer/OSX/launchers/duplicati.m
new file mode 100644
index 000000000..407b923dd
--- /dev/null
+++ b/Installer/OSX/launchers/duplicati.m
@@ -0,0 +1,13 @@
+#import "run-with-mono.h"
+
+NSString * const ASSEMBLY = @"Duplicati.GUI.TrayIcon.exe";
+NSString * const APP_NAME = @"Duplicati";
+int const MONO_VERSION_MAJOR = 4;
+int const MONO_VERSION_MINOR = 0;
+
+int main() {
+ @autoreleasepool {
+ return [RunWithMono runAssemblyWithMono:APP_NAME assembly:ASSEMBLY major:MONO_VERSION_MAJOR minor:MONO_VERSION_MINOR];
+ }
+}
+
diff --git a/Installer/OSX/launchers/run-with-mono.h b/Installer/OSX/launchers/run-with-mono.h
new file mode 100644
index 000000000..7131bd6df
--- /dev/null
+++ b/Installer/OSX/launchers/run-with-mono.h
@@ -0,0 +1,8 @@
+@import Foundation;
+
+@interface RunWithMono : NSObject {
+}
+
++ (int) runAssemblyWithMono:(NSString *)appName assembly:(NSString *)assembly major:(int) major minor:(int) minor;
+
+@end \ No newline at end of file
diff --git a/Installer/OSX/launchers/run-with-mono.m b/Installer/OSX/launchers/run-with-mono.m
new file mode 100644
index 000000000..99e4647a1
--- /dev/null
+++ b/Installer/OSX/launchers/run-with-mono.m
@@ -0,0 +1,162 @@
+#import "run-with-mono.h"
+
+@import Foundation;
+@import AppKit;
+
+NSString * const VERSION_TITLE = @"Cannot launch %@";
+NSString * const VERSION_MSG = @"%@ requires the Mono Framework version %d.%d or later.";
+NSString * const DOWNLOAD_URL = @"http://www.mono-project.com/download/stable/#download-mac";
+
+// Helper method to see if the user has requested debug output
+bool D() {
+ NSString* v = [[[NSProcessInfo processInfo]environment]objectForKey:@"DEBUG"];
+ if (v == nil || v.length == 0 || [v isEqual:@"0"] || [v isEqual:@"false"] || [v isEqual:@"f"])
+ return false;
+ return true;
+}
+
+// Wrapper method to invoke commandline operations and return the string output
+NSString *runCommand(NSString *program, NSArray<NSString *> *arguments) {
+ NSPipe *pipe = [NSPipe pipe];
+ NSFileHandle *file = pipe.fileHandleForReading;
+
+ NSTask *task = [[NSTask alloc] init];
+ task.launchPath = program;
+ task.arguments = arguments;
+ task.standardOutput = pipe;
+
+ [task launch];
+
+ NSData *data = [file readDataToEndOfFile];
+ [file closeFile];
+ [task waitUntilExit];
+
+ NSString *cmdOutput = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
+ if (cmdOutput == nil || cmdOutput.length == 0)
+ return nil;
+
+ return [cmdOutput stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+}
+
+// Checks if the Mono version is greater than or equal to the desired version
+bool isValidMono(NSString *mono, int major, int minor) {
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+
+ if (mono == nil)
+ return false;
+
+ if (![fileManager fileExistsAtPath:mono] || ![fileManager isExecutableFileAtPath:mono])
+ return false;
+
+ NSString *versionInfo = runCommand(mono, @[@"--version"]);
+
+ NSRange rg = [versionInfo rangeOfString:@"Mono JIT compiler version \\d+\\.\\d+" options:NSRegularExpressionSearch];
+ if (rg.location != NSNotFound) {
+ versionInfo = [versionInfo substringWithRange:rg];
+ if (D()) NSLog(@"Matched version: %@", versionInfo);
+ rg = [versionInfo rangeOfString:@"\\d+\\.\\d+" options:NSRegularExpressionSearch];
+ if (rg.location != NSNotFound) {
+ versionInfo = [versionInfo substringWithRange:rg];
+ if (D()) NSLog(@"Matched version: %@", versionInfo);
+
+ NSArray<NSString *> *versionComponents = [versionInfo componentsSeparatedByString:@"."];
+ if ([versionComponents[0] intValue] < major)
+ return false;
+ if ([versionComponents[1] intValue] < minor)
+ return false;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Attempts to locate a mono with a valid version
+NSString *findMono(int major, int minor) {
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+
+ NSString *currentMono = runCommand(@"/usr/bin/which", @[@"mono"]);
+ if (D()) NSLog(@"which mono: %@", currentMono);
+
+ if (isValidMono(currentMono, major, minor)) {
+ if (D()) NSLog(@"Found mono with: %@", currentMono);
+ return currentMono;
+ }
+
+ NSArray *probepaths = @[@"/usr/local/bin/mono", @"/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono", @"/opt/local/bin/mono"];
+ for(NSString* probepath in probepaths) {
+ if (D()) NSLog(@"Trying mono with: %@", probepath);
+ if (isValidMono(probepath, major, minor)) {
+ if (D()) NSLog(@"Found mono with: %@", probepath);
+ return probepath;
+ }
+ }
+
+ if (D()) NSLog(@"Failed to find Mono, returning: %@", nil);
+ return nil;
+}
+
+// Shows the download dialog, prompting to download Mono
+void showDownloadMonoDialog(NSString *appName, int major, int minor) {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setInformativeText:[NSString stringWithFormat:VERSION_MSG, appName, major, minor]];
+ [alert setMessageText:[NSString stringWithFormat:VERSION_TITLE, appName]];
+ [alert addButtonWithTitle:@"Cancel"];
+ [alert addButtonWithTitle:@"Download"];
+ NSModalResponse btn = [alert runModal];
+ if (btn == NSAlertSecondButtonReturn) {
+ if (D()) NSLog(@"Clicked download");
+ runCommand(@"/usr/bin/open", @[DOWNLOAD_URL]);
+ //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:DOWNLOAD_URL] options:@{} completionHandler:nil];
+ }
+}
+
+// Top-level method, finds Mono with an appropriate version and launches the assembly
+int runAssemblyWithMono(NSString *appName, NSString *assembly, int major, int minor) {
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+
+ NSString *entryFolder = [[NSBundle mainBundle] resourcePath];
+ if (D()) NSLog(@"entryFolder: %@", entryFolder);
+
+ NSString *assemblyPath = [NSString pathWithComponents:@[entryFolder, assembly]];
+ if (D()) NSLog(@"assemblyPath: %@", assemblyPath);
+
+ if (![fileManager fileExistsAtPath:assemblyPath]) {
+ NSLog(@"Assembly file not found: %@", assemblyPath);
+ return 1;
+ }
+
+ NSString *currentMono = findMono(major, minor);
+ if (currentMono == nil) {
+ NSLog(@"No valid mono found!");
+ showDownloadMonoDialog(appName, major, minor);
+ return 1;
+ }
+
+ if (D()) NSLog(@"Running %@ %@", currentMono, assemblyPath);
+
+ // Copy commandline arguments
+ NSMutableArray* arguments = [[NSMutableArray alloc] init];
+ [arguments addObjectsFromArray:[[NSProcessInfo processInfo] arguments]];
+
+ // replace the executable-path with the assembly path
+ [arguments replaceObjectAtIndex:0 withObject:assemblyPath];
+
+ NSTask *task = [[NSTask alloc] init];
+ task.launchPath = currentMono;
+ task.arguments = arguments;
+
+ [task launch];
+ [task waitUntilExit];
+
+ return [task terminationStatus];
+}
+
+@implementation RunWithMono
++ (int) runAssemblyWithMono:(NSString *)appName assembly:(NSString *)assembly major:(int) major minor:(int) minor {
+ return runAssemblyWithMono(appName, assembly, major, minor);
+}
+@end
+
diff --git a/Installer/OSX/make-dmg.sh b/Installer/OSX/make-dmg.sh
index 68feae02a..1889e6985 100644
--- a/Installer/OSX/make-dmg.sh
+++ b/Installer/OSX/make-dmg.sh
@@ -99,14 +99,17 @@ done
# Install the LauncAgent if anyone needs it
cp -R "daemon" "Duplicati.app/Contents/Resources"
-# Install executables
-cp "run-with-mono.sh" "Duplicati.app/Contents/MacOS/"
-cp "Duplicati-trayicon-launcher" "Duplicati.app/Contents/MacOS/duplicati"
-cp "Duplicati-commandline-launcher" "Duplicati.app/Contents/MacOS/duplicati-cli"
-cp "Duplicati-server-launcher" "Duplicati.app/Contents/MacOS/duplicati-server"
+# Build launchers
+cd "launchers"
+bash "compile.sh"
+cd ..
+
+# Install launchers
+mv "launchers/bin/duplicati" "Duplicati.app/Contents/MacOS/duplicati"
+mv "launchers/bin/duplicati-cli" "Duplicati.app/Contents/MacOS/duplicati-cli"
+mv "launchers/bin/duplicati-server" "Duplicati.app/Contents/MacOS/duplicati-server"
cp "uninstall.sh" "Duplicati.app/Contents/MacOS/"
-chmod +x "Duplicati.app/Contents/MacOS/run-with-mono.sh"
chmod +x "Duplicati.app/Contents/MacOS/duplicati"
chmod +x "Duplicati.app/Contents/MacOS/duplicati-cli"
chmod +x "Duplicati.app/Contents/MacOS/duplicati-server"
diff --git a/Installer/OSX/run-with-mono.sh b/Installer/OSX/run-with-mono.sh
deleted file mode 100644
index a954b4123..000000000
--- a/Installer/OSX/run-with-mono.sh
+++ /dev/null
@@ -1,106 +0,0 @@
-#!/bin/bash
-#
-# This is a stub script that allows .apps to be relocatable on OSX but still
-# find the managed assembly.
-#
-# This is copied from the Mono macpack tool and modified to fit Duplicati
-#
-# The Mono Version Check is from here:
-# http://mjhutchinson.com/journal/2010/01/24/creating_mac_app_bundle_for_gtk_app
-#
-
-# Figure out the full path to this script
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-if [ "z${SCRIPT_DIR}" == "z" ]; then
- SCRIPT_DIR="$( dirname "$0" )"
-fi
-
-APP_PATH="$( dirname "${SCRIPT_DIR}" )"
-
-export GDIPLUS_NOX=1
-
-#mono version check
-
-REQUIRED_MAJOR=3
-REQUIRED_MINOR=0
-
-VERSION_TITLE="Cannot launch $APP_NAME"
-VERSION_MSG="$APP_NAME requires the Mono Framework version $REQUIRED_MAJOR.$REQUIRED_MINOR or later."
-DOWNLOAD_URL="http://www.mono-project.com/download/stable/#download-mac"
-
-# Try to find system default Mono if an override was not supplied
-if [ "z${MONO_BIN}" == "z" ]; then
- MONO_BIN=$(which mono)
-
- # If the result is broken, don't use it
- if [ ! -f "${MONO_BIN}" ]; then
- MONO_BIN=""
- fi
-
- # Check if there was no Mono found
- if [ "z${MONO_BIN}" == "z" ]; then
- # Check if there is a HomeBrew install of Mono
- if [ -f "/usr/local/bin/mono" ]; then
- MONO_BIN="/usr/local/bin/mono"
-
- # Check if there is a system version of Mono
- elif [ -f "/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono" ]; then
- MONO_BIN="/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono"
-
- # Check if there is a MacPorts version of Mono
- elif [ -f "/opt/local/bin/mono" ]; then
- MONO_BIN="/opt/local/bin/mono"
-
- # Set up some default that will likely fail
- else
- MONO_BIN="mono"
-
- fi
- fi
-fi
-
-MONO_VERSION="$(${MONO_BIN} --version | grep 'Mono JIT compiler version ' | cut -f5 -d\ )"
-MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
-MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
-if [ -z "$MONO_VERSION" ] \
- || [ $MONO_VERSION_MAJOR -lt $REQUIRED_MAJOR ] \
- || [ $MONO_VERSION_MAJOR -eq $REQUIRED_MAJOR -a $MONO_VERSION_MINOR -lt $REQUIRED_MINOR ]
-then
- osascript \
- -e "set question to display dialog \"$VERSION_MSG\" with title \"$VERSION_TITLE\" buttons {\"Cancel\", \"Download...\"} default button 2" \
- -e "if button returned of question is equal to \"Download...\" then open location \"$DOWNLOAD_URL\""
- echo "$VERSION_TITLE"
- echo "$VERSION_MSG"
- exit 1
-fi
-
-# Move into the folder where all the assemblies are located
-cd "${APP_PATH}/Resources"
-
-# Get the current running version
-OSX_VERSION=$(uname -r | cut -f1 -d.)
-
-if [ $OSX_VERSION -lt 9 ]; then # If OSX version is 10.4 or less, the exec command is missing the -a option
-
- # Attempt to create a folder inside the Resources folder
- if [ ! -d "./bin" ]; then mkdir bin ; fi
-
- # If we failed, the meduim is probably read-only, so we revert to exec
- if [ ! -d "./bin" ]
- then
- exec "${MONO_BIN}" "$ASSEMBLY" $@
- else
-
- # We can make the helper file, lets use that
- if [ -f "./bin/$APP_NAME" ]; then rm -f "./bin/$APP_NAME" ; fi
- ln -s "${MONO_BIN}" "./bin/$APP_NAME"
-
- # Start Duplicati using the renamed symlink to Mono
- "./bin/$APP_NAME" "$ASSEMBLY" $@
- fi
-else
- # On a modern OSX, so we avoid modifying the bundle contents
- exec -a "$APP_NAME" "${MONO_BIN}" "$ASSEMBLY" $@
-fi
-
-