Preparing for XCode project. Removing now obsolete files.
BIN
launchers/macosx/I2PLauncher/16x16-Itoopie Transparent.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
144
launchers/macosx/I2PLauncher/AppDelegate.h
Normal file
@ -0,0 +1,144 @@
|
||||
#ifndef __APPDELEGATE_H__
|
||||
#define __APPDELEGATE_H__
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include "RouterTask.h"
|
||||
#include "StatusItemButton.h"
|
||||
#include "JavaHelper.h"
|
||||
#include "neither/maybe.hpp"
|
||||
#include "optional.hpp"
|
||||
#include "subprocess.hpp"
|
||||
#include <glob.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#define DEF_I2P_VERSION "0.9.35"
|
||||
#define APPDOMAIN "net.i2p.launcher"
|
||||
#define NSAPPDOMAIN @APPDOMAIN
|
||||
#define CFAPPDOMAIN CFSTR(APPDOMAIN)
|
||||
#define APP_IDSTR @"I2P Launcher"
|
||||
|
||||
|
||||
using namespace neither;
|
||||
|
||||
@class ExtractMetaInfo;
|
||||
using maybeAnRouterRunner = std::experimental::optional<I2PRouterTask*>;
|
||||
|
||||
std::vector<std::string> buildClassPath(std::string basePath);
|
||||
|
||||
extern JvmListSharedPtr gRawJvmList;
|
||||
|
||||
// DO NOT ACCESS THIS GLOBAL VARIABLE DIRECTLY.
|
||||
static std::mutex globalRouterStatusMutex;
|
||||
static maybeAnRouterRunner globalRouterStatus = maybeAnRouterRunner{};
|
||||
static bool isRuterRunning = false;
|
||||
|
||||
maybeAnRouterRunner getGlobalRouterObject();
|
||||
void setGlobalRouterObject(I2PRouterTask* newRouter);
|
||||
bool getGlobalRouterIsRunning();
|
||||
void setGlobalRouterIsRunning(bool running);
|
||||
|
||||
@interface ExtractMetaInfo : NSObject
|
||||
@property (copy) NSString* i2pBase;
|
||||
@property (copy) NSString* javaBinary;
|
||||
@property (copy) NSString* zipFile;
|
||||
@property (copy) NSString* jarFile;
|
||||
@end
|
||||
|
||||
@class I2PStatusMenu;
|
||||
@interface I2PStatusMenu : NSMenu
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem;
|
||||
@end
|
||||
|
||||
inline void sendUserNotification(NSString* title, NSString* informativeText, NSImage* contentImage = NULL, bool makeSound = false) {
|
||||
NSUserNotification *userNotification = [[[NSUserNotification alloc] init] autorelease];
|
||||
|
||||
userNotification.title = title;
|
||||
userNotification.informativeText = informativeText;
|
||||
if (contentImage != NULL) userNotification.contentImage = contentImage;
|
||||
if (makeSound) userNotification.soundName = NSUserNotificationDefaultSoundName;
|
||||
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] scheduleNotification:userNotification];
|
||||
};
|
||||
|
||||
inline std::vector<std::string> globVector(const std::string& pattern){
|
||||
glob_t glob_result;
|
||||
glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
|
||||
std::vector<std::string> files;
|
||||
for(unsigned int i=0;i<glob_result.gl_pathc;++i){
|
||||
files.push_back(std::string(glob_result.gl_pathv[i]));
|
||||
}
|
||||
globfree(&glob_result);
|
||||
return files;
|
||||
}
|
||||
|
||||
inline std::string getDefaultBaseDir()
|
||||
{
|
||||
// Figure out base directory
|
||||
const char* pathFromHome = "/Users/%s/Library/I2P";
|
||||
auto username = getenv("USER");
|
||||
char buffer[strlen(pathFromHome)+strlen(username)];
|
||||
sprintf(buffer, pathFromHome, username);
|
||||
std::string i2pBaseDir(buffer);
|
||||
return i2pBaseDir;
|
||||
}
|
||||
|
||||
@interface MenuBarCtrl : NSObject <StatusItemButtonDelegate, NSMenuDelegate>
|
||||
@property BOOL enableLogging;
|
||||
@property BOOL enableVerboseLogging;
|
||||
@property (strong) I2PStatusMenu *menu;
|
||||
@property (strong) StatusItemButton* statusBarButton;
|
||||
@property (strong) NSUserDefaults *userPreferences;
|
||||
@property (strong, nonatomic) NSImage * image;
|
||||
@property (strong, nonatomic) NSStatusItem *statusItem;
|
||||
// Event handlers
|
||||
- (void) statusItemButtonLeftClick: (StatusItemButton *) button;
|
||||
- (void) statusItemButtonRightClick: (StatusItemButton *) button;
|
||||
- (void) statusBarImageBtnClicked;
|
||||
- (void) btnPressedAction:(id)sender;
|
||||
- (void) menuWillOpen:(I2PStatusMenu *)menu;
|
||||
|
||||
- (void) openRouterConsoleBtnHandler: (NSMenuItem *) menuItem;
|
||||
- (void) startJavaRouterBtnHandler: (NSMenuItem *) menuItem;
|
||||
- (void) restartJavaRouterBtnHandler: (NSMenuItem *) menuItem;
|
||||
- (void) stopJavaRouterBtnHandler: (NSMenuItem *) menuItem;
|
||||
- (void) quitWrapperBtnHandler: (NSMenuItem *) menuItem;
|
||||
// Methods
|
||||
- (MenuBarCtrl *) init;
|
||||
- (void) dealloc;
|
||||
- (I2PStatusMenu *) createStatusBarMenu;
|
||||
@end
|
||||
|
||||
@protocol MenuBarCtrlDelegate
|
||||
- (void) menuBarCtrlStatusChanged: (BOOL) active;
|
||||
@end
|
||||
|
||||
@interface AppDelegate : NSObject <NSUserNotificationCenterDelegate, NSApplicationDelegate> {
|
||||
@public
|
||||
//NSImageView *imageCell;
|
||||
}
|
||||
@property (strong) MenuBarCtrl *menuBarCtrl;
|
||||
@property (strong) NSUserDefaults *userPreferences;
|
||||
@property BOOL enableLogging;
|
||||
@property BOOL enableVerboseLogging;
|
||||
@property ExtractMetaInfo *metaInfo;
|
||||
@property (copy) NSImage *contentImage NS_AVAILABLE(10_9, NA);
|
||||
- (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion;
|
||||
- (void)startupI2PRouter;
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification;
|
||||
- (void)setApplicationDefaultPreferences;
|
||||
- (void)userChooseJavaHome;
|
||||
- (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv;
|
||||
- (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList;
|
||||
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
|
||||
shouldPresentNotification:(NSUserNotification *)notification;
|
||||
@end
|
||||
|
||||
|
||||
#endif
|
49
launchers/macosx/I2PLauncher/Info.plist
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>I2PLauncher</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Public Domain</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>0.9.35-experimental</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>images/AppIcon.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>net.i2p.launcher</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>I2P</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>I2P</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.0.1</string>
|
||||
<key>NSUserNotificationAlertStyle</key>
|
||||
<string>alert</string>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<true/>
|
||||
<key>CGDisableCoalescedUpdates</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.5</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>I2P</string>
|
||||
<key>LSMinimumSystemVersionByArchitecture</key>
|
||||
<dict>
|
||||
<key>i386</key>
|
||||
<string>10.5.0</string>
|
||||
<key>x86_64</key>
|
||||
<string>10.6.0</string>
|
||||
</dict>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
BIN
launchers/macosx/I2PLauncher/ItoopieTransparent.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
177
launchers/macosx/I2PLauncher/JavaHelper.h
Normal file
@ -0,0 +1,177 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreFoundation/CFStream.h>
|
||||
#include <CoreFoundation/CFPropertyList.h>
|
||||
#include <CoreFoundation/CFDictionary.h>
|
||||
#include <CoreFoundation/CFArray.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
|
||||
#include "optional.hpp"
|
||||
#include "strutil.hpp"
|
||||
#include "subprocess.hpp"
|
||||
#include "neither/maybe.hpp"
|
||||
#include "RouterTask.h"
|
||||
|
||||
using namespace subprocess;
|
||||
using namespace neither;
|
||||
|
||||
#define DEF_MIN_JVM_VER "1.7+"
|
||||
|
||||
class JvmVersion
|
||||
{
|
||||
public:
|
||||
std::string JVMName;
|
||||
std::string JVMHomePath;
|
||||
std::string JVMPlatformVersion;
|
||||
|
||||
inline const char * ToCString()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "JvmVersion(name=" << JVMName.c_str() << ",version=";
|
||||
ss << JVMPlatformVersion.c_str() << ",home=" << JVMHomePath.c_str() << ")";
|
||||
std::string s = ss.str();
|
||||
return s.c_str();
|
||||
}
|
||||
|
||||
inline bool HasContent()
|
||||
{
|
||||
return (
|
||||
std::strlen(JVMName.c_str()) > 0 &&
|
||||
std::strlen(JVMHomePath.c_str()) > 0 &&
|
||||
std::strlen(JVMPlatformVersion.c_str()) > 0
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<JvmVersion> JvmVersionPtr;
|
||||
typedef std::shared_ptr<std::list<JvmVersionPtr> > JvmListPtr;
|
||||
typedef std::shared_ptr<std::list<std::shared_ptr<JvmVersion> > > JvmListSharedPtr;
|
||||
typedef void(^MenuBarControllerActionBlock)(BOOL active);
|
||||
|
||||
extern JvmListSharedPtr gRawJvmList;
|
||||
|
||||
class JvmHomeContext : public std::enable_shared_from_this<JvmHomeContext>
|
||||
{
|
||||
public:
|
||||
|
||||
inline void setJvm(JvmVersionPtr* current)
|
||||
{
|
||||
mCurrent = *current;
|
||||
}
|
||||
|
||||
inline JvmListPtr getJvmList()
|
||||
{
|
||||
return gRawJvmList;
|
||||
}
|
||||
|
||||
inline std::shared_ptr<JvmHomeContext> getContext()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
inline std::string getJavaHome()
|
||||
{
|
||||
if (mCurrent)
|
||||
{
|
||||
return mCurrent->JVMHomePath;
|
||||
}
|
||||
return gRawJvmList->back()->JVMHomePath;
|
||||
}
|
||||
private:
|
||||
JvmVersionPtr mCurrent;
|
||||
};
|
||||
|
||||
static void processJvmEntry (const void* key, const void* value, void* context) {
|
||||
//CFShow(key);
|
||||
//CFShow(value);
|
||||
|
||||
// The reason for using strprintf is to "force" a copy of the values,
|
||||
// since local variables gets deleted once this function returns.
|
||||
auto currentJvm = reinterpret_cast<JvmVersion*>(context);
|
||||
if (CFEqual((CFStringRef)key,CFSTR("JVMName"))) {
|
||||
auto strVal = extractString((CFStringRef)value);
|
||||
currentJvm->JVMName = strprintf("%s",strVal.c_str());
|
||||
}
|
||||
if (CFEqual((CFStringRef)key,CFSTR("JVMHomePath"))) {
|
||||
auto strVal = extractString((CFStringRef)value);
|
||||
currentJvm->JVMHomePath = strprintf("%s",strVal.c_str());
|
||||
}
|
||||
if (CFEqual((CFStringRef)key,CFSTR("JVMPlatformVersion"))) {
|
||||
auto strVal = extractString((CFStringRef)value);
|
||||
currentJvm->JVMPlatformVersion = strprintf("%s",strVal.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void processJvmPlistEntries (const void* item, void* context) {
|
||||
CFDictionaryRef dict = CFDictionaryCreateCopy(kCFAllocatorDefault, (CFDictionaryRef)item);
|
||||
|
||||
JvmVersionPtr currentJvmPtr = std::shared_ptr<JvmVersion>(new JvmVersion());
|
||||
struct CFunctional
|
||||
{
|
||||
static void applier(const void* key, const void* value, void* context){
|
||||
// The reason for using strprintf is to "force" a copy of the values,
|
||||
// since local variables gets deleted once this function returns.
|
||||
auto currentJvm = static_cast<JvmVersion*>(context);
|
||||
if (CFEqual((CFStringRef)key,CFSTR("JVMName"))) {
|
||||
auto d = extractString((CFStringRef)value);
|
||||
currentJvm->JVMName = trim_copy(d);
|
||||
}
|
||||
if (CFEqual((CFStringRef)key,CFSTR("JVMHomePath"))) {
|
||||
auto d = extractString((CFStringRef)value);
|
||||
currentJvm->JVMHomePath = trim_copy(d);
|
||||
}
|
||||
if (CFEqual((CFStringRef)key,CFSTR("JVMPlatformVersion"))) {
|
||||
auto d = extractString((CFStringRef)value);
|
||||
currentJvm->JVMPlatformVersion = trim_copy(d);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
CFDictionaryApplyFunction(dict, CFunctional::applier, (void*)currentJvmPtr.get());
|
||||
|
||||
if (currentJvmPtr->HasContent())
|
||||
{
|
||||
printf("Found JVM: %s\n\n", currentJvmPtr->ToCString());
|
||||
gRawJvmList->push_back(currentJvmPtr);
|
||||
}
|
||||
}
|
||||
|
||||
static void listAllJavaInstallsAvailable()
|
||||
{
|
||||
auto javaHomeRes = check_output({"/usr/libexec/java_home","-v",DEF_MIN_JVM_VER,"-X"});
|
||||
CFDataRef javaHomes = CFDataCreate(NULL, (const UInt8 *)javaHomeRes.buf.data(), strlen(javaHomeRes.buf.data()));
|
||||
|
||||
//CFErrorRef err;
|
||||
CFPropertyListRef propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, javaHomes, kCFPropertyListImmutable, NULL, NULL);
|
||||
/*if (err)
|
||||
{
|
||||
NSError *error = (__bridge NSError *)err;
|
||||
NSLog(@"Failed to read property list: %@", error);
|
||||
[NSApp presentError: error];
|
||||
return nullptr;
|
||||
}*/
|
||||
|
||||
|
||||
//auto typeId = CFCopyTypeIDDescription(CFGetTypeID(propertyList));
|
||||
//auto test = CFCopyDescription(propertyList);
|
||||
//std::cout << "test: " << [test UTF8String] << " Type: " << [typeId UTF8String] << " num: " << jCount << std::endl;
|
||||
|
||||
// Count number of entries in the property array list.
|
||||
// This is used to set max CRange for CFArrayApplyFunction.
|
||||
auto jCount = CFArrayGetCount((CFArrayRef)propertyList);
|
||||
|
||||
CFArrayApplyFunction((CFArrayRef)propertyList, CFRangeMake(0, jCount), processJvmPlistEntries, NULL);
|
||||
//CFShow(propertyList);
|
||||
}
|
42
launchers/macosx/I2PLauncher/PidWatcher.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/event.h>
|
||||
|
||||
#include "neither/either.hpp"
|
||||
#include "AppDelegate.h"
|
||||
|
||||
using callbackType = void (CFFileDescriptorRef, CFOptionFlags, void *);
|
||||
using HandleFunction = std::function<void(int)>;
|
||||
|
||||
static void noteProcDeath(CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void *info) {
|
||||
struct kevent kev;
|
||||
int fd = CFFileDescriptorGetNativeDescriptor(fdref);
|
||||
kevent(fd, NULL, 0, &kev, 1, NULL);
|
||||
// take action on death of process here
|
||||
NSLog(@"process with pid '%u' died\n", (unsigned int)kev.ident);
|
||||
sendUserNotification(APP_IDSTR, @"The I2P router has stopped.");
|
||||
CFFileDescriptorInvalidate(fdref);
|
||||
CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example
|
||||
}
|
||||
// one argument, an integer pid to watch, required
|
||||
int watchPid(int pid, callbackType callback = noteProcDeath) {
|
||||
int fd = kqueue();
|
||||
struct kevent kev;
|
||||
EV_SET(&kev, pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
|
||||
kevent(fd, &kev, 1, NULL, 0, NULL);
|
||||
CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, callback, NULL);
|
||||
CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
|
||||
CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
|
||||
CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
|
||||
CFRelease(source);
|
||||
/*
|
||||
seconds
|
||||
The length of time to run the run loop. If 0, only one pass is made through the run loop before returning;
|
||||
if multiple sources or timers are ready to fire immediately, only one (possibly two if one is a version
|
||||
0 source) will be fired, regardless of the value of returnAfterSourceHandled.
|
||||
*/
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
|
||||
return 0;
|
||||
}
|
11
launchers/macosx/I2PLauncher/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# The Objective-C++ part of the Mac OS X launcher
|
||||
|
||||
## Why?
|
||||
|
||||
Code signing, OS X integration.
|
||||
|
||||
## Howto build?
|
||||
|
||||
Preferred tool is [ninja](https://ninja-build.org/). A makefile is also available.
|
||||
|
||||
Build with: `ninja`
|
81
launchers/macosx/I2PLauncher/RouterTask.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#include "optional.hpp"
|
||||
#include "subprocess.hpp"
|
||||
|
||||
|
||||
@class RTaskOptions;
|
||||
@interface RTaskOptions : NSObject
|
||||
@property (strong) NSString* binPath;
|
||||
@property (strong) NSArray<NSString *>* arguments;
|
||||
@property (strong) NSString* i2pBaseDir;
|
||||
@end
|
||||
|
||||
@class I2PRouterTask;
|
||||
@interface I2PRouterTask : NSObject
|
||||
@property (strong) NSTask* routerTask;
|
||||
@property (strong) NSUserDefaults *userPreferences;
|
||||
@property (strong) NSFileHandle *readLogHandle;
|
||||
@property (strong) NSMutableData *totalLogData;
|
||||
@property (strong) NSPipe *processPipe;
|
||||
@property (strong) NSFileHandle *input;
|
||||
@property (atomic) BOOL isRouterRunning;
|
||||
@property (atomic) BOOL userRequestedRestart;
|
||||
- (instancetype) initWithOptions : (RTaskOptions*) options;
|
||||
- (int) execute;
|
||||
- (void) requestShutdown;
|
||||
- (void) requestRestart;
|
||||
- (BOOL) isRunning;
|
||||
- (int) getPID;
|
||||
- (void)routerStdoutData:(NSNotification *)notification;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
using namespace subprocess;
|
||||
|
||||
class JavaRunner;
|
||||
|
||||
typedef std::function<void(void)> fp_t;
|
||||
typedef std::function<void(JavaRunner *ptr)> fp_proc_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* class JavaRunner
|
||||
*
|
||||
**/
|
||||
class JavaRunner
|
||||
{
|
||||
public:
|
||||
// copy fn
|
||||
JavaRunner(std::string& javaBin, std::string& arguments, std::string& i2pBaseDir, const fp_proc_t& executingFn, const fp_t& cb);
|
||||
~JavaRunner() = default;
|
||||
|
||||
static const std::vector<NSString*> defaultStartupFlags;
|
||||
static const std::vector<std::string> defaultFlagsForExtractorJob;
|
||||
|
||||
void requestRouterShutdown();
|
||||
|
||||
std::experimental::optional<std::future<int> > execute();
|
||||
std::shared_ptr<Popen> javaProcess;
|
||||
std::string javaBinaryPath;
|
||||
std::string javaRouterArgs;
|
||||
std::string execLine;
|
||||
std::string _i2pBaseDir;
|
||||
private:
|
||||
const fp_proc_t& executingFn;
|
||||
const fp_t& exitCallbackFn;
|
||||
};
|
||||
|
||||
|
165
launchers/macosx/I2PLauncher/RouterTask.mm
Normal file
@ -0,0 +1,165 @@
|
||||
#include "RouterTask.h"
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <future>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "optional.hpp"
|
||||
#include "subprocess.hpp"
|
||||
#include "PidWatcher.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@implementation RTaskOptions
|
||||
@end
|
||||
|
||||
@implementation I2PRouterTask
|
||||
|
||||
|
||||
- (void)routerStdoutData:(NSNotification *)notification
|
||||
{
|
||||
NSLog(@"%@", [[NSString alloc] initWithData:[notification.object availableData] encoding:NSUTF8StringEncoding]);
|
||||
[notification.object waitForDataInBackgroundAndNotify];
|
||||
}
|
||||
|
||||
- (instancetype) initWithOptions : (RTaskOptions*) options
|
||||
{
|
||||
self.userRequestedRestart = NO;
|
||||
self.isRouterRunning = NO;
|
||||
self.input = [NSFileHandle fileHandleWithStandardInput];
|
||||
self.routerTask = [NSTask new];
|
||||
self.processPipe = [NSPipe new];
|
||||
[self.routerTask setLaunchPath:options.binPath];
|
||||
[self.routerTask setArguments:options.arguments];
|
||||
NSDictionary *envDict = @{
|
||||
@"I2PBASE": options.i2pBaseDir
|
||||
};
|
||||
[self.routerTask setEnvironment: envDict];
|
||||
[self.routerTask setStandardOutput:self.processPipe];
|
||||
[self.routerTask setStandardError:self.processPipe];
|
||||
|
||||
NSFileHandle *stdoutFileHandle = [self.processPipe fileHandleForReading];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(routerStdoutData:)
|
||||
name:NSFileHandleDataAvailableNotification
|
||||
object:stdoutFileHandle];
|
||||
|
||||
[stdoutFileHandle waitForDataInBackgroundAndNotify];
|
||||
|
||||
[self.routerTask setTerminationHandler:^(NSTask* task) {
|
||||
NSLog(@"termHandler triggered!");
|
||||
NSBundle *launcherBundle = [NSBundle mainBundle];
|
||||
auto iconImage = [launcherBundle pathForResource:@"ItoopieTransparent" ofType:@"png"];
|
||||
sendUserNotification(APP_IDSTR, @"I2P Router has stopped", [NSImage imageNamed:iconImage]);
|
||||
// Cleanup
|
||||
self.isRouterRunning = NO;
|
||||
}];
|
||||
/*
|
||||
self.readLogHandle = [self.processPipe fileHandleForReading];
|
||||
NSData *inData = nil;
|
||||
self.totalLogData = [[[NSMutableData alloc] init] autorelease];
|
||||
|
||||
while ((inData = [self.readLogHandle availableData]) &&
|
||||
[inData length]) {
|
||||
[self.totalLogData appendData:inData];
|
||||
}
|
||||
*/
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) requestShutdown
|
||||
{
|
||||
[self.routerTask interrupt];
|
||||
}
|
||||
|
||||
- (void) requestRestart
|
||||
{
|
||||
self.userRequestedRestart = YES;
|
||||
kill([self.routerTask processIdentifier], SIGHUP);
|
||||
}
|
||||
|
||||
- (BOOL) isRunning
|
||||
{
|
||||
return self.routerTask.running;
|
||||
}
|
||||
|
||||
- (int) execute
|
||||
{
|
||||
@try {
|
||||
[self.routerTask launch];
|
||||
watchPid([self.routerTask processIdentifier]);
|
||||
self.isRouterRunning = YES;
|
||||
return 1;
|
||||
}
|
||||
@catch (NSException *e)
|
||||
{
|
||||
NSLog(@"Expection occurred %@", [e reason]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (int) getPID
|
||||
{
|
||||
return [self.routerTask processIdentifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace subprocess;
|
||||
|
||||
const std::vector<NSString*> JavaRunner::defaultStartupFlags {
|
||||
@"-Xmx512M",
|
||||
@"-Xms128m",
|
||||
@"-Djava.awt.headless=true",
|
||||
@"-Dwrapper.logfile=/tmp/router.log",
|
||||
@"-Dwrapper.logfile.loglevel=DEBUG",
|
||||
@"-Dwrapper.java.pidfile=/tmp/routerjvm.pid",
|
||||
@"-Dwrapper.console.loglevel=DEBUG"
|
||||
};
|
||||
|
||||
const std::vector<std::string> JavaRunner::defaultFlagsForExtractorJob {
|
||||
"-Xmx512M",
|
||||
"-Xms128m",
|
||||
"-Djava.awt.headless=true"
|
||||
};
|
||||
|
||||
JavaRunner::JavaRunner(std::string& javaBin, std::string& arguments, std::string& i2pBaseDir, const fp_proc_t& execFn, const fp_t& cb)
|
||||
: javaBinaryPath(javaBin), javaRouterArgs(arguments), _i2pBaseDir(i2pBaseDir), executingFn(execFn), exitCallbackFn(cb)
|
||||
{
|
||||
execLine = javaBinaryPath;
|
||||
execLine += " " + std::string(javaRouterArgs.c_str());
|
||||
printf("CLI: %s\n",execLine.c_str());
|
||||
javaProcess = std::shared_ptr<Popen>(new Popen(execLine, environment{{
|
||||
{"I2PBASE", _i2pBaseDir},
|
||||
{"JAVA_OPTS", getenv("JAVA_OPTS")}
|
||||
}}, defer_spawn{true}));
|
||||
}
|
||||
|
||||
void JavaRunner::requestRouterShutdown()
|
||||
{
|
||||
// SIGHUP
|
||||
javaProcess->kill(1);
|
||||
}
|
||||
|
||||
std::experimental::optional<std::future<int> > JavaRunner::execute()
|
||||
{
|
||||
try {
|
||||
auto executingFn = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{
|
||||
this->executingFn(this);
|
||||
});
|
||||
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), executingFn);
|
||||
dispatch_block_wait(executingFn, DISPATCH_TIME_FOREVER);
|
||||
|
||||
// Here, the process is done executing.
|
||||
|
||||
printf("Finished executingFn - Runs callbackFn\n");
|
||||
this->exitCallbackFn();
|
||||
return std::async(std::launch::async, []{ return 0; });
|
||||
} catch (std::exception* ex) {
|
||||
printf("ERROR: %s\n", ex->what());
|
||||
return std::experimental::nullopt;
|
||||
}
|
||||
}
|
9
launchers/macosx/I2PLauncher/fullBuild.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd ../../..
|
||||
ant preppkg-osx-only
|
||||
cd pkg-temp
|
||||
zip -r7 $DIR/base.zip *
|
||||
echo "[+] Done building base.zip from ant's pkg-temp."
|
||||
ninja appbundle
|
||||
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 131 KiB |
BIN
launchers/macosx/I2PLauncher/images/AppIcon.icns
Normal file
596
launchers/macosx/I2PLauncher/main.mm
Normal file
@ -0,0 +1,596 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreFoundation/CFStream.h>
|
||||
#include <CoreFoundation/CFPropertyList.h>
|
||||
#include <CoreFoundation/CFDictionary.h>
|
||||
#include <CoreFoundation/CFArray.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
#include <CoreFoundation/CFPreferences.h>
|
||||
|
||||
#import <objc/Object.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <AppKit/NSApplication.h>
|
||||
|
||||
#include "AppDelegate.h"
|
||||
#include "StatusItemButton.h"
|
||||
#include "RouterTask.h"
|
||||
#include "JavaHelper.h"
|
||||
#include "fn.h"
|
||||
#include "optional.hpp"
|
||||
#include "portcheck.h"
|
||||
|
||||
#define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]);
|
||||
|
||||
JvmListSharedPtr gRawJvmList = nullptr;
|
||||
|
||||
|
||||
@interface MenuBarCtrl () <StatusItemButtonDelegate, NSMenuDelegate>
|
||||
@end
|
||||
|
||||
@interface AppDelegate () <NSUserNotificationCenterDelegate, NSApplicationDelegate>
|
||||
@end
|
||||
|
||||
std::vector<std::string> buildClassPath(std::string basePath)
|
||||
{
|
||||
return globVector(basePath+std::string("/lib/*.jar"));
|
||||
}
|
||||
|
||||
maybeAnRouterRunner getGlobalRouterObject()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(globalRouterStatusMutex);
|
||||
return globalRouterStatus;
|
||||
}
|
||||
|
||||
void setGlobalRouterObject(I2PRouterTask* newRouter)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(globalRouterStatusMutex);
|
||||
globalRouterStatus.emplace(newRouter);
|
||||
}
|
||||
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
bool getGlobalRouterIsRunning()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
bool current = isRuterRunning;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return current;
|
||||
}
|
||||
void setGlobalRouterIsRunning(bool running)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
isRuterRunning = running;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir) {
|
||||
@try {
|
||||
RTaskOptions* options = [RTaskOptions alloc];
|
||||
options.binPath = javaBin;
|
||||
options.arguments = arguments;
|
||||
options.i2pBaseDir = i2pBaseDir;
|
||||
auto instance = [[[I2PRouterTask alloc] initWithOptions: options] autorelease];
|
||||
setGlobalRouterObject(instance);
|
||||
//NSThread *thr = [[NSThread alloc] initWithTarget:instance selector:@selector(execute) object:nil];
|
||||
[instance execute];
|
||||
sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
|
||||
auto pid = [instance getPID];
|
||||
return std::async(std::launch::async, [&pid]{
|
||||
return pid;
|
||||
});
|
||||
}
|
||||
@catch (NSException *e)
|
||||
{
|
||||
auto errStr = [NSString stringWithFormat:@"Expection occurred %@",[e reason]];
|
||||
NSLog(@"%@", errStr);
|
||||
sendUserNotification(APP_IDSTR, errStr);
|
||||
return std::async(std::launch::async, [&]{
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void openUrl(NSString* url)
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: url]];
|
||||
}
|
||||
|
||||
@implementation I2PStatusMenu
|
||||
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)item
|
||||
{
|
||||
NSLog(@"item is: %@",item);
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MenuBarCtrl
|
||||
|
||||
- (void) statusItemButtonLeftClick: (StatusItemButton *) button
|
||||
{
|
||||
CFShow(CFSTR("Left button clicked!"));
|
||||
NSEvent *event = [NSApp currentEvent];
|
||||
}
|
||||
|
||||
- (void) statusItemButtonRightClick: (StatusItemButton *) button
|
||||
{
|
||||
CFShow(CFSTR("Right button clicked!"));
|
||||
NSEvent *event = [NSApp currentEvent];
|
||||
[self.statusItem popUpStatusItemMenu: self.menu];
|
||||
}
|
||||
|
||||
- (void)statusBarImageBtnClicked
|
||||
{
|
||||
[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(btnPressedAction) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
- (void)btnPressedAction:(id)sender
|
||||
{
|
||||
NSLog(@"Button presseeeeeeed");
|
||||
NSEvent *event = [NSApp currentEvent];
|
||||
}
|
||||
|
||||
- (void) openRouterConsoleBtnHandler: (NSMenuItem *) menuItem
|
||||
{
|
||||
NSLog(@"Clicked openRouterConsoleBtnHandler");
|
||||
openUrl(@"http://127.0.0.1:7657");
|
||||
}
|
||||
|
||||
- (void) startJavaRouterBtnHandler: (NSMenuItem *) menuItem
|
||||
{
|
||||
NSLog(@"Clicked startJavaRouterBtnHandler");
|
||||
AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
|
||||
[appDelegate startupI2PRouter];
|
||||
}
|
||||
|
||||
- (void) restartJavaRouterBtnHandler: (NSMenuItem *) menuItem
|
||||
{
|
||||
NSLog(@"Clicked restartJavaRouterBtnHandler");
|
||||
if (getGlobalRouterObject().has_value())
|
||||
{
|
||||
sendUserNotification(APP_IDSTR, @"Requesting the I2P router to restart.");
|
||||
[getGlobalRouterObject().value() requestRestart];
|
||||
NSLog(@"Requested restart");
|
||||
}
|
||||
}
|
||||
|
||||
- (void) stopJavaRouterBtnHandler: (NSMenuItem *) menuItem
|
||||
{
|
||||
NSLog(@"Clicked stopJavaRouterBtnHandler");
|
||||
if (getGlobalRouterObject().has_value())
|
||||
{
|
||||
sendUserNotification(APP_IDSTR, @"Requesting the I2P router to shutdown.");
|
||||
[getGlobalRouterObject().value() requestShutdown];
|
||||
NSLog(@"Requested shutdown");
|
||||
}
|
||||
}
|
||||
|
||||
- (void) quitWrapperBtnHandler: (NSMenuItem *) menuItem
|
||||
{
|
||||
NSLog(@"quitWrapper event handler called!");
|
||||
[[NSApplication sharedApplication] terminate:self];
|
||||
}
|
||||
|
||||
- (MenuBarCtrl *) init
|
||||
{
|
||||
self.menu = [self createStatusBarMenu];
|
||||
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
|
||||
|
||||
self.image = [NSImage imageNamed:@"ItoopieTransparent.png"];
|
||||
[self.image setTemplate:YES];
|
||||
self.statusItem.image = self.image;
|
||||
|
||||
self.statusItem.highlightMode = NO;
|
||||
self.statusItem.toolTip = @"I2P Router Controller";
|
||||
|
||||
self.statusBarButton = [[StatusItemButton alloc] initWithImage:self.image];
|
||||
self.statusBarButton.menu = self.menu;
|
||||
|
||||
// Selecting action
|
||||
//[self.statusBarButton setAction:@selector(statusBarImageBtnClicked)];
|
||||
//[self.statusBarButton setTarget:self];
|
||||
self.statusBarButton.delegate = self;
|
||||
[self.statusItem popUpStatusItemMenu: self.menu];
|
||||
|
||||
[self.statusItem setView: self.statusBarButton];
|
||||
NSLog(@"Initialized statusbar and such");
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
[self.image release];
|
||||
[self.menu release];
|
||||
}
|
||||
|
||||
- (I2PStatusMenu *)createStatusBarMenu
|
||||
{
|
||||
I2PStatusMenu *menu = [[I2PStatusMenu alloc] init];
|
||||
[menu setAutoenablesItems:NO];
|
||||
|
||||
NSMenuItem *openConsoleI2Pbtn =
|
||||
[[NSMenuItem alloc] initWithTitle:@"Open Console"
|
||||
action:@selector(openRouterConsoleBtnHandler:)
|
||||
keyEquivalent:@""];
|
||||
[openConsoleI2Pbtn setTarget:self];
|
||||
[openConsoleI2Pbtn setEnabled:YES];
|
||||
|
||||
NSMenuItem *startI2Pbtn =
|
||||
[[NSMenuItem alloc] initWithTitle:@"Start I2P"
|
||||
action:@selector(startJavaRouterBtnHandler:)
|
||||
keyEquivalent:@""];
|
||||
[startI2Pbtn setTarget:self];
|
||||
if ([self.userPreferences boolForKey:@"autoStartRouter"])
|
||||
{
|
||||
[startI2Pbtn setEnabled:NO];
|
||||
} else {
|
||||
[startI2Pbtn setEnabled:YES];
|
||||
}
|
||||
|
||||
NSMenuItem *restartI2Pbtn =
|
||||
[[NSMenuItem alloc] initWithTitle:@"Restart I2P"
|
||||
action:@selector(restartJavaRouterBtnHandler:)
|
||||
keyEquivalent:@""];
|
||||
[restartI2Pbtn setTarget:self];
|
||||
[restartI2Pbtn setEnabled:YES];
|
||||
|
||||
NSMenuItem *stopI2Pbtn =
|
||||
[[NSMenuItem alloc] initWithTitle:@"Stop I2P"
|
||||
action:@selector(stopJavaRouterBtnHandler:)
|
||||
keyEquivalent:@""];
|
||||
[stopI2Pbtn setTarget:self];
|
||||
[stopI2Pbtn setEnabled:YES];
|
||||
|
||||
NSMenuItem *quitWrapperBtn =
|
||||
[[NSMenuItem alloc] initWithTitle:@"Quit I2P Wrapper"
|
||||
action:@selector(quitWrapperBtnHandler:)
|
||||
keyEquivalent:@""];
|
||||
[quitWrapperBtn setTarget:self];
|
||||
[quitWrapperBtn setEnabled:YES];
|
||||
|
||||
|
||||
[menu addItem:openConsoleI2Pbtn];
|
||||
[menu addItem:startI2Pbtn];
|
||||
[menu addItem:stopI2Pbtn];
|
||||
[menu addItem:restartI2Pbtn];
|
||||
[menu addItem:quitWrapperBtn];
|
||||
return menu;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ExtractMetaInfo
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion
|
||||
{
|
||||
std::string basePath([self.metaInfo.i2pBase UTF8String]);
|
||||
NSParameterAssert(self.metaInfo.i2pBase);
|
||||
NSError *error = NULL;
|
||||
BOOL success;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
// Get paths
|
||||
NSBundle *launcherBundle = [NSBundle mainBundle];
|
||||
|
||||
std::string basearg("-Di2p.dir.base=");
|
||||
basearg += basePath;
|
||||
|
||||
std::string zippath("-Di2p.base.zip=");
|
||||
zippath += [self.metaInfo.zipFile UTF8String];
|
||||
|
||||
std::string jarfile("-cp ");
|
||||
jarfile += [self.metaInfo.jarFile UTF8String];
|
||||
|
||||
// Create directory
|
||||
mkdir(basePath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
|
||||
|
||||
auto cli = JavaRunner::defaultFlagsForExtractorJob;
|
||||
setenv("I2PBASE", basePath.c_str(), true);
|
||||
setenv("ZIPPATH", zippath.c_str(), true);
|
||||
//setenv("DYLD_LIBRARY_PATH",".:/usr/lib:/lib:/usr/local/lib", true);
|
||||
|
||||
cli.push_back(basearg);
|
||||
cli.push_back(zippath);
|
||||
cli.push_back(jarfile);
|
||||
cli.push_back("net.i2p.launchers.BaseExtractor");
|
||||
|
||||
//auto charCli = map(cli, [](std::string str){ return str.c_str(); });
|
||||
std::string execStr = [self.metaInfo.javaBinary UTF8String];
|
||||
for_each(cli, [&execStr](std::string str){ execStr += std::string(" ") + str; });
|
||||
|
||||
NSLog(@"Trying cmd: %@", [NSString stringWithUTF8String:execStr.c_str()]);
|
||||
try {
|
||||
sendUserNotification(APP_IDSTR, @"Please hold on while we extract I2P. You'll get a new message once done!", self.contentImage);
|
||||
int extractStatus = Popen(execStr.c_str(), environment{{
|
||||
{"ZIPPATH", zippath.c_str()},
|
||||
{"I2PBASE", basePath.c_str()}
|
||||
}}).wait();
|
||||
NSLog(@"Extraction exit code %@",[NSString stringWithUTF8String:(std::to_string(extractStatus)).c_str()]);
|
||||
if (extractStatus == 0)
|
||||
{
|
||||
//success = YES;
|
||||
}
|
||||
} catch (subprocess::OSError &err) {
|
||||
auto errMsg = [NSString stringWithUTF8String:err.what()];
|
||||
//success = NO;
|
||||
NSLog(@"Exception: %@", errMsg);
|
||||
sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg], self.contentImage);
|
||||
}
|
||||
|
||||
// All done. Assume success and error are already set.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
sendUserNotification(APP_IDSTR, @"Extraction complete!", self.contentImage);
|
||||
if (completion) {
|
||||
completion(success, error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)startupI2PRouter
|
||||
{
|
||||
std::string basePath([self.metaInfo.i2pBase UTF8String]);
|
||||
|
||||
// Get paths
|
||||
NSBundle *launcherBundle = [NSBundle mainBundle];
|
||||
auto jarList = buildClassPath(basePath);
|
||||
std::string classpathStrHead = "-classpath";
|
||||
std::string classpathStr = "";
|
||||
classpathStr += [[launcherBundle pathForResource:@"launcher" ofType:@"jar"] UTF8String];
|
||||
std::string prefix(basePath);
|
||||
prefix += "/lib/";
|
||||
for_each(jarList, [&classpathStr](std::string str){ classpathStr += std::string(":") + str; });
|
||||
//if (self.enableVerboseLogging) NSLog(@"Classpath: %@\n",[NSString stringWithUTF8String:classpathStr.c_str()]);
|
||||
|
||||
try {
|
||||
auto argList = JavaRunner::defaultStartupFlags;
|
||||
|
||||
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");
|
||||
|
||||
argList.push_back([NSString stringWithUTF8String:baseDirArg.c_str()]);
|
||||
argList.push_back([NSString stringWithUTF8String:javaLibArg.c_str()]);
|
||||
argList.push_back([NSString stringWithUTF8String:classpathStrHead.c_str()]);
|
||||
argList.push_back([NSString stringWithUTF8String:classpathStr.c_str()]);
|
||||
argList.push_back(@"net.i2p.router.Router");
|
||||
auto javaBin = std::string([self.metaInfo.javaBinary UTF8String]);
|
||||
|
||||
|
||||
sendUserNotification(APP_IDSTR, @"I2P Router is starting up!", self.contentImage);
|
||||
auto nsJavaBin = self.metaInfo.javaBinary;
|
||||
auto nsBasePath = self.metaInfo.i2pBase;
|
||||
NSArray* arrArguments = [NSArray arrayWithObjects:&argList[0] count:argList.size()];
|
||||
startupRouter(nsJavaBin, arrArguments, nsBasePath);
|
||||
//if (self.enableVerboseLogging) NSLog(@"Defaults: %@", [pref dictionaryRepresentation]);
|
||||
} catch (std::exception &err) {
|
||||
auto errMsg = [NSString stringWithUTF8String:err.what()];
|
||||
NSLog(@"Exception: %@", errMsg);
|
||||
sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg], self.contentImage);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
|
||||
shouldPresentNotification:(NSUserNotification *)notification {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList
|
||||
{
|
||||
NSString *appleScriptString = @"set jvmlist to {\"Newest\"";
|
||||
for (auto item : *rawJvmList) {
|
||||
auto str = strprintf(",\"%s\"", item->JVMName.c_str()).c_str();
|
||||
NSString* tmp = [NSString stringWithUTF8String:str];
|
||||
appleScriptString = [appleScriptString stringByAppendingString:tmp];
|
||||
}
|
||||
appleScriptString = [appleScriptString stringByAppendingString:@"}\nchoose from list jvmlist\n"];
|
||||
NSAppleScript *theScript = [[NSAppleScript alloc] initWithSource:appleScriptString];
|
||||
NSDictionary *theError = nil;
|
||||
NSString* userResult = [[theScript executeAndReturnError: &theError] stringValue];
|
||||
NSLog(@"User choosed %@.\n", userResult);
|
||||
if (theError != nil)
|
||||
{
|
||||
NSLog(@"Error: %@.\n", theError);
|
||||
}
|
||||
return userResult;
|
||||
}
|
||||
|
||||
|
||||
- (void)userChooseJavaHome {
|
||||
listAllJavaInstallsAvailable();
|
||||
std::shared_ptr<JvmHomeContext> appContext = std::shared_ptr<JvmHomeContext>( new JvmHomeContext() );
|
||||
for (auto item : *appContext->getJvmList()) {
|
||||
printf("JVM %s (Version: %s, Directory: %s)\n", item->JVMName.c_str(), item->JVMPlatformVersion.c_str(), item->JVMHomePath.c_str());
|
||||
}
|
||||
JvmListPtr rawJvmList = appContext->getJvmList();
|
||||
NSString * userJavaHome = [self userSelectJavaHome: rawJvmList];
|
||||
// TODO: Add logic so user can set preferred JVM
|
||||
}
|
||||
|
||||
- (void)setApplicationDefaultPreferences {
|
||||
auto defaultJVMHome = check_output({"/usr/libexec/java_home","-v",DEF_MIN_JVM_VER});
|
||||
auto tmpStdStr = std::string(defaultJVMHome.buf.data());
|
||||
trim(tmpStdStr);
|
||||
auto cfDefaultHome = CFStringCreateWithCString(NULL, const_cast<const char *>(tmpStdStr.c_str()), kCFStringEncodingUTF8);
|
||||
[self.userPreferences registerDefaults:@{
|
||||
@"javaHome" : (NSString *)cfDefaultHome,
|
||||
@"lastI2PVersion" : (NSString *)CFSTR(DEF_I2P_VERSION),
|
||||
@"enableLogging": @true,
|
||||
@"enableVerboseLogging": @true,
|
||||
@"autoStartRouter": @true,
|
||||
@"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast<const char *>(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8)
|
||||
}];
|
||||
if (self.enableVerboseLogging) NSLog(@"Default JVM home preference set to: %@", (NSString *)cfDefaultHome);
|
||||
|
||||
auto dict = [self.userPreferences dictionaryRepresentation];
|
||||
[self.userPreferences setPersistentDomain:dict forName:NSAPPDOMAIN];
|
||||
|
||||
CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
|
||||
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
|
||||
//CFPreferencesSetAppValue(@"javaHome", (CFPropertyListRef)cfDefaultHome, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
|
||||
|
||||
if (self.enableVerboseLogging) NSLog(@"Default preferences stored!");
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
// Init application here
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
|
||||
// Start with user preferences
|
||||
self.userPreferences = [NSUserDefaults standardUserDefaults];
|
||||
[self setApplicationDefaultPreferences];
|
||||
self.enableLogging = [self.userPreferences boolForKey:@"enableLogging"];
|
||||
self.enableVerboseLogging = [self.userPreferences boolForKey:@"enableVerboseLogging"];
|
||||
|
||||
|
||||
// Get paths
|
||||
NSBundle *launcherBundle = [NSBundle mainBundle];
|
||||
auto iconImage = [launcherBundle pathForResource:@"ItoopieTransparent" ofType:@"png"];
|
||||
self.contentImage = [NSImage imageNamed:iconImage];
|
||||
|
||||
gRawJvmList = std::make_shared<std::list<JvmVersionPtr> >(std::list<JvmVersionPtr>());
|
||||
// In case we are unbundled, make us a proper UI application
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
auto javaHomePref = [self.userPreferences stringForKey:@"javaHome"];
|
||||
if (self.enableVerboseLogging) NSLog(@"Java home from preferences: %@", javaHomePref);
|
||||
|
||||
// This is the only GUI the user experience on a regular basis.
|
||||
self.menuBarCtrl = [[MenuBarCtrl alloc] init];
|
||||
|
||||
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
|
||||
if (self.enableVerboseLogging) NSLog(@"Appdomain is: %@", appDomain);
|
||||
|
||||
NSLog(@"We should have started the statusbar object by now...");
|
||||
|
||||
std::string i2pBaseDir(getDefaultBaseDir());
|
||||
//if (self.enableVerboseLogging) printf("Home directory is: %s\n", buffer);
|
||||
|
||||
|
||||
//[statusBarButton setAction:@selector(itemClicked:)];
|
||||
//dispatch_async(dispatch_get_main_queue(), ^{
|
||||
//});
|
||||
auto pref = self.userPreferences;
|
||||
self.menuBarCtrl.userPreferences = self.userPreferences;
|
||||
self.menuBarCtrl.enableLogging = self.enableLogging;
|
||||
self.menuBarCtrl.enableVerboseLogging = self.enableVerboseLogging;
|
||||
|
||||
|
||||
|
||||
if (port_check() != 0)
|
||||
{
|
||||
NSLog(@"Seems i2p is already running - I will not start the router (port 7657 is in use..)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.enableVerboseLogging) NSLog(@"processinfo %@", [[NSProcessInfo processInfo] arguments]);
|
||||
|
||||
auto getJavaBin = [&pref,&self]() -> std::string {
|
||||
// Get Java home
|
||||
NSString* val = @"";
|
||||
val = [pref stringForKey:@"javaHome"];
|
||||
if (val == NULL) val = @"";
|
||||
if (self.enableVerboseLogging) NSLog(@"Javahome: %@", val);
|
||||
auto javaHome = std::string([val UTF8String]);
|
||||
trim(javaHome); // Trim to remove endline
|
||||
auto javaBin = std::string(javaHome);
|
||||
javaBin += "/bin/java"; // Append java binary to path.
|
||||
return javaBin;
|
||||
};
|
||||
|
||||
|
||||
self.metaInfo = [[ExtractMetaInfo alloc] init];
|
||||
self.metaInfo.i2pBase = [NSString stringWithUTF8String:i2pBaseDir.c_str()];
|
||||
self.metaInfo.javaBinary = [NSString stringWithUTF8String:getJavaBin().c_str()];
|
||||
self.metaInfo.jarFile = [launcherBundle pathForResource:@"launcher" ofType:@"jar"];
|
||||
self.metaInfo.zipFile = [launcherBundle pathForResource:@"base" ofType:@"zip"];
|
||||
|
||||
std::string basearg("-Di2p.dir.base=");
|
||||
basearg += i2pBaseDir;
|
||||
|
||||
std::string jarfile("-cp ");
|
||||
jarfile += [self.metaInfo.zipFile UTF8String];
|
||||
|
||||
struct stat sb;
|
||||
if ( !(stat(i2pBaseDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) )
|
||||
{
|
||||
// I2P is not extracted.
|
||||
if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!");
|
||||
|
||||
[self extractI2PBaseDir:^(BOOL success, NSError *error) {
|
||||
//__typeof__(self) strongSelf = weakSelf;
|
||||
//if (strongSelf == nil) return;
|
||||
[self startupI2PRouter];
|
||||
}];
|
||||
|
||||
} else {
|
||||
if (self.enableVerboseLogging) NSLog(@"I2P directory found!");
|
||||
[self startupI2PRouter];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Exit sequence
|
||||
*
|
||||
**/
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
||||
// Tear down here
|
||||
NSString *string = @"applicationWillTerminate executed";
|
||||
NSLog(@"%@", string);
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
|
||||
}
|
||||
|
||||
|
||||
/* wrapper for main */
|
||||
- (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv {
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
|
||||
|
||||
app.delegate = [[AppDelegate alloc] initWithArgc:argc argv:argv];
|
||||
[NSBundle loadNibNamed:@"I2Launcher" owner:NSApp];
|
||||
|
||||
[NSApp run];
|
||||
// Handle any errors
|
||||
//CFRelease(javaHomes);
|
||||
//CFRelease(err);
|
||||
[pool drain];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
59
launchers/macosx/I2PLauncher/portcheck.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
|
||||
typedef struct sockaddr *sad; /* A necesary dummy typedef */
|
||||
|
||||
|
||||
int port_check(int portNum=7657)
|
||||
{
|
||||
int sock; /* Socket that will be bind */
|
||||
struct sockaddr_in sin; /* Address Structure */
|
||||
|
||||
/* Create the socket */
|
||||
/* PF_INET is the option for make a TCP socket.
|
||||
You can try "man socket" for more info */
|
||||
sock = socket( PF_INET, SOCK_STREAM, 0 );
|
||||
|
||||
/* The socket creation failed */
|
||||
if ( 0 > sock ) {
|
||||
perror( "socket" );
|
||||
return ( -1 );
|
||||
}
|
||||
|
||||
/* Address */
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons( portNum ); /* htons() convert the number
|
||||
to big endian */
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
/* We bind the socket to the port PORT to check if
|
||||
the port is in use */
|
||||
if ( 0 > bind( sock, (sad)&sin, sizeof( sin ) ) ) {
|
||||
/* Bind failed, now we can check if the address is
|
||||
in use */
|
||||
|
||||
if ( EADDRINUSE == errno ) {
|
||||
/* We put the code necesary to manage this case */
|
||||
printf( "The TCP port %d is in use.\n", portNum );
|
||||
}
|
||||
|
||||
else
|
||||
/* If the error were other than EADDRINUSE, we print it */
|
||||
perror( "bind" );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we arrive to this point, the port weren't in use and
|
||||
we have it attached to our program. We can close
|
||||
the socket or use it */
|
||||
close( sock ); /* Close the socket */
|
||||
|
||||
return 0;
|
||||
}
|