705 lines
22 KiB
JavaScript
705 lines
22 KiB
JavaScript
// vim: set sw=2 sts=2 ts=8 et syntax=javascript:
|
|
|
|
const Cc = Components.classes
|
|
const Ci = Components.interfaces
|
|
const Cr = Components.results
|
|
const Cu = Components.utils
|
|
|
|
const nsISupports = Components.interfaces.nsISupports;
|
|
const nsIClassInfo = Components.interfaces.nsIClassInfo;
|
|
const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
|
|
const nsIObserverService = Components.interfaces.nsIObserverService;
|
|
|
|
// ctypes can be disabled at build time
|
|
try { Cu.import("resource://gre/modules/ctypes.jsm") } catch(e) {}
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm")
|
|
Cu.import("resource://gre/modules/Services.jsm")
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "LauncherUtil", "resource://i2pbutton/modules/launcher-util.jsm")
|
|
|
|
//let observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService)
|
|
|
|
const timer = Cc["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer)
|
|
|
|
let routerStateLoglines = [
|
|
'Router state change from STARTING_1 to STARTING_2',
|
|
'Router state change from STARTING_2 to STARTING_3',
|
|
'Router state change from STARTING_3 to NETDB_READY',
|
|
'Router state change from NETDB_READY to RUNNING'
|
|
]
|
|
|
|
function setTimeout(fn, sleep) {
|
|
let event = {
|
|
notify: fn
|
|
}
|
|
return timer.initWithCallback(event, sleep, Components.interfaces.nsITimer.TYPE_ONE_SHOT)
|
|
}
|
|
|
|
|
|
function I2PProcessService()
|
|
{
|
|
this._logger = Cc["@geti2p.net/i2pbutton-logger;1"].getService(Ci.nsISupports).wrappedJSObject
|
|
this._config_checker = Cc["@geti2p.net/i2pbutton-router-config-mgr;1"].getService(Ci.nsISupports).wrappedJSObject
|
|
this._logger.log(3, "I2pbutton I2P Router Process Service initialized")
|
|
this.wrappedJSObject = this
|
|
}
|
|
|
|
I2PProcessService.prototype =
|
|
{
|
|
contractID : "@geti2p.net/i2pbutton-process-service;1",
|
|
serviceName : "I2P Launcher Process Service",
|
|
classID: Components.ID("{f77babef-dead-b00b-beff-babe6c9afda7}"),
|
|
kI2PLauncherExtPath: "i2pbutton@geti2p.net", // This could vary.
|
|
|
|
|
|
classDescription: this.kServiceName,
|
|
flags: Ci.nsIClassInfo.SINGLETON,
|
|
|
|
kPrefPromptAtStartup: "extensions.i2pbutton.prompt_at_startup",
|
|
|
|
kWizardProgressPageID: "progress",
|
|
|
|
kInitialControlConnDelayMS: 25,
|
|
kMaxControlConnRetryMS: 2000, // Retry at least every 2 seconds.
|
|
kControlConnTimeoutMS: 5*60*1000, // Wait at most 5 minutes for i2p to start.
|
|
|
|
kStatusUnknown: 0, // I2P process status.
|
|
kStatusStarting: 1,
|
|
kStatusRunning: 2,
|
|
kStatusExited: 3, // Exited or failed to start.
|
|
|
|
kI2PImplJava: "i2pj",
|
|
kI2PImplCpp: "i2pd",
|
|
|
|
kI2PProcessDidNotStartTopic: "I2PProcessDidNotStart",
|
|
kI2PBootstrapErrorTopic: "I2PBootstrapError",
|
|
|
|
// nsISupports implementation.
|
|
QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo]),
|
|
|
|
// nsIFactory implementation.
|
|
createInstance: function(aOuter, aIID)
|
|
{
|
|
if (null != aOuter)
|
|
throw Cr.NS_ERROR_NO_AGGREGATION;
|
|
|
|
return this.QueryInterface(aIID);
|
|
},
|
|
|
|
QueryInterface: function(iid)
|
|
{
|
|
if (!iid.equals(nsIClassInfo) &&
|
|
!iid.equals(nsISupports)) {
|
|
Components.returnCode = Cr.NS_ERROR_NO_INTERFACE;
|
|
return null;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
init: function(aWindow) {},
|
|
uninit: function unit() {},
|
|
|
|
lockFactory: function(aDoLock) {},
|
|
|
|
// nsIObserver implementation.
|
|
observe: function(aSubject, aTopic, aParam)
|
|
{
|
|
const kUserQuitTopic = "I2PUserRequestedQuit"
|
|
const kBootstrapStatusTopic = "I2PBootstrapStatus"
|
|
|
|
if (!this.mObsSvc)
|
|
{
|
|
this.mObsSvc = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService)
|
|
}
|
|
|
|
if ("profile-after-change" == aTopic)
|
|
{
|
|
this.mObsSvc.addObserver(this, "quit-application-granted", false)
|
|
this.mObsSvc.addObserver(this, kUserQuitTopic, false)
|
|
this.mObsSvc.addObserver(this, kBootstrapStatusTopic, false)
|
|
|
|
|
|
const self = this
|
|
//this._logger.log(3, 'Checking if a console is already up (an router already running)')
|
|
|
|
let prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch)
|
|
let shouldShowDelayUserDialog = prefs.getBoolPref("extensions.i2pbutton.delay_user_with_dialog", true)
|
|
|
|
let canStartPromise = self._config_checker.ensure_config()
|
|
canStartPromise.then(() => {
|
|
self._logger.log(3, 'Starting the router')
|
|
self.I2PStartAndControlI2P(true)
|
|
|
|
})
|
|
|
|
try {
|
|
if (shouldShowDelayUserDialog) {
|
|
self.openWaitForRouterDialog()
|
|
setTimeout(() => {
|
|
let progressmeter = self.mDelayUserDialog.document.getElementById('progressMeter')
|
|
progressmeter.value = progressmeter.value + 35
|
|
var text = self.mDelayUserDialog.document.getElementById('progressPleaseWait')
|
|
text.value = 'Waiting for the router to open the console and proxy port.'
|
|
}, 5000)
|
|
}
|
|
} catch (err) {
|
|
self._logger.log(5, `Unknown error while executing delay user dialog: ${err}`)
|
|
}
|
|
|
|
// After the router process is spawned.
|
|
/*if (self.mDelayUserDialog) {
|
|
setTimeout(() => {
|
|
let progressmeter = self.mDelayUserDialog.document.getElementById('progressMeter')
|
|
progressmeter.value = progressmeter.value + 35
|
|
var text = self.mDelayUserDialog.document.getElementById('progressPleaseWait')
|
|
text.value = 'Waiting for the router to open the console and proxy port.'
|
|
}, 5000)
|
|
}*/
|
|
/*this._isConsoleRunning(function(res) {
|
|
if (res!=4) {
|
|
// Yes, 4 is success
|
|
|
|
} else {
|
|
self._logger.log(3, 'Already found a router, won\'t launch.')
|
|
}
|
|
})*/
|
|
}
|
|
else if ("quit-application-granted" == aTopic)
|
|
{
|
|
this.mIsQuitting = true;
|
|
this.mObsSvc.removeObserver(this, "quit-application-granted");
|
|
this.mObsSvc.removeObserver(this, kUserQuitTopic);
|
|
this.mObsSvc.removeObserver(this, kBootstrapStatusTopic);
|
|
if (this.mI2PProcess)
|
|
{
|
|
let prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch)
|
|
let shouldKillRouter = prefs.getBoolPref("extensions.i2pbutton.kill_router_on_exit", true)
|
|
if (shouldKillRouter) this.mI2PProcess.kill()
|
|
this._logger.log(4, "Disconnecting from i2p process (pid " + this.mI2PProcess.pid + ")")
|
|
this.mI2PProcess = null
|
|
}
|
|
}
|
|
else if (("process-failed" == aTopic) || ("process-finished" == aTopic))
|
|
{
|
|
if (this.mControlConnTimer)
|
|
{
|
|
this.mControlConnTimer.cancel();
|
|
this.mControlConnTimer = null;
|
|
}
|
|
|
|
this.mI2PProcess = null;
|
|
this.mI2PProcessStatus = this.kStatusExited;
|
|
this.mIsBootstrapDone = false;
|
|
|
|
this.mObsSvc.notifyObservers(null, "I2PProcessExited", null)
|
|
|
|
if (this.mIsQuitting)
|
|
{
|
|
LauncherUtil.cleanupTempDirectories()
|
|
}
|
|
else
|
|
{
|
|
var defaultBtnLabel = LauncherUtil.getLocalizedString("restart_i2p");
|
|
var cancelBtnLabel = "OK";
|
|
try
|
|
{
|
|
const kSysBundleURI = "chrome://global/locale/commonDialogs.properties"
|
|
var sysBundle = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService).createBundle(kSysBundleURI)
|
|
cancelBtnLabel = sysBundle.GetStringFromName(cancelBtnLabel)
|
|
} catch(e) {}
|
|
|
|
this._logger.log(3, 'The router stopped..')
|
|
|
|
/*if (LauncherUtil.showConfirm(null, s, defaultBtnLabel, cancelBtnLabel) && !this.mIsQuitting)
|
|
{
|
|
this.I2PStartAndControlI2P(false);
|
|
}*/
|
|
}
|
|
}
|
|
else if ("timer-callback" == aTopic)
|
|
{
|
|
if (aSubject == this.mControlConnTimer)
|
|
{
|
|
this.mObsSvc.notifyObservers(null, "I2PProcessIsReady", null)
|
|
}
|
|
} else if (kBootstrapStatusTopic == aTopic) {
|
|
this._processBootstrapStatus(aSubject.wrappedJSObject)
|
|
} else if (kUserQuitTopic == aTopic) {
|
|
this.mQuitSoon = true
|
|
}
|
|
},
|
|
|
|
canUnload: function(aCompMgr) { return true; },
|
|
|
|
// nsIClassInfo implementation.
|
|
getInterfaces: function(aCount)
|
|
{
|
|
var iList = [ Ci.nsISupports,
|
|
Ci.nsIFactory,
|
|
Ci.nsIObserver,
|
|
Ci.nsIClassInfo ];
|
|
aCount.value = iList.length;
|
|
return iList;
|
|
},
|
|
|
|
getHelperForLanguage: function (aLanguage) { return null; },
|
|
|
|
|
|
// Public Properties and Methods ///////////////////////////////////////////
|
|
get I2PProcessStatus()
|
|
{
|
|
return this.mI2PProcessStatus;
|
|
},
|
|
|
|
get I2PIsBootstrapDone()
|
|
{
|
|
return this.mIsBootstrapDone;
|
|
},
|
|
|
|
get I2PBootstrapErrorOccurred()
|
|
{
|
|
|
|
},
|
|
|
|
openWaitForRouterDialog: function() {
|
|
const self = this
|
|
//var win = ww.openWindow(null, "chrome://i2pbutton/content/progress.xul", "wizard", "chrome,dialog=no,modal,centerscreen", {blabla:0})
|
|
const ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].getService(Components.interfaces.nsIWindowWatcher)
|
|
|
|
self.mDelayUserDialog = ww.openWindow(
|
|
null,
|
|
"chrome://i2pbutton/content/progress.xul",
|
|
"startingrouter",
|
|
"chrome,dialog=no,modal,centerscreen",
|
|
[true])
|
|
this._logger.log(3, 'After open wait for router dialog')
|
|
|
|
setTimeout(() => {
|
|
let progressmeter = self.mDelayUserDialog.document.getElementById('progressMeter')
|
|
progressmeter.value = progressmeter.value + 15
|
|
|
|
var text = self.mDelayUserDialog.document.getElementById('progressPleaseWait')
|
|
text.hidden = false
|
|
}, 1000)
|
|
return self.mDelayUserDialog
|
|
},
|
|
|
|
getWrapperLog: function() {
|
|
let wrapperLogFile = LauncherUtil.dataDirectoryObject
|
|
wrapperLogFile.append('I2P')
|
|
wrapperLogFile.append('wrapper.log')
|
|
return this._getFileAsString(wrapperLogFile)
|
|
},
|
|
|
|
getLogsLogTxt: function() {
|
|
let logTxtFile = LauncherUtil.dataDirectoryObject
|
|
logTxtFile.append('I2P')
|
|
logTxtFile.append('logs')
|
|
logTxtFile.append('log-0.txt')
|
|
return this._getFileAsString(logTxtFile)
|
|
},
|
|
|
|
retrieveBootstrapStatus: function() {},
|
|
|
|
I2PStartAndControlI2P: function()
|
|
{
|
|
this._startI2P()
|
|
let isRunningI2P = (this.mI2PProcessStatus == this.kStatusStarting) || (this.mI2PProcessStatus == this.kStatusRunning)
|
|
this._controlI2P(isRunningI2P)
|
|
},
|
|
|
|
I2PClearBootstrapError: function()
|
|
{
|
|
this.mBootstrapErrorOccurred = false
|
|
this.mLastI2PWarningPhase = null
|
|
this.mLastI2PWarningReason = null
|
|
},
|
|
|
|
// Private Member Variables ////////////////////////////////////////////////
|
|
mI2PProcessStatus: 0, // kStatusUnknown
|
|
mIsBootstrapDone: false,
|
|
mBootstrapErrorOccurred: false,
|
|
mIsQuitting: false,
|
|
mObsSvc: null,
|
|
mI2PUseImpl: this.kI2PImplJava,
|
|
mI2PProcess: null, // nsIProcess
|
|
mI2PProcessStartTime: null, // JS Date.now()
|
|
mControlConnTimer: null,
|
|
mControlConnDelayMS: 0,
|
|
mQuitSoon: false, // Quit was requested by the user; do so soon.
|
|
mLastI2PWarningPhase: null,
|
|
mLastI2PWarningReason: null,
|
|
mDefaultPreferencesAreLoaded: false,
|
|
mDelayUserDialog: null,
|
|
|
|
// Private Methods /////////////////////////////////////////////////////////
|
|
|
|
_resetLogFiles: function() {
|
|
let wrapperLogFile = LauncherUtil.dataDirectoryObject
|
|
wrapperLogFile.append('I2P')
|
|
wrapperLogFile.append('wrapper.log')
|
|
if (wrapperLogFile.exists()) wrapperLogFile.remove(false)
|
|
let logTxtFile = LauncherUtil.dataDirectoryObject
|
|
logTxtFile.append('I2P')
|
|
logTxtFile.append('logs')
|
|
logTxtFile.append('log-0.txt')
|
|
if (logTxtFile.exists()) logTxtFile.remove(false)
|
|
},
|
|
|
|
_startI2P: function()
|
|
{
|
|
this.mI2PProcessStatus = this.kStatusUnknown;
|
|
|
|
// Avoid starting the router if extensions.i2pbutton.start_i2p is set to false.
|
|
let prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch)
|
|
let shouldStart = prefs.getBoolPref("extensions.i2pbutton.start_i2p")
|
|
if (!shouldStart) return
|
|
|
|
try
|
|
{
|
|
// Ideally, we would cd to the Firefox application directory before
|
|
// starting i2p (but we don't know how to do that). Instead, we
|
|
// rely on the IBB launcher to start Firefox from the right place.
|
|
|
|
// Get the I2P data directory first so it is created before we try to
|
|
// construct paths to files that will be inside it.
|
|
let dataDir = LauncherUtil.getI2PConfigPath(true)
|
|
let exeFile = LauncherUtil.getI2PBinary()
|
|
this._logger.log(3, `Datadir => ${dataDir.path}\nExeFile => ${exeFile.path}`)
|
|
|
|
var detailsKey;
|
|
if (!exeFile)
|
|
detailsKey = "i2p_missing";
|
|
else if (!dataDir)
|
|
detailsKey = "datadir_missing";
|
|
|
|
if (detailsKey)
|
|
{
|
|
var details = LauncherUtil.getLocalizedString(detailsKey);
|
|
var key = "unable_to_start_i2p";
|
|
var err = LauncherUtil.getFormattedLocalizedString(key, [details], 1);
|
|
this._notifyUserOfError(err, null, this.kI2PProcessDidNotStartTopic);
|
|
return;
|
|
}
|
|
|
|
this._resetLogFiles()
|
|
|
|
let args = LauncherUtil.getRouterDefaultArgs()
|
|
|
|
// Set an environment variable that points to the I2P data directory.
|
|
let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment)
|
|
env.set('I2P_BROWSER_I2P_DATA_DIR', dataDir.path)
|
|
|
|
// On Windows, prepend the I2P program directory to PATH. This is
|
|
// needed so that pluggable transports can find OpenSSL DLLs, etc.
|
|
// See https://trac.torproject.org/projects/tor/ticket/10845
|
|
if (LauncherUtil.isWindows)
|
|
{
|
|
var path = exeFile.parent.path
|
|
if (env.exists("PATH"))
|
|
path += ";" + env.get("PATH")
|
|
env.set("PATH", path)
|
|
}
|
|
|
|
this.mI2PProcessStatus = this.kStatusStarting
|
|
|
|
this._logger.log(3, `Trying to start with ${args}`)
|
|
|
|
this._logger.log(2, "Starting " + exeFile.path)
|
|
var p = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess)
|
|
p.init(exeFile)
|
|
|
|
for (var i = 0; i < args.length; ++i)
|
|
this._logger.log(2, ` ${args[i]}`)
|
|
|
|
// Possible fix for Windows and cmd.exe window spawn.
|
|
p.startHidden = true
|
|
p.noShell = true
|
|
|
|
p.runwAsync(args, args.length, this, false)
|
|
this.mI2PProcess = p
|
|
this.mI2PProcessStartTime = Date.now()
|
|
}
|
|
catch (e)
|
|
{
|
|
this.mI2PProcessStatus = this.kStatusExited
|
|
//var s = LauncherUtil.getLocalizedString("i2p_failed_to_start");
|
|
this._notifyUserOfError('Failed to start the I2P router', null, this.kI2PProcessDidNotStartTopic);
|
|
this._logger.log(4, "_startI2P error: ", e)
|
|
}
|
|
}, // _startI2P()
|
|
|
|
_isConsoleRunning: function(callback) {
|
|
let checkSvc = Cc["@geti2p.net/i2pbutton-i2pCheckService;1"].getService(Ci.nsISupports).wrappedJSObject
|
|
let obsSvc = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService)
|
|
let req = checkSvc.createCheckConsoleRequest(true)
|
|
const self = this
|
|
req.onreadystatechange = function(event) {
|
|
if (req.readyState === 4) {
|
|
// Done
|
|
let result = checkSvc.parseCheckConsoleResponse(req)
|
|
self.mIsBootstrapDone = true
|
|
self.mI2PProcessStatus = self.kStatusRunning
|
|
self._processBootstrapStatus({ PROGRESS: 100, message: 'The router has succcessfully started.' })
|
|
|
|
var statusObj = { bootstrap: 'done', success: true }
|
|
statusObj.wrappedJSObject = statusObj
|
|
obsSvc.notifyObservers(statusObj, "I2PBootstrapStatus", null)
|
|
callback(result)
|
|
}
|
|
}
|
|
req.send(null)
|
|
},
|
|
|
|
_processBootstrapStatus: function(aStatusObj)
|
|
{
|
|
if (!aStatusObj)
|
|
return
|
|
|
|
if (100 == aStatusObj.PROGRESS)
|
|
{
|
|
this.mIsBootstrapDone = true
|
|
this.mBootstrapErrorOccurred = false
|
|
LauncherUtil.setBoolPref(this.kPrefPromptAtStartup, false)
|
|
}
|
|
else
|
|
{
|
|
this.mIsBootstrapDone = false
|
|
|
|
if (aStatusObj._errorOccurred)
|
|
{
|
|
this.mBootstrapErrorOccurred = true
|
|
LauncherUtil.setBoolPref(this.kPrefPromptAtStartup, true)
|
|
let phase = LauncherUtil.getLocalizedBootstrapStatus(aStatusObj, "TAG")
|
|
let reason = LauncherUtil.getLocalizedBootstrapStatus(aStatusObj, "REASON")
|
|
let details = LauncherUtil.getFormattedLocalizedString(
|
|
"i2p_bootstrap_failed_details", [phase, reason], 2)
|
|
I2PLauncherLogger.log(5, "I2P bootstrap error: [" + aStatusObj.TAG +
|
|
"/" + aStatusObj.REASON + "] " + details);
|
|
|
|
if ((aStatusObj.TAG != this.mLastI2PWarningPhase) ||
|
|
(aStatusObj.REASON != this.mLastI2PWarningReason))
|
|
{
|
|
this.mLastI2PWarningPhase = aStatusObj.TAG
|
|
this.mLastI2PWarningReason = aStatusObj.REASON
|
|
|
|
let msg = LauncherUtil.getLocalizedString("i2p_bootstrap_failed")
|
|
this._notifyUserOfError(msg, details, this.kI2PBootstrapErrorTopic)
|
|
}
|
|
}
|
|
}
|
|
}, // _processBootstrapStatus()
|
|
|
|
_controlI2P: function(aIsRunningI2P)
|
|
{
|
|
|
|
try
|
|
{
|
|
if (aIsRunningI2P)
|
|
this._monitorI2PProcessStartup()
|
|
|
|
// If the user pressed "Quit" within settings/progress, exit.
|
|
if (this.mQuitSoon)
|
|
this._quitApp()
|
|
}
|
|
catch (e)
|
|
{
|
|
this.mI2PProcessStatus = this.kStatusExited
|
|
var s = LauncherUtil.getLocalizedString("i2p_control_failed")
|
|
this._notifyUserOfError(s, null, null)
|
|
this._logger.log(4, "_controlI2P error: ", e)
|
|
}
|
|
}, // controlI2P()
|
|
|
|
_quitApp: function()
|
|
{
|
|
try
|
|
{
|
|
this.mQuitSoon = false;
|
|
if (this.mI2PProcess != null) {
|
|
this.mI2PProcess.kill()
|
|
}
|
|
|
|
let asSvc = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
|
|
let flags = asSvc.eAttemptQuit;
|
|
asSvc.quit(flags);
|
|
}
|
|
catch (e)
|
|
{
|
|
this._logger.log(4, "unable to quit", e);
|
|
}
|
|
},
|
|
|
|
_monitorI2PProcessStartup: function()
|
|
{
|
|
this.mControlConnDelayMS = this.kInitialControlConnDelayMS
|
|
this.mControlConnTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer)
|
|
this.mControlConnTimer.init(this, this.mControlConnDelayMS, this.mControlConnTimer.TYPE_ONE_SHOT)
|
|
},
|
|
|
|
_notifyUserOfError: function(aMessage, aDetails, aNotifyTopic)
|
|
{
|
|
let errorObj = { handled: false, message: aMessage }
|
|
if (aDetails)
|
|
errorObj.details = aDetails
|
|
|
|
if (aNotifyTopic)
|
|
{
|
|
// Give other code an opportunity to handle this error, e.g., if the
|
|
// network settings window is open, errors are displayed using an
|
|
// overlaid XUL element.
|
|
errorObj.wrappedJSObject = errorObj
|
|
this.mObsSvc.notifyObservers(errorObj, aNotifyTopic, null)
|
|
}
|
|
|
|
if (!errorObj.handled)
|
|
{
|
|
let msg = aMessage
|
|
if (aDetails)
|
|
msg += "\n\n" + aDetails
|
|
LauncherUtil.showAlert(null, msg)
|
|
}
|
|
},
|
|
|
|
_getpid: function()
|
|
{
|
|
// Use nsIXULRuntime.processID if it is available.
|
|
var pid = 0;
|
|
|
|
try
|
|
{
|
|
var xreSvc = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
|
|
pid = xreSvc.processID;
|
|
}
|
|
catch (e)
|
|
{
|
|
this._logger.log(2, "failed to get process ID via XUL runtime:", e);
|
|
}
|
|
|
|
// Try libc.getpid() via js-ctypes.
|
|
if (!pid) try
|
|
{
|
|
var getpid;
|
|
if (LauncherUtil.isMac) {
|
|
var libc = ctypes.open("libc.dylib")
|
|
getpid = libc.declare("getpid", ctypes.default_abi, ctypes.uint32_t)
|
|
} else if (LauncherUtil.isWindows) {
|
|
var libc = ctypes.open("Kernel32.dll")
|
|
getpid = libc.declare("GetCurrentProcessId", ctypes.default_abi, ctypes.uint32_t)
|
|
} else {// Linux and others.
|
|
var libc;
|
|
try {
|
|
libc = ctypes.open("libc.so.6")
|
|
} catch(e) {
|
|
libc = ctypes.open("libc.so")
|
|
}
|
|
|
|
getpid = libc.declare("getpid", ctypes.default_abi, ctypes.int)
|
|
}
|
|
|
|
pid = getpid()
|
|
} catch(e) {
|
|
this._logger.log(4, "unable to get process ID: ", e)
|
|
}
|
|
|
|
return pid;
|
|
},
|
|
|
|
// Returns undefined if file contents could not be read.
|
|
_getFileAsString: function(aFile)
|
|
{
|
|
let str = ""
|
|
let inStream;
|
|
try
|
|
{
|
|
let fis = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream);
|
|
const kOpenFlagsReadOnly = 0x01;
|
|
fis.init(aFile, kOpenFlagsReadOnly, 0, 0);
|
|
inStream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
|
|
inStream.init(fis, "UTF-8", 0, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
|
const kReadSize = 0xffffffff; // PR_UINT32_MAX
|
|
while (true)
|
|
{
|
|
let outStr = {};
|
|
let count = inStream.readString(kReadSize, outStr);
|
|
if (count == 0)
|
|
break;
|
|
|
|
str += outStr.value;
|
|
}
|
|
}
|
|
catch (e)
|
|
{
|
|
this._logger.log(5, "_getFileAsString " + aFile.path + " error: " + e);
|
|
str = undefined;
|
|
}
|
|
|
|
if (inStream)
|
|
inStream.close();
|
|
|
|
return str;
|
|
},
|
|
|
|
// After making a backup, replace the contents of aFile with aStr.
|
|
// Returns true if successful.
|
|
_overwriteFile: function(aFile, aStr)
|
|
{
|
|
let backupFile;
|
|
|
|
try
|
|
{
|
|
// Convert the data to UTF-8.
|
|
let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
|
|
conv.charset = "UTF-8";
|
|
let data = conv.ConvertFromUnicode(aStr) + conv.Finish();
|
|
|
|
// Rename the file to .bak (we avoid .orig because tor uses it). This
|
|
// backup will be left on disk so the user can recover the original
|
|
// file contents.
|
|
backupFile = aFile.clone();
|
|
backupFile.leafName += ".bak";
|
|
backupFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, aFile.permissions);
|
|
aFile.renameTo(null, backupFile.leafName);
|
|
this._logger.log(3, "created backup of " + aFile.leafName + " in " + backupFile.leafName);
|
|
|
|
// Write the new data to the file.
|
|
let stream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
|
stream.init(aFile, 0x02 | 0x08 | 0x20, /* WRONLY CREATE TRUNCATE */
|
|
0o600, 0);
|
|
stream.write(data, data.length);
|
|
stream.QueryInterface(Ci.nsISafeOutputStream).finish();
|
|
}
|
|
catch (e)
|
|
{
|
|
// Report an error and try to recover by renaming the backup to the
|
|
// original name.
|
|
this._logger.log(5, "failed to overwrite file " + aFile.path + ": " + e);
|
|
if (backupFile)
|
|
backupFile.renameTo(null, aFile.leafName);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
endOfObject: true
|
|
}
|
|
|
|
let gI2PProcessService = new I2PProcessService
|
|
|
|
|
|
// TODO: Mark wants to research use of XPCOMUtils.generateNSGetFactory
|
|
// Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
function NSGetFactory(aClassID)
|
|
{
|
|
if (!aClassID.equals(gI2PProcessService.kClassID))
|
|
throw Cr.NS_ERROR_FACTORY_NOT_REGISTERED
|
|
|
|
return gI2PProcessService
|
|
}
|
|
|
|
|
|
// This is the new stuff, stay away from generateNSGetModule which is the old stuff..
|
|
var NSGetFactory = XPCOMUtils.generateNSGetFactory([I2PProcessService])
|