1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is Mail utility functions for GMail Conversation View 15 * 16 * The Initial Developer of the Original Code is 17 * Jonathan Protzenko 18 * Portions created by the Initial Developer are Copyright (C) 2010 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * 23 * Alternatively, the contents of this file may be used under the terms of 24 * either the GNU General Public License Version 2 or later (the "GPL"), or 25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 26 * in which case the provisions of the GPL or the LGPL are applicable instead 27 * of those above. If you wish to allow use of your version of this file only 28 * under the terms of either the GPL or the LGPL, and not to allow others to 29 * use your version of this file under the terms of the MPL, indicate your 30 * decision by deleting the provisions above and replace them with the notice 31 * and other provisions required by the GPL or the LGPL. If you do not delete 32 * the provisions above, a recipient may use your version of this file under 33 * the terms of any one of the MPL, the GPL or the LGPL. 34 * 35 * ***** END LICENSE BLOCK ***** */ 36 37 /** 38 * @fileoverview A whole bunch of utility functions that will abstract away 39 * various low-level nsIMsgDbHdr operations. The idea is to save time by not 40 * having to lookup how to do simple actions. 41 * @author Jonathan Protzenko 42 */ 43 44 var EXPORTED_SYMBOLS = [ 45 // Low-level XPCOM boring stuff 46 'msgHdrToMessageBody', 'msgHdrToNeckoURL', 'msgHdrGetTags', 'msgUriToMsgHdr', 'msgHdrGetUri', 47 // Quickly identify a message 48 'msgHdrIsDraft', 'msgHdrIsSent', 'msgHdrIsArchive', 'msgHdrIsInbox', 49 'msgHdrIsRss', 'msgHdrIsNntp', 'msgHdrIsJunk', 50 // Actions on a set of message headers 51 'msgHdrsMarkAsRead', 'msgHdrsArchive', 'msgHdrsDelete', 52 // Doesn't really belong here 53 'getMail3Pane', 54 ] 55 56 const Ci = Components.interfaces; 57 const Cc = Components.classes; 58 const Cu = Components.utils; 59 const Cr = Components.results; 60 61 // from mailnews/base/public/nsMsgFolderFlags.idl 62 const nsMsgFolderFlags_SentMail = 0x00000200; 63 const nsMsgFolderFlags_Drafts = 0x00000400; 64 const nsMsgFolderFlags_Archive = 0x00004000; 65 const nsMsgFolderFlags_Inbox = 0x00001000; 66 67 const gMessenger = Cc["@mozilla.org/messenger;1"] 68 .createInstance(Ci.nsIMessenger); 69 const gMsgTagService = Cc["@mozilla.org/messenger/tagservice;1"] 70 .getService(Ci.nsIMsgTagService); 71 72 73 /** 74 * Get a given message header's uri. 75 * @param {nsIMsgDbHdr} aMsg The message 76 * @return {String} 77 */ 78 function msgHdrGetUri (aMsg) 79 aMsg.folder.getUriForMsg(aMsg) 80 81 /** 82 * Get a msgHdr from a message URI (msgHdr.URI). 83 * @param {String} aUri The URI of the message 84 * @return {nsIMsgDbHdr} 85 */ 86 function msgUriToMsgHdr(aUri) { 87 let messageService = gMessenger.messageServiceFromURI(aUri); 88 return messageService.messageURIToMsgHdr(aUri); 89 } 90 91 /** 92 * Tells if the message is in the account's inbox 93 * @param {nsIMsgDbHdr} msgHdr The message header to examine 94 * @return {bool} 95 */ 96 function msgHdrIsInbox(msgHdr) 97 msgHdr.folder.getFlag(nsMsgFolderFlags_Inbox) 98 99 /** 100 * Tells if the message is a draft message 101 * @param {nsIMsgDbHdr} msgHdr The message header to examine 102 * @return {bool} 103 */ 104 function msgHdrIsDraft(msgHdr) 105 msgHdr.folder.getFlag(nsMsgFolderFlags_Drafts) 106 107 /** 108 * Tells if the message is a sent message 109 * @param {nsIMsgDbHdr} msgHdr The message header to examine 110 * @return {bool} 111 */ 112 function msgHdrIsSent(msgHdr) 113 msgHdr.folder.getFlag(nsMsgFolderFlags_SentMail) 114 115 /** 116 * Tells if the message is an archived message 117 * @param {nsIMsgDbHdr} msgHdr The message header to examine 118 * @return {bool} 119 */ 120 function msgHdrIsArchive(msgHdr) { 121 return msgHdr.folder.getFlag(nsMsgFolderFlags_Archive); 122 } 123 124 /** 125 * Get a string containing the body of a messsage. 126 * @param {nsIMsgDbHdr} aMessageHeader The message header 127 * @param {bool} aStripHtml Keep html? 128 * @return {string} 129 */ 130 function msgHdrToMessageBody(aMessageHeader, aStripHtml, aLength) { 131 let messenger = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger); 132 let listener = Cc["@mozilla.org/network/sync-stream-listener;1"].createInstance(Ci.nsISyncStreamListener); 133 let uri = aMessageHeader.folder.getUriForMsg(aMessageHeader); 134 messenger.messageServiceFromURI(uri).streamMessage(uri, listener, null, null, false, ""); 135 let folder = aMessageHeader.folder; 136 /* 137 * AUTF8String getMsgTextFromStream(in nsIInputStream aStream, in ACString aCharset, 138 in unsigned long aBytesToRead, in unsigned long aMaxOutputLen, 139 in boolean aCompressQuotes, in boolean aStripHTMLTags, 140 out ACString aContentType); 141 */ 142 return folder.getMsgTextFromStream( 143 listener.inputStream, aMessageHeader.Charset, 2*aLength, aLength, false, aStripHtml, { }); 144 } 145 146 /** 147 * Get a nsIURI from a nsIMsgDBHdr 148 * @param {nsIMsgDbHdr} aMsgHdr The message header 149 * @return {nsIURI} 150 */ 151 function msgHdrToNeckoURL(aMsgHdr) { 152 let uri = aMsgHdr.folder.getUriForMsg(aMsgHdr); 153 let neckoURL = {}; 154 let msgService = gMessenger.messageServiceFromURI(uri); 155 msgService.GetUrlForUri(uri, neckoURL, null); 156 return neckoURL.value; 157 } 158 159 /** 160 * Given a msgHdr, return a list of tag objects. This function 161 * just does the messy work of understanding how tags are 162 * stored in nsIMsgDBHdrs. 163 * 164 * @param {nsIMsgDbHdr} aMsgHdr the msgHdr whose tags we want 165 * @return {nsIMsgTag array} a list of tag objects 166 */ 167 function msgHdrGetTags (aMsgHdr) { 168 let keywords = aMsgHdr.getStringProperty("keywords"); 169 let keywordList = keywords.split(' '); 170 let keywordMap = {}; 171 for (let iKeyword = 0; iKeyword < keywordList.length; iKeyword++) { 172 let keyword = keywordList[iKeyword]; 173 keywordMap[keyword] = true; 174 } 175 176 let tagArray = gMsgTagService.getAllTags({}); 177 let tags = []; 178 for (let iTag = 0; iTag < tagArray.length; iTag++) { 179 let tag = tagArray[iTag]; 180 if (tag.key in keywordMap) 181 tags.push(tag); 182 } 183 return tags; 184 } 185 186 /** 187 * Mark an array of msgHdrs read (or unread) 188 * @param {nsIMsgDbHdr array} msgHdrs The message headers 189 * @param {bool} read True to mark them read, false to mark them unread 190 */ 191 function msgHdrsMarkAsRead(msgHdrs, read) { 192 let pending = {}; 193 for each (msgHdr in msgHdrs) { 194 if (msgHdr.isRead == read) 195 continue; 196 if (!pending[msgHdr.folder.URI]) { 197 pending[msgHdr.folder.URI] = { 198 folder: msgHdr.folder, 199 msgs: Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray) 200 }; 201 } 202 pending[msgHdr.folder.URI].msgs.appendElement(msgHdr, false); 203 } 204 for each (let { folder, msgs } in pending) { 205 folder.markMessagesRead(msgs, read); 206 folder.msgDatabase = null; /* don't leak */ 207 } 208 } 209 210 /** 211 * Delete a set of messages. 212 * @param {nsIMsgDbHdr array} msgHdrs The message headers 213 */ 214 function msgHdrsDelete(msgHdrs) { 215 let pending = {}; 216 for each (msgHdr in msgHdrs) { 217 if (!pending[msgHdr.folder.URI]) { 218 pending[msgHdr.folder.URI] = { 219 folder: msgHdr.folder, 220 msgs: Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray) 221 }; 222 } 223 pending[msgHdr.folder.URI].msgs.appendElement(msgHdr, false); 224 } 225 for each (let { folder, msgs } in pending) { 226 folder.deleteMessages(msgs, getMail3Pane().msgWindow, false, false, null, true); 227 folder.msgDatabase = null; /* don't leak */ 228 } 229 } 230 231 let w = null; 232 233 /** 234 * Get the main Thunderbird window. Used heavily to get a reference to globals 235 * that are defined in mail/base/content/. 236 * You should call this function with true everytime your overlay on the main 237 * Thunderbird window is loaded, so that it invalidates the previous cached 238 * reference to the main window (closing the main window doesn't necessarily 239 * mean restarting Thunderbird). 240 * @param {bool} aForce The results are cached, should we reset the cache? 241 * @return The window object for the main window. 242 */ 243 function getMail3Pane(aForce) { 244 if (!w || aForce) 245 w = Cc["@mozilla.org/appshell/window-mediator;1"] 246 .getService(Ci.nsIWindowMediator) 247 .getMostRecentWindow("mail:3pane"); 248 return w; 249 } 250 251 /** 252 * Archive a set of messages 253 * @param {nsIMsgDbHdr array} msgHdrs The message headers 254 */ 255 function msgHdrsArchive(msgHdrs) { 256 /* See 257 * http://mxr.mozilla.org/comm-central/source/suite/mailnews/mailWindowOverlay.js#1337 258 * 259 * The window is here because otherwise we don't have access to 260 * BatchMessageMover. 261 * */ 262 let mail3PaneWindow = getMail3Pane(); 263 let batchMover = new mail3PaneWindow.BatchMessageMover(); 264 batchMover.archiveMessages(msgHdrs); 265 } 266 267 /** 268 * Tell if a message is an RSS feed iteme 269 * @param {nsIMsgDbHdr} msgHdr The message header 270 * @return {Bool} 271 */ 272 function msgHdrIsRss(msgHdr) 273 (msgHdr.folder.server instanceof Ci.nsIRssIncomingServer) 274 275 /** 276 * Tell if a message is a NNTP message 277 * @param {nsIMsgDbHdr} msgHdr The message header 278 * @return {Bool} 279 */ 280 function msgHdrIsNntp(msgHdr) 281 (msgHdr.folder.server instanceof Ci.nsINntpIncomingServer) 282 283 /** 284 * Tell if a message has been marked as junk. 285 * @param {nsIMsgDbHdr} msgHdr The message header 286 * @return {Bool} 287 */ 288 function msgHdrIsJunk(aMsgHdr) 289 aMsgHdr.getStringProperty("junkscore") == Ci.nsIJunkMailPlugin.IS_SPAM_SCORE 290 291 // XXX implement some day 292 function msgHdrMarkAsJunk(msgHdr) { 293 //starts here http://mxr.mozilla.org/comm-central/source/mailnews/base/content/junkCommands.js#384 294 /* 2733 nsCOMPtr<nsIJunkMailPlugin> junkPlugin; 295 2734 296 2735 // if this is a junk command, get the junk plugin. 297 2736 if (command == nsMsgViewCommandType::junk || 298 2737 command == nsMsgViewCommandType::unjunk) 299 2738 { 300 2739 // get the folder from the first item; we assume that 301 2740 // all messages in the view are from the same folder (no 302 2741 // more junk status column in the 'search messages' dialog 303 2742 // like in earlier versions...) 304 2743 305 2744 nsCOMPtr<nsIMsgIncomingServer> server; 306 2745 rv = folder->GetServer(getter_AddRefs(server)); 307 2746 NS_ENSURE_SUCCESS(rv, rv); 308 2747 309 2748 nsCOMPtr<nsIMsgFilterPlugin> filterPlugin; 310 2749 rv = server->GetSpamFilterPlugin(getter_AddRefs(filterPlugin)); 311 2750 NS_ENSURE_SUCCESS(rv, rv); 312 2751 313 2752 junkPlugin = do_QueryInterface(filterPlugin, &rv); 314 2753 NS_ENSURE_SUCCESS(rv, rv); 315 2754 if (!mJunkHdrs) 316 2755 { 317 2756 mJunkHdrs = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); 318 2757 NS_ENSURE_SUCCESS(rv,rv); 319 2758 } 320 2759 } 321 322 323 2817 case nsMsgViewCommandType::junk: 324 2818 mNumMessagesRemainingInBatch++; 325 2819 mJunkHdrs->AppendElement(msgHdr, PR_FALSE); 326 2820 rv = SetMsgHdrJunkStatus(junkPlugin.get(), msgHdr, 327 2821 nsIJunkMailPlugin::JUNK); 328 2822 break; 329 330 2837 // Provide junk-related batch notifications 331 2838 if ((command == nsMsgViewCommandType::junk) && 332 2839 (command == nsMsgViewCommandType::unjunk)) { 333 2840 nsCOMPtr<nsIMsgFolderNotificationService> 334 2841 notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID)); 335 2842 if (notifier) 336 2843 notifier->NotifyItemEvent(messages, 337 2844 NS_LITERAL_CSTRING("JunkStatusChanged"), 338 2845 (command == nsMsgViewCommandType::junk) ? 339 2846 kJunkMsgAtom : kNotJunkMsgAtom); 340 2847 } */ 341 342 //check OnMessageClassified for the rest of the actions 343 //http://mxr.mozilla.org/comm-central/source/mailnews/base/content/junkCommands.js#241 344 //the listener is for automatic classification, for manual marking, the 345 //junkstatusorigin needs to be changed 346 } 347