commit c48bf013150c6a5cd0bfc17cf219615824208f44 Author: Kathleen Brade brade@pearlcrescent.com Date: Fri Mar 29 14:05:13 2013 -0400
Bug 7494: Create local home page for TBB.
Instead of a remote check, we now check browser proxy settings against the values specified by the tor control port.
If the control port is unavailable or broken, we send background request to check.torproject.org and update the about:tor homepage and torbutton icon retroactively if that fails. --- src/chrome.manifest | 6 + src/chrome/content/aboutTor/aboutTor.xhtml | 117 ++++++++ src/chrome/content/aboutTor/onionArrow.png | Bin 0 -> 2381 bytes src/chrome/content/aboutTor/onionArrowRight.png | Bin 0 -> 1695 bytes src/chrome/content/aboutTor/search.png | Bin 0 -> 3419 bytes src/chrome/content/aboutTor/tor-off.png | Bin 0 -> 5835 bytes src/chrome/content/aboutTor/tor-on.png | Bin 0 -> 2665 bytes src/chrome/content/torbutton.js | 354 ++++++++++++++++++----- src/chrome/locale/en/aboutTor.dtd | 64 ++++ src/chrome/skin/aboutTor.css | 316 ++++++++++++++++++++ src/components/aboutTor.js | 55 ++++ src/components/torCheckService.js | 134 +++++++++ src/defaults/preferences/preferences.js | 1 + 13 files changed, 972 insertions(+), 75 deletions(-)
diff --git a/src/chrome.manifest b/src/chrome.manifest index e57e62f..0318f79 100644 --- a/src/chrome.manifest +++ b/src/chrome.manifest @@ -146,6 +146,12 @@ contract @torproject.org/startup-observer;1 {06322def-6fde-4c06-aef6-47ae8e79962 component {e6204253-b690-4159-bfe8-d4eedab6b3be} components/cookie-jar-selector.js contract @torproject.org/cookie-jar-selector;1 {e6204253-b690-4159-bfe8-d4eedab6b3be}
+component {84d47da6-79c3-4661-aa9f-8049476f7bf5} components/aboutTor.js +contract @mozilla.org/network/protocol/about;1?what=tor {84d47da6-79c3-4661-aa9f-8049476f7bf5} + +component {5d57312b-5d8c-4169-b4af-e80d6a28a72e} components/torCheckService.js +contract @torproject.org/torbutton-torCheckService;1 {5d57312b-5d8c-4169-b4af-e80d6a28a72e} + component {f36d72c9-9718-4134-b550-e109638331d7} components/torbutton-logger.js contract @torproject.org/torbutton-logger;1 {f36d72c9-9718-4134-b550-e109638331d7}
diff --git a/src/chrome/content/aboutTor/aboutTor.xhtml b/src/chrome/content/aboutTor/aboutTor.xhtml new file mode 100644 index 0000000..f3d07a6 --- /dev/null +++ b/src/chrome/content/aboutTor/aboutTor.xhtml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + - Copyright (c) 2013, The Tor Project, Inc. + - See LICENSE for licensing information. + - vim: set sw=2 sts=2 ts=8 et syntax=xml: + --> + +<!DOCTYPE html [ + <!ENTITY % htmlDTD + PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "DTD/xhtml1-strict.dtd"> + %htmlDTD; + <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> + %globalDTD; + <!ENTITY % aboutTorDTD SYSTEM "chrome://torbutton/locale/aboutTor.dtd"> + %aboutTorDTD; +]> + +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>&aboutTor.title;</title> + <link rel="stylesheet" type="text/css" media="all" + href="chrome://torbutton/skin/aboutTor.css"/> +<script type="text/javascript"> + <![CDATA[ +function onLoad() +{ + window.addEventListener("resize", function() { + resizeToolbarIconArrow(); + }, false); + + window.setTimeout( function() { + resizeToolbarIconArrow(); + document.getElementById('sx').focus(); + }, 0); +} + +function resizeToolbarIconArrow() +{ + var textElem = document.getElementById("updatePrompt"); + var rightDiv = document.getElementById("toolbarIconArrowRight"); + if (textElem && rightDiv) + { + var width = textElem.offsetLeft - rightDiv.offsetLeft - 6; + rightDiv.style.width = width + "px"; + } +} +]]> +</script> +</head> +<body dir="&locale.dir;" onload="onLoad();"> +<div id="torstatus" class="top"> + <div id="torstatus-image"/> + <div class="hideIfTorOff"> + <h1>&aboutTor.success.label;</h1> + <h2 id="success2">&aboutTor.success2.label;</h2> + <h3 class="hideIfTBBNeedsUpdate">&aboutTor.success3.label;</h3> + <a id="testTorSettings" href="about:blank">&aboutTor.check.label;</a> + </div> + <div class="hideIfTorOn"> + <h1>&aboutTor.failure.label;</h1> + <h2>&aboutTor.failure2.label;</h2> + <h3>&aboutTor.failure3prefix.label;<a href="mailto:&aboutTor.failure3Link;" +>&aboutTor.failure3Link;</a>&aboutTor.failure3suffix.label;</h3> + </div> +</div> +<div class="top"> + <div class="hideIfTorIsUpToDate"> + <h1 class="hideIfTorOff">&aboutTor.outOfDateTorOn.label;</h1> + <h1 class="hideIfTorOn">&aboutTor.outOfDateTorOff.label;</h1> + <h3 id="updatePrompt">&aboutTor.outOfDate2.label;</h3> + <div id="toolbarIconArrowLeft"/> + <div id="toolbarIconArrowRight"/> + </div> +</div> + +<div class="searchbox hideIfTorOff"> <!-- begin form based search --> + <form action="&aboutTor.searchSPPost.link;" method="post"> + <div id="sxw"> + <div id="sbutton"> + <input name="b" id="sb" value="" title="&aboutTor.search.label;" + alt="&aboutTor.search.label;" type="submit"/> + </div> + <input name="q" autocomplete="off" id="sx" type="text"/> + </div> + <h4>&aboutTor.searchSP.privacy.beforeLink.label;<a href="&aboutTor.searchSP.privacy.link;">&aboutTor.searchSP.privacy.label;</a>&aboutTor.searchSP.search.beforeLink.label; + <a href="&aboutTor.searchSP.search.link;">&aboutTor.searchSP.search.label;</a></h4> + </form> +</div> + +<div class="hideIfTorOn" style="height:100px"/> + +<div id="middle" class="hideIfTorOff"> + <div class="container two"> + <h1>&aboutTor.whatnextQuestion.label;</h1> + <p>&aboutTor.whatnextAnswer.label;</p> + <a class="tips" href="&aboutTor.whatnext.link;">&aboutTor.whatnext.label;</a> + </div> + + <div class="container three"> + <h1>&aboutTor.helpInfo1.label;</h1> + <p>&aboutTor.helpInfo2.label;</p> + <ul> + <li><a href="&aboutTor.helpInfo3.link;">&aboutTor.helpInfo3.label;</a></li> + <li><a href="&aboutTor.helpInfo4.link;">&aboutTor.helpInfo4.label;</a></li> + <li><a href="&aboutTor.helpInfo5.link;">&aboutTor.helpInfo5.label;</a></li> + </ul> + </div> +</div> <!-- middle --> + +<div id="bottom"> + <p>&aboutTor.footer.label; +<a href="&aboutTor.learnMore.link;">&aboutTor.learnMore.label;</a></p> +</div> + +</body> +</html> diff --git a/src/chrome/content/aboutTor/onionArrow.png b/src/chrome/content/aboutTor/onionArrow.png new file mode 100644 index 0000000..c778568 Binary files /dev/null and b/src/chrome/content/aboutTor/onionArrow.png differ diff --git a/src/chrome/content/aboutTor/onionArrowRight.png b/src/chrome/content/aboutTor/onionArrowRight.png new file mode 100644 index 0000000..7552f2d Binary files /dev/null and b/src/chrome/content/aboutTor/onionArrowRight.png differ diff --git a/src/chrome/content/aboutTor/search.png b/src/chrome/content/aboutTor/search.png new file mode 100644 index 0000000..379d094 Binary files /dev/null and b/src/chrome/content/aboutTor/search.png differ diff --git a/src/chrome/content/aboutTor/tor-off.png b/src/chrome/content/aboutTor/tor-off.png new file mode 100644 index 0000000..a4509ba Binary files /dev/null and b/src/chrome/content/aboutTor/tor-off.png differ diff --git a/src/chrome/content/aboutTor/tor-on.png b/src/chrome/content/aboutTor/tor-on.png new file mode 100644 index 0000000..aa938e1 Binary files /dev/null and b/src/chrome/content/aboutTor/tor-on.png differ diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js index bb58cce..12660d7 100644 --- a/src/chrome/content/torbutton.js +++ b/src/chrome/content/torbutton.js @@ -1,4 +1,3 @@ -// Bug 1506 P0-P5: This is the main Torbutton overlay file. Much needs to be // preserved here, but in an ideal world, most of this code should perhaps be // moved into an XPCOM service, and much can also be tossed. See also // individual 1506 comments for details. @@ -8,6 +7,7 @@ // http://kb.mozillazine.org/Links_to_local_pages_don%27t_work
const k_tb_browser_update_needed_pref = "extensions.torbutton.updateNeeded"; +const k_tb_tor_check_failed_topic = "Torbutton:TorCheckFailed";
// status var m_tb_wasinited = false; @@ -34,6 +34,8 @@ var m_tb_control_port = null; var m_tb_control_host = null; var m_tb_control_pass = null;
+var m_tb_orig_BrowserOnAboutPageLoad = null; + // Bug 1506 P1: This object is only for updating the UI for toggling and style var torbutton_window_pref_observer = { @@ -72,6 +74,9 @@ var torbutton_window_pref_observer = var mode = m_tb_prefs.getBoolPref("extensions.torbutton.settings_applied"); torbutton_update_toolbutton(mode); torbutton_update_statusbar(mode); + + // Update all open about:tor pages. + torbutton_update_all_abouttor_pages(undefined, undefined); break; case k_tb_browser_update_needed_pref: torbutton_notify_if_update_needed(); @@ -201,6 +206,41 @@ var torbutton_unique_pref_observer = } }
+var torbutton_tor_check_observer = { + register: function() + { + this._obsSvc = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + this._obsSvc.addObserver(this, k_tb_tor_check_failed_topic, false); + }, + + unregister: function() + { + if (this._obsSvc) + this._obsSvc.removeObserver(this, k_tb_tor_check_failed_topic); + }, + + observe: function(subject, topic, data) + { + if (topic == k_tb_tor_check_failed_topic) { + // Update toolbar icon and tooltip. + var mode = m_tb_prefs.getBoolPref("extensions.torbutton.tor_enabled"); + torbutton_update_toolbutton(mode); + + // Update all open about:tor pages. If the user does not have an + // about:tor page open in the front most window, open one. + if (torbutton_update_all_abouttor_pages(undefined, false) < 1) { + var wm = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var win = wm.getMostRecentWindow("navigator:browser"); + if (win == window) { + gBrowser.selectedTab = gBrowser.addTab("about:tor"); + } + } + } + } +}; + // Bug 1506 P1 function torbutton_set_panel_view() { var o_statuspanel = false; @@ -457,6 +497,10 @@ function torbutton_init() { } }
+ // Add our hook into about page load + m_tb_orig_BrowserOnAboutPageLoad = window.BrowserOnAboutPageLoad; + window.BrowserOnAboutPageLoad = torbutton_override_BrowserOnAboutPageLoad; + // initialize preferences before we start our prefs observer torbutton_init_prefs();
@@ -468,6 +512,9 @@ function torbutton_init() {
torbutton_log(1, 'registering pref observer'); torbutton_window_pref_observer.register(); + + torbutton_log(1, "registering Tor check observer"); + torbutton_tor_check_observer.register();
//setting up context menu //var contextMenu = document.getElementById("contentAreaContextMenu"); @@ -716,6 +763,15 @@ function torbutton_get_statuspanel() { return o_statuspanel; }
+function torbutton_update_is_needed() { + var updateNeeded = false; + try { + updateNeeded = m_tb_prefs.getBoolPref(k_tb_browser_update_needed_pref); + } catch (e) {} + + return updateNeeded; +} + function torbutton_notify_if_update_needed() { function setOrClearAttribute(aElement, aAttrName, aValue) { @@ -728,15 +784,15 @@ function torbutton_notify_if_update_needed() { aElement.removeAttribute(aAttrName); }
- var updateNeeded = false; - try { - updateNeeded = m_tb_prefs.getBoolPref(k_tb_browser_update_needed_pref); - } catch (e) {} + let updateNeeded = torbutton_update_is_needed();
// Change look of toolbar item (enable/disable animated update icon). var btn = torbutton_get_toolbutton(); setOrClearAttribute(btn, "tbUpdateNeeded", updateNeeded);
+ // Update all open about:tor pages. + torbutton_update_all_abouttor_pages(updateNeeded, undefined); + // Hide/show download menu item and preceding separator. var item = document.getElementById("torbutton-downloadUpdate"); setOrClearAttribute(item, "hidden", !updateNeeded); @@ -760,6 +816,77 @@ function torbutton_download_update() { gBrowser.selectedTab = newTab; }
+// Pass undefined for a parameter to have this function determine it. +// Returns a count of open pages that were updated, +function torbutton_update_all_abouttor_pages(aUpdateNeeded, aTorIsOn) { + if (aUpdateNeeded === undefined) + aUpdateNeeded = torbutton_update_is_needed(); + if (aTorIsOn === undefined) + aTorIsOn = torbutton_tor_check_ok(); + + var count = 0; + var tabBrowser = top.getBrowser(); + var tabs = tabBrowser.mTabs; + if (tabs && (tabs.length > 0)) { + for (var tab = tabs[0]; tab != null; tab = tab.nextSibling) { + try { + let doc = tabBrowser.getBrowserForTab(tab).contentDocument; + if (torbutton_update_abouttor_doc(doc, aTorIsOn, aUpdateNeeded)) + ++count; + } catch(e) {} + } + } + + return count; +} + +// Returns true if aDoc is an about:tor page. +function torbutton_update_abouttor_doc(aDoc, aTorOn, aUpdateNeeded) { + var isAboutTor = torbutton_is_abouttor_doc(aDoc); + if (isAboutTor) { + if (aTorOn) + aDoc.body.setAttribute("toron", "yes"); + else + aDoc.body.removeAttribute("toron"); + + if (aUpdateNeeded) + aDoc.body.setAttribute("torNeedsUpdate", "yes"); + else + aDoc.body.removeAttribute("torNeedsUpdate"); + } + + return isAboutTor; +} + +function torbutton_override_BrowserOnAboutPageLoad(aDoc) { + if (torbutton_is_abouttor_doc(aDoc) && + !aDoc.documentElement.hasAttribute("aboutTorLoaded")) { + aDoc.documentElement.setAttribute("aboutTorLoaded", true); + + // Show correct Tor on/off and "update needed" status. + let torOn = torbutton_tor_check_ok(); + let needsUpdate = torbutton_update_is_needed(); + torbutton_update_abouttor_doc(aDoc, torOn, needsUpdate); + + // Insert "Test Tor Network Settings" url. + let elem = aDoc.getElementById("testTorSettings"); + if (elem) { + let locale = m_tb_prefs.getCharPref("general.useragent.locale"); + locale = locale.replace(/-/g, '_'); + let url = m_tb_prefs.getCharPref( + "extensions.torbutton.test_url_interactive"); + elem.href = url.replace(/__LANG__/g, locale); + } + } + + if (m_tb_orig_BrowserOnAboutPageLoad) + m_tb_orig_BrowserOnAboutPageLoad(aDoc); +} + +function torbutton_is_abouttor_doc(aDoc) { + return (aDoc && /^about:tor$/i.test(aDoc.documentURI.toLowerCase())); +} + // Bug 1506 P0: Toggle. Kill kill kill. function torbutton_save_nontor_settings() { @@ -890,35 +1017,17 @@ function torbutton_do_async_versioncheck() { return -1; } try { - var locale = m_tb_prefs.getCharPref("general.useragent.locale"); var version_list = JSON.parse(req.responseText); var my_version = m_tb_prefs.getCharPref("torbrowser.version"); for (var v in version_list) { if (version_list[v] == my_version) { torbutton_log(3, "Version check passed."); m_tb_prefs.setBoolPref(k_tb_browser_update_needed_pref, false); - var homepage = m_tb_prefs.getComplexValue("browser.startup.homepage", - Components.interfaces.nsIPrefLocalizedString).data; - if (homepage.indexOf("https://check.torproject.org/") == 0) { - var str = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - str.data = "https://check.torproject.org/?lang=%22+locale+%22&small=1&uptodate=1"; - m_tb_prefs.setComplexValue("browser.startup.homepage", - Components.interfaces.nsISupportsString, - str); - } return; } } torbutton_log(5, "Your Tor Browser is out of date."); m_tb_prefs.setBoolPref(k_tb_browser_update_needed_pref, true); - // Not up to date - var str = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - str.data = "https://check.torproject.org/?lang=%22+locale+%22&small=1&uptodate=0"; - m_tb_prefs.setComplexValue("browser.startup.homepage", - Components.interfaces.nsISupportsString, - str); return; } catch(e) { torbutton_log(5, "Version check failed! JSON parsing error: "+e); @@ -996,7 +1105,6 @@ function torbutton_check_version() { // but will need to be decoupled from the toggle logic :/ function torbutton_test_settings() { var wasEnabled = true; - var ret = 0; if(!torbutton_check_status()) { wasEnabled = false; torbutton_enable_tor(true); @@ -1006,12 +1114,9 @@ function torbutton_test_settings() {
m_tb_prefs.setBoolPref("extensions.torbutton.test_failed", true); try { - var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Components.interfaces.nsIXMLHttpRequest); - //var req = new XMLHttpRequest(); Blocked by content policy - var url = m_tb_prefs.getCharPref("extensions.torbutton.test_url"); - req.open('GET', url, false); - req.overrideMimeType("text/xml"); + var checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"] + .getService(Ci.nsISupports).wrappedJSObject; + var req = checkSvc.createCheckRequest(false); req.send(null); } catch(e) { // FIXME: This happens if this function is called from a browser @@ -1027,44 +1132,12 @@ function torbutton_test_settings() { torbutton_log(5, "Test failed! Tor internal error: "+e); return 0; } - if(req.status == 200) { - if(!req.responseXML) { - if(!wasEnabled) torbutton_disable_tor(); - torbutton_log(5, "Test failed! Not text/xml!"); - return 1; - }
- var result = req.responseXML.getElementById('TorCheckResult'); - - if(result===null) { - torbutton_log(5, "Test failed! No TorCheckResult element"); - ret = 2; - } else if(typeof(result.target) == 'undefined' - || result.target === null) { - torbutton_log(5, "Test failed! No target"); - ret = 3; - } else if(result.target === "success") { - torbutton_log(3, "Test Successful"); - m_tb_prefs.setBoolPref("extensions.torbutton.test_failed", false); - ret = 4; - } else if(result.target === "failure") { - torbutton_log(5, "Tor test failed!"); - ret = 5; - } else if(result.target === "unknown") { - torbutton_log(5, "Tor test failed. TorDNSEL Failure?"); - ret = 6; - } else { - torbutton_log(5, "Tor test failed. Strange target."); - ret = 7; - } - } else { - torbutton_log(5, "Tor test failed. HTTP Error: "+req.status); - ret = -req.status; - } - - torbutton_log(3, "Done testing Tor settings. Result: "+ret); - + var ret = checkSvc.parseCheckResponse(req); + if (ret == 4) + m_tb_prefs.setBoolPref("extensions.torbutton.test_failed", false); if(!wasEnabled) torbutton_disable_tor(); + torbutton_log(3, "Done testing Tor settings. Result: "+ret); return ret; }
@@ -1103,7 +1176,7 @@ function torbutton_update_toolbutton(mode) var o_stringbundle = torbutton_get_stringbundle(); var tooltip = "";
- if (mode) { + if (mode && torbutton_tor_check_ok()) { tooltip = o_stringbundle.GetStringFromName("torbutton.panel.label.enabled"); o_toolbutton.setAttribute('tbstatus', 'on'); o_toolbutton.setAttribute('tooltiptext', tooltip); @@ -1123,7 +1196,7 @@ function torbutton_update_statusbar(mode) var label = ""; var tooltip = "";
- if (mode) { + if (mode && torbutton_tor_check_ok()) { label = o_stringbundle.GetStringFromName("torbutton.panel.label.enabled"); tooltip = o_stringbundle.GetStringFromName("torbutton.panel.tooltip.enabled"); o_statuspanel.style.color = "#390"; @@ -1224,7 +1297,8 @@ function torbutton_socket_readline(input) { var str = ""; var bytes; while((bytes = input.readBytes(1)) != "\n") { - str += bytes; + if (bytes != '\r') + str += bytes; } return str; } @@ -1256,7 +1330,7 @@ function torbutton_array_to_hexdigits(array) { // Bug 1506 P4: Control port interaction. Needed for New Identity. // // Executes a command on the control port. -// Return 0 in error, 1 for success. +// Return a string response upon success and null upon error. function torbutton_send_ctrl_cmd(command) { try { var socketTransportService = Components.classes["@mozilla.org/network/socket-transport-service;1"] @@ -1282,21 +1356,21 @@ function torbutton_send_ctrl_cmd(command) {
if (bytes.indexOf("250") != 0) { torbutton_safelog(4, "Unexpected auth response on control port "+m_tb_control_port+":", bytes); - return 0; + return null; }
outputStream.writeBytes(command, command.length); bytes = torbutton_socket_readline(inputStream); if(bytes.indexOf("250") != 0) { torbutton_safelog(4, "Unexpected command response on control port "+m_tb_control_port+":", bytes); - return 0; + return null; }
socket.close(1); - return 1; + return bytes.substr(4); } catch(e) { torbutton_log(4, "Exception on control port "+e); - return 0; + return null; } }
@@ -1515,7 +1589,7 @@ function torbutton_do_new_identity() { torbutton_log(5, "Torbutton cannot safely newnym. It does not have access to the Tor Control Port."); window.alert(warning); } else { - if(torbutton_send_ctrl_cmd("SIGNAL NEWNYM\r\n") == 0) { + if (!torbutton_send_ctrl_cmd("SIGNAL NEWNYM\r\n")) { var o_stringbundle = torbutton_get_stringbundle(); var warning = o_stringbundle.GetStringFromName("torbutton.popup.no_newnym"); torbutton_log(5, "Torbutton was unable to request a new circuit from Tor"); @@ -1534,6 +1608,133 @@ function torbutton_do_new_identity() { window.close(); }
+function torbutton_do_tor_check() +{ + let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"] + .getService(Ci.nsISupports).wrappedJSObject; + if (checkSvc.kCheckNotInitiated != checkSvc.statusOfTorCheck) + return; // Only do the check once. + + // If we have a tor control port and transparent torification is off, + // perform a check via the control port. + if (m_tb_control_port && + !m_tb_prefs.getBoolPref("extensions.torbutton.saved.transparentTor")) { + if (torbutton_local_tor_check()) + checkSvc.statusOfTorCheck = checkSvc.kCheckSuccessful; + else { + // The check failed. Update toolbar icon and tooltip. + checkSvc.statusOfTorCheck = checkSvc.kCheckFailed; + var mode = m_tb_prefs.getBoolPref("extensions.torbutton.tor_enabled"); + torbutton_update_toolbutton(mode); + } + } + else { + // A local check is not possible, so perform a remote check. + torbutton_initiate_remote_tor_check(); + } +} + +function torbutton_local_tor_check() +{ + let proxyType = m_tb_prefs.getIntPref("network.proxy.type"); + if (0 == proxyType) + return false; + + // Ask tor for its SOCKS listener address and port and compare to the + // browser preferences. + const kCmdArg = "net/listeners/socks"; + let resp = torbutton_send_ctrl_cmd("GETINFO " + kCmdArg + "\r\n"); + if (!resp) + return false; + + function logUnexpectedResponse() + { + torbutton_log(5, "unexpected tor response: " + resp); + } + + // Sample response: net/listeners/socks="127.0.0.1:9150" + // First, check for command argument prefix. + resp = resp.toLowerCase(); + if (0 != resp.indexOf(kCmdArg + '=')) { + logUnexpectedResponse(); + return false; + } + + // Remove double quotes if present. + resp = resp.substr(kCmdArg.length + 1); + let len = resp.length; + if ((len > 2) && ('"' == resp.charAt(0)) && ('"' == resp.charAt(len - 1))) + resp = resp.substring(1, len - 1); + + let tokens = resp.split(':'); + if (tokens.length < 2) { + logUnexpectedResponse(); + return false; + } + + let torSocksAddr = tokens[0]; + let torSocksPort = parseInt(tokens[1], 10); + if ((torSocksAddr.length < 1) || isNaN(torSocksPort)) { + logUnexpectedResponse(); + return false; + } + + torbutton_log(2, "Tor socks listener: " + torSocksAddr + ':' + torSocksPort); + + let socksAddr = m_tb_prefs.getCharPref("network.proxy.socks"); + let socksPort = m_tb_prefs.getIntPref("network.proxy.socks_port"); + + return ((socksAddr == torSocksAddr) && (socksPort == torSocksPort)); +} // torbutton_local_tor_check + + +function torbutton_initiate_remote_tor_check() +{ + let obsSvc = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + + try { + let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"] + .getService(Ci.nsISupports).wrappedJSObject; + let req = checkSvc.createCheckRequest(true); // async + req.onreadystatechange = function (aEvent) { + if (req.readyState === 4) { + let ret = checkSvc.parseCheckResponse(req); + + // If we received an error response from check.torproject.org, + // set the status of the tor check to failure (we don't want + // to indicate failure if we didn't receive a response). + if (ret == 2 || ret == 3 || ret == 5 || ret == 6 || ret == 7) { + checkSvc.statusOfTorCheck = checkSvc.kCheckFailed; + obsSvc.notifyObservers(null, k_tb_tor_check_failed_topic, null); + } else if (ret == 4) { + checkSvc.statusOfTorCheck = checkSvc.kCheckSuccessful; + } // Otherwise, redo the check later + + torbutton_log(3, "Tor remote check done. Result: " + ret); + } + }; + + torbutton_log(3, "Sending async Tor remote check"); + req.send(null); + } catch(e) { + if (e.result == 0x80004005) // NS_ERROR_FAILURE + torbutton_log(5, "Tor check failed! Is tor running?"); + else + torbutton_log(5, "Tor check failed! Tor internal error: "+e); + + checkSvc.statusOfTorCheck = checkSvc.kCheckFailed; + obsSvc.notifyObservers(null, k_tb_tor_check_failed_topic, null); + } +} // torbutton_initiate_remote_tor_check() + +function torbutton_tor_check_ok() +{ + let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"] + .getService(Ci.nsISupports).wrappedJSObject; + return (checkSvc.kCheckFailed != checkSvc.statusOfTorCheck); +} + // Bug 1506 P5: Despite the name, this is the way we disable // plugins for Tor Browser, too. // @@ -2251,12 +2452,15 @@ function torbutton_new_window(event)
// Check the version on every new window. We're already pinging check in these cases. torbutton_do_async_versioncheck(); + + torbutton_do_tor_check(); }
// Bug 1506 P2: This is only needed because we have observers // in XUL that should be in an XPCOM component function torbutton_close_window(event) { torbutton_window_pref_observer.unregister(); + torbutton_tor_check_observer.unregister();
// TODO: This is a real ghetto hack.. When the original window // closes, we need to find another window to handle observing diff --git a/src/chrome/locale/en/aboutTor.dtd b/src/chrome/locale/en/aboutTor.dtd new file mode 100644 index 0000000..2fc51c6 --- /dev/null +++ b/src/chrome/locale/en/aboutTor.dtd @@ -0,0 +1,64 @@ +<!-- + - Copyright (c) 2013, The Tor Project, Inc. + - See LICENSE for licensing information. + - vim: set sw=2 sts=2 ts=8 et syntax=xml: + --> + +<!ENTITY aboutTor.title "About Tor"> + +<!ENTITY aboutTor.outOfDateTorOn.label "HOWEVER, this browser is out of date."> +<!ENTITY aboutTor.outOfDateTorOff.label "ALSO, this browser is out of date."> +<!ENTITY aboutTor.outOfDate2.label "Click on the onion and then choose Download Tor Browser Bundle Update."> + +<!ENTITY aboutTor.check.label "Test Tor Network Settings"> + +<!ENTITY aboutTor.success.label "Congratulations!"> +<!ENTITY aboutTor.success2.label "This browser is configured to use Tor."> +<!ENTITY aboutTor.success3.label "You are now free to browse the Internet anonymously."> +<!ENTITY aboutTor.failure.label "Something Went Wrong!"> +<!ENTITY aboutTor.failure2.label "Tor is not working in this browser."> +<!ENTITY aboutTor.failure3prefix.label "For assistance, please contact "> +<!ENTITY aboutTor.failure3Link "help@rt.torproject.org"> +<!ENTITY aboutTor.failure3suffix.label "."> + +<!ENTITY aboutTor.search.label "Search"> +<!ENTITY aboutTor.searchSPPost.link "https://startpage.com/do/search"> +<!ENTITY aboutTor.searchDDGPost.link "https://duckduckgo.com/html/"> +<!ENTITY aboutTor.searchSP.privacy.beforeLink.label "Search "> +<!ENTITY aboutTor.searchDDG.privacy.beforeLink.label "Search "> +<!ENTITY aboutTor.searchSP.privacy.label "securely"> +<!ENTITY aboutTor.searchDDG.privacy.label "securely"> +<!ENTITY aboutTor.searchSP.privacy.link "https://startpage.com/eng/protect-privacy.html?"> +<!ENTITY aboutTor.searchDDG.privacy.link "https://duckduckgo.com/privacy.html"> +<!ENTITY aboutTor.searchSP.privacy.afterLink.label ""> +<!ENTITY aboutTor.searchDDG.privacy.afterLink.label ""> + +<!ENTITY aboutTor.searchSP.search.beforeLink.label " with "> +<!ENTITY aboutTor.searchDDG.search.beforeLink.label " with "> +<!ENTITY aboutTor.searchSP.search.label "Startpage"> +<!ENTITY aboutTor.searchDDG.search.label "DuckDuckGo"> +<!ENTITY aboutTor.searchSP.search.link "https://startpage.com/"> +<!ENTITY aboutTor.searchDDG.search.link "https://duckduckgo.com/"> +<!ENTITY aboutTor.searchSP.search.afterLink.label "."> +<!ENTITY aboutTor.searchDDG.search.afterLink.label "."> + +<!ENTITY aboutTor.torInfo1.label "Additional Info:"> +<!ENTITY aboutTor.torInfo2.label "Country & IP Address:"> +<!ENTITY aboutTor.torInfo3.label "Exit Node:"> +<!ENTITY aboutTor.torInfo4.label "This server does not log any information about visitors."> +<!ENTITY aboutTor.whatnextQuestion.label "What Next?"> +<!ENTITY aboutTor.whatnextAnswer.label "Tor is NOT all you need to browse anonymously! You may need to change some of your browsing habits to ensure your identity stays safe."> +<!ENTITY aboutTor.whatnext.label "Tips On Staying Anonymous »"> +<!ENTITY aboutTor.whatnext.link "https://www.torproject.org/download/download.html.en#warning"> +<!ENTITY aboutTor.helpInfo1.label "You Can Help!"> +<!ENTITY aboutTor.helpInfo2.label "There are many ways you can help make the Tor Network faster and stronger:"> +<!ENTITY aboutTor.helpInfo3.label "Run a Tor Relay Node »"> +<!ENTITY aboutTor.helpInfo3.link "https://www.torproject.org/docs/tor-doc-relay.html.en"> +<!ENTITY aboutTor.helpInfo4.label "Volunteer Your Services »"> +<!ENTITY aboutTor.helpInfo4.link "https://www.torproject.org/getinvolved/volunteer.html.en"> +<!ENTITY aboutTor.helpInfo5.label "Make a Donation »"> +<!ENTITY aboutTor.helpInfo5.link "https://www.torproject.org/donate/donate.html.en"> + +<!ENTITY aboutTor.footer.label "The Tor Project is a US 501(c)(3) non-profit dedicated to the research, development, and education of online anonymity and privacy."> +<!ENTITY aboutTor.learnMore.label "Learn more about The Tor Project »"> +<!ENTITY aboutTor.learnMore.link "https://www.torproject.org/about/overview.html.en"> diff --git a/src/chrome/skin/aboutTor.css b/src/chrome/skin/aboutTor.css new file mode 100644 index 0000000..b5e8f02 --- /dev/null +++ b/src/chrome/skin/aboutTor.css @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2013, The Tor Project, Inc. + * See LICENSE for licensing information. + * + * vim: set sw=2 sts=2 ts=8 et syntax=css: + */ + +* { + padding: 0px; + margin: 0px; +} + +body { + min-width: 920px; + max-width: 920px; + width: 100%; + height: 100%; + margin: 0px auto; + padding: 0px 0px; + font-size: 62.5%; + font-family: "Liberation Serif", "Times New Roman", Times, serif; + font-weight: normal; + color: #4d4d4d; + background-color: #FFFFFF; + background-attachment: fixed; + background-size: 100% 100%; +} + +body { + background-image: -moz-linear-gradient(top, #ffffff, #ffffff 10%, #dddddd 50%, #dddddd); +} + +body[toron] { + background-image: -moz-linear-gradient(top, #ffffff, #ffffff 10%, #d5ffd5 50%, #d5ffd5); +} + +body[toron] #torstatus-image { + background-image: url('chrome://torbutton/content/aboutTor/tor-on.png'); +} + +#torstatus-image { + position: absolute; + left: 70px; + height: 128px; + width: 128px; + background-image: url('chrome://torbutton/content/aboutTor/tor-off.png'); +} + +#toolbarIconArrowLeft, #toolbarIconArrowRight { + position: absolute; + top: 4px; + height: 250px; +} + +#toolbarIconArrowLeft { + left: 78px; + width: 30px; + background-image: url('chrome://torbutton/content/aboutTor/onionArrow.png'); +} + +#toolbarIconArrowRight { + left: 108px; + width: 10px; + background-image: url('chrome://torbutton/content/aboutTor/onionArrowRight.png'); +} + +a { + color: #008000; + text-decoration: none; +} + +a:hover { + color: #00A000; + text-decoration: underline; +} + +#testTorSettings { + font-size: 1.6em; + text-decoration: underline; + margin-bottom: 5px; +} + +#torstatus { + position: relative; /* needed for torstatus-image positioning */ + max-width: 620px; + height: 148px; + padding: 15px 128px 0px 128px; + margin: 20px auto 0px auto; +} + +.top { + text-align: center; + white-space: nowrap; +} + +body[toron][torNeedsUpdate] .hideIfTBBNeedsUpdate, +body:not([torNeedsUpdate]) .hideIfTorIsUpToDate { + display: none; +} + +body[toron] .hideIfTorOn, +body:not([toron]) .hideIfTorOff { + display: none; +} + +body[toron] .top h1, body[toron] .top h2 { + color: #008000; +} + +div.hideIfTorIsUpToDate, +body .top div.hideIfTorIsUpToDate h1 { + color: black; +} + +body .top div.hideIfTorIsUpToDate h1.hideIfTorOff { + margin-left: 30px; +} + +.top div.hideIfTorIsUpToDate h3 { + display: inline-block; +} + +.top div.hideIfTorOff h1 { + margin-top: 20px; +} + +.top h1 { + font-size: 4.14em; + font-weight: bold; + margin-bottom: 5px; +} + +.top h2 { + font-size: 2.934em; + margin-bottom: 20px; + font-weight: normal; +} + +.top #success2 { + margin-bottom: 5px; +} + +.top h3 { + font-size: 2.04em; + font-style: italic; + font-weight: normal; +} + +#middle { + position: relative; + width: 920px; + height: 20em; + text-align: center; +} + +#middle div.container { + position: absolute; + top: 3.1em; + width: 280px; + min-width: 280px; + min-height: 12.5em; + padding: 10px 10px; + margin: 8px 8px; + color: #222222; + background-color: #FFFFFF; + border: 1px solid #008000; + border-radius: 16px; + text-align: center; + vertical-align: top; +} + +#middle div.two { + left: 120px; +} + +#middle div.three { + left: 520px; +} + +#middle h1 { + font-family: "Liberation Sans", Arial, Helvetica, sans-serif; + font-size: 1.9em; + margin-bottom: 10px; +} + +#middle h2 { + font-size: 1.4em; + margin: 9px 0px 3px 0px; + font-weight: 500; +} + +#middle h6 { + font-family: "Liberation Sans", Arial, Helvetica, sans-serif; + font-size: 1em; + line-height: 1em; + font-style: italic; + font-weight: normal; + padding-top: 10px; +} + +#middle p { + font-size: 1.35em; + text-align: left; +} + +#middle a { + font-size: 1.35em; +} + +#middle a.tips { + display: block; + margin-top: 1.35em; +} + +#middle ul { + text-align: left; + padding: 5px 0 0 22px; +} + +#middle li { + padding-top: 2px; +} + +#bottom { + margin: 0px auto; + padding-bottom: 40px; + float: left; +} + +#bottom p { + font-size: 1.8em; + text-align: justify; + margin: 10px 125px 0px 125px; +} + +#bottom a { + color: #800080; + text-decoration: underline; +} + +#bottom a:hover { + color: #A000A0; + text-decoration: none; +} + +#bottom h4 { + margin-top: 50px; + line-height: 1em; + font-family: "Liberation Sans", Arial, Helvetica, sans-serif; + font-size: 1em; + font-weight: normal; + text-align: center; +} + +#bottom p.lang { + max-width: 620px; + margin: 10px auto; + font-family: "Liberation Sans", Arial, Helvetica, sans-serif; + font-size: 1em; + font-weight: normal; + text-align: center; +} + +.searchbox form { + width: 396px; + margin: 35px auto 1px auto; + text-align: left; +} + +.searchbox h4 { + padding-top: 5px; + font-size: 12px; + font-weight: normal; + font-family: "Liberation Sans", Arial, Helvetica, sans-serif; + text-align: center; +} + +#sbutton { + display: block; + float: right; + width: 33px; +} + +#sbutton input { + height: 32px; + width: 33px; + border: 0; + background: url('chrome://torbutton/content/aboutTor/search.png') no-repeat top left; + cursor: pointer; +} + +/* #sxw is the container div for the search field and button */ +#sxw { + border-top: rgb(31,119,45) solid 1px; + border-left: rgb(31,119,45) solid 1px; + border-bottom: rgb(94,213,99) solid 1px; + border-right: none; + border-radius: 3px 0 0 3px; + box-shadow: 0 1px 2px 0 rgba(0,0,0,0.2); + border-top-left-radius: 3px 3px; + border-bottom-left-radius: 3px 3px; +} + +/* #sx is the search input (text) field */ +#sx { + width: 350px; + height: 23px; + padding: 4px 6px 5px 6px; + margin: 0; + outline: none; + color: #222; + border: none; + font-family: "Segoe UI","Arial",sans-serif; + font-size: 18px; +} + diff --git a/src/components/aboutTor.js b/src/components/aboutTor.js new file mode 100644 index 0000000..2a3431f --- /dev/null +++ b/src/components/aboutTor.js @@ -0,0 +1,55 @@ +/************************************************************************* + * Copyright (c) 2013, The Tor Project, Inc. + * See LICENSE for licensing information. + * + * vim: set sw=2 sts=2 ts=8 et syntax=javascript: + * + * about:tor component + *************************************************************************/ + +// Module specific constants +const kMODULE_NAME = "about:tor"; +const kMODULE_CONTRACTID = "@mozilla.org/network/protocol/about;1?what=tor"; +const kMODULE_CID = Components.ID("84d47da6-79c3-4661-aa9f-8049476f7bf5"); + +const kAboutTorURL = "chrome://torbutton/content/aboutTor/aboutTor.xhtml"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function AboutTor() +{ +} + + +AboutTor.prototype = +{ + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), + + // nsIClassInfo implementation: + classDescription: kMODULE_NAME, + classID: kMODULE_CID, + contractID: kMODULE_CONTRACTID, + + // nsIAboutModule implementation: + newChannel: function(aURI) + { + let ioSvc = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + let channel = ioSvc.newChannel(kAboutTorURL, null, null); + channel.originalURI = aURI; + + return channel; + }, + + getURIFlags: function(aURI) + { + return Ci.nsIAboutModule.ALLOW_SCRIPT; + } +}; + + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutTor]); diff --git a/src/components/torCheckService.js b/src/components/torCheckService.js new file mode 100644 index 0000000..4972f05 --- /dev/null +++ b/src/components/torCheckService.js @@ -0,0 +1,134 @@ +/************************************************************************* + * Copyright (c) 2013, The Tor Project, Inc. + * See LICENSE for licensing information. + * + * vim: set sw=2 sts=2 ts=8 et syntax=javascript: + * + * Tor check service + *************************************************************************/ + +// Module specific constants +const kMODULE_NAME = "Torbutton Tor Check Service"; +const kMODULE_CONTRACTID = "@torproject.org/torbutton-torCheckService;1"; +const kMODULE_CID = Components.ID("5d57312b-5d8c-4169-b4af-e80d6a28a72e"); + +const Cr = Components.results; +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +function TBTorCheckService() { + this._logger = Cc["@torproject.org/torbutton-logger;1"] + .getService(Ci.nsISupports).wrappedJSObject; + this._logger.log(3, "Torbutton Tor Check Service initialized"); + + this._statusOfTorCheck = this.kCheckNotInitiated; + this.wrappedJSObject = this; +} + +TBTorCheckService.prototype = +{ + QueryInterface: function(iid) { + if (!iid.equals(Ci.nsIClassInfo) && + !iid.equals(Ci.nsISupports)) { + Components.returnCode = Cr.NS_ERROR_NO_INTERFACE; + return null; + } + + return this; + }, + + kCheckNotInitiated: 0, // Possible values for statusOfTorCheck. + kCheckSuccessful: 1, + kCheckFailed: 2, + + wrappedJSObject: null, + _logger: null, + _statusOfTorCheck: 0, // this.kCheckNotInitiated, + + // make this an nsIClassInfo object + flags: Ci.nsIClassInfo.DOM_OBJECT, + + // method of nsIClassInfo + classDescription: kMODULE_NAME, + classID: kMODULE_CID, + contractID: kMODULE_CONTRACTID, + + // method of nsIClassInfo + getInterfaces: function(count) { + var interfaceList = [Ci.nsIClassInfo]; + count.value = interfaceList.length; + return interfaceList; + }, + + // method of nsIClassInfo + getHelperForLanguage: function(count) { return null; }, + + // Public methods. + get statusOfTorCheck() + { + return this._statusOfTorCheck; + }, + + set statusOfTorCheck(aStatus) + { + this._statusOfTorCheck = aStatus; + }, + + createCheckRequest: function(aAsync) + { + let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Ci.nsIXMLHttpRequest); + //let req = new XMLHttpRequest(); Blocked by content policy + let prefs = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefBranch); + let url = prefs.getCharPref("extensions.torbutton.test_url"); + req.open('GET', url, aAsync); + req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; + req.overrideMimeType("text/xml"); + req.timeout = 120000; // Wait at most two minutes for a response. + return req; + }, + + parseCheckResponse: function(aReq) + { + let ret = 0; + if(aReq.status == 200) { + if(!aReq.responseXML) { + this._logger.log(5, "Check failed! Not text/xml!"); + ret = 1; + } else { + let result = aReq.responseXML.getElementById('TorCheckResult'); + + if(result===null) { + this._logger.log(5, "Test failed! No TorCheckResult element"); + ret = 2; + } else if(typeof(result.target) == 'undefined' + || result.target === null) { + this._logger.log(5, "Test failed! No target"); + ret = 3; + } else if(result.target === "success") { + this._logger.log(3, "Test Successful"); + ret = 4; + } else if(result.target === "failure") { + this._logger.log(5, "Tor test failed!"); + ret = 5; + } else if(result.target === "unknown") { + this._logger.log(5, "Tor test failed. TorDNSEL Failure?"); + ret = 6; + } else { + this._logger.log(5, "Tor test failed. Strange target."); + ret = 7; + } + } + } else { + this._logger.log(5, "Tor test failed. HTTP Error: "+aReq.status); + ret = -aReq.status; + } + + return ret; + } +}; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +var NSGetFactory = XPCOMUtils.generateNSGetFactory([TBTorCheckService]); diff --git a/src/defaults/preferences/preferences.js b/src/defaults/preferences/preferences.js index 7559977..670e322 100644 --- a/src/defaults/preferences/preferences.js +++ b/src/defaults/preferences/preferences.js @@ -25,6 +25,7 @@ pref("extensions.torbutton.socks_port",0); pref("extensions.torbutton.socks_version",5); pref("extensions.torbutton.locked_mode",true); pref("extensions.torbutton.test_url","https://check.torproject.org/?TorButton=true"); +pref("extensions.torbutton.test_url_interactive", "https://check.torproject.org/?lang=__LANG__"); pref("extensions.torbutton.test_failed",false); pref("extensions.torbutton.no_proxies_on","127.0.0.1"); pref("extensions.torbutton.versioncheck_url","https://check.torproject.org/RecommendedTBBVersions");
tor-commits@lists.torproject.org