/* 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/. */ this.EXPORTED_SYMBOLS = ['GlodaUtils']; var Cc = Components.classes; var Ci = Components.interfaces; var Cr = Components.results; var Cu = Components.utils; Cu.import("resource:///modules/mailServices.js"); /** * @namespace A holding place for logic that is not gloda-specific and should * reside elsewhere. */ var GlodaUtils = { /** * This Regexp is super-complicated and used at least in two different parts of * the code, so let's expose it from one single location. */ PART_RE: new RegExp("^[^?]+\\?(?:/;section=\\d+\\?)?(?:[^&]+&)*part=([^&]+)(?:&[^&]+)*$"), deMime: function gloda_utils_deMime(aString) { return MailServices.mimeConverter.decodeMimeHeader(aString, null, false, true); }, _headerParser: MailServices.headerParser, /** * Parses an RFC 2822 list of e-mail addresses and returns an object with * 4 attributes, as described below. We will use the example of the user * passing an argument of '"Bob Smith" '. * * This method (by way of nsIMsgHeaderParser) takes care of decoding mime * headers, but is not aware of folder-level character set overrides. * * count: the number of addresses parsed. (ex: 1) * addresses: a list of e-mail addresses (ex: ["bob@example.com"]) * names: a list of names (ex: ["Bob Smith"]) * fullAddresses: aka the list of name and e-mail together (ex: ['"Bob Smith" * ']). * * This method is a convenience wrapper around nsIMsgHeaderParser. */ parseMailAddresses: function gloda_utils_parseMailAddresses(aMailAddresses) { let addresses = {}, names = {}, fullAddresses = {}; this._headerParser.parseHeadersWithArray(aMailAddresses, addresses, names, fullAddresses); return {names: names.value, addresses: addresses.value, fullAddresses: fullAddresses.value, count: names.value.length}; }, /** * MD5 hash a string and return the hex-string result. Impl from nsICryptoHash * docs. */ md5HashString: function gloda_utils_md5hash(aString) { let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. createInstance(Ci.nsIScriptableUnicodeConverter); let trash = {}; converter.charset = "UTF-8"; let data = converter.convertToByteArray(aString, trash); let hasher = Cc['@mozilla.org/security/hash;1']. createInstance(Ci.nsICryptoHash); hasher.init(Ci.nsICryptoHash.MD5); hasher.update(data, data.length); let hash = hasher.finish(false); // return the two-digit hexadecimal code for a byte function toHexString(charCode) { return ("0" + charCode.toString(16)).slice(-2); } // convert the binary hash data to a hex string. let hex = Object.keys(hash).map(i => toHexString(hash.charCodeAt(i))); return hex.join(""); }, getCardForEmail: function gloda_utils_getCardForEmail(aAddress) { // search through all of our local address books looking for a match. let enumerator = MailServices.ab.directories; let cardForEmailAddress; let addrbook; while (!cardForEmailAddress && enumerator.hasMoreElements()) { addrbook = enumerator.getNext().QueryInterface(Ci.nsIAbDirectory); try { cardForEmailAddress = addrbook.cardForEmailAddress(aAddress); if (cardForEmailAddress) return cardForEmailAddress; } catch (ex) {} } return null; }, _FORCE_GC_AFTER_NUM_HEADERS: 4096, _headersSeen: 0, /** * As |forceGarbageCollection| says, once XPConnect sees a header, it likes * to hold onto that reference. This method is used to track the number of * headers we have seen and force a GC when we have to. * * Ideally the indexer's idle-biased GC mechanism would take care of all the * GC; we are just a failsafe to make sure that our memory usage is bounded * based on the number of headers we have seen rather than just time. * Since holding onto headers can keep databases around too, this also * helps avoid keeping file handles open, etc. * * |forceGarbageCollection| will zero our tracking variable when a GC happens * so we are informed by the indexer's GC triggering. * * And of course, we don't want to trigger collections willy nilly because * they have a cost even if there is no garbage. * * @param aNumHeadersSeen The number of headers code has seen. A granularity * of hundreds of messages should be fine. */ considerHeaderBasedGC: function(aNumHeadersSeen) { this._headersSeen += aNumHeadersSeen; if (this._headersSeen >= this._FORCE_GC_AFTER_NUM_HEADERS) this.forceGarbageCollection(); }, /** * Force a garbage-collection sweep. Gloda has to force garbage collection * periodically because XPConnect's XPCJSRuntime::DeferredRelease mechanism * can end up holding onto a ridiculously high number of XPConnect objects in * between normal garbage collections. This has mainly posed a problem * because nsAutolock is a jerk in DEBUG builds in 1.9.1, but in theory this * also helps us even out our memory usage. * We also are starting to do this more to try and keep the garbage collection * durations acceptable. We intentionally avoid triggering the cycle * collector in those cases, as we do presume a non-trivial fixed cost for * cycle collection. (And really all we want is XPConnect to not be a jerk.) * This method exists mainly to centralize our GC activities and because if * we do start involving the cycle collector, that is a non-trivial block of * code to copy-and-paste all over the place (at least in a module). * * @param aCycleCollecting Do we need the cycle collector to run? Currently * unused / unimplemented, but we would use * nsIDOMWindowUtils.garbageCollect() to do so. */ forceGarbageCollection: function gloda_utils_garbageCollection(aCycleCollecting) { Cu.forceGC(); this._headersSeen = 0; } };