From e36a3b318a4cdf4e8dde00d1449d404fb6e2101a Mon Sep 17 00:00:00 2001 From: meeh <meeh@mail.i2p> Date: Sun, 10 Mar 2019 11:16:56 +0000 Subject: [PATCH] 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. --- launchers/macosx/AppDelegate.h | 1 - launchers/macosx/Changes.md | 17 ++ .../I2PLauncher/Base.lproj/UserInterfaces.xib | 52 +++++- .../macosx/I2PLauncher/Preferences.storyboard | 17 +- .../I2PLauncher/SwiftMainDelegate.swift | 26 +++ .../I2PLauncher/Utils/Preferences.swift | 2 +- .../macosx/I2PLauncher/Utils/Startup.swift | 156 +++++++++++------- .../Utils/browser/FirefoxManager.swift | 29 +++- .../RouterProcessStatus+ObjectiveC.swift | 10 -- .../userinterface/RouterStatusView.swift | 3 - .../userinterface/StatusBarController.swift | 3 + .../PreferencesViewController+TableView.swift | 18 +- .../PreferencesViewController.swift | 40 ++++- launchers/macosx/SBridge.h | 4 - launchers/macosx/SBridge.mm | 148 ----------------- launchers/macosx/main.mm | 26 --- 16 files changed, 270 insertions(+), 282 deletions(-) create mode 100644 launchers/macosx/Changes.md diff --git a/launchers/macosx/AppDelegate.h b/launchers/macosx/AppDelegate.h index 171c6b9d31..4088f0bf58 100644 --- a/launchers/macosx/AppDelegate.h +++ b/launchers/macosx/AppDelegate.h @@ -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; diff --git a/launchers/macosx/Changes.md b/launchers/macosx/Changes.md new file mode 100644 index 0000000000..5e7c8f4bc7 --- /dev/null +++ b/launchers/macosx/Changes.md @@ -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. + diff --git a/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib b/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib index 2233374394..d5d200a06e 100644 --- a/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib +++ b/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib @@ -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> diff --git a/launchers/macosx/I2PLauncher/Preferences.storyboard b/launchers/macosx/I2PLauncher/Preferences.storyboard index 836b9144cb..49de4db2e2 100644 --- a/launchers/macosx/I2PLauncher/Preferences.storyboard +++ b/launchers/macosx/I2PLauncher/Preferences.storyboard @@ -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> diff --git a/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift b/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift index 26f18bc38f..7eee3d1da0 100644 --- a/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift +++ b/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift @@ -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) { diff --git a/launchers/macosx/I2PLauncher/Utils/Preferences.swift b/launchers/macosx/I2PLauncher/Utils/Preferences.swift index 2fd174d62c..1122e830cf 100644 --- a/launchers/macosx/I2PLauncher/Utils/Preferences.swift +++ b/launchers/macosx/I2PLauncher/Utils/Preferences.swift @@ -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) diff --git a/launchers/macosx/I2PLauncher/Utils/Startup.swift b/launchers/macosx/I2PLauncher/Utils/Startup.swift index 2a28bfedf2..b563537bb6 100644 --- a/launchers/macosx/I2PLauncher/Utils/Startup.swift +++ b/launchers/macosx/I2PLauncher/Utils/Startup.swift @@ -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 addLoginItem(_ path: CFURL) -> Bool { + + 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 toggleLaunchAtStartup() { - let itemReferences = itemReferencesInLoginItems() - let shouldBeToggled = (itemReferences.existingReference == nil) - let loginItemsRef = LSSharedFileListCreate( - nil, - kLSSharedFileListSessionLoginItems.takeRetainedValue(), - nil - ).takeRetainedValue() as LSSharedFileList? - - 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") - } + + func removeLoginItem(_ path: CFURL) -> Bool { + + // 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; } - 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 getLoginItem(_ path : CFURL) -> LSSharedFileListItem! { + + var path : CFURL = CFURLCreateWithString(nil, Bundle.main.bundleURL.absoluteString as CFString, nil); + + + // 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 loginItemsRef != nil { - let loginItems = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray - print("There are \(loginItems.count) login items") + + if(LSSharedFileListItemResolve(nextLoginItem, 0, &nextItemUrl, nil) == noErr) { + + - if(loginItems.count > 0) { - let lastItemRef = loginItems.lastObject as! LSSharedFileListItem - - for var currentItem in loginItems { - let currentItemRef = currentItem as! 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) - - } else { - let addatstart: LSSharedFileListItem = kLSSharedFileListItemBeforeFirst.takeRetainedValue() - return(nil,addatstart) + 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; + } + } diff --git a/launchers/macosx/I2PLauncher/Utils/browser/FirefoxManager.swift b/launchers/macosx/I2PLauncher/Utils/browser/FirefoxManager.swift index d30c35691a..12a842d2e0 100644 --- a/launchers/macosx/I2PLauncher/Utils/browser/FirefoxManager.swift +++ b/launchers/macosx/I2PLauncher/Utils/browser/FirefoxManager.swift @@ -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 diff --git a/launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus+ObjectiveC.swift b/launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus+ObjectiveC.swift index a4f2b4868f..8810883d5b 100644 --- a/launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus+ObjectiveC.swift +++ b/launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus+ObjectiveC.swift @@ -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() - } } diff --git a/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift b/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift index a26e4aef14..b8a53c54d2 100644 --- a/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift +++ b/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift @@ -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 { diff --git a/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift b/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift index 35529e001c..af15c41466 100644 --- a/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift +++ b/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift @@ -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?) { diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift index 1b009ddd63..2d6363c5fe 100644 --- a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift +++ b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift @@ -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") diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift index 799813486f..9670b97cc6 100644 --- a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift +++ b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift @@ -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 diff --git a/launchers/macosx/SBridge.h b/launchers/macosx/SBridge.h index 7e6abff08c..a0cd50b4a0 100644 --- a/launchers/macosx/SBridge.h +++ b/launchers/macosx/SBridge.h @@ -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; diff --git a/launchers/macosx/SBridge.mm b/launchers/macosx/SBridge.mm index a56e6e46a3..b573b01329 100644 --- a/launchers/macosx/SBridge.mm +++ b/launchers/macosx/SBridge.mm @@ -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 diff --git a/launchers/macosx/main.mm b/launchers/macosx/main.mm index 16ccaa78c3..5230e53c36 100644 --- a/launchers/macosx/main.mm +++ b/launchers/macosx/main.mm @@ -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]; -- GitLab