diff options
Diffstat (limited to '')
-rw-r--r-- | comm/mail/base/content/sanitize.js | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/comm/mail/base/content/sanitize.js b/comm/mail/base/content/sanitize.js new file mode 100644 index 0000000000..68510faeac --- /dev/null +++ b/comm/mail/base/content/sanitize.js @@ -0,0 +1,241 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var { PlacesUtils } = ChromeUtils.importESModule( + "resource://gre/modules/PlacesUtils.sys.mjs" +); +var { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +function Sanitizer() {} +Sanitizer.prototype = { + // warning to the caller: this one may raise an exception (e.g. bug #265028) + clearItem(aItemName) { + if (this.items[aItemName].canClear) { + this.items[aItemName].clear(); + } + }, + + canClearItem(aItemName) { + return this.items[aItemName].canClear; + }, + + prefDomain: "", + + getNameFromPreference(aPreferenceName) { + return aPreferenceName.substr(this.prefDomain.length); + }, + + /** + * Deletes privacy sensitive data in a batch, according to user preferences + * + * @returns null if everything's fine; an object in the form + * { itemName: error, ... } on (partial) failure + */ + sanitize() { + var branch = Services.prefs.getBranch(this.prefDomain); + var errors = null; + + // Cache the range of times to clear + if (this.ignoreTimespan) { + // If we ignore timespan, clear everything. + var range = null; + } else { + range = this.range || Sanitizer.getClearRange(); + } + + for (var itemName in this.items) { + var item = this.items[itemName]; + item.range = range; + if ("clear" in item && item.canClear && branch.getBoolPref(itemName)) { + // Some of these clear() may raise exceptions (see bug #265028) + // to sanitize as much as possible, we catch and store them, + // rather than fail fast. + // Callers should check returned errors and give user feedback + // about items that could not be sanitized + try { + item.clear(); + } catch (er) { + if (!errors) { + errors = {}; + } + errors[itemName] = er; + dump("Error sanitizing " + itemName + ": " + er + "\n"); + } + } + } + return errors; + }, + + // Time span only makes sense in certain cases. Consumers who want + // to only clear some private data can opt in by setting this to false, + // and can optionally specify a specific range. If timespan is not ignored, + // and range is not set, sanitize() will use the value of the timespan + // pref to determine a range + ignoreTimespan: true, + range: null, + + items: { + cache: { + clear() { + try { + // Cache doesn't consult timespan, nor does it have the + // facility for timespan-based eviction. Wipe it. + Services.cache2.clear(); + } catch (ex) {} + }, + + get canClear() { + return true; + }, + }, + + cookies: { + clear() { + if (this.range) { + // Iterate through the cookies and delete any created after our cutoff. + for (let cookie of Services.cookies.cookies) { + if (cookie.creationTime > this.range[0]) { + // This cookie was created after our cutoff, clear it + Services.cookies.remove( + cookie.host, + cookie.name, + cookie.path, + cookie.originAttributes + ); + } + } + } else { + // Remove everything + Services.cookies.removeAll(); + } + }, + + get canClear() { + return true; + }, + }, + + history: { + clear() { + if (this.range) { + PlacesUtils.history.removeVisitsByFilter({ + beginDate: new Date(this.range[0]), + endDate: new Date(this.range[1]), + }); + } else { + PlacesUtils.history.clear(); + } + + try { + Services.obs.notifyObservers(null, "browser:purge-session-history"); + } catch (e) {} + + try { + var predictor = Cc["@mozilla.org/network/predictor;1"].getService( + Ci.nsINetworkPredictor + ); + predictor.reset(); + } catch (e) {} + }, + + get canClear() { + // bug 347231: Always allow clearing history due to dependencies on + // the browser:purge-session-history notification. (like error console) + return true; + }, + }, + }, +}; + +// "Static" members +Sanitizer.prefDomain = "privacy.sanitize."; +Sanitizer.prefShutdown = "sanitizeOnShutdown"; +Sanitizer.prefDidShutdown = "didShutdownSanitize"; + +// Time span constants corresponding to values of the privacy.sanitize.timeSpan +// pref. Used to determine how much history to clear, for various items +Sanitizer.TIMESPAN_EVERYTHING = 0; +Sanitizer.TIMESPAN_HOUR = 1; +Sanitizer.TIMESPAN_2HOURS = 2; +Sanitizer.TIMESPAN_4HOURS = 3; +Sanitizer.TIMESPAN_TODAY = 4; + +// Return a 2 element array representing the start and end times, +// in the uSec-since-epoch format that PRTime likes. If we should +// clear everything, return null. Use ts if it is defined; otherwise +// use the timeSpan pref. +Sanitizer.getClearRange = function (ts) { + if (ts === undefined) { + ts = Sanitizer.prefs.getIntPref("timeSpan"); + } + if (ts === Sanitizer.TIMESPAN_EVERYTHING) { + return null; + } + + // PRTime is microseconds while JS time is milliseconds + var endDate = Date.now() * 1000; + switch (ts) { + case Sanitizer.TIMESPAN_HOUR: + var startDate = endDate - 3600000000; // 1*60*60*1000000 + break; + case Sanitizer.TIMESPAN_2HOURS: + startDate = endDate - 7200000000; // 2*60*60*1000000 + break; + case Sanitizer.TIMESPAN_4HOURS: + startDate = endDate - 14400000000; // 4*60*60*1000000 + break; + case Sanitizer.TIMESPAN_TODAY: + var d = new Date(); // Start with today + d.setHours(0); // zero us back to midnight... + d.setMinutes(0); + d.setSeconds(0); + startDate = d.valueOf() * 1000; // convert to epoch usec + break; + default: + throw new Error("Invalid time span for clear private data: " + ts); + } + return [startDate, endDate]; +}; + +Sanitizer._prefs = null; +Sanitizer.__defineGetter__("prefs", function () { + return Sanitizer._prefs + ? Sanitizer._prefs + : (Sanitizer._prefs = Services.prefs.getBranch(Sanitizer.prefDomain)); +}); + +// Shows sanitization UI +Sanitizer.showUI = function (aParentWindow) { + Services.ww.openWindow( + AppConstants.platform == "macosx" ? null : aParentWindow, + "chrome://messenger/content/sanitize.xhtml", + "Sanitize", + "chrome,titlebar,dialog,centerscreen,modal", + null + ); +}; + +/** + * Deletes privacy sensitive data in a batch, optionally showing the + * sanitize UI, according to user preferences + */ +Sanitizer.sanitize = function (aParentWindow) { + Sanitizer.showUI(aParentWindow); +}; + +// this is called on startup and shutdown, to perform pending sanitizations +Sanitizer._checkAndSanitize = function () { + const prefs = Sanitizer.prefs; + if ( + prefs.getBoolPref(Sanitizer.prefShutdown) && + !prefs.prefHasUserValue(Sanitizer.prefDidShutdown) + ) { + // this is a shutdown or a startup after an unclean exit + var s = new Sanitizer(); + s.prefDomain = "privacy.clearOnShutdown."; + s.sanitize() || prefs.setBoolPref(Sanitizer.prefDidShutdown, true); // sanitize() returns null on full success + } +}; |