1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
|
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include content/searchCommon.js
var EXPORTED_SYMBOLS = ["SearchIntegration"];
Components.utils.import("resource://gre/modules/Services.jsm");
var MSG_DB_LARGE_COMMIT = 1;
var CRLF="\r\n";
/**
* Required to access the 64-bit registry, even though we're probably a 32-bit
* program
*/
var ACCESS_WOW64_64KEY = 0x0100;
/**
* The contract ID for the helper service.
*/
var WINSEARCHHELPER_CONTRACTID = "@mozilla.org/mail/windows-search-helper;1";
/**
* All the registry keys required for integration
*/
var gRegKeys =
[
// This is the property handler
{
root: Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
key: "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PropertySystem\\PropertyHandlers\\.wdseml",
name: "",
value: "{5FA29220-36A1-40f9-89C6-F4B384B7642E}"
},
// These two are the association with the MIME IFilter
{
root: Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
key: ".wdseml",
name: "Content Type",
value: "message/rfc822"
},
{
root: Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
key: ".wdseml\\PersistentHandler",
name: "",
value: "{5645c8c4-e277-11cf-8fda-00aa00a14f93}"
},
// This is the association with the Windows mail preview handler
{
root: Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
key: ".wdseml\\shellex\\{8895B1C6-B41F-4C1C-A562-0D564250836F}",
name: "",
value: "{b9815375-5d7f-4ce2-9245-c9d4da436930}"
},
// This is the association made to display results under email
{
root: Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
key: "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\KindMap",
name: ".wdseml",
value: "email;communication"
}
];
/**
* @namespace Windows Search-specific desktop search integration functionality
*/
var SearchIntegration =
{
__proto__: SearchSupport,
/// The property of the header and (sometimes) folders that's used to check
/// if a message is indexed
_hdrIndexedProperty: "winsearch_reindex_time",
/// The file extension that is used for support files of this component
_fileExt: ".wdseml",
/// The Windows Search pref base
_prefBase: "mail.winsearch.",
/// Helper (native) component
__winSearchHelper: null,
get _winSearchHelper()
{
if (!this.__winSearchHelper)
this.__winSearchHelper = Cc[WINSEARCHHELPER_CONTRACTID]
.getService(Ci.nsIMailWinSearchHelper);
return this.__winSearchHelper;
},
/// Whether the folders are already in the crawl scope
get _foldersInCrawlScope()
{
return this._winSearchHelper.foldersInCrawlScope;
},
/**
* Whether all the required registry keys are present
* We'll be optimistic here and assume that once the registry keys have been
* added, they won't be removed, at least while Thunderbird is open
*/
__regKeysPresent: false,
get _regKeysPresent()
{
if (!this.__regKeysPresent)
{
for (let i = 0; i < gRegKeys.length; i++)
{
let regKey = Cc["@mozilla.org/windows-registry-key;1"]
.createInstance(Ci.nsIWindowsRegKey);
try {
regKey.open(gRegKeys[i].root, gRegKeys[i].key, regKey.ACCESS_READ |
ACCESS_WOW64_64KEY);
}
catch (e) { return false; }
let valuePresent = regKey.hasValue(gRegKeys[i].name) &&
(regKey.readStringValue(gRegKeys[i].name) ==
gRegKeys[i].value);
regKey.close();
if (!valuePresent)
return false;
}
this.__regKeysPresent = true;
}
return true;
},
/// Use the folder's path (i.e., in profile dir) as is
_getSearchPathForFolder: function winsearch_get_search_path(aFolder)
{
return aFolder.filePath;
},
/// Use the search path as is
_getFolderForSearchPath: function winsearch_get_folder_for_search_path(aDir)
{
return MailUtils.getFolderForFileInProfile(aDir);
},
_pathNeedsReindexing: function winsearch_pathNeedsReindexing(aPath) {
// only needed on MacOSX (see bug 670566).
return false;
},
_init: function winsearch_init()
{
this._initLogging();
// If the helper service isn't present, we weren't compiled with the needed
// support. Mark ourselves null and return
if (!(WINSEARCHHELPER_CONTRACTID in Cc))
{
SearchIntegration = null;
return;
}
// We're currently only enabled on Vista and above
let windowsVersion = Services.sysinfo.getProperty("version");
if (parseFloat(windowsVersion) < 6)
{
this._log.fatal("Windows version " + windowsVersion + " < 6.0");
this.osVersionTooLow = true;
return;
}
let serviceRunning = false;
try {
serviceRunning = this._winSearchHelper.serviceRunning;
}
catch (e) {}
// If the service isn't running, then we should stay in backoff mode
if (!serviceRunning)
{
this._log.info("Windows Search service not running");
this.osComponentsNotRunning = true;
this._initSupport(false);
return;
}
let enabled = this.prefEnabled;
if (enabled)
this._log.info("Initializing Windows Search integration");
this._initSupport(enabled);
},
/**
* Add necessary hooks to Windows
*
* @return false if registration did not succeed, because the elevation
* request was denied
*/
register: function winsearch_register()
{
// If any of the two are not present, we need to elevate.
if (!this._foldersInCrawlScope || !this._regKeysPresent)
{
try {
this._winSearchHelper.runSetup(true);
}
catch (e) { return false; }
}
if (!this._winSearchHelper.isFileAssociationSet)
{
try {
this._winSearchHelper.setFileAssociation();
}
catch (e) { this._log.warn("File association not set"); }
}
// Also set the FANCI bit to 0 for the profile directory
let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
this._winSearchHelper.setFANCIBit(profD, false, true);
return true;
},
/**
* Remove integration from Windows. The only thing removed is the directory
* from the index list. This will ask for elevation.
*
* @return false if deregistration did not succeed, because the elevation
* request was denied
*/
deregister: function winsearch_deregister()
{
try {
this._winSearchHelper.runSetup(false);
}
catch (e) { return false; }
return true;
},
/// The stream listener to read messages
_streamListener: {
__proto__: SearchSupport._streamListenerBase,
/// Buffer to store the message
_message: "",
onStartRequest: function(request, context) {
try {
let outputFileStream = Cc["@mozilla.org/network/file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream);
outputFileStream.init(this._outputFile, -1, -1, 0);
this._outputStream = Cc["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Ci.nsIConverterOutputStream);
this._outputStream.init(outputFileStream, "UTF-8", 0, 0x0000);
}
catch (ex) { this._onDoneStreaming(false); }
},
onStopRequest: function(request, context, status, errorMsg) {
try {
// XXX Once the JS emitter gets checked in, this code should probably be
// switched over to use that
// Decode using getMsgTextFromStream
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
stringStream.setData(this._message, this._message.length);
let contentType = {};
let folder = this._msgHdr.folder;
let text = folder.getMsgTextFromStream(stringStream,
this._msgHdr.Charset, 65536,
50000, false, false,
contentType);
// To get the Received header, we need to parse the message headers.
// We only need the first header, which contains the latest received
// date
let headers = this._message.split(/\r\n\r\n|\r\r|\n\n/, 1)[0];
let mimeHeaders = Cc["@mozilla.org/messenger/mimeheaders;1"]
.createInstance(Ci.nsIMimeHeaders);
mimeHeaders.initialize(headers);
let receivedHeader = mimeHeaders.extractHeader("Received", false);
this._outputStream.writeString("From: " + this._msgHdr.author + CRLF);
// If we're a newsgroup, then add the name of the folder as the
// newsgroups header
if (folder instanceof Ci.nsIMsgNewsFolder)
this._outputStream.writeString("Newsgroups: " + folder.name + CRLF);
else
this._outputStream.writeString("To: " + this._msgHdr.recipients +
CRLF);
this._outputStream.writeString("CC: " + this._msgHdr.ccList + CRLF);
this._outputStream.writeString("Subject: " + this._msgHdr.subject +
CRLF);
if (receivedHeader)
this._outputStream.writeString("Received: " + receivedHeader + CRLF);
this._outputStream.writeString(
"Date: " + new Date(this._msgHdr.date / 1000).toUTCString() + CRLF);
this._outputStream.writeString("Content-Type: " + contentType.value +
"; charset=utf-8" + CRLF + CRLF);
this._outputStream.writeString(text + CRLF + CRLF);
this._msgHdr.setUint32Property(SearchIntegration._hdrIndexedProperty,
this._reindexTime);
folder.msgDatabase.Commit(MSG_DB_LARGE_COMMIT);
this._message = "";
SearchIntegration._log.info("Successfully written file");
}
catch (ex) {
SearchIntegration._log.error(ex);
this._onDoneStreaming(false);
return;
}
this._onDoneStreaming(true);
},
onDataAvailable: function(request, context, inputStream, offset, count) {
try {
let inStream = Cc["@mozilla.org/scriptableinputstream;1"]
.createInstance(Ci.nsIScriptableInputStream);
inStream.init(inputStream);
// It is necessary to read in data from the input stream
let inData = inStream.read(count);
// Ignore stuff after the first 50K or so
if (this._message && this._message.length > 50000)
return 0;
this._message += inData;
return 0;
}
catch (ex) {
SearchIntegration._log.error(ex);
this._onDoneStreaming(false);
}
}
}
};
SearchIntegration._init();
|