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:
meeh
2019-03-10 11:16:56 +00:00
parent 5d389c8855
commit e36a3b318a
16 changed files with 270 additions and 282 deletions

View File

@ -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;

View 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.

View File

@ -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>

View File

@ -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>

View File

@ -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) {

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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

View File

@ -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()
}
}

View File

@ -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 {

View File

@ -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?) {

View File

@ -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")

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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];