Bug 430614 [GSoC] Thunderbird integration into Windows Vista/Windows Search indexer. p=Siddharth Agarwal <sid1337@gmail.com>,r=beckley,sr=bienvenu
git-svn-id: svn://10.0.0.236/trunk@253074 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -52,6 +52,11 @@ ifdef MOZ_SAFE_BROWSING
|
||||
DIRS += phishing
|
||||
endif
|
||||
|
||||
# Mac and Windows have search integration components
|
||||
ifneq (,$(filter windows cocoa mac, $(MOZ_WIDGET_TOOLKIT)))
|
||||
DIRS += search
|
||||
endif
|
||||
|
||||
DIRS += build
|
||||
|
||||
EXTRA_PP_COMPONENTS = nsMailDefaultHandler.js
|
||||
|
||||
@@ -54,4 +54,12 @@ EXTRA_PP_COMPONENTS = \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
# If on Windows, build Windows Search integration
|
||||
ifneq (,$(filter windows, $(MOZ_WIDGET_TOOLKIT)))
|
||||
# Windows Search component
|
||||
EXTRA_PP_COMPONENTS = \
|
||||
nsWinSearchIntegration.js \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
413
mozilla/mail/components/search/content/searchCommon.js
Normal file
413
mozilla/mail/components/search/content/searchCommon.js
Normal file
@@ -0,0 +1,413 @@
|
||||
#if 0
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is spotlight integration code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* David Bienvenu <bienvenu@mozilla.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Siddharth Agarwal <sid1337@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*
|
||||
* Common, useful functions for desktop search integration components.
|
||||
*
|
||||
* The following symbols have to be defined for each component that includes this:
|
||||
* - gHdrIndexedProperty: the property in the database that indicates whether a message
|
||||
* has been indexed
|
||||
* - gFileExt: the file extension to be used for support files
|
||||
* - gPrefBase: the base for preferences that are stored
|
||||
* - gStreamListener: an nsIStreamListener to read message text
|
||||
*/
|
||||
#endif
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
var gCurrentFolderToIndex;
|
||||
var gLastFolderIndexedUri = ""; // this is stored in a pref
|
||||
var gHeaderEnumerator;
|
||||
var gMsgHdrsToIndex;
|
||||
var gMessenger;
|
||||
var gAlarm;
|
||||
var gBackgroundIndexingDone;
|
||||
var gPrefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch(null);
|
||||
|
||||
/*
|
||||
* Init function -- this should be called from the component's init function
|
||||
*/
|
||||
function InitSupportIntegration()
|
||||
{
|
||||
gMessenger = Cc["@mozilla.org/messenger;1"].createInstance().QueryInterface(Ci.nsIMessenger);
|
||||
|
||||
var notificationService = Cc["@mozilla.org/messenger/msgnotificationservice;1"]
|
||||
.getService(Ci.nsIMsgFolderNotificationService);
|
||||
notificationService.addListener(gFolderListener);
|
||||
var ObserverService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
ObserverService.addObserver(MsgMsgDisplayedObserver, "MsgMsgDisplayed", false);
|
||||
gMsgHdrsToIndex = new Array();
|
||||
|
||||
restartTimer(60);
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions are to index already existing messages
|
||||
*/
|
||||
function FindNextFolderToIndex()
|
||||
{
|
||||
accountManager = Cc["@mozilla.org/messenger/account-manager;1"].getService(Ci.nsIMsgAccountManager);
|
||||
var servers = accountManager.allServers;
|
||||
var foundFolder = false;
|
||||
var useNextFolder = false;
|
||||
|
||||
for (var i = 0; i < servers.Count() && !foundFolder; i++)
|
||||
{
|
||||
var server = servers.QueryElementAt(i, Ci.nsIMsgIncomingServer);
|
||||
var rootFolder = server.rootFolder;
|
||||
var allFolders = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
|
||||
rootFolder.ListDescendents(allFolders);
|
||||
var numFolders = allFolders.Count();
|
||||
SIDump("in find next folder, gLastFolderIndexedUri = " + gLastFolderIndexedUri + "\n");
|
||||
for (var folderIndex = 0; folderIndex < numFolders && !foundFolder; folderIndex++)
|
||||
{
|
||||
var folder = allFolders.GetElementAt(folderIndex).QueryInterface(Ci.nsIMsgFolder);
|
||||
// if no folder was indexed (or the pref's not set), just use the first folder
|
||||
if (!gLastFolderIndexedUri.length || useNextFolder)
|
||||
{
|
||||
gCurrentFolderToIndex = folder;
|
||||
foundFolder = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gLastFolderIndexedUri == folder.URI)
|
||||
useNextFolder = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundFolder)
|
||||
gCurrentFolderToIndex = null;
|
||||
}
|
||||
|
||||
function FindNextHdrToIndex()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!gHeaderEnumerator)
|
||||
gHeaderEnumerator = gCurrentFolderToIndex.getMessages(null);
|
||||
|
||||
// iterate over the folder finding the next message to index
|
||||
while (gHeaderEnumerator.hasMoreElements())
|
||||
{
|
||||
var msgHdr = gHeaderEnumerator.getNext().QueryInterface(Ci.nsIMsgDBHdr);
|
||||
if (!msgHdr.getUint32Property(gHdrIndexedProperty))
|
||||
return msgHdr;
|
||||
}
|
||||
}
|
||||
catch(ex) {}
|
||||
gHeaderEnumerator = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
function onTimer()
|
||||
{
|
||||
var msgHdrToIndex = null;
|
||||
|
||||
if (gBackgroundIndexingDone)
|
||||
return;
|
||||
|
||||
// find the current folder we're working on
|
||||
if (!gCurrentFolderToIndex)
|
||||
FindNextFolderToIndex();
|
||||
|
||||
// we'd like to index more than one message on each timer fire,
|
||||
// but since streaming is async, it's hard to know how long
|
||||
// it's going to take to stream any particular message.
|
||||
if (gCurrentFolderToIndex)
|
||||
{
|
||||
var msgHdrToIndex = FindNextHdrToIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we've cycled through all the folders, we should take a break
|
||||
// from indexing of existing messages
|
||||
gBackgroundIndexingDone = true;
|
||||
gPrefBranch.setCharPref(gPrefBase + ".lastFolderIndexedUri", "");
|
||||
}
|
||||
if (!msgHdrToIndex)
|
||||
{
|
||||
SIDump("reached end of folder\n");
|
||||
if (gCurrentFolderToIndex)
|
||||
{
|
||||
gLastFolderIndexedUri = gCurrentFolderToIndex.URI;
|
||||
gPrefBranch.setCharPref(gPrefBase + ".lastFolderIndexedUri", gLastFolderIndexedUri);
|
||||
gCurrentFolderToIndex = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueMessageToGetIndexed(msgHdrToIndex);
|
||||
}
|
||||
restartTimer(gMsgHdrsToIndex.length > 1 ? 5 : 1);
|
||||
}
|
||||
|
||||
function restartTimer(seconds)
|
||||
{
|
||||
if (gAlarm)
|
||||
gAlarm.cancel();
|
||||
var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
|
||||
.getService().wrappedJSObject;
|
||||
|
||||
gAlarm = new jslib.G_Alarm(onTimer, seconds*1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* This object gets notifications for messages that are read, giving them a
|
||||
* higher priority
|
||||
*/
|
||||
var MsgMsgDisplayedObserver =
|
||||
{
|
||||
// Components.interfaces.nsIObserver
|
||||
observe: function(aHeaderSink, aTopic, aData)
|
||||
{
|
||||
// if the user is reading messages, we're not idle, so restart timer.
|
||||
restartTimer(60);
|
||||
SIDump("topic = " + aTopic + " uri = " + aData + "\n");
|
||||
var msgHdr = gMessenger.msgHdrFromURI(aData);
|
||||
var indexed = msgHdr.getUint32Property(gHdrIndexedProperty);
|
||||
if (!indexed)
|
||||
{
|
||||
var file = GetSupportFileForMsgHdr(msgHdr);
|
||||
if (!file.exists())
|
||||
QueueMessageToGetIndexed(msgHdr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This object gets notifications for new/moved/copied/deleted messages/folders
|
||||
*/
|
||||
var gFolderListener = {
|
||||
msgAdded: function(aMsg)
|
||||
{
|
||||
SIDump("in msgAdded\n");
|
||||
restartTimer(30);
|
||||
// The message already being there is an expected case
|
||||
var file = GetSupportFileForMsgHdr(aMsg);
|
||||
if (!file.exists())
|
||||
QueueMessageToGetIndexed(aMsg);
|
||||
},
|
||||
|
||||
msgsDeleted: function(aMsgs)
|
||||
{
|
||||
SIDump("in msgsDeleted\n");
|
||||
// mail getting deleted, we're not idle, so restart timer.
|
||||
restartTimer(60);
|
||||
var count = aMsgs.length;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var file = GetSupportFileForMsgHdr(aMsgs.queryElementAt(i, Ci.nsIMsgDBHdr));
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
}
|
||||
},
|
||||
|
||||
msgsMoveCopyCompleted: function(aMove, aSrcMsgs, aDestFolder)
|
||||
{
|
||||
SIDump("in msgsMoveCopyCompleted\n");
|
||||
var count = aSrcMsgs.length;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var msg = aSrcMsgs.queryElementAt(i, Ci.nsIMsgDBHdr);
|
||||
var srcFile = GetSupportFileForMsgHdr(msg);
|
||||
if (srcFile && srcFile.exists())
|
||||
{
|
||||
var destFile = aDestFolder.filePath;
|
||||
destFile.leafName = destFile.leafName + ".mozmsgs";
|
||||
if (!destFile.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
// create the directory, if it doesn't exist
|
||||
destFile.create(Ci.nsIFile.DIRECTORY_TYPE, 0644);
|
||||
}
|
||||
catch(ex) {SIDump(ex);}
|
||||
}
|
||||
SIDump ("dst file path = " + destFile.path + "\n");
|
||||
SIDump ("src file path = " + srcFile.path + "\n");
|
||||
if (destFile.exists())
|
||||
if (aMove)
|
||||
srcFile.moveTo(destFile, "");
|
||||
else
|
||||
srcFile.copyTo(destFile, "");
|
||||
}
|
||||
}
|
||||
restartTimer(30);
|
||||
SIDump("moveCopyCompleted move = " + aMove + "\n");
|
||||
},
|
||||
|
||||
folderDeleted: function(aFolder)
|
||||
{
|
||||
SIDump("in folderDeleted, folder name = " + aFolder.prettiestName + "\n");
|
||||
var srcFile = aFolder.filePath;
|
||||
srcFile.leafName = srcFile.leafName + ".mozmsgs";
|
||||
srcFile.remove(true);
|
||||
},
|
||||
|
||||
folderMoveCopyCompleted: function(aMove, aSrcFolder, aDestFolder)
|
||||
{
|
||||
SIDump("in folderMoveCopyCompleted, aMove = " + aMove + "\n");
|
||||
var srcFile = aSrcFolder.filePath;
|
||||
var destFile = aDestFolder.filePath;
|
||||
srcFile.leafName = srcFile.leafName + ".mozmsgs";
|
||||
destFile.leafName += ".sbd";
|
||||
SIDump("src file path = " + srcFile.path + "\n");
|
||||
SIDump("dst file path = " + destFile.path + "\n");
|
||||
if (srcFile.exists())
|
||||
{
|
||||
if (aMove)
|
||||
srcFile.moveTo(destFile, "");
|
||||
else
|
||||
srcFile.copyTo(destFile, "");
|
||||
}
|
||||
},
|
||||
|
||||
folderRenamed: function(aOrigFolder, aNewFolder)
|
||||
{
|
||||
SIDump("in folderRenamed, aOrigFolder = "+aOrigFolder.prettiestName+", aNewFolder = "+aNewFolder.prettiestName+"\n");
|
||||
var srcFile = aOrigFolder.filePath;
|
||||
srcFile.leafName = srcFile.leafName + ".mozmsgs";
|
||||
var destName = aNewFolder.name + ".mozmsgs";
|
||||
SIDump("src file path = " + srcFile.path + "\n");
|
||||
SIDump("dst name = " + destName + "\n");
|
||||
if (srcFile.exists())
|
||||
srcFile.moveTo(null, destName);
|
||||
},
|
||||
|
||||
itemEvent: function(aItem, aEvent, aData)
|
||||
{
|
||||
SIDump("in itemEvent, aItem = "+aItem+", aEvent = "+aEvent+", aData = "+aData+"\n");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Support functions to queue/generate files
|
||||
*/
|
||||
function QueueMessageToGetIndexed(msgHdr)
|
||||
{
|
||||
if (gMsgHdrsToIndex.push(msgHdr) == 1)
|
||||
{
|
||||
SIDump("generating support file\n");
|
||||
GenerateSupportFile(msgHdr);
|
||||
}
|
||||
else
|
||||
SIDump("queueing support file generation\n");
|
||||
}
|
||||
|
||||
function GetSupportFileForMsgHdr(msgHdr)
|
||||
{
|
||||
var folder = msgHdr.folder;
|
||||
if (folder)
|
||||
{
|
||||
var messageId = msgHdr.messageId;
|
||||
messageId = encodeURIComponent(messageId);
|
||||
SIDump("encoded message id = " + messageId + "\n");
|
||||
// this should work on the trunk, but not in 2.0
|
||||
// messageId = netUtils.escapeString(messageId, 3 /* netUtils.ESCAPE_URL_PATH */);
|
||||
if (folder)
|
||||
{
|
||||
var file = folder.filePath;
|
||||
file.leafName = file.leafName + ".mozmsgs";
|
||||
file.appendRelativePath(messageId + gFileExt);
|
||||
SIDump("getting support file path = " + file.path + "\n");
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const MSG_FLAG_HAS_RE = 0x0010;
|
||||
|
||||
function GenerateSupportFile(msgHdr)
|
||||
{
|
||||
try
|
||||
{
|
||||
var folder = msgHdr.folder;
|
||||
if (folder)
|
||||
{
|
||||
var messageId = msgHdr.messageId;
|
||||
// for the trunk, this should work
|
||||
// var netUtils = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsINetUtil);
|
||||
// messageId = netUtils.escapeString(messageId, netUtils.ESCAPE_URL_PATH);
|
||||
messageId = encodeURIComponent(messageId);
|
||||
// We don't require the subject for this, keeping it if necessary later.
|
||||
// gStreamListener.subject = ((msgHdr.flags & MSG_FLAG_HAS_RE) ? "Re: " : "") + msgHdr.mime2DecodedSubject;
|
||||
SIDump("generate support file, message id = " + messageId + "\n");
|
||||
var file = folder.filePath;
|
||||
|
||||
file.leafName = file.leafName + ".mozmsgs";
|
||||
SIDump("file leafname = " + file.leafName + "\n");
|
||||
if (!file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
// create the directory, if it doesn't exist
|
||||
file.create(Ci.nsIFile.DIRECTORY_TYPE, 0644);
|
||||
}
|
||||
catch(ex) {SIDump(ex);}
|
||||
}
|
||||
gStreamListener.msgHdr = msgHdr;
|
||||
|
||||
file.appendRelativePath(messageId + gFileExt);
|
||||
//file.leafName = messageId + gFileExt;
|
||||
SIDump("file path = " + file.path + "\n");
|
||||
file.create(0, 0644);
|
||||
var uri = folder.getUriForMsg(msgHdr);
|
||||
//SIDump("in onItemAdded messenger = " + messenger + "\n");
|
||||
var msgService = gMessenger.messageServiceFromURI(uri);
|
||||
gStreamListener.outputFile = file;
|
||||
|
||||
msgService.streamMessage(uri, gStreamListener, null, null, false, "", null);
|
||||
}
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
SIDump(ex);
|
||||
gStreamListener.onDoneStreamingCurMessage(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Debug function */
|
||||
var gSIDump = true;
|
||||
function SIDump(str)
|
||||
{
|
||||
if (gSIDump)
|
||||
dump(str);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
@@ -20,6 +20,7 @@
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Siddharth Agarwal <sid1337@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@@ -35,216 +36,34 @@
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
#include content/searchCommon.js
|
||||
|
||||
const MSG_DB_LARGE_COMMIT = 1;
|
||||
|
||||
// Module object
|
||||
var SpotlightIntegrationMod = {
|
||||
firstTime: true,
|
||||
cid : Components.ID("{42EFAD76-FCDC-4757-951E-26896759E87E}"),
|
||||
progid: "@mozilla.org/desktop-search-integration;1",
|
||||
className: "Spotlight Integration",
|
||||
// The property of the header that's used to check if a message is indexed
|
||||
const gHdrIndexedProperty = "indexed";
|
||||
|
||||
factory:
|
||||
{
|
||||
createInstance: function (aOuter, aIID)
|
||||
{
|
||||
if (aOuter != null)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
if (!aIID.equals(Components.interfaces.nsISupports))
|
||||
throw Components.results.NS_ERROR_INVALID_ARG;
|
||||
|
||||
InitSpotlightIntegration();
|
||||
// return the singleton
|
||||
return nsSpotlightIntegration.QueryInterface(aIID);
|
||||
}
|
||||
}, // factory
|
||||
|
||||
// The file extension that is used for support files of this component
|
||||
const gFileExt = ".mozeml";
|
||||
|
||||
getClassObject: function(aCompMgr, aCID, aIID)
|
||||
{
|
||||
if (!aIID.equals(Components.interfaces.nsIFactory))
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
if (aCID.equals(this.cid))
|
||||
return this.factory;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
// The pref base
|
||||
const gPrefBase = "mail.spotlight";
|
||||
|
||||
registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
|
||||
{
|
||||
aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
|
||||
aCompMgr.registerFactoryLocation(this.cid, this.className, this.progid, aFileSpec, aLocation, aType);
|
||||
},
|
||||
|
||||
unregisterSelf: function(aCompMgr, aFileSpec, aLocation)
|
||||
{
|
||||
aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
|
||||
aCompMgr.unregisterFactoryLocation(this.cid, aFileSpec);
|
||||
},
|
||||
|
||||
canUnload: function(aCompMgr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
function NSGetModule(aCompMgr, aFileSpec)
|
||||
{
|
||||
return SpotlightIntegrationMod;
|
||||
}
|
||||
|
||||
var nsSpotlightIntegration = {
|
||||
|
||||
QueryInterface: function(aIID)
|
||||
{
|
||||
if (aIID.equals(Components.interfaces.nsISupports))
|
||||
return this;
|
||||
|
||||
Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var gCurrentFolderToIndex;
|
||||
var gLastFolderIndexedUri = ""; // this is stored in the pref "mail.spotlight.lastFolderIndexedUri"
|
||||
var gHeaderEnumerator;
|
||||
var gPrefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch(null);
|
||||
var gIndexMsgsToSpotlight;
|
||||
var gAlarm;
|
||||
var gBackgroundIndexingDone;
|
||||
var gMessenger;
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function InitSpotlightIntegration()
|
||||
{
|
||||
SIDump("initializing spotlight integration\n");
|
||||
|
||||
var enabled;
|
||||
try {
|
||||
gIndexMsgsToSpotlight = gPrefBranch.getBoolPref("mail.spotlight.enable");
|
||||
gLastFolderIndexedUri = gPrefBranch.getCharPref("mail.spotlight.lastFolderIndexedUri");
|
||||
enabled = gPrefBranch.getBoolPref(gPrefBase + ".enable");
|
||||
gLastFolderIndexedUri = gPrefBranch.getCharPref(gPrefBase + ".lastFolderIndexedUri");
|
||||
} catch (ex) {}
|
||||
|
||||
if (!gIndexMsgsToSpotlight)
|
||||
|
||||
if (!enabled)
|
||||
return;
|
||||
var nsIFolderListener = Components.interfaces.nsIFolderListener;
|
||||
gMessenger = Components.classes["@mozilla.org/messenger;1"].createInstance().QueryInterface(Components.interfaces.nsIMessenger);
|
||||
|
||||
var notificationService = Components.classes["@mozilla.org/messenger/msgnotificationservice;1"].getService(Components.interfaces.nsIMsgFolderNotificationService);
|
||||
notificationService.addListener(gFolderListener);
|
||||
var ObserverService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
|
||||
ObserverService.addObserver(CreateMsgDisplayedObserver, "MsgMsgDisplayed", false);
|
||||
gMsgHdrsToIndex = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
|
||||
|
||||
restartTimer(60);
|
||||
}
|
||||
|
||||
function FindNextFolderToIndex()
|
||||
{
|
||||
accountManager = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Components.interfaces.nsIMsgAccountManager);
|
||||
var servers = accountManager.allServers;
|
||||
var foundFolder = false;
|
||||
var useNextFolder = false;
|
||||
|
||||
for (var i = 0; i < servers.Count() && !foundFolder; i++)
|
||||
{
|
||||
var server = servers.QueryElementAt(i, Components.interfaces.nsIMsgIncomingServer);
|
||||
var rootFolder = server.rootFolder;
|
||||
var allFolders = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
|
||||
rootFolder.ListDescendents(allFolders);
|
||||
var numFolders = allFolders.Count();
|
||||
SIDump("in find next folder, gLastFolderIndexedUri = " + gLastFolderIndexedUri + "\n");
|
||||
for (var folderIndex = 0; folderIndex < numFolders && !foundFolder; folderIndex++)
|
||||
{
|
||||
var folder = allFolders.GetElementAt(folderIndex).QueryInterface(Components.interfaces.nsIMsgFolder);
|
||||
// if no folder was indexed (or the pref's not set), just use the first folder
|
||||
if (!gLastFolderIndexedUri.length || useNextFolder)
|
||||
{
|
||||
gCurrentFolderToIndex = folder;
|
||||
foundFolder = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gLastFolderIndexedUri == folder.URI)
|
||||
useNextFolder = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function FindNextHdrToIndex()
|
||||
{
|
||||
if (!gHeaderEnumerator)
|
||||
{
|
||||
var msgDB = gCurrentFolderToIndex.getMsgDatabase(null);
|
||||
gHeaderEnumerator = msgDB.EnumerateMessages();
|
||||
}
|
||||
// iterate over the folder finding the next message to
|
||||
// index...
|
||||
while (gHeaderEnumerator.hasMoreElements())
|
||||
{
|
||||
var msgHdr = gHeaderEnumerator.getNext().QueryInterface(Components.interfaces.nsIMsgDBHdr);
|
||||
if (!msgHdr.getUint32Property("indexed"))
|
||||
return msgHdr;
|
||||
}
|
||||
gHeaderEnumerator = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
function onTimer()
|
||||
{
|
||||
var msgHdrToIndex = null;
|
||||
|
||||
if (gBackgroundIndexingDone)
|
||||
return;
|
||||
|
||||
// find the current folder we're working on
|
||||
if (!gCurrentFolderToIndex)
|
||||
FindNextFolderToIndex();
|
||||
|
||||
|
||||
// we'd like to index more than one message on each timer fire,
|
||||
// but since streaming is async, it's hard to know how long
|
||||
// it's going to take to stream any particular message. Mozilla has no way of telling
|
||||
// us when the system is idle.
|
||||
if (gCurrentFolderToIndex)
|
||||
{
|
||||
var msgHdrToIndex = FindNextHdrToIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we've cycled through all the folders, we should take a break
|
||||
// from indexing of existing messages
|
||||
gBackgroundIndexingDone = true;
|
||||
|
||||
}
|
||||
if (!msgHdrToIndex)
|
||||
{
|
||||
SIDump("reached end of folder\n");
|
||||
if (gCurrentFolderToIndex)
|
||||
{
|
||||
gLastFolderIndexedUri = gCurrentFolderToIndex.URI;
|
||||
gPrefBranch.setCharPref("mail.spotlight.lastFolderIndexedUri", gLastFolderIndexedUri);
|
||||
gCurrentFolderToIndex = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueMessageToGetIndexed(msgHdrToIndex);
|
||||
}
|
||||
restartTimer(gMsgHdrsToIndex.Count() > 1 ? 5 : 1);
|
||||
}
|
||||
|
||||
function restartTimer(seconds)
|
||||
{
|
||||
if (gAlarm)
|
||||
gAlarm.cancel();
|
||||
var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
|
||||
.getService().wrappedJSObject;
|
||||
|
||||
gAlarm = new jslib.G_Alarm(onTimer, seconds*1000);
|
||||
SIDump("initializing spotlight integration\n");
|
||||
InitSupportIntegration();
|
||||
}
|
||||
|
||||
function xmlEscapeString(s)
|
||||
@@ -252,32 +71,9 @@ function xmlEscapeString(s)
|
||||
s = s.replace(/&/g, "&");
|
||||
s = s.replace(/>/g, ">");
|
||||
s = s.replace(/</g, "<");
|
||||
return s;
|
||||
return s;
|
||||
}
|
||||
|
||||
var CreateMsgDisplayedObserver =
|
||||
{
|
||||
// Components.interfaces.nsIObserver
|
||||
observe: function(aHeaderSink, aTopic, aData)
|
||||
{
|
||||
// if the user is reading messages, we're not idle, so restart timer.
|
||||
restartTimer(60);
|
||||
SIDump("topic = " + aTopic + " uri = " + aData + "\n");
|
||||
var msgHdr = gMessenger.msgHdrFromURI(aData);
|
||||
var indexed = msgHdr.getUint32Property("indexed");
|
||||
if (!indexed)
|
||||
{
|
||||
var file = GetSpotlightFileForMsgHdr(msgHdr);
|
||||
if (!file.exists())
|
||||
QueueMessageToGetIndexed(msgHdr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var gMsgHdrsToIndex;
|
||||
|
||||
|
||||
var fileHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.\ncom/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>";
|
||||
|
||||
var gStreamListener = {
|
||||
@@ -293,27 +89,22 @@ onDoneStreamingCurMessage: function(successful)
|
||||
{
|
||||
if (!successful && this.msgHdr)
|
||||
{
|
||||
var file = GetSpotlightFileForMsgHdr(this.msgHdr);
|
||||
var file = GetSupportFileForMsgHdr(this.msgHdr);
|
||||
if (file && file.exists())
|
||||
file.remove(false);
|
||||
}
|
||||
// should we try to delete the file on disk in case not successful?
|
||||
gMsgHdrsToIndex.DeleteElementAt(0);
|
||||
|
||||
if (gMsgHdrsToIndex.GetElementAt(0))
|
||||
{
|
||||
var msgHdr = gMsgHdrsToIndex.QueryElementAt(0, Components.interfaces.nsIMsgDBHdr);
|
||||
GenerateSpotlightFile(msgHdr);
|
||||
}
|
||||
gMsgHdrsToIndex.shift();
|
||||
|
||||
if (gMsgHdrsToIndex.length > 0)
|
||||
GenerateSupportFile(gMsgHdrsToIndex[0]);
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
QueryInterface: function(aIId, instance) {
|
||||
if (aIId.equals(Components.interfaces.nsIStreamListener) ||
|
||||
aIId.equals(Components.interfaces.nsISupports))
|
||||
if (aIId.equals(Ci.nsIStreamListener) ||
|
||||
aIId.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
|
||||
Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return null;
|
||||
},
|
||||
@@ -321,11 +112,11 @@ QueryInterface: function(aIId, instance) {
|
||||
onStartRequest: function(request, context) {
|
||||
try
|
||||
{
|
||||
|
||||
var outputFileStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Components.interfaces.nsIFileOutputStream);
|
||||
|
||||
var outputFileStream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
outputFileStream.init(this.outputFile, -1, -1, 0);
|
||||
this.outputStream = outputFileStream.QueryInterface(Components.interfaces.nsIOutputStream);
|
||||
this.outputStream = outputFileStream.QueryInterface(Ci.nsIOutputStream);
|
||||
this.outputStream.write(fileHeader, fileHeader.length);
|
||||
this.outputStream.write("<key>kMDItemLastUsedDate</key><string>", 38);
|
||||
// need to write the date as a string
|
||||
@@ -333,54 +124,53 @@ onStartRequest: function(request, context) {
|
||||
this.outputStream.write(curTimeStr, curTimeStr.length);
|
||||
// need to write the subject in utf8 as the title
|
||||
this.outputStream.write("</string>\n<key>kMDItemTitle</key>\n<string>", 42);
|
||||
|
||||
|
||||
if (!this.unicodeConverter)
|
||||
{
|
||||
this.unicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
|
||||
this.unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
this.unicodeConverter.charset = "UTF-8";
|
||||
|
||||
|
||||
}
|
||||
var utf8Subject = this.unicodeConverter.ConvertFromUnicode(this.subject);
|
||||
utf8Subject += this.unicodeConverter.Finish();
|
||||
utf8Subject = xmlEscapeString(utf8Subject);
|
||||
this.outputStream.write(utf8Subject, utf8Subject.length);
|
||||
|
||||
this.outputStream.write(utf8Subject, utf8Subject.length);
|
||||
|
||||
// need to write the subject in utf8 as the title
|
||||
this.outputStream.write("</string>\n<key>kMDItemDisplayName</key>\n<string>", 48);
|
||||
this.outputStream.write(utf8Subject, utf8Subject.length);
|
||||
|
||||
this.outputStream.write(utf8Subject, utf8Subject.length);
|
||||
|
||||
this.outputStream.write("</string>\n<key>kMDItemTextContent</key>\n<string>", 48);
|
||||
var author = this.msgHdr.mime2DecodedAuthor;
|
||||
var recipients = this.msgHdr.mime2DecodedRecipients;
|
||||
|
||||
|
||||
var utf8Author = this.unicodeConverter.ConvertFromUnicode(author);
|
||||
utf8Author += this.unicodeConverter.Finish() + " ";
|
||||
utf8Author = xmlEscapeString(utf8Author);
|
||||
var utf8Recipients = this.unicodeConverter.ConvertFromUnicode(recipients);
|
||||
utf8Recipients += this.unicodeConverter.Finish() + " ";
|
||||
utf8Recipients = xmlEscapeString(utf8Recipients);
|
||||
this.outputStream.write(utf8Author, utf8Author.length);
|
||||
this.outputStream.write(utf8Recipients, utf8Recipients.length);
|
||||
|
||||
this.outputStream.write(utf8Subject, utf8Subject.length);
|
||||
this.outputStream.write(utf8Author, utf8Author.length);
|
||||
this.outputStream.write(utf8Recipients, utf8Recipients.length);
|
||||
|
||||
this.outputStream.write(utf8Subject, utf8Subject.length);
|
||||
this.outputStream.write(" ", 1);
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
onDoneStreamingCurMessage(false);
|
||||
onDoneStreamingCurMessage(false);
|
||||
}
|
||||
},
|
||||
|
||||
onStopRequest: function(request, context, status, errorMsg) {
|
||||
try
|
||||
{
|
||||
|
||||
// we want to write out the from, to, cc, and subject headers into the
|
||||
// Text Content value, so they'll be indexed.
|
||||
|
||||
var stringStream = Components.classes["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Components.interfaces.nsIStringInputStream);
|
||||
|
||||
var stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsIStringInputStream);
|
||||
stringStream.setData(this.message, this.message.length);
|
||||
var temp = this.msgHdr.folder.getMsgTextFromStream(this.msgHdr, stringStream, 20000, 20000, false);
|
||||
temp = xmlEscapeString(temp);
|
||||
@@ -388,37 +178,39 @@ onStopRequest: function(request, context, status, errorMsg) {
|
||||
this.outputStream.write(temp, temp.length);
|
||||
// close out the content, dict, and plist
|
||||
this.outputStream.write("</string>\n</dict>\n</plist>\n", 26);
|
||||
|
||||
|
||||
this.outputStream.close();
|
||||
// this.outputFile.
|
||||
this.msgHdr.setUint32Property("indexed", 1);
|
||||
this.msgHdr.setUint32Property(gHdrIndexedProperty, 1);
|
||||
var msgDB = this.msgHdr.folder.getMsgDatabase(null);
|
||||
msgDB.Commit(MSG_DB_LARGE_COMMIT);
|
||||
|
||||
|
||||
this.message = "";
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
dump(ex);
|
||||
this.onDoneStreamingCurMessage(false);
|
||||
this.onDoneStreamingCurMessage(false);
|
||||
return;
|
||||
}
|
||||
this.onDoneStreamingCurMessage(true);
|
||||
},
|
||||
|
||||
onDataAvailable: function(request, context, inputStream, offset, count) {
|
||||
try
|
||||
try
|
||||
{
|
||||
|
||||
// ignore stuff after the first 20K or so
|
||||
var inStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
inStream.init(inputStream);
|
||||
|
||||
// It is necessary to read in data from the input stream
|
||||
var inData = inStream.read(count);
|
||||
|
||||
// ignore stuff after the first 20K or so
|
||||
if (this.message && this.message.length > 20000)
|
||||
return 0;
|
||||
var inStream = Components.classes["@mozilla.org/scriptableinputstream;1"].
|
||||
createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||
|
||||
inStream.init(inputStream);
|
||||
|
||||
this.message += inStream.read(count);
|
||||
|
||||
this.message += inData;
|
||||
return 0;
|
||||
}
|
||||
catch (ex)
|
||||
@@ -430,217 +222,39 @@ onDataAvailable: function(request, context, inputStream, offset, count) {
|
||||
|
||||
}
|
||||
|
||||
// the folderListener object
|
||||
var gFolderListener = {
|
||||
|
||||
itemAdded: function(aItem)
|
||||
/* XPCOM boilerplate code */
|
||||
function SpotlightIntegration() { }
|
||||
SpotlightIntegration.prototype = {
|
||||
classDescription: "Spotlight Integration",
|
||||
classID: Components.ID("{cc9c2a34-567b-451a-a942-1a1c3ec26e07}"),
|
||||
contractID: "@mozilla.org/spotlight-search-integration;1",
|
||||
|
||||
_xpcom_categories: [{
|
||||
category: "app-startup",
|
||||
service: true
|
||||
}],
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupports]),
|
||||
|
||||
observe : function(aSubject, aTopic, aData)
|
||||
{
|
||||
restartTimer(30);
|
||||
SIDump("itemAdded\n");
|
||||
var msgHdr;
|
||||
try
|
||||
switch(aTopic)
|
||||
{
|
||||
msgHdr = aItem.QueryInterface(Components.interfaces.nsIMsgDBHdr);
|
||||
case "app-startup":
|
||||
var obsSvc = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
obsSvc.addObserver(this, "profile-after-change", false);
|
||||
break;
|
||||
case "profile-after-change":
|
||||
try { InitSpotlightIntegration(); }
|
||||
catch(err) { SIDump("Could not initialize spotlight component"); }
|
||||
break;
|
||||
default:
|
||||
throw Components.Exception("Unknown topic: " + aTopic);
|
||||
}
|
||||
catch (ex) {}
|
||||
if (msgHdr)
|
||||
QueueMessageToGetIndexed(msgHdr);
|
||||
},
|
||||
|
||||
// folder or msg deleted (no trash)
|
||||
itemDeleted: function(aItem)
|
||||
{
|
||||
SIDump("in itemDeleted\n");
|
||||
// mail getting deleted, we're not idle, so restart timer.
|
||||
restartTimer(60);
|
||||
var msgHdr;
|
||||
try{
|
||||
msgHdr = aItem.QueryInterface(Components.interfaces.nsIMsgDBHdr);
|
||||
} catch (ex) {}
|
||||
|
||||
if (msgHdr)
|
||||
{
|
||||
var file = GetSpotlightFileForMsgHdr(msgHdr);
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var folder = aItem.QueryInterface(Components.interfaces.nsIMsgFolder);
|
||||
if (folder)
|
||||
{
|
||||
var srcFile = folder.filePath;
|
||||
srcFile.leafName = srcFile.leafName + ".mozmsgs";
|
||||
srcFile.remove(true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
itemMoveCopyCompleted: function(aMove, aSrcItems, aDestFolder)
|
||||
{
|
||||
var folder;
|
||||
try {
|
||||
folder = aSrcItems.QueryElementAt(0, Components.interfaces.nsIMsgFolder);
|
||||
} catch (ex) { }
|
||||
if (folder)
|
||||
{
|
||||
var destFile = aDestFolder.filePath;
|
||||
var srcFile = folder.filePath;
|
||||
srcFile.leafName = srcFile.leafName + ".mozmsgs";
|
||||
destFile.leafName += ".sbd";
|
||||
SIDump ("dst file path = " + destFile.path + "\n");
|
||||
SIDump ("src file path = " + srcFile.path + "\n");
|
||||
if (srcFile.exists())
|
||||
{
|
||||
if (aMove)
|
||||
srcFile.moveTo(destFile, "");
|
||||
else
|
||||
srcFile.copyTo(destFile, "");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = aSrcItems.QueryElementAt(0, Components.interfaces.nsIMsgDBHdr);
|
||||
if (msg)
|
||||
{
|
||||
var numMsgs = aSrcItems.Count();
|
||||
for (var msgIndex = 0; msgIndex < numMsgs; msgIndex++)
|
||||
{
|
||||
msg = aSrcItems.QueryElementAt(msgIndex, Components.interfaces.nsIMsgDBHdr);
|
||||
var srcFile = GetSpotlightFileForMsgHdr(msg);
|
||||
if (srcFile && srcFile.exists())
|
||||
{
|
||||
var destFile = aDestFolder.filePath;
|
||||
destFile.leafName = destFile.leafName + ".mozmsgs";
|
||||
if (!destFile.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
// create the directory, if it doesn't exist
|
||||
destFile.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0644);
|
||||
}
|
||||
catch(ex) {dump(ex);}
|
||||
}
|
||||
SIDump ("dst file path = " + destFile.path + "\n");
|
||||
SIDump ("src file path = " + srcFile.path + "\n");
|
||||
if (destFile.exists())
|
||||
if (aMove)
|
||||
srcFile.moveTo(destFile, "");
|
||||
else
|
||||
srcFile.copyTo(destfile, "");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
restartTimer(30);
|
||||
SIDump("moveCopyCompleted move = " + aMove + "\n");
|
||||
},
|
||||
|
||||
folderRenamed: function(aOrigFolder, aNewFolder)
|
||||
{
|
||||
|
||||
},
|
||||
// extensibility hook
|
||||
itemEvent: function(aItem, aEvent, aData)
|
||||
{
|
||||
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function QueueMessageToGetIndexed(msgHdr)
|
||||
var components = [SpotlightIntegration];
|
||||
function NSGetModule(aCompMgr, aFileSpec)
|
||||
{
|
||||
var isupportsHdr = msgHdr.QueryInterface(Components.interfaces.nsISupports);
|
||||
gMsgHdrsToIndex.AppendElement(isupportsHdr);
|
||||
if (gMsgHdrsToIndex.Count() == 1)
|
||||
{
|
||||
SIDump("generating spotlight file\n");
|
||||
GenerateSpotlightFile(msgHdr);
|
||||
}
|
||||
else
|
||||
SIDump("queueing spotlight file generation\n");
|
||||
}
|
||||
|
||||
function GetSpotlightFileForMsgHdr(msgHdr)
|
||||
{
|
||||
var folder = msgHdr.folder;
|
||||
if (folder)
|
||||
{
|
||||
var messageId = msgHdr.messageId;
|
||||
messageId = encodeURIComponent(messageId);
|
||||
SIDump("encoded message id = " + messageId + "\n");
|
||||
// this should work on the trunk, but not in 2.0
|
||||
// messageId = netUtils.escapeString(messageId, 3 /* netUtils.ESCAPE_URL_PATH */);
|
||||
if (folder)
|
||||
{
|
||||
var file = folder.filePath;
|
||||
file.leafName = file.leafName + ".mozmsgs";
|
||||
file.appendRelativePath(messageId + ".mozeml");
|
||||
SIDump("getting spotlight file path = " + file.path + "\n");
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
const MSG_FLAG_HAS_RE = 0x0010;
|
||||
|
||||
function GenerateSpotlightFile(msgHdr)
|
||||
{
|
||||
try
|
||||
{
|
||||
var folder = msgHdr.folder;
|
||||
if (folder)
|
||||
{
|
||||
var messageId = msgHdr.messageId;
|
||||
// for the trunk, this should work
|
||||
// var netUtils = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsINetUtil);
|
||||
// messageId = netUtils.escapeString(messageId, netUtils.ESCAPE_URL_PATH);
|
||||
messageId = encodeURIComponent(messageId);
|
||||
gStreamListener.subject = ((msgHdr.flags & MSG_FLAG_HAS_RE) ? "Re: " : "") + msgHdr.mime2DecodedSubject;
|
||||
SIDump("generate spotlight file subject = " + gStreamListener.subject + "message id = " + messageId + "\n");
|
||||
var file = folder.filePath;
|
||||
|
||||
file.leafName = file.leafName + ".mozmsgs";
|
||||
SIDump("file leafname = " + file.leafName + "\n");
|
||||
if (!file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
// create the directory, if it doesn't exist
|
||||
file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0644);
|
||||
}
|
||||
catch(ex) {dump(ex);}
|
||||
}
|
||||
gStreamListener.msgHdr = msgHdr;
|
||||
file.appendRelativePath(messageId + ".mozeml");
|
||||
//file.leafName = messageId + ".mozeml";
|
||||
SIDump("file path = " + file.path + "\n");
|
||||
file.create(0, 0644);
|
||||
var uri = folder.getUriForMsg(msgHdr);
|
||||
//SIDump("in onItemAdded messenger = " + messenger + "\n");
|
||||
var msgService = gMessenger.messageServiceFromURI(uri);
|
||||
gStreamListener.outputFile = file;
|
||||
|
||||
msgService.streamMessage(uri, gStreamListener, null, null, false, "", null);
|
||||
}
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
dump(ex);
|
||||
gStreamListener.onDoneStreamingCurMessage(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var gSIDump = true;
|
||||
|
||||
function SIDump(str)
|
||||
{
|
||||
if (gSIDump)
|
||||
dump(str);
|
||||
return XPCOMUtils.generateModule(components);
|
||||
}
|
||||
|
||||
250
mozilla/mail/components/search/nsWinSearchIntegration.js
Normal file
250
mozilla/mail/components/search/nsWinSearchIntegration.js
Normal file
@@ -0,0 +1,250 @@
|
||||
# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is spotlight integration code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# David Bienvenu <bienvenu@mozilla.com>
|
||||
# Portions created by the Initial Developer are Copyright (C) 2007
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Siddharth Agarwal <sid1337@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
#include content/searchCommon.js
|
||||
|
||||
const MSG_DB_LARGE_COMMIT = 1;
|
||||
const MSG_FLAG_ATTACHMENT = 0x10000000;
|
||||
const CRLF="\r\n";
|
||||
|
||||
// The property of the header that's used to check if a message is indexed
|
||||
const gHdrIndexedProperty = "wds_indexed";
|
||||
|
||||
// The file extension that is used for support files of this component
|
||||
const gFileExt = ".wdseml";
|
||||
|
||||
// The pref base
|
||||
const gPrefBase = "mail.winsearch";
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function InitWinSearchIntegration()
|
||||
{
|
||||
var enabled;
|
||||
try {
|
||||
enabled = gPrefBranch.getBoolPref(gPrefBase + ".enable");
|
||||
gLastFolderIndexedUri = gPrefBranch.getCharPref(gPrefBase + ".lastFolderIndexedUri");
|
||||
} catch (ex) {}
|
||||
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
SIDump("Initializing Windows Search integration\n");
|
||||
InitSupportIntegration();
|
||||
}
|
||||
|
||||
// The stream listener to read messages
|
||||
var gStreamListener = {
|
||||
_buffer: "",
|
||||
outputFile: null,
|
||||
outputStream: null,
|
||||
unicodeConverter: null,
|
||||
// subject: null,
|
||||
message: null,
|
||||
msgHdr: null,
|
||||
mimeHdrObj: null,
|
||||
mimeHdrParamObj: null,
|
||||
|
||||
onDoneStreamingCurMessage: function(successful)
|
||||
{
|
||||
if (this.outputStream)
|
||||
this.outputStream.close();
|
||||
if (!successful && this.msgHdr)
|
||||
{
|
||||
var file = GetSupportFileForMsgHdr(this.msgHdr);
|
||||
if (file && file.exists())
|
||||
file.remove(false);
|
||||
}
|
||||
// should we try to delete the file on disk in case not successful?
|
||||
gMsgHdrsToIndex.shift();
|
||||
|
||||
if (gMsgHdrsToIndex.length > 0)
|
||||
GenerateSupportFile(gMsgHdrsToIndex[0]);
|
||||
},
|
||||
|
||||
QueryInterface: function(aIId, instance) {
|
||||
if (aIId.equals(Ci.nsIStreamListener) || aIId.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return null;
|
||||
},
|
||||
|
||||
onStartRequest: function(request, context) {
|
||||
try
|
||||
{
|
||||
var 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)
|
||||
{
|
||||
onDoneStreamingCurMessage(false);
|
||||
}
|
||||
},
|
||||
|
||||
onStopRequest: function(request, context, status, errorMsg) {
|
||||
try
|
||||
{
|
||||
// If, for some reason, the first four characters are "null", then remove them
|
||||
if (this.message.substring(0, 4) == "null")
|
||||
this.message = this.message.substring(4);
|
||||
|
||||
// First get all the headers (everything till a double CRLF)
|
||||
var headerLength = this.message.indexOf(CRLF+CRLF)+1;
|
||||
|
||||
// XXX the below has to be replaced with getMsgTextFromStream
|
||||
|
||||
// Filter out attachments. First locate the content-type header.
|
||||
if (!this.mimeHdrObj)
|
||||
this.mimeHdrObj = Cc["@mozilla.org/messenger/mimeheaders;1"].createInstance(Ci.nsIMimeHeaders);
|
||||
this.mimeHdrObj.initialize(this.message.substring(0, headerLength), headerLength);
|
||||
var contentTypeHeader = this.mimeHdrObj.extractHeader("Content-Type", false);
|
||||
|
||||
if (!this.mimeHdrParamObj)
|
||||
this.mimeHdrParamObj = Cc["@mozilla.org/network/mime-hdrparam;1"].createInstance(Ci.nsIMIMEHeaderParam);
|
||||
// If a multipart header, then strip out attachments
|
||||
var contentType = this.mimeHdrParamObj.getParameter(contentTypeHeader, null, null, true, {});
|
||||
if (contentType.substring(0,10) == "multipart/")
|
||||
{
|
||||
var boundary = this.mimeHdrParamObj.getParameter(contentTypeHeader, "boundary", null, true, {});
|
||||
|
||||
// Search for the boundary after the headers. Remove everything from the second occurrence.
|
||||
boundary = CRLF + "--" + boundary;
|
||||
var getRidFrom = this.message.indexOf(boundary, headerLength);
|
||||
getRidFrom = this.message.indexOf(boundary, getRidFrom + boundary.length);
|
||||
|
||||
// In case we've got more than one boundary so far
|
||||
if (getRidFrom != -1) {
|
||||
this.message = this.message.substring(0, getRidFrom);
|
||||
|
||||
// Add RFC required stuff to the end
|
||||
this.message += boundary + "--" + CRLF;
|
||||
}
|
||||
}
|
||||
|
||||
// We just write whatever we have in |message| out to the file
|
||||
this.outputStream.writeString(this.message);
|
||||
|
||||
this.msgHdr.setUint32Property(gHdrIndexedProperty, 1);
|
||||
var msgDB = this.msgHdr.folder.getMsgDatabase(null);
|
||||
msgDB.Commit(MSG_DB_LARGE_COMMIT);
|
||||
|
||||
this.message = "";
|
||||
SIDump("Successfully written file\n");
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
SIDump(ex);
|
||||
this.onDoneStreamingCurMessage(false);
|
||||
return;
|
||||
}
|
||||
this.onDoneStreamingCurMessage(true);
|
||||
},
|
||||
|
||||
onDataAvailable: function(request, context, inputStream, offset, count) {
|
||||
try
|
||||
{
|
||||
var inStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
|
||||
inStream.init(inputStream);
|
||||
|
||||
// It is necessary to read in data from the input stream
|
||||
var inData = inStream.read(count);
|
||||
|
||||
// If we've already reached the attachments, safely ignore.
|
||||
if (this.filteredAttachments)
|
||||
return 0;
|
||||
|
||||
// Also ignore stuff after the first 20K or so
|
||||
if (this.message && this.message.length > 20000)
|
||||
return 0;
|
||||
var inStream = Cc["@mozilla.org/scriptableinputstream;1"].
|
||||
createInstance(Ci.nsIScriptableInputStream);
|
||||
|
||||
inStream.init(inputStream);
|
||||
|
||||
this.message += inData;
|
||||
return 0;
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
SIDump(ex);
|
||||
onDoneStreamingCurMessage(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* XPCOM boilerplate code */
|
||||
function WinSearchIntegration() { }
|
||||
WinSearchIntegration.prototype = {
|
||||
classDescription: "Windows Search Integration",
|
||||
classID: Components.ID("{451a70f0-1b4f-11dd-bd0b-0800200c9a66}"),
|
||||
contractID: "@mozilla.org/windows-search-integration;1",
|
||||
|
||||
_xpcom_categories: [{
|
||||
category: "app-startup",
|
||||
service: true
|
||||
}],
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupports]),
|
||||
|
||||
observe : function(aSubject, aTopic, aData)
|
||||
{
|
||||
switch(aTopic)
|
||||
{
|
||||
case "app-startup":
|
||||
var obsSvc = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
obsSvc.addObserver(this, "profile-after-change", false);
|
||||
break;
|
||||
case "profile-after-change":
|
||||
try { InitWinSearchIntegration(); }
|
||||
catch(err) { SIDump("Could not initialize winsearch component"); }
|
||||
break;
|
||||
default:
|
||||
throw Components.Exception("Unknown topic: " + aTopic);
|
||||
}
|
||||
}
|
||||
};
|
||||
var components = [WinSearchIntegration];
|
||||
function NSGetModule(aCompMgr, aFileSpec)
|
||||
{
|
||||
return XPCOMUtils.generateModule(components);
|
||||
}
|
||||
Reference in New Issue
Block a user