source: launchers/macosx/main.mm @ 9b958e4

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

Moving replace function to strutil.hpp

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