[tor-commits] [torbutton/master] Remove cookie-jar-selector component

gk at torproject.org gk at torproject.org
Mon Nov 4 12:07:38 UTC 2019


commit 2dfa0e0c9cff7cfad93664e0b0b6cdc05b24b7f2
Author: Alex Catarineu <acat at torproject.org>
Date:   Sat Jun 8 15:10:01 2019 +0200

    Remove cookie-jar-selector component
---
 chrome.manifest                     |   5 -
 chrome/content/torbutton.js         |  18 +-
 components/cookie-jar-selector.js   | 460 ------------------------------------
 components/startup-observer.js      |  18 ++
 defaults/preferences/preferences.js |   2 -
 jar.mn                              |   5 -
 6 files changed, 19 insertions(+), 489 deletions(-)

diff --git a/chrome.manifest b/chrome.manifest
index 13bef661..d1ffe6d6 100644
--- a/chrome.manifest
+++ b/chrome.manifest
@@ -143,9 +143,6 @@ contract @torproject.org/torbutton-extAppBlocker;1 {3da0269f-fc29-4e9e-a678-c3b1
 component {06322def-6fde-4c06-aef6-47ae8e799629} components/startup-observer.js
 contract @torproject.org/startup-observer;1 {06322def-6fde-4c06-aef6-47ae8e799629}
 
-component {e6204253-b690-4159-bfe8-d4eedab6b3be} components/cookie-jar-selector.js
-contract @torproject.org/cookie-jar-selector;1 {e6204253-b690-4159-bfe8-d4eedab6b3be}
-
 component {5d57312b-5d8c-4169-b4af-e80d6a28a72e} components/torCheckService.js
 contract @torproject.org/torbutton-torCheckService;1 {5d57312b-5d8c-4169-b4af-e80d6a28a72e}
 
@@ -155,8 +152,6 @@ contract @torproject.org/torbutton-logger;1 {f36d72c9-9718-4134-b550-e109638331d
 component {e33fd6d4-270f-475f-a96f-ff3140279f68} components/domain-isolator.js
 contract @torproject.org/domain-isolator;1 {e33fd6d4-270f-475f-a96f-ff3140279f68}
 
-category profile-after-change CookieJarSelector @torproject.org/cookie-jar-selector;1
-
 category profile-after-change StartupObserver @torproject.org/startup-observer;1
 category profile-after-change DomainIsolator @torproject.org/domain-isolator;1
 category profile-after-change DragDropFilter @torproject.org/torbutton-dragDropFilter;1
diff --git a/chrome/content/torbutton.js b/chrome/content/torbutton.js
index 9846b864..72636125 100644
--- a/chrome/content/torbutton.js
+++ b/chrome/content/torbutton.js
@@ -1039,16 +1039,7 @@ async function torbutton_do_new_identity() {
 
   torbutton_log(3, "New Identity: Clearing Cookies and DOM Storage");
 
-  if (m_tb_prefs.getBoolPref("extensions.torbutton.cookie_protections")) {
-    var selector = Cc["@torproject.org/cookie-jar-selector;1"]
-                    .getService(Ci.nsISupports)
-                    .wrappedJSObject;
-    // This emits "cookie-changed", "cleared", which kills DOM storage
-    // and the safe browsing API key
-    selector.clearUnprotectedCookies("tor");
-  } else {
-    torbutton_clear_cookies();
-  }
+  torbutton_clear_cookies();
 
   torbutton_log(3, "New Identity: Closing open connections");
 
@@ -1558,13 +1549,6 @@ function torbutton_check_protections()
     document.getElementById("torbutton-checkForUpdate").hidden = false;
   }
 
-  var cookie_pref = m_tb_prefs.getBoolPref("extensions.torbutton.cookie_protections");
-  document.getElementById("torbutton-cookie-protector").disabled = !cookie_pref;
-
-  // XXX: Bug 14632: The cookie dialog is useless in private browsing mode in FF31ESR
-  // See https://trac.torproject.org/projects/tor/ticket/10353 for more info.
-  document.getElementById("torbutton-cookie-protector").hidden = m_tb_prefs.getBoolPref("browser.privatebrowsing.autostart");
-
   if (!m_tb_control_pass || (!m_tb_control_ipc_file && !m_tb_control_port)) {
     // TODO: Remove the Torbutton menu entry again once we have done our
     // security control redesign.
diff --git a/components/cookie-jar-selector.js b/components/cookie-jar-selector.js
deleted file mode 100644
index 79a66e8a..00000000
--- a/components/cookie-jar-selector.js
+++ /dev/null
@@ -1,460 +0,0 @@
-// Bug 1506 P1: This component is currently only used to protect
-// user-selected cookies from deletion. Moreover, all the E4X code is
-// deprecated and needs to be replaced with JSON.
-
-/*************************************************************************
- * Cookie Jar Selector (JavaScript XPCOM component)
- * Enables selection of separate cookie jars for (more) anonymous browsing.
- * Designed as a component of FoxTor, http://cups.cs.cmu.edu/foxtor/
- * Copyright 2006, distributed under the same (open source) license as FoxTor
- *
- * Contributor(s):
- *         Collin Jackson <mozilla at collinjackson.com>
- *
- *************************************************************************/
-
-// Module specific constants
-const kMODULE_NAME = "Cookie Jar Selector";
-const kMODULE_CONTRACTID = "@torproject.org/cookie-jar-selector;1";
-const kMODULE_CID = Components.ID("e6204253-b690-4159-bfe8-d4eedab6b3be");
-
-ChromeUtils.import("resource://torbutton/modules/default-prefs.js", {})
-  .ensureDefaultPrefs();
-
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-function Cookie(number,name,value,isDomain,host,rawHost,HttpOnly,path,isSecure,isSession,
-                expires,isProtected) {
-  this.number = number;
-  this.name = name;
-  this.value = value;
-  this.isDomain = isDomain;
-  this.host = host;
-  this.rawHost = rawHost;
-  this.isHttpOnly = HttpOnly;
-  this.path = path;
-  this.isSecure = isSecure;
-  this.isSession = isSession;
-  this.expires = expires;
-  this.isProtected = isProtected;
-}
-
-function CookieJarSelector() {
-  this.logger = Cc["@torproject.org/torbutton-logger;1"]
-      .getService(Ci.nsISupports).wrappedJSObject;
-
-  this.logger.log(3, "Component Load 5: New CookieJarSelector " + kMODULE_CONTRACTID);
-
-  this.prefs = Services.prefs;
-
-  var getProfileFile = function(filename) {
-    var loc = "ProfD"; // profile directory
-    var file = Services.dirsvc
-      .get(loc, Ci.nsIFile)
-      .clone();
-    file.append(filename); 
-    return file;
-  };
-
-  this.clearCookies = function() {
-    try {
-        Services.cookies.removeAll();
-    } catch (e) {
-        this.logger.log(4, "Cookie clearing exception: " + e);
-    }
-  };
-
-  this._cookiesToJS = function(getSession) {
-    var cookieManager = Services.cookies;
-    var cookiesEnum = cookieManager.enumerator;
-    var cookiesAsJS = [];
-    var count = 0;
-    while (cookiesEnum.hasMoreElements()) {
-        var nextCookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
-        var JSCookie = new Cookie(count++, nextCookie.name, nextCookie.value, nextCookie.isDomain, nextCookie.host,
-                   (nextCookie.host.charAt(0)==".") ? nextCookie.host.substring(1,nextCookie.host.length) : nextCookie.host,
-                   nextCookie.isHttpOnly, nextCookie.path, nextCookie.isSecure, nextCookie.isSession, nextCookie.expires,
-                   false);
-        // Save either session or non-session cookies this time around:
-        if (JSCookie.isSession && getSession ||
-                !JSCookie.isSession && !getSession)
-            cookiesAsJS.push(JSCookie);
-    }
-    return cookiesAsJS;
-  };
-
-  this._loadCookiesFromJS = function(cookiesAsJS) {
-        if (typeof(cookiesAsJS) == "undefined" || !cookiesAsJS)
-            return;
-
-        var cookieManager = Services.cookies;
-
-        for (var i = 0; i < cookiesAsJS.length; i++) {
-            var cookie = cookiesAsJS[i];
-            //this.logger.log(2, "Loading cookie: "+host+":"+cname+" until: "+expiry);
-            cookieManager.add(cookie.host, cookie.path, cookie.name, cookie.value,
-                              cookie.isSecure, cookie.isHttpOnly, cookie.isSession,
-                              cookie.expires);
-        }
-  };
-
-  this._cookiesToFile = function(name) {
-    var file = getProfileFile("cookies-" + name + ".json");
-    var foStream = Cc["@mozilla.org/network/file-output-stream;1"]
-          .createInstance(Ci.nsIFileOutputStream);
-    foStream.init(file, 0x02 | 0x08 | 0x20, 0o666, 0);
-    var data = JSON.stringify(this["cookiesobj-" + name]);
-    foStream.write(data, data.length);
-    foStream.close();
-  };
-
-  // Start1506
-  this._protectedCookiesToFile = function(name) {
-    var file = getProfileFile("protected-" + name + ".json");
-    var foStream = Cc["@mozilla.org/network/file-output-stream;1"]
-        .createInstance(Ci.nsIFileOutputStream);
-    foStream.init(file, 0x02 | 0x08 | 0x20, 0o666, 0);
-    var data = JSON.stringify(this["protected-" + name]);
-    foStream.write(data, data.length);
-    foStream.close();
-  };
-
-  this.addProtectedCookie = function(cookie) {
-    var name = "tor";
-    var cookies = this.getProtectedCookies(name);
-
-    if (typeof(cookies) == "undefined" || cookies == null
-            || cookies.length == 0)
-      cookies = [];
-
-    if (cookie.isSession) {
-      // session cookies get fucked up expiry. Give it 1yr if
-      // the user wants to save their session cookies
-      cookie.expires = Date.now()/1000 + 365*24*60*60;
-    }
-
-    cookies.push(cookie);
-    this["protected-" + name] = cookies;
-
-    if (!this.prefs.getBoolPref("browser.privatebrowsing.autostart")) {
-      // save protected cookies to file
-      this._protectedCookiesToFile(name);
-    } else {
-      try {
-        var file = getProfileFile("protected-" + name + ".json");
-        if (file.exists()) {
-          file.remove(false);
-        }
-      } catch(e) {
-        this.logger.log(5, "Can't remove "+name+" cookie file: "+e);
-      }
-    }
-  };
-
-  this.getProtectedCookies = function(name) {
-      var file = getProfileFile("protected-" + name + ".json");
-      if (!file.exists()) {
-        return this["protected-" + name];
-      }
-      var data = "";
-      var fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-          .createInstance(Ci.nsIFileInputStream);
-      var sstream = Cc["@mozilla.org/scriptableinputstream;1"]
-          .createInstance(Ci.nsIScriptableInputStream);
-      fstream.init(file, -1, 0, 0);
-      sstream.init(fstream); 
-
-      var str = sstream.read(4096);
-      while (str.length > 0) {
-          data += str;
-          str = sstream.read(4096);
-      }
-
-      sstream.close();
-      fstream.close();
-      try {
-          var ret = JSON.parse(data);
-      } catch(e) { // file has been corrupted; XXX: handle error differently
-          this.logger.log(5, "Cookies corrupted: "+e);
-          try {
-              file.remove(false); //XXX: is it necessary to remove it ?
-              var ret = null;
-          } catch(e2) {
-              this.logger.log(5, "Can't remove file "+e);
-          }
-      }
-      return ret;
-  };
-
-  this.protectCookies = function(cookies) {
-    var name = "tor";
-    this._writeProtectCookies(cookies,name);
-    if (!this.prefs.getBoolPref("browser.privatebrowsing.autostart")) {
-      // save protected cookies to file
-      this._protectedCookiesToFile(name);
-    } else {
-      try {
-        var file = getProfileFile("protected-" + name + ".json");
-        if (file.exists()) {
-          file.remove(false);
-        }
-      } catch(e) {
-        this.logger.log(5, "Can't remove "+name+" cookie file: "+e);
-      }
-    }
-  };
-
-  this._writeProtectCookies = function(cookies, name) {
-    for (var i = 0; i < cookies.length; i++) {
-        if (cookies[i].isSession) {
-            // session cookies get fucked up expiry. Give it 1yr if
-            // the user wants to save their session cookies
-            cookies[i].expires = Date.now()/1000 + 365*24*60*60;
-        }
-        cookies[i].isProtected = true;
-    }
-    this["protected-" + name] = cookies;
-  };
-  // End1506
-
-  this._cookiesFromFile = function(name) {
-      var file = getProfileFile("cookies-" + name + ".json");
-      if (!file.exists())
-          return null;
-      var data = "";
-      var fstream = Cc["@mozilla.org/network/file-input-stream;1"]
-          .createInstance(Ci.nsIFileInputStream);
-      var sstream = Cc["@mozilla.org/scriptableinputstream;1"]
-          .createInstance(Ci.nsIScriptableInputStream);
-      fstream.init(file, -1, 0, 0);
-      sstream.init(fstream); 
-
-      var str = sstream.read(4096);
-      while (str.length > 0) {
-          data += str;
-          str = sstream.read(4096);
-      }
-
-      sstream.close();
-      fstream.close();
-      try {
-          var ret = JSON.parse(data);
-      } catch(e) { // file has been corrupted; XXX: handle error differently
-          this.logger.log(5, "Cookies corrupted: "+e);
-          try {
-              file.remove(false); //XXX: is it necessary to remove it ?
-              var ret = null;
-          } catch(e2) {
-              this.logger.log(5, "Can't remove file "+e);
-          }
-      }
-      return ret;
-  };
-
-  this.saveCookies = function(name) {
-    // transition removes old tor-style cookie file
-    try {
-        var oldCookieFile = getProfileFile("cookies-"+name+".xml");
-        if (oldCookieFile.exists()) {
-            oldCookieFile.remove(false);
-        }
-    } catch(e) {
-        this.logger.log(5, "Can't remove old "+name+" file "+e);
-    }
-
-    // save cookies to JS objects
-    this["session-cookiesobj-" + name] = this._cookiesToJS(true);
-    this["cookiesobj-" + name] = this._cookiesToJS(false);
-
-    if (!this.prefs.getBoolPref("browser.privatebrowsing.autostart")) {
-        // save cookies to file
-        this._cookiesToFile(name);
-    } else {
-        // Clear the old file
-        try {
-            var file = getProfileFile("cookies-" + name + ".json");
-            if (file.exists()) {
-                file.remove(false);
-            }
-        } catch(e) {
-            this.logger.log(5, "Can't remove "+name+" cookie file "+e);
-        }
-    }
-
-    // ok, everything's fine
-    this.logger.log(2, "Cookies saved");
-  };
-
-  // Start1506
-  this.clearUnprotectedCookies = function(name) {
-    try {
-      var protCookies = this.getProtectedCookies(name);
-      if (protCookies == null || typeof(protCookies) == "undefined"
-              || protCookies.length == 0) {
-        //file does not exist - no protected cookies. Clear them all.
-        this.logger.log(3, "No protected cookies. Clearing all cookies.");
-        this.clearCookies();
-        return;
-      }
-      var cookiemanager = Services.cookies;
-
-      var enumerator = cookiemanager.enumerator;
-      var count = 0;
-      var protcookie = false;
-
-      while (enumerator.hasMoreElements()) {
-        var nextCookie = enumerator.getNext();
-        if (!nextCookie) break;
-
-        nextCookie = nextCookie.QueryInterface(Ci.nsICookie);
-        for (var i = 0; i < protCookies.length; i++) {
-          protcookie = protcookie || (nextCookie.host == protCookies[i].host &&
-                                      nextCookie.name == protCookies[i].name &&
-                                      nextCookie.path == protCookies[i].path);
-        }
-
-        if (!protcookie) {
-          cookiemanager.remove(nextCookie.host,
-                             nextCookie.name,
-                             nextCookie.path, false);
-        } else {
-          this.logger.log(3, "Found protected cookie for "+nextCookie.host);
-        }
-        protcookie = false;
-      }
-      // Emit cookie-changed event. This instructs other components to clear their identifiers
-      // (Specifically DOM storage and safe browsing, but possibly others)
-      var obsSvc = Services.obs;
-      obsSvc.notifyObservers(this, "cookie-changed", "cleared");
-    } catch (e) {
-      this.logger.log(5, "Error deleting unprotected cookies: " + e);
-    }
-  };
-  // End1506
-
-  this.loadCookies = function(name, deleteSavedCookieJar) {
-    // remove cookies before loading old ones
-    this.clearCookies();
-
-    if (!this.prefs.getBoolPref("browser.privatebrowsing.autostart")) {
-        // load cookies from file
-        this["cookiesobj-" + name] = this._cookiesFromFile(name);
-    }
-
-    //delete file if needed
-    if (deleteSavedCookieJar) { 
-        try {
-            var file = getProfileFile("cookies-" + name + ".json");
-            if (file.exists())
-                file.remove(false);
-        } catch(e) {
-            this.logger.log(5, "Can't remove saved "+name+" file "+e);
-        }
-    }
-
-    // load cookies from JS objects
-    this._loadCookiesFromJS(this["cookiesobj-"+name]);
-    this._loadCookiesFromJS(this["session-cookiesobj-"+name]);
-
-    // XXX: send a profile-do-change event?
-
-    // ok, everything's fine
-    this.logger.log(2, "Cookies reloaded");
-  };
-
-  // This JSObject is exported directly to chrome
-  this.wrappedJSObject = this;
-
-  // This timer is done so that in the event of a crash, we at least
-  // have recent cookies in a jar to reload from.
-  var jarThis = this;
-  this.timerCallback = {
-    cookie_changed: false,
-
-    QueryInterface: ChromeUtils.generateQI(["nsITimer"]),
-    notify() {
-       // this refers to timerCallback object. use jarThis to reference
-       // CookieJarSelector object.
-       if(!this.cookie_changed) {
-           jarThis.logger.log(2, "Got timer update, but no cookie change.");
-           return;
-       }
-       jarThis.logger.log(3, "Got timer update. Saving changed cookies to jar.");
-
-       this.cookie_changed = false;
-
-       jarThis.saveCookies("tor");
-       jarThis.logger.log(2, "Timer done. Cookies saved");
-    }
-  };
-
-}
-
-const nsIClassInfo = Ci.nsIClassInfo;
-const nsIObserver = Ci.nsIObserver;
-const nsITimer = Ci.nsITimer;
-
-// Start1506: You may or may not care about this:
-CookieJarSelector.prototype =
-{
-  QueryInterface: ChromeUtils.generateQI(["nsIClassInfo", "nsIObserver"]),
-
-  wrappedJSObject: null,  // Initialized by constructor
-
-  // make this an nsIClassInfo object
-  flags: nsIClassInfo.DOM_OBJECT,
-
-  _xpcom_categories: [{category:"profile-after-change"}],
-  classID: kMODULE_CID,
-  contractID: kMODULE_CONTRACTID,
-  classDescription: "CookieJarSelector",
-
-  // method of nsIClassInfo
-  getInterfaces: function(count) {
-    var interfaceList = [nsIClassInfo];
-    count.value = interfaceList.length;
-    return interfaceList;
-  },
-
-  // method of nsIClassInfo
-  getHelperForLanguage: function(count) { return null; },
-
-  // method of nsIObserver
-  observe : function(aSubject, aTopic, aData) {
-       switch(aTopic) { 
-        case "cookie-changed":
-            var prefs = Services.prefs;
-            this.timerCallback.cookie_changed = true;
-
-            if (aData == "added"
-                && prefs.getBoolPref("extensions.torbutton.cookie_auto_protect")
-                && !prefs.getBoolPref("extensions.torbutton.tor_memory_jar")) {
-              this.addProtectedCookie(aSubject.QueryInterface(Ci.nsICookie2));// protect the new cookie!
-            }
-            break;
-        case "profile-after-change":
-            var obsSvc = Services.obs;
-            obsSvc.addObserver(this, "cookie-changed");
-            // after profil loading, initialize a timer to call timerCallback
-            // at a specified interval
-            this.timer.initWithCallback(this.timerCallback, 60 * 1000, nsITimer.TYPE_REPEATING_SLACK); // 1 minute
-            this.logger.log(3, "Cookie jar selector got profile-after-change");
-            break;
-       }
-  },
-
-  timer:  Cc["@mozilla.org/timer;1"].createInstance(nsITimer),
-
-}
-
-/**
-* XPCOMUtils.generateNSGetFactory was introduced in Mozilla 2 (Firefox 4).
-* XPCOMUtils.generateNSGetModule is for Mozilla 1.9.2 (Firefox 3.6).
-*/
-if (XPCOMUtils.generateNSGetFactory)
-    var NSGetFactory = XPCOMUtils.generateNSGetFactory([CookieJarSelector]);
-else
-    var NSGetModule = XPCOMUtils.generateNSGetModule([CookieJarSelector]);
-
-// End1506
diff --git a/components/startup-observer.js b/components/startup-observer.js
index bf2f0f48..008134f8 100644
--- a/components/startup-observer.js
+++ b/components/startup-observer.js
@@ -29,6 +29,22 @@ const kMODULE_NAME = "Startup";
 const kMODULE_CONTRACTID = "@torproject.org/startup-observer;1";
 const kMODULE_CID = Components.ID("06322def-6fde-4c06-aef6-47ae8e799629");
 
+function cleanupCookies() {
+  const migratedPref = "extensions.torbutton.cookiejar_migrated";
+  if (!Services.prefs.getBoolPref(migratedPref, false)) {
+    // Cleanup stored cookie-jar-selector json files
+    const profileFolder = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
+    for (const file of profileFolder.directoryEntries) {
+      if (file.leafName.match(/^(cookies|protected)-.*[.]json$/)) {
+        try {
+          file.remove(false);
+        } catch (e) {}
+      }
+    }
+    Services.prefs.setBoolPref(migratedPref, true);
+  }
+}
+
 function StartupObserver() {
     this.logger = Cc["@torproject.org/torbutton-logger;1"]
                     .getService(Ci.nsISupports).wrappedJSObject;
@@ -62,6 +78,8 @@ function StartupObserver() {
       this.logger.log(4, "Early proxy change failed. Will try again at profile load. Error: "+e);
     }
 
+    cleanupCookies();
+
     // Using all possible locales so that we do not have to change this list every time we support
     // a new one.
     const allLocales = [
diff --git a/defaults/preferences/preferences.js b/defaults/preferences/preferences.js
index acea0a3d..2d1bd99f 100644
--- a/defaults/preferences/preferences.js
+++ b/defaults/preferences/preferences.js
@@ -26,8 +26,6 @@ pref("extensions.torbutton.inserted_security_level",false);
 pref("extensions.torbutton.maximize_warnings_remaining", 3);
 
 // Security prefs:
-pref("extensions.torbutton.cookie_protections",true);
-pref("extensions.torbutton.cookie_auto_protect",false);
 pref("extensions.torbutton.clear_http_auth",true);
 pref("extensions.torbutton.close_newnym",true);
 pref("extensions.torbutton.resize_new_windows",false);
diff --git a/jar.mn b/jar.mn
index 6697b543..8b6cbcf7 100644
--- a/jar.mn
+++ b/jar.mn
@@ -97,9 +97,6 @@ torbutton.jar:
 % component {06322def-6fde-4c06-aef6-47ae8e799629} %components/startup-observer.js
 % contract @torproject.org/startup-observer;1 {06322def-6fde-4c06-aef6-47ae8e799629}
 
-% component {e6204253-b690-4159-bfe8-d4eedab6b3be} %components/cookie-jar-selector.js
-% contract @torproject.org/cookie-jar-selector;1 {e6204253-b690-4159-bfe8-d4eedab6b3be}
-
 % component {5d57312b-5d8c-4169-b4af-e80d6a28a72e} %components/torCheckService.js
 % contract @torproject.org/torbutton-torCheckService;1 {5d57312b-5d8c-4169-b4af-e80d6a28a72e}
 
@@ -109,8 +106,6 @@ torbutton.jar:
 % component {e33fd6d4-270f-475f-a96f-ff3140279f68} %components/domain-isolator.js
 % contract @torproject.org/domain-isolator;1 {e33fd6d4-270f-475f-a96f-ff3140279f68}
 
-% category profile-after-change CookieJarSelector @torproject.org/cookie-jar-selector;1
-
 % category profile-after-change StartupObserver @torproject.org/startup-observer;1
 % category profile-after-change DomainIsolator @torproject.org/domain-isolator;1
 % category profile-after-change DragDropFilter @torproject.org/torbutton-dragDropFilter;1





More information about the tor-commits mailing list