forked from I2P_Developers/i2p.i2p
Mac OSX Launcher:
* Fixed startup option so the launcher can start at OSX login/bootup. * Added I2P Browser to the list of "firefox" browsers to detect. * Changed hardcoded path lookup to native "registry" lookup for firefox application. * Made the advanced preferences table editable by the user. * Cleanup of old and/or unused code. * Bugfixes.
This commit is contained in:
@ -100,7 +100,6 @@ inline void sendUserNotification(NSString* title, NSString* informativeText, boo
|
||||
- (void) awakeFromNib;
|
||||
- (void) applicationDidFinishLaunching:(NSNotification *)aNotification;
|
||||
- (void) applicationWillTerminate:(NSNotification *)aNotification;
|
||||
- (void) setApplicationDefaultPreferences;
|
||||
- (AppDelegate *) initWithArgc:(int)argc argv:(const char **)argv;
|
||||
- (BOOL) userNotificationCenter:(NSUserNotificationCenter *)center
|
||||
shouldPresentNotification:(NSUserNotification *)notification;
|
||||
|
17
launchers/macosx/Changes.md
Normal file
17
launchers/macosx/Changes.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Change Log
|
||||
|
||||
## 0.9.38
|
||||
|
||||
* Initial alpha/beta ish.
|
||||
* Preferences dialog (unfinished).
|
||||
* Firefox detection.
|
||||
|
||||
## 0.9.39
|
||||
|
||||
* Fixed startup option so the launcher can start at OSX login/bootup.
|
||||
* Added I2P Browser to the list of "firefox" browsers to detect.
|
||||
* Changed hardcoded path lookup to native "registry" lookup for firefox application.
|
||||
* Made the advanced preferences table editable by the user.
|
||||
* Cleanup of old and/or unused code.
|
||||
* Bugfixes.
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14313.18"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
@ -37,5 +37,53 @@
|
||||
</items>
|
||||
<point key="canvasLocation" x="17" y="167"/>
|
||||
</menu>
|
||||
<menuItem title="Application" allowsKeyEquivalentWhenHidden="YES" id="84R-pF-Wt1">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Application" id="Trv-j7-WYu">
|
||||
<items>
|
||||
<menuItem title="About Application" id="XDp-94-iig">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="-1" id="gJe-90-jzd"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="xT4-H9-l6g"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="2Kn-gO-qBP">
|
||||
<connections>
|
||||
<action selector="handleNativePreferencesClicked:" target="-1" id="wIn-2L-u1k"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="45f-s8-MiT"/>
|
||||
<menuItem title="Services" id="x2v-WG-Nuy">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="DkL-DH-gJf"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="T1H-h7-Sat"/>
|
||||
<menuItem title="Hide Application" keyEquivalent="h" id="uAA-NV-src">
|
||||
<connections>
|
||||
<action selector="hide:" target="-1" id="wFN-Nz-FpI"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="ext-76-4lm">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="hideOtherApplications:" target="-1" id="KyT-0x-vod"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="HmI-6K-eUt">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unhideAllApplications:" target="-1" id="mXg-9c-azx"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="xOX-eV-fcA"/>
|
||||
<menuItem title="Quit Application" keyEquivalent="q" id="Fap-30-HH0">
|
||||
<connections>
|
||||
<action selector="terminate:" target="-1" id="IM7-XC-JlF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</objects>
|
||||
</document>
|
||||
|
@ -67,7 +67,7 @@
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aAH-4e-tuf">
|
||||
<rect key="frame" x="18" y="198" width="325" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Start the I2P Launcher at User login (Mac startup)" bezelStyle="regularSquare" imagePosition="left" enabled="NO" inset="2" id="EOY-1C-aqa">
|
||||
<buttonCell key="cell" type="check" title="Start the I2P Launcher at User login (Mac startup)" bezelStyle="regularSquare" imagePosition="left" inset="2" id="EOY-1C-aqa">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
@ -132,7 +132,7 @@
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XPH-xk-vMg">
|
||||
<rect key="frame" x="18" y="173" width="313" height="23"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Start Firefox with a I2P enabled profile at launch" bezelStyle="regularSquare" imagePosition="left" enabled="NO" state="on" inset="2" id="i7v-mQ-d1Y">
|
||||
<buttonCell key="cell" type="check" title="Start Firefox with a I2P enabled profile at launch" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="i7v-mQ-d1Y">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
@ -218,7 +218,7 @@
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SSS-Fz-fYY">
|
||||
<rect key="frame" x="18" y="345" width="257" height="28"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Yes, I want to edit advanced settings" bezelStyle="regularSquare" imagePosition="left" enabled="NO" inset="2" id="UzU-4G-MLw">
|
||||
<buttonCell key="cell" type="check" title="Yes, I want to edit advanced settings" bezelStyle="regularSquare" imagePosition="left" inset="2" id="UzU-4G-MLw">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
@ -243,14 +243,14 @@
|
||||
<rect key="frame" x="1" y="0.0" width="559" height="310"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView identifier="AdvancedView" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="M6Y-Yi-YWr" viewBased="YES" id="lzO-OC-oiQ" customClass="AdvancedTableView" customModule="I2PLauncher" customModuleProvider="target">
|
||||
<tableView identifier="AdvancedView" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="M6Y-Yi-YWr" viewBased="YES" id="lzO-OC-oiQ" customClass="AdvancedTableView" customModule="I2PLauncher" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="645" height="285"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="KeyColumnID" width="116" minWidth="40" maxWidth="1000" id="3Hj-6J-5ww">
|
||||
<tableColumn identifier="KeyColumnID" editable="NO" width="116" minWidth="40" maxWidth="1000" id="3Hj-6J-5ww">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Key">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -283,7 +283,7 @@
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="DefaultColumnID" width="120" minWidth="40" maxWidth="1000" id="xna-T0-L5h">
|
||||
<tableColumn identifier="DefaultColumnID" editable="NO" width="120" minWidth="40" maxWidth="1000" id="xna-T0-L5h">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Default Value">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -336,11 +336,14 @@
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2ro-Gm-4DU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Sj7-Se-KMC">
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" title="Table View Cell" usesSingleLineMode="YES" id="Sj7-Se-KMC">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<action selector="onEnterInTextField:" target="mVJ-sm-WjL" id="4y0-HJ-teb"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<connections>
|
||||
|
@ -32,8 +32,10 @@ class Logger {
|
||||
let statusBarController = StatusBarController()
|
||||
let sharedRouterMgmr = RouterManager.shared()
|
||||
|
||||
// Constructor, think of it like an early entrypoint.
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
if (!DetectJava.shared().isJavaFound()) {
|
||||
DetectJava.shared().findIt()
|
||||
if (!DetectJava.shared().isJavaFound()) {
|
||||
@ -55,6 +57,7 @@ class Logger {
|
||||
}
|
||||
} // End of init()
|
||||
|
||||
// A function which detects the current installed I2P router version
|
||||
@objc func findInstalledI2PVersion() {
|
||||
var i2pPath = Preferences.shared().i2pBaseDirectory
|
||||
let jExecPath:String = Preferences.shared().javaCommandPath
|
||||
@ -84,6 +87,8 @@ class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper functions for the optional dock icon
|
||||
func triggerDockIconShowHide(showIcon state: Bool) -> Bool {
|
||||
var result: Bool
|
||||
if state {
|
||||
@ -94,6 +99,7 @@ class Logger {
|
||||
return result
|
||||
}
|
||||
|
||||
// Helper functions for the optional dock icon
|
||||
func getDockIconStateIsShowing() -> Bool {
|
||||
if NSApp.activationPolicy() == NSApplicationActivationPolicy.regular {
|
||||
return true
|
||||
@ -102,6 +108,11 @@ class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This is the swift "entrypoint". In C it would been "main(argc,argv)"
|
||||
*
|
||||
*/
|
||||
@objc func applicationDidFinishLaunching() {
|
||||
switch Preferences.shared().showAsIconMode {
|
||||
case .bothIcon, .dockIcon:
|
||||
@ -121,6 +132,15 @@ class Logger {
|
||||
if isRunning {
|
||||
DistributedNotificationCenter.default().post(name: .killLauncher, object: Bundle.main.bundleIdentifier!)
|
||||
}
|
||||
|
||||
if (Preferences.shared().alsoStartFirefoxOnLaunch)
|
||||
{
|
||||
// TODO: For some reason it does not seem to obay the two minutes delay.
|
||||
// If set, execute i2p browser / firefox after two minutes.
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
||||
FirefoxManager.shared().executeFirefox()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func listenForEvent(eventName: String, callbackActionFn: @escaping ((Any?)->()) ) {
|
||||
@ -136,6 +156,12 @@ class Logger {
|
||||
NSWorkspace.shared().open(NSURL(string: url)! as URL)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This function will execute when the launcher shuts down for some reason.
|
||||
* Could be either OS or user triggered.
|
||||
*
|
||||
*/
|
||||
@objc func applicationWillTerminate() {
|
||||
// Shutdown stuff
|
||||
if (Preferences.shared().stopRouterOnLauncherShutdown) {
|
||||
|
@ -116,7 +116,7 @@ class Preferences : NSObject {
|
||||
defaults["I2Pref_letRouterLiveEvenLauncherDied"] = false
|
||||
defaults["I2Pref_allowAdvancedPreferences"] = false
|
||||
defaults["I2Pref_alsoStartFirefoxOnLaunch"] = true
|
||||
defaults["I2Pref_firefoxBundlePath"] = "/Applications/Firefox.app"
|
||||
defaults["I2Pref_useServiceManagementAsStartupTool"] = false
|
||||
defaults["I2Pref_firefoxProfilePath"] = NSString(format: "%@/Library/Application Support/i2p/profile", home)
|
||||
defaults["I2Pref_consolePortCheckNum"] = 7657
|
||||
defaults["I2Pref_i2pBaseDirectory"] = NSString(format: "%@/Library/I2P", home)
|
||||
|
@ -8,81 +8,111 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
class Startup {
|
||||
class Startup : NSObject {
|
||||
|
||||
let loginItemsList : LSSharedFileList = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue();
|
||||
|
||||
|
||||
/*
|
||||
func applicationIsInStartUpItems() -> Bool {
|
||||
return itemReferencesInLoginItems().existingReference != nil
|
||||
}
|
||||
|
||||
func toggleLaunchAtStartup() {
|
||||
let itemReferences = itemReferencesInLoginItems()
|
||||
let shouldBeToggled = (itemReferences.existingReference == nil)
|
||||
let loginItemsRef = LSSharedFileListCreate(
|
||||
nil,
|
||||
kLSSharedFileListSessionLoginItems.takeRetainedValue(),
|
||||
nil
|
||||
).takeRetainedValue() as LSSharedFileList?
|
||||
func addLoginItem(_ path: CFURL) -> Bool {
|
||||
|
||||
if loginItemsRef != nil {
|
||||
if shouldBeToggled {
|
||||
if let appUrl: CFURL = NSURL.fileURLWithPath(Bundle.mainBundle().bundlePath) {
|
||||
LSSharedFileListInsertItemURL(loginItemsRef, itemReferences.lastReference, nil, nil, appUrl, nil, nil)
|
||||
print("Application was added to login items")
|
||||
}
|
||||
} else {
|
||||
if let itemRef = itemReferences.existingReference {
|
||||
LSSharedFileListItemRemove(loginItemsRef,itemRef);
|
||||
print("Application was removed from login items")
|
||||
}
|
||||
}
|
||||
if(getLoginItem(path) != nil) {
|
||||
print("Login Item has already been added to the list.");
|
||||
return true;
|
||||
}
|
||||
|
||||
var path : CFURL = CFURLCreateWithString(nil, Bundle.main.bundleURL.absoluteString as CFString, nil);
|
||||
print("Path adding to Login Item list is: ", path);
|
||||
|
||||
// add new Login Item at the end of Login Items list
|
||||
if let loginItem = LSSharedFileListInsertItemURL(loginItemsList,
|
||||
getLastLoginItemInList(),
|
||||
nil, nil,
|
||||
path,
|
||||
nil, nil) {
|
||||
print("Added login item is: ", loginItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItem?, lastReference: LSSharedFileListItem?) {
|
||||
var itemUrl = UnsafeMutablePointer<Unmanaged<CFURL>?>.allocate(capacity: 1)
|
||||
|
||||
let appUrl = NSURL.fileURL(withPath: Bundle.main.bundlePath)
|
||||
if !appUrl.absoluteString.isEmpty {
|
||||
let loginItemsRef = LSSharedFileListCreate(
|
||||
nil,
|
||||
kLSSharedFileListSessionLoginItems.takeRetainedValue(),
|
||||
nil
|
||||
).takeRetainedValue() as LSSharedFileList?
|
||||
func removeLoginItem(_ path: CFURL) -> Bool {
|
||||
|
||||
if loginItemsRef != nil {
|
||||
let loginItems = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
|
||||
print("There are \(loginItems.count) login items")
|
||||
// remove Login Item from the Login Items list
|
||||
if let oldLoginItem = getLoginItem(path) {
|
||||
print("Old login item is: ", oldLoginItem);
|
||||
if(LSSharedFileListItemRemove(loginItemsList, oldLoginItem) == noErr) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
print("Login Item for given path not found in the list.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(loginItems.count > 0) {
|
||||
let lastItemRef = loginItems.lastObject as! LSSharedFileListItem
|
||||
|
||||
for var currentItem in loginItems {
|
||||
let currentItemRef = currentItem as! LSSharedFileListItem
|
||||
func getLoginItem(_ path : CFURL) -> LSSharedFileListItem! {
|
||||
|
||||
let urlRef: CFURL = LSSharedFileListItemCopyResolvedURL(currentItemRef, 0, nil) as! CFURL
|
||||
let url = urlRef.takeUnretainedValue()
|
||||
if !urlRef?.isEmpty {
|
||||
print("URL Ref: \(urlRef.lastPathComponent)")
|
||||
if urlRef.isEqual(appUrl) {
|
||||
return (currentItemRef, lastItemRef)
|
||||
}
|
||||
}
|
||||
else {
|
||||
print("Unknown login application")
|
||||
}
|
||||
}
|
||||
// The application was not found in the startup list
|
||||
return (nil, lastItemRef)
|
||||
var path : CFURL = CFURLCreateWithString(nil, Bundle.main.bundleURL.absoluteString as CFString, nil);
|
||||
|
||||
} else {
|
||||
let addatstart: LSSharedFileListItem = kLSSharedFileListItemBeforeFirst.takeRetainedValue()
|
||||
return(nil,addatstart)
|
||||
|
||||
// Copy all login items in the list
|
||||
let loginItems : NSArray = LSSharedFileListCopySnapshot(loginItemsList, nil).takeRetainedValue();
|
||||
|
||||
var foundLoginItem : LSSharedFileListItem?;
|
||||
var nextItemUrl : Unmanaged<CFURL>?;
|
||||
|
||||
// Iterate through login items to find one for given path
|
||||
print("App URL: ", path);
|
||||
for var i in (0..<loginItems.count) // CFArrayGetCount(loginItems)
|
||||
{
|
||||
|
||||
var nextLoginItem : LSSharedFileListItem = loginItems.object(at: i) as! LSSharedFileListItem; // CFArrayGetValueAtIndex(loginItems, i).;
|
||||
|
||||
|
||||
if(LSSharedFileListItemResolve(nextLoginItem, 0, &nextItemUrl, nil) == noErr) {
|
||||
|
||||
|
||||
|
||||
print("Next login item URL: ", nextItemUrl!.takeUnretainedValue());
|
||||
// compare searched item URL passed in argument with next item URL
|
||||
if(nextItemUrl!.takeRetainedValue() == path) {
|
||||
foundLoginItem = nextLoginItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (nil, nil)
|
||||
}*/
|
||||
return foundLoginItem;
|
||||
}
|
||||
|
||||
func getLastLoginItemInList() -> LSSharedFileListItem! {
|
||||
|
||||
// Copy all login items in the list
|
||||
let loginItems : NSArray = LSSharedFileListCopySnapshot(loginItemsList, nil).takeRetainedValue() as NSArray;
|
||||
if(loginItems.count > 0) {
|
||||
let lastLoginItem = loginItems.lastObject as! LSSharedFileListItem;
|
||||
|
||||
print("Last login item is: ", lastLoginItem);
|
||||
return lastLoginItem
|
||||
}
|
||||
|
||||
return kLSSharedFileListItemBeforeFirst.takeRetainedValue();
|
||||
}
|
||||
|
||||
func isLoginItemInList(_ path : CFURL) -> Bool {
|
||||
|
||||
if(getLoginItem(path) != nil) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static func appPath() -> CFURL {
|
||||
|
||||
return NSURL.fileURL(withPath: Bundle.main.bundlePath) as CFURL;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ class FirefoxManager {
|
||||
return self.isFirefoxProfileExtracted
|
||||
}
|
||||
|
||||
// Since we execute in the "unix/POSIX way", we need the full path of the binary.
|
||||
func bundleExecutableSuffixPath() -> String {
|
||||
return "/Contents/MacOS/firefox"
|
||||
}
|
||||
@ -42,15 +43,37 @@ class FirefoxManager {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* First, try find I2P Browser, if it fails, then try Firefox or Firefox Developer.
|
||||
*
|
||||
* Instead of using hardcoded paths, or file search we use OS X's internal "registry" API
|
||||
* and detects I2P Browser or Firefox by bundle name. (or id, but name is more readable)
|
||||
*
|
||||
*/
|
||||
func tryAutoDetect() -> Bool {
|
||||
let expectedPath = Preferences.shared()["I2Pref_firefoxBundlePath"] as! String
|
||||
var browserPath = NSWorkspace.shared().fullPath(forApplication: "I2P Browser")
|
||||
if (browserPath == nil)
|
||||
{
|
||||
browserPath = NSWorkspace.shared().fullPath(forApplication: "Firefox")
|
||||
if (browserPath == nil)
|
||||
{
|
||||
browserPath = NSWorkspace.shared().fullPath(forApplication: "Firefox Developer")
|
||||
}
|
||||
}
|
||||
|
||||
self.isFirefoxProfileExtracted = directoryExistsAtPath(Preferences.shared()["I2Pref_firefoxProfilePath"] as! String)
|
||||
|
||||
let result = directoryExistsAtPath(expectedPath)
|
||||
// If browserPath is still nil, then nothing above was found and we can return early.
|
||||
if (browserPath == nil)
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
||||
let result = directoryExistsAtPath(browserPath!)
|
||||
self.isFirefoxFound = result
|
||||
if (result) {
|
||||
self.firefoxAppPath = expectedPath
|
||||
self.firefoxAppPath = browserPath!
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -10,14 +10,4 @@ import Foundation
|
||||
|
||||
extension RouterProcessStatus {
|
||||
|
||||
static func createNewRouterProcess(i2pPath: String) {
|
||||
let timeWhenStarted = Date()
|
||||
RouterProcessStatus.routerStartedAt = timeWhenStarted
|
||||
SBridge.sharedInstance().startupI2PRouter(i2pPath)
|
||||
RouterManager.shared().updateState()
|
||||
}
|
||||
static func shutdownRouterChildProcess() {
|
||||
RouterManager.shared().getRouterTask()?.requestShutdown()
|
||||
RouterManager.shared().updateState()
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +39,6 @@ import Cocoa
|
||||
|
||||
@objc func actionBtnStartRouter(_ sender: Any?) {
|
||||
NSLog("Router start clicked")
|
||||
/*if (RouterManager.shared().getRouterTask() == nil) {
|
||||
SBridge.sharedInstance().startupI2PRouter(RouterProcessStatus.i2pDirectoryPath)
|
||||
}*/
|
||||
(sender as! NSButton).isTransparent = true
|
||||
let routerStatus = RouterRunner.launchAgent?.status()
|
||||
DispatchQueue(label: "background_start").async {
|
||||
|
@ -21,6 +21,9 @@ import Cocoa
|
||||
|
||||
@IBOutlet var routerStatusTabView: RouterStatusView?
|
||||
|
||||
@IBAction func handleNativePreferencesClicked(_ sender: Any) {
|
||||
StatusBarController.launchPreferences(sender)
|
||||
}
|
||||
//var updateObjectRef : SUUpdater?
|
||||
|
||||
@objc func handleOpenConsole(_ sender: Any?) {
|
||||
|
@ -25,13 +25,15 @@ extension PreferencesViewController: NSTableViewDelegate {
|
||||
}
|
||||
|
||||
func tableViewDoubleClick(_ sender:AnyObject) {
|
||||
|
||||
print("Double click")
|
||||
// 1
|
||||
/*guard tableView.selectedRow >= 0,
|
||||
let item = Preferences.shared()[tableView.selectedRow] else {
|
||||
return
|
||||
}
|
||||
|
||||
print(self.advPrefTableView.selectedRow)
|
||||
guard self.advPrefTableView.selectedRow >= 0,
|
||||
let item = Preferences.shared()[self.advPrefTableView.selectedRow] else {
|
||||
return
|
||||
}
|
||||
print(item.name)
|
||||
/*
|
||||
if item.isFolder {
|
||||
// 2
|
||||
self.representedObject = item.url as Any
|
||||
@ -45,7 +47,7 @@ extension PreferencesViewController: NSTableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
|
||||
// 1
|
||||
guard let sortDescriptor = tableView.sortDescriptors.first else {
|
||||
guard let sortDescriptor = self.advPrefTableView.sortDescriptors.first else {
|
||||
return
|
||||
}
|
||||
/*if let order = Directory.FileOrder(rawValue: sortDescriptor.key!) {
|
||||
@ -74,7 +76,7 @@ extension PreferencesViewController: NSTableViewDelegate {
|
||||
text = item.name!
|
||||
cellIdentifier = CellIdentifiers.NameCell
|
||||
} else if tableColumn == tableView.tableColumns[1] {
|
||||
text = "\(item.defaultValue!)"
|
||||
text = "\(item.defaultValue ?? "")"
|
||||
cellIdentifier = CellIdentifiers.DefaultCell
|
||||
} else if tableColumn == tableView.tableColumns[2] {
|
||||
let thing = (item.selectedValue ?? "none")
|
||||
|
@ -38,6 +38,15 @@ class PreferencesViewController: NSViewController {
|
||||
@IBOutlet var checkboxStopWithLauncher: NSButton?
|
||||
@IBOutlet var buttonResetRouterConfig: NSButton?
|
||||
|
||||
@IBAction func onEnterInTextField(_ sender: NSTextField) {
|
||||
let selectedRowNumber = advPrefTableView.selectedRow
|
||||
print("Trying to store preferences")
|
||||
let currentItem = Preferences.shared()[selectedRowNumber]
|
||||
currentItem?.selectedValue = sender.stringValue
|
||||
Preferences.shared()[selectedRowNumber] = currentItem
|
||||
UserDefaults.standard.set(sender.stringValue, forKey: (currentItem?.name)!)
|
||||
Preferences.shared().syncPref()
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@ -60,6 +69,8 @@ class PreferencesViewController: NSViewController {
|
||||
advPrefTableView.tableColumns[0].sortDescriptorPrototype = NSSortDescriptor(key: "name", ascending: true)
|
||||
advPrefTableView.tableColumns[1].sortDescriptorPrototype = NSSortDescriptor(key: "defaultValue", ascending: true)
|
||||
advPrefTableView.tableColumns[2].sortDescriptorPrototype = NSSortDescriptor(key: "selectedValue", ascending: true)
|
||||
|
||||
self.advPrefTableView.isEnabled = Preferences.shared().allowAdvancedPreferenceEdit
|
||||
}
|
||||
|
||||
// Update radio buttons to reflect runtime/stored preferences
|
||||
@ -124,17 +135,30 @@ class PreferencesViewController: NSViewController {
|
||||
|
||||
@IBAction func checkboxStartLauncherOnOSXStartupClicked(_ sender: NSButton) {
|
||||
let launcherAppId = "net.i2p.bootstrap.macosx.StartupItemApp"
|
||||
let startupMgr = Startup()
|
||||
switch sender.state {
|
||||
case NSOnState:
|
||||
print("on")
|
||||
Preferences.shared()["I2Pref_startLauncherAtLogin"] = true
|
||||
let success = SMLoginItemSetEnabled(launcherAppId as CFString, true)
|
||||
print("SMLoginItemSetEnabled returned \(success)....")
|
||||
if (Preferences.shared()["I2Pref_useServiceManagementAsStartupTool"] as! Bool)
|
||||
{
|
||||
let success = SMLoginItemSetEnabled(launcherAppId as CFString, true)
|
||||
print("SMLoginItemSetEnabled returned \(success)....")
|
||||
} else {
|
||||
startupMgr.addLoginItem(Startup.appPath())
|
||||
print("Shared file for auto-startup added. (viewable via OSX Preferences -> Users -> Login Items)")
|
||||
}
|
||||
case NSOffState:
|
||||
print("off")
|
||||
Preferences.shared()["I2Pref_startLauncherAtLogin"] = false
|
||||
let success = SMLoginItemSetEnabled(launcherAppId as CFString, false)
|
||||
print("SMLoginItemSetEnabled returned \(success)....")
|
||||
if (Preferences.shared()["I2Pref_useServiceManagementAsStartupTool"] as! Bool)
|
||||
{
|
||||
let success = SMLoginItemSetEnabled(launcherAppId as CFString, false)
|
||||
print("SMLoginItemSetEnabled returned \(success)....")
|
||||
} else {
|
||||
startupMgr.removeLoginItem(Startup.appPath())
|
||||
print("Shared file for auto-startup removed (if any). (viewable via OSX Preferences -> Users -> Login Items)")
|
||||
}
|
||||
case NSMixedState:
|
||||
print("mixed")
|
||||
default: break
|
||||
@ -143,9 +167,11 @@ class PreferencesViewController: NSViewController {
|
||||
@IBAction func checkboxStartFirefoxAlsoAtLaunchClicked(_ sender: NSButton) {
|
||||
switch sender.state {
|
||||
case NSOnState:
|
||||
print("on")
|
||||
print("launch firefox: on")
|
||||
Preferences.shared().alsoStartFirefoxOnLaunch = true
|
||||
case NSOffState:
|
||||
print("off")
|
||||
print("launch firefox: off")
|
||||
Preferences.shared().alsoStartFirefoxOnLaunch = false
|
||||
case NSMixedState:
|
||||
print("mixed")
|
||||
default: break
|
||||
@ -261,9 +287,11 @@ class PreferencesViewController: NSViewController {
|
||||
case NSOnState:
|
||||
print("on")
|
||||
Preferences.shared().allowAdvancedPreferenceEdit = true
|
||||
self.advPrefTableView.isEnabled = true
|
||||
case NSOffState:
|
||||
print("off")
|
||||
Preferences.shared().allowAdvancedPreferenceEdit = false
|
||||
self.advPrefTableView.isEnabled = false
|
||||
case NSMixedState:
|
||||
print("mixed")
|
||||
default: break
|
||||
|
@ -18,8 +18,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "include/fn.h"
|
||||
//std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir, RouterProcessStatus* routerStatus = nil);
|
||||
|
||||
|
||||
namespace osx {
|
||||
inline void openUrl(NSString* url)
|
||||
@ -58,8 +56,6 @@ inline std::string buildClassPathForObjC(std::string basePath)
|
||||
|
||||
@interface SBridge : NSObject
|
||||
@property (nonatomic, assign) I2PRouterTask* currentRouterInstance;
|
||||
- (NSString*) buildClassPath:(NSString*)i2pPath;
|
||||
- (void) startupI2PRouter:(NSString*)i2pRootPath;
|
||||
- (void) openUrl:(NSString*)url;
|
||||
+ (void) logProxy:(int)level formattedMsg:(NSString*)formattedMsg;
|
||||
+ (void) sendUserNotification:(NSString*)title formattedMsg:(NSString*)formattedMsg;
|
||||
|
@ -28,84 +28,6 @@
|
||||
#include "include/fn.h"
|
||||
|
||||
|
||||
|
||||
std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir, RouterProcessStatus* routerStatus) {
|
||||
@try {
|
||||
|
||||
/**
|
||||
*
|
||||
* The following code will do a test, where it lists all known processes in the OS (visibility depending on user rights)
|
||||
* and scan for any command/arguments matching the substring "i2p.jar" - and in which case it won't start I2P itself.
|
||||
*
|
||||
**/
|
||||
IIProcessInfo* processInfoObj = [[IIProcessInfo alloc] init];
|
||||
[processInfoObj obtainFreshProcessList];
|
||||
auto anyRouterLookingProcs = [processInfoObj findProcessWithStringInNameOrArguments:@"i2p.jar"];
|
||||
if (anyRouterLookingProcs) {
|
||||
/**
|
||||
* The router was found running
|
||||
*/
|
||||
auto errMessage = @"Seems i2p is already running - I've detected another process with i2p.jar in it's arguments.";
|
||||
MLog(4, @"%@", errMessage);
|
||||
sendUserNotification(APP_IDSTR, errMessage);
|
||||
[routerStatus triggerEventWithEn:@"router_already_running" details:@"won't start - another router is running"];
|
||||
return std::async(std::launch::async, []{
|
||||
return -1;
|
||||
});
|
||||
} else {
|
||||
/**
|
||||
* No router was detected running
|
||||
**/
|
||||
RTaskOptions* options = [RTaskOptions alloc];
|
||||
options.binPath = javaBin;
|
||||
options.arguments = arguments;
|
||||
options.i2pBaseDir = i2pBaseDir;
|
||||
auto instance = [[I2PRouterTask alloc] initWithOptions: options];
|
||||
|
||||
[[SBridge sharedInstance] setCurrentRouterInstance:instance];
|
||||
[instance execute];
|
||||
sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
|
||||
auto pid = [instance getPID];
|
||||
MLog(2, @"Got pid: %d", pid);
|
||||
if (routerStatus != nil) {
|
||||
// TODO: Merge events router_start and router_pid ?
|
||||
[routerStatus triggerEventWithEn:@"router_start" details:@"normal start"];
|
||||
[routerStatus triggerEventWithEn:@"router_pid" details:[NSString stringWithFormat:@"%d", pid]];
|
||||
}
|
||||
NSString *applicationSupportDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject];
|
||||
auto pidFile = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/i2p/router.pid", applicationSupportDirectory]];
|
||||
NSError *err;
|
||||
|
||||
if (![[NSString stringWithFormat:@"%d", pid] writeToURL:pidFile atomically:YES encoding:NSUTF8StringEncoding error:&err]) {
|
||||
MLog(4, @"Error; %@", err);
|
||||
} else {
|
||||
MLog(3, @"Wrote pid file to %@", pidFile);
|
||||
}
|
||||
|
||||
return std::async(std::launch::async, [&pid]{
|
||||
return pid;
|
||||
});
|
||||
}
|
||||
}
|
||||
@catch (NSException *e)
|
||||
{
|
||||
auto errStr = [NSString stringWithFormat:@"Expection occurred %@",[e reason]];
|
||||
MLog(4, @"%@", errStr);
|
||||
sendUserNotification(APP_IDSTR, errStr);
|
||||
[[SBridge sharedInstance] setCurrentRouterInstance:nil];
|
||||
|
||||
if (routerStatus != nil) {
|
||||
[routerStatus triggerEventWithEn:@"router_exception" details:errStr];
|
||||
}
|
||||
|
||||
return std::async(std::launch::async, [&]{
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@implementation SBridge
|
||||
|
||||
// this makes it a singleton
|
||||
@ -129,81 +51,11 @@ std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments,
|
||||
osx::openUrl(url);
|
||||
}
|
||||
|
||||
- (NSString*) buildClassPath:(NSString*)i2pPath
|
||||
{
|
||||
const char * basePath = [i2pPath UTF8String];
|
||||
auto jarList = buildClassPathForObjC(basePath);
|
||||
const char * classpath = jarList.c_str();
|
||||
MLog(0, @"Classpath from ObjC = %s", classpath);
|
||||
return [[NSString alloc] initWithUTF8String:classpath];
|
||||
}
|
||||
|
||||
+ (void) logProxy:(int)level formattedMsg:(NSString*)formattedMsg
|
||||
{
|
||||
MLog(level, formattedMsg);
|
||||
}
|
||||
|
||||
|
||||
- (void)startupI2PRouter:(NSString*)i2pRootPath
|
||||
{
|
||||
std::string basePath([i2pRootPath UTF8String]);
|
||||
|
||||
auto classPathStr = buildClassPathForObjC(basePath);
|
||||
|
||||
RouterProcessStatus* routerStatus = [[RouterProcessStatus alloc] init];
|
||||
|
||||
NSString *confDir = [NSString stringWithFormat:@"%@/Library/Application\\ Support/i2p", NSHomeDirectory()];
|
||||
|
||||
try {
|
||||
std::vector<NSString*> argList = {
|
||||
@"-v",
|
||||
@"1.7+",
|
||||
@"--exec",
|
||||
@"java",
|
||||
@"-Xmx512M",
|
||||
@"-Xms128m",
|
||||
@"-Djava.awt.headless=true",
|
||||
[NSString stringWithFormat:@"-Dwrapper.logfile=%@/router.log", [NSString stringWithUTF8String:getDefaultLogDir().c_str()]],
|
||||
@"-Dwrapper.logfile.loglevel=DEBUG",
|
||||
[NSString stringWithFormat:@"-Dwrapper.java.pidfile=%@/router.pid", confDir],
|
||||
@"-Dwrapper.console.loglevel=DEBUG"
|
||||
};
|
||||
|
||||
std::string baseDirArg("-Di2p.dir.base=");
|
||||
baseDirArg += basePath;
|
||||
std::string javaLibArg("-Djava.library.path=");
|
||||
javaLibArg += basePath;
|
||||
// TODO: pass this to JVM
|
||||
//auto java_opts = getenv("JAVA_OPTS");
|
||||
|
||||
std::string cpString = std::string("-cp");
|
||||
|
||||
argList.push_back([NSString stringWithUTF8String:baseDirArg.c_str()]);
|
||||
argList.push_back([NSString stringWithUTF8String:javaLibArg.c_str()]);
|
||||
argList.push_back([NSString stringWithUTF8String:cpString.c_str()]);
|
||||
argList.push_back([NSString stringWithUTF8String:classPathStr.c_str()]);
|
||||
argList.push_back(@"net.i2p.router.Router");
|
||||
auto javaBin = std::string("/usr/libexec/java_home");
|
||||
|
||||
|
||||
sendUserNotification(APP_IDSTR, @"I2P Router is starting up!");
|
||||
auto nsJavaBin = [NSString stringWithUTF8String:javaBin.c_str()];
|
||||
auto nsBasePath = i2pRootPath;
|
||||
NSArray* arrArguments = [NSArray arrayWithObjects:&argList[0] count:argList.size()];
|
||||
|
||||
MLog(0, @"Trying to run command: %@", nsJavaBin);
|
||||
MLog(0, @"With I2P Base dir: %@", i2pRootPath);
|
||||
MLog(0, @"And Arguments: %@", arrArguments);
|
||||
startupRouter(nsJavaBin, arrArguments, nsBasePath, routerStatus);
|
||||
} catch (std::exception &err) {
|
||||
auto errMsg = [NSString stringWithUTF8String:err.what()];
|
||||
MLog(4, @"Exception: %@", errMsg);
|
||||
sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg]);
|
||||
[routerStatus setRouterStatus: false];
|
||||
[routerStatus setRouterRanByUs: false];
|
||||
[routerStatus triggerEventWithEn:@"router_exception" details:[NSString stringWithFormat:@"Error: %@", errMsg]];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
@ -69,29 +69,6 @@ using namespace subprocess;
|
||||
[self.deployer extractI2PBaseDir:completion];
|
||||
}
|
||||
|
||||
- (void)setApplicationDefaultPreferences {
|
||||
[self.userPreferences registerDefaults:@{
|
||||
@"enableLogging": @YES,
|
||||
@"enableVerboseLogging": @YES,
|
||||
@"autoStartRouterAtBoot": @NO,
|
||||
@"startLauncherAtLogin": @NO,
|
||||
@"startRouterAtStartup": @YES,
|
||||
@"stopRouterAtShutdown": @YES,
|
||||
@"letRouterLiveEvenLauncherDied": @NO,
|
||||
@"consolePortCheckNum": @7657,
|
||||
@"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast<const char *>(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8)
|
||||
}];
|
||||
|
||||
auto dict = [self.userPreferences dictionaryRepresentation];
|
||||
[self.userPreferences setPersistentDomain:dict forName:NSAPPDOMAIN];
|
||||
|
||||
CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
|
||||
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
|
||||
|
||||
NSLog(@"Default preferences stored!");
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
// Init application here
|
||||
|
||||
@ -103,9 +80,6 @@ using namespace subprocess;
|
||||
|
||||
// Start with user preferences
|
||||
self.userPreferences = [NSUserDefaults standardUserDefaults];
|
||||
[self setApplicationDefaultPreferences];
|
||||
self.enableLogging = [self.userPreferences boolForKey:@"enableLogging"];
|
||||
self.enableVerboseLogging = [self.userPreferences boolForKey:@"enableVerboseLogging"];
|
||||
// In case we are unbundled, make us a proper UI application
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
Reference in New Issue
Block a user