Commit c48bf013 authored by Kathleen Brade's avatar Kathleen Brade Committed by Mike Perry
Browse files

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.
parent d183e972
......@@ -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}
......
<?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>
// 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="+locale+"&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="+locale+"&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