source: launchers/macosx/main.mm @ f76874a

Last change on this file since f76874a was f76874a, checked in by meeh <meeh@…>, 2 years ago

Adding XCode workspace & Xcode project, and some files missing from last commit.

Please note that Xcode project embedds the packing script (meaning it runs "ant" - zip files, and moves it to bundle)

  • Property mode set to 100644
File size: 12.1 KB
Line 
1#include <functional>
2#include <memory>
3#include <iostream>
4#include <algorithm>
5#include <string>
6#include <list>
7#include <sys/stat.h>
8#include <stdlib.h>
9#include <future>
10#include <vector>
11
12#import <Foundation/Foundation.h>
13
14
15#include <CoreFoundation/CoreFoundation.h>
16#include <CoreFoundation/CFStream.h>
17#include <CoreFoundation/CFPropertyList.h>
18#include <CoreFoundation/CFDictionary.h>
19#include <CoreFoundation/CFArray.h>
20#include <CoreFoundation/CFString.h>
21#include <CoreFoundation/CFPreferences.h>
22
23#import <objc/Object.h>
24#import <Cocoa/Cocoa.h>
25#import <AppKit/AppKit.h>
26#import <AppKit/NSApplication.h>
27
28#import "I2PLauncher-Swift.h"
29
30#include "AppDelegate.h"
31#include "RouterTask.h"
32#include "JavaHelper.h"
33#include "include/fn.h"
34#include "include/portcheck.h"
35
36#define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]);
37
38@interface AppDelegate () <NSUserNotificationCenterDelegate, NSApplicationDelegate>
39@end
40
41#ifdef __cplusplus
42#import "SBridge.h"
43JvmListSharedPtr gRawJvmList = nullptr;
44#endif
45
46
47@interface AppDelegate () <NSUserNotificationCenterDelegate, NSApplicationDelegate>
48@end
49
50#ifdef __cplusplus
51maybeAnRouterRunner getGlobalRouterObject()
52{
53    std::lock_guard<std::mutex> lock(globalRouterStatusMutex);
54    return globalRouterStatus; // Remember this might be nullptr now.
55}
56
57void setGlobalRouterObject(I2PRouterTask* newRouter)
58{
59    std::lock_guard<std::mutex> lock(globalRouterStatusMutex);
60    globalRouterStatus = newRouter;
61}
62
63
64pthread_mutex_t mutex;
65
66bool getGlobalRouterIsRunning()
67{
68    pthread_mutex_lock(&mutex);
69    bool current = isRuterRunning;
70    pthread_mutex_unlock(&mutex);
71    return current;
72}
73void setGlobalRouterIsRunning(bool running)
74{
75    pthread_mutex_lock(&mutex);
76    isRuterRunning = running;
77    pthread_mutex_unlock(&mutex);
78}
79
80#endif
81
82
83@implementation ExtractMetaInfo : NSObject
84@end
85
86@implementation AppDelegate
87
88- (void) awakeFromNib {
89}
90
91#ifdef __cplusplus
92
93- (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion
94{
95  std::string basePath([self.metaInfo.i2pBase UTF8String]);
96  NSParameterAssert(self.metaInfo.i2pBase);
97  NSError *error = NULL;
98  BOOL success = NO;
99  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
100
101    // Get paths
102    NSBundle *launcherBundle = [NSBundle mainBundle];
103
104    std::string basearg("-Di2p.dir.base=");
105    basearg += basePath;
106
107    std::string zippath("-Di2p.base.zip=");
108    zippath += [self.metaInfo.zipFile UTF8String];
109
110    std::string jarfile("-cp ");
111    jarfile += [self.metaInfo.jarFile UTF8String];
112
113    // Create directory
114    mkdir(basePath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
115
116    auto cli = JavaRunner::defaultFlagsForExtractorJob;
117    setenv("I2PBASE", basePath.c_str(), true);
118    setenv("ZIPPATH", zippath.c_str(), true);
119    //setenv("DYLD_LIBRARY_PATH",".:/usr/lib:/lib:/usr/local/lib", true);
120
121    cli.push_back(basearg);
122    cli.push_back(zippath);
123    cli.push_back(jarfile);
124    cli.push_back("net.i2p.launchers.BaseExtractor");
125
126    auto charCli = map(cli, [](std::string str){ return str.c_str(); });
127    std::string execStr = [self.metaInfo.javaBinary UTF8String];
128    for_each(cli, [&execStr](std::string str){ execStr += std::string(" ") + str; });
129
130    NSLog(@"Trying cmd: %@", [NSString stringWithUTF8String:execStr.c_str()]);
131    try {
132        sendUserNotification(APP_IDSTR, @"Please hold on while we extract I2P. You'll get a new message once done!", self.contentImage);
133        int extractStatus = Popen(execStr.c_str(), environment{{
134            {"ZIPPATH", zippath.c_str()},
135            {"I2PBASE", basePath.c_str()}
136        }}).wait();
137        NSLog(@"Extraction exit code %@",[NSString stringWithUTF8String:(std::to_string(extractStatus)).c_str()]);
138        if (extractStatus == 0)
139        {
140            //success = YES;
141        }
142    } catch (subprocess::OSError &err) {
143        auto errMsg = [NSString stringWithUTF8String:err.what()];
144        //success = NO;
145        NSLog(@"Exception: %@", errMsg);
146        sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg], self.contentImage);
147    }
148
149    // All done. Assume success and error are already set.
150    dispatch_async(dispatch_get_main_queue(), ^{
151      //sendUserNotification(APP_IDSTR, @"Extraction complete!", self.contentImage);
152      if (completion) {
153        completion(success, error);
154      }
155    });
156  });
157}
158
159#endif
160
161- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
162                               shouldPresentNotification:(NSUserNotification *)notification {
163    return YES;
164}
165
166
167#ifdef __cplusplus
168
169inline std::string getDefaultBaseDir()
170{
171  // Figure out base directory
172  const char* pathFromHome = "/Users/%s/Library/I2P";
173  auto username = getenv("USER");
174  char buffer[strlen(pathFromHome)+strlen(username)];
175  sprintf(buffer, pathFromHome, username);
176  std::string i2pBaseDir(buffer);
177  return i2pBaseDir;
178}
179
180- (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList
181{
182  NSString *appleScriptString = @"set jvmlist to {\"Newest\"";
183  for (auto item : *rawJvmList) {
184    auto str = strprintf(",\"%s\"", item->JVMName.c_str()).c_str();
185    NSString* tmp = [NSString stringWithUTF8String:str];
186    appleScriptString = [appleScriptString stringByAppendingString:tmp];
187  }
188  appleScriptString = [appleScriptString stringByAppendingString:@"}\nchoose from list jvmlist\n"];
189  NSAppleScript *theScript = [[NSAppleScript alloc] initWithSource:appleScriptString];
190  NSDictionary *theError = nil;
191  NSString* userResult = [[theScript executeAndReturnError: &theError] stringValue];
192  NSLog(@"User choosed %@.\n", userResult);
193  if (theError != nil)
194  {
195    NSLog(@"Error: %@.\n", theError);
196  }
197  return userResult;
198}
199
200
201- (void)userChooseJavaHome {
202  listAllJavaInstallsAvailable();
203  std::shared_ptr<JvmHomeContext> appContext = std::shared_ptr<JvmHomeContext>( new JvmHomeContext() );
204  for (auto item : *appContext->getJvmList()) {
205    printf("JVM %s (Version: %s, Directory: %s)\n", item->JVMName.c_str(), item->JVMPlatformVersion.c_str(), item->JVMHomePath.c_str());
206  }
207  JvmListPtr rawJvmList = appContext->getJvmList();
208  NSString * userJavaHome = [self userSelectJavaHome: rawJvmList];
209  // TODO: Add logic so user can set preferred JVM
210}
211
212#endif
213
214- (void)setApplicationDefaultPreferences {
215  auto defaultJVMHome = check_output({"/usr/libexec/java_home","-v",DEF_MIN_JVM_VER});
216  auto tmpStdStr = std::string(defaultJVMHome.buf.data());
217  trim(tmpStdStr);
218  auto cfDefaultHome  = CFStringCreateWithCString(NULL, const_cast<const char *>(tmpStdStr.c_str()), kCFStringEncodingUTF8);
219  /*[self.userPreferences registerDefaults:@{
220    @"javaHome" : (NSString *)cfDefaultHome,
221    @"lastI2PVersion" : (NSString *)CFSTR(DEF_I2P_VERSION),
222    @"enableLogging": @YES,
223    @"enableVerboseLogging": @YES,
224    @"autoStartRouter": @YES,
225    @"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast<const char *>(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8)
226  }];*/
227  if (self.enableVerboseLogging) NSLog(@"Default JVM home preference set to: %@", (NSString *)cfDefaultHome);
228
229  auto dict = [self.userPreferences dictionaryRepresentation];
230  [self.userPreferences setPersistentDomain:dict forName:NSAPPDOMAIN];
231
232  CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
233  CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
234  //CFPreferencesSetAppValue(@"javaHome", (CFPropertyListRef)cfDefaultHome, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
235
236  if (self.enableVerboseLogging) NSLog(@"Default preferences stored!");
237}
238
239
240- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
241  // Init application here
242 
243  SwiftMainDelegate *swiftRuntime = [[SwiftMainDelegate alloc] init];
244  swiftRuntime.applicationDidFinishLaunching;
245 
246  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
247  // Start with user preferences
248  self.userPreferences = [NSUserDefaults standardUserDefaults];
249  [self setApplicationDefaultPreferences];
250  self.enableLogging = [self.userPreferences boolForKey:@"enableLogging"];
251  self.enableVerboseLogging = [self.userPreferences boolForKey:@"enableVerboseLogging"];
252
253
254    // Get paths
255    NSBundle *launcherBundle = [NSBundle mainBundle];
256    //auto iconImage = [launcherBundle pathForResource:@"ItoopieTransparent" ofType:@"png"];
257 
258    // This is the only GUI the user experience on a regular basis.
259    //self.menuBarCtrl = [[MenuBarCtrl alloc] init];
260
261#ifdef __cplusplus
262  gRawJvmList = std::make_shared<std::list<JvmVersionPtr> >(std::list<JvmVersionPtr>());
263#endif
264  // In case we are unbundled, make us a proper UI application
265  [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
266  [NSApp activateIgnoringOtherApps:YES];
267
268  // TODO: Also check for new installations from time to time.
269 
270#ifdef __cplusplus
271  auto javaHomePref = [self.userPreferences stringForKey:@"javaHome"];
272  if (self.enableVerboseLogging)
273  {
274    NSLog(@"Java home from preferences: %@", javaHomePref);
275  }
276
277  if (self.enableVerboseLogging)
278  {
279    NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
280    NSLog(@"Appdomain is: %@", appDomain);
281  }
282
283  NSLog(@"We should have started the statusbar object by now...");
284  RouterProcessStatus* routerStatus = [[RouterProcessStatus alloc] init];
285
286  std::string i2pBaseDir(getDefaultBaseDir());
287
288  auto pref = self.userPreferences;
289 
290  bool shouldAutoStartRouter = false;
291
292  if (port_check(7657) != 0)
293  {
294    NSLog(@"Seems i2p is already running - I will not start the router (port 7657 is in use..)");
295    sendUserNotification(@"Found already running router", @"TCP port 7657 seem to be used by another i2p instance.");
296   
297    [routerStatus setRouterStatus: true];
298    [routerStatus setRouterRanByUs: false];
299    return;
300  } else {
301    shouldAutoStartRouter = true;
302  }
303
304  if (self.enableVerboseLogging) NSLog(@"processinfo %@", [[NSProcessInfo processInfo] arguments]);
305
306  auto getJavaBin = [&pref,&self]() -> std::string {
307    // Get Java home
308    NSString* val = @"";
309    val = [pref stringForKey:@"javaHome"];
310    if (val == NULL) val = @"";
311    if (self.enableVerboseLogging) NSLog(@"Javahome: %@", val);
312    auto javaHome = std::string([val UTF8String]);
313    //trim(javaHome); // Trim to remove endline
314    auto javaBin = std::string(javaHome);
315    javaBin += "/bin/java"; // Append java binary to path.
316    return javaBin;
317  };
318
319
320  //NSBundle *launcherBundle = [NSBundle mainBundle];
321   
322  self.metaInfo = [[ExtractMetaInfo alloc] init];
323  //self.metaInfo.i2pBase = [NSString stringWithUTF8String:i2pBaseDir.c_str()];
324  self.metaInfo.javaBinary = [NSString stringWithUTF8String:getJavaBin().c_str()];
325  self.metaInfo.jarFile = [launcherBundle pathForResource:@"launcher" ofType:@"jar"];
326  self.metaInfo.zipFile = [launcherBundle pathForResource:@"base" ofType:@"zip"];
327
328  std::string basearg("-Di2p.dir.base=");
329  //basearg += i2pBaseDir;
330
331  std::string jarfile("-cp ");
332  jarfile += [self.metaInfo.zipFile UTF8String];
333
334  struct stat sb;
335  if ( !(stat(i2pBaseDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) )
336  {
337    // I2P is not extracted.
338    if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!");
339
340    [self extractI2PBaseDir:^(BOOL success, NSError *error) {
341      sendUserNotification(@"I2P is done extracting", @"I2P is now installed and ready to run!");
342      NSLog(@"Done extracting I2P");
343      if (shouldAutoStartRouter) [self startupI2PRouter];
344    }];
345
346  } else {
347    if (self.enableVerboseLogging) NSLog(@"I2P directory found!");
348    if (shouldAutoStartRouter) [self startupI2PRouter];
349  }
350#endif
351}
352
353
354
355/**
356 *
357 * Exit sequence
358 *
359 **/
360- (void)applicationWillTerminate:(NSNotification *)aNotification {
361  // Tear down here
362  NSString *string = @"applicationWillTerminate executed";
363  NSLog(@"%@", string);
364  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
365}
366
367
368/* wrapper for main */
369- (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv {
370  return self;
371}
372@end
373
374
375
376int main(int argc, const char **argv)
377{
378  NSApplication *app = [NSApplication sharedApplication];
379  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
380
381  app.delegate = [[AppDelegate alloc] initWithArgc:argc argv:argv];
382  [NSBundle loadNibNamed:@"I2Launcher" owner:NSApp];
383
384  [NSApp run];
385  // Handle any errors
386  [pool drain];
387  return 0;
388}
389
390
391
Note: See TracBrowser for help on using the repository browser.