This is just some static backup of the original site, don't expect every link to work!

source: modules/stdlib/msgHdrUtils.js @ cbe8c6

ng_0.9
Last change on this file since cbe8c6 was cbe8c6, checked in by rene <rene@…>, 6 years ago

add stdlib

  • Property mode set to 100644
File size: 13.3 KB
Line 
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
44var EXPORTED_SYMBOLS = [
45  // Low-level XPCOM boring stuff
46  'msgHdrToMessageBody', 'msgHdrToNeckoURL', 'msgHdrGetTags', 'msgUriToMsgHdr',
47  'msgHdrGetUri', 'msgHdrFromNeckoUrl', 'msgHdrSetTags',
48  // Quickly identify a message
49  'msgHdrIsDraft', 'msgHdrIsSent', 'msgHdrIsArchive', 'msgHdrIsInbox',
50  'msgHdrIsRss', 'msgHdrIsNntp', 'msgHdrIsJunk',
51  // Actions on a set of message headers
52  'msgHdrsMarkAsRead', 'msgHdrsArchive', 'msgHdrsDelete',
53  // Doesn't really belong here
54  'getMail3Pane',
55  // Higher-level functions
56  'msgHdrGetHeaders',
57]
58
59const {
60  classes: Cc,
61  interfaces: Ci,
62  utils: Cu,
63  results: Cr
64} = Components;
65
66// from mailnews/base/public/nsMsgFolderFlags.idl
67const nsMsgFolderFlags_SentMail = 0x00000200;
68const nsMsgFolderFlags_Drafts = 0x00000400;
69const nsMsgFolderFlags_Archive = 0x00004000;
70const nsMsgFolderFlags_Inbox = 0x00001000;
71
72Cu.import("resource://gre/modules/XPCOMUtils.jsm"); // for defineLazyServiceGetter
73Cu.import("resource:///modules/gloda/mimemsg.js");
74Cu.import("resource:///modules/gloda/utils.js");
75Cu.import("resource:///modules/iteratorUtils.jsm"); // for toXPCOMArray
76Cu.import("resource:///modules/mailServices.js");
77
78// Adding a messenger lazy getter to the MailServices even though it's not a service
79XPCOMUtils.defineLazyGetter(MailServices, "messenger", function () {
80  return Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger);
81});
82
83/**
84 * Get a given message header's uri.
85 * @param {nsIMsgDbHdr} aMsg The message
86 * @return {String}
87 */
88function msgHdrGetUri(aMsg)
89aMsg.folder.getUriForMsg(aMsg)
90
91/**
92 * Get a msgHdr from a message URI (msgHdr.URI).
93 * @param {String} aUri The URI of the message
94 * @return {nsIMsgDbHdr}
95 */
96function msgUriToMsgHdr(aUri) {
97  try {
98    let messageService = MailServices.messenger.messageServiceFromURI(aUri);
99    return messageService.messageURIToMsgHdr(aUri);
100  } catch (e) {
101    dump("Unable to get " + aUri + " — returning null instead");
102    return null;
103  }
104}
105
106/**
107 * Tells if the message is in the account's inbox
108 * @param {nsIMsgDbHdr} msgHdr The message header to examine
109 * @return {bool}
110 */
111function msgHdrIsInbox(msgHdr)
112msgHdr.folder.getFlag(nsMsgFolderFlags_Inbox)
113
114/**
115 * Tells if the message is a draft message
116 * @param {nsIMsgDbHdr} msgHdr The message header to examine
117 * @return {bool}
118 */
119function msgHdrIsDraft(msgHdr)
120msgHdr.folder.getFlag(nsMsgFolderFlags_Drafts)
121
122/**
123 * Tells if the message is a sent message
124 * @param {nsIMsgDbHdr} msgHdr The message header to examine
125 * @return {bool}
126 */
127function msgHdrIsSent(msgHdr)
128msgHdr.folder.getFlag(nsMsgFolderFlags_SentMail)
129
130/**
131 * Tells if the message is an archived message
132 * @param {nsIMsgDbHdr} msgHdr The message header to examine
133 * @return {bool}
134 */
135function msgHdrIsArchive(msgHdr)
136msgHdr.folder.getFlag(nsMsgFolderFlags_Archive)
137
138/**
139 * Get a nsIMsgDbHdr from a Necko URL.
140 * @param {String} The URL
141 * @return {nsIMsgDbHdr} The message header.
142 */
143function msgHdrFromNeckoUrl(aUrl)
144aUrl.QueryInterface(Ci.nsIMsgMessageUrl).messageHeader
145
146/**
147 * Get a string containing the body of a messsage.
148 * @param {nsIMsgDbHdr} aMessageHeader The message header
149 * @param {bool} aStripHtml Keep html?
150 * @return {string}
151 */
152function msgHdrToMessageBody(aMessageHeader, aStripHtml, aLength) {
153  let messenger = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger);
154  let listener = Cc["@mozilla.org/network/sync-stream-listener;1"].createInstance(Ci.nsISyncStreamListener);
155  let uri = aMessageHeader.folder.getUriForMsg(aMessageHeader);
156  messenger.messageServiceFromURI(uri).streamMessage(uri, listener, null, null, false, "");
157  let folder = aMessageHeader.folder;
158  /*
159   * AUTF8String getMsgTextFromStream(in nsIInputStream aStream, in ACString aCharset,
160                                      in unsigned long aBytesToRead, in unsigned long aMaxOutputLen,
161                                      in boolean aCompressQuotes, in boolean aStripHTMLTags,
162                                      out ACString aContentType);
163  */
164  return folder.getMsgTextFromStream(
165    listener.inputStream, aMessageHeader.Charset, 2 * aLength, aLength, false, aStripHtml, {});
166}
167
168/**
169 * Get a nsIURI from a nsIMsgDBHdr
170 * @param {nsIMsgDbHdr} aMsgHdr The message header
171 * @return {nsIURI}
172 */
173function msgHdrToNeckoURL(aMsgHdr) {
174  let uri = aMsgHdr.folder.getUriForMsg(aMsgHdr);
175  let neckoURL = {};
176  let msgService = MailServices.messenger.messageServiceFromURI(uri);
177  msgService.GetUrlForUri(uri, neckoURL, null);
178  return neckoURL.value;
179}
180
181/**
182 * Given a msgHdr, return a list of tag objects. This function
183 * just does the messy work of understanding how tags are
184 * stored in nsIMsgDBHdrs.
185 *
186 * @param {nsIMsgDbHdr} aMsgHdr the msgHdr whose tags we want
187 * @return {nsIMsgTag array} a list of tag objects
188 */
189function msgHdrGetTags(aMsgHdr) {
190  let keywords = aMsgHdr.getStringProperty("keywords");
191  let keywordList = keywords.split(' ');
192  let keywordMap = {};
193  for (let [, keyword] in Iterator(keywordList)) {
194    keywordMap[keyword] = true;
195  }
196
197  let tagArray = MailServices.tags.getAllTags({});
198  let tags = [];
199  for (let [iTag, tag] in Iterator(tagArray)) {
200    let tag = tagArray[iTag];
201    if (tag.key in keywordMap)
202      tags.push(tag);
203  }
204  return tags;
205}
206
207/**
208 * Set the tags for a given msgHdr.
209 *
210 * @param {nsIMsgDBHdr} aMsgHdr
211 * @param {nsIMsgTag array} aTags
212 */
213function msgHdrSetTags(aMsgHdr, aTags) {
214  let oldTagList = msgHdrGetTags(aMsgHdr);
215  let oldTags = {}; // hashmap
216  for each(let [, tag] in Iterator(oldTagList))
217  oldTags[tag.key] = null;
218
219  let newTags = {};
220  let newTagList = aTags;
221  for each(let [, tag] in Iterator(newTagList))
222  newTags[tag.key] = null;
223
224  let toAdd = newTagList.map(function(x) {if (!(x.key in oldTags)) return x.key});
225
226  let toRemove = oldTagList.map(function(x) {if (!(x.key in newTags)) return x.key});
227 
228  let folder = aMsgHdr.folder;
229  let msgHdr = toXPCOMArray([aMsgHdr], Ci.nsIMutableArray);
230  folder.addKeywordsToMessages(msgHdr, toAdd.join(" "));
231  folder.removeKeywordsFromMessages(msgHdr, toRemove.join(" "));
232  aMsgHdr.folder.msgDatabase = null;
233}
234
235/**
236 * Mark an array of msgHdrs read (or unread)
237 * @param {nsIMsgDbHdr array} msgHdrs The message headers
238 * @param {bool} read True to mark them read, false to mark them unread
239 */
240function msgHdrsMarkAsRead(msgHdrs, read) {
241  let pending = {};
242  for each(let msgHdr in msgHdrs) {
243    if (msgHdr.isRead == read)
244      continue;
245    if (!pending[msgHdr.folder.URI]) {
246      pending[msgHdr.folder.URI] = {
247        folder: msgHdr.folder,
248        msgs: Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray)
249      };
250    }
251    pending[msgHdr.folder.URI].msgs.appendElement(msgHdr, false);
252  }
253  for each(let {
254    folder, msgs
255  } in pending) {
256    folder.markMessagesRead(msgs, read);
257    folder.msgDatabase = null; /* don't leak */
258  }
259}
260
261/**
262 * Delete a set of messages.
263 * @param {nsIMsgDbHdr array} msgHdrs The message headers
264 */
265function msgHdrsDelete(msgHdrs) {
266  let pending = {};
267  for each(let msgHdr in msgHdrs) {
268    if (!pending[msgHdr.folder.URI]) {
269      pending[msgHdr.folder.URI] = {
270        folder: msgHdr.folder,
271        msgs: Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray)
272      };
273    }
274    pending[msgHdr.folder.URI].msgs.appendElement(msgHdr, false);
275  }
276  for each(let {
277    folder, msgs
278  } in pending) {
279    folder.deleteMessages(msgs, getMail3Pane().msgWindow, false, false, null, true);
280    folder.msgDatabase = null; /* don't leak */
281  }
282}
283
284/**
285 * Get the main Thunderbird window. Used heavily to get a reference to globals
286 *  that are defined in mail/base/content/.
287 * @return The window object for the main window.
288 */
289function getMail3Pane() {
290  return Cc["@mozilla.org/appshell/window-mediator;1"]
291    .getService(Ci.nsIWindowMediator)
292    .getMostRecentWindow("mail:3pane");
293}
294
295/**
296 * Archive a set of messages
297 * @param {nsIMsgDbHdr array} msgHdrs The message headers
298 */
299function msgHdrsArchive(msgHdrs) {
300  /* See
301   * http://mxr.mozilla.org/comm-central/source/suite/mailnews/mailWindowOverlay.js#1337
302   *
303   * The window is here because otherwise we don't have access to
304   * BatchMessageMover.
305   * */
306  let mail3PaneWindow = getMail3Pane();
307  let batchMover = new mail3PaneWindow.BatchMessageMover();
308  batchMover.archiveMessages(msgHdrs.filter(
309    function (x) !msgHdrIsArchive(x) && getMail3Pane().getIdentityForHeader(x).archiveEnabled
310  ));
311}
312
313/**
314 * Tell if a message is an RSS feed iteme
315 * @param {nsIMsgDbHdr} msgHdr The message header
316 * @return {Bool}
317 */
318function msgHdrIsRss(msgHdr)
319  (msgHdr.folder.server instanceof Ci.nsIRssIncomingServer)
320
321/**
322 * Tell if a message is a NNTP message
323 * @param {nsIMsgDbHdr} msgHdr The message header
324 * @return {Bool}
325 */
326function msgHdrIsNntp(msgHdr)
327  (msgHdr.folder.server instanceof Ci.nsINntpIncomingServer)
328
329/**
330 * Tell if a message has been marked as junk.
331 * @param {nsIMsgDbHdr} msgHdr The message header
332 * @return {Bool}
333 */
334function msgHdrIsJunk(aMsgHdr)
335aMsgHdr.getStringProperty("junkscore") == Ci.nsIJunkMailPlugin.IS_SPAM_SCORE
336
337/**
338 * Recycling the HeaderHandlerBase from mimemsg.js
339 */
340function HeaderHandler(aHeaders) {
341  this.headers = aHeaders;
342}
343
344HeaderHandler.prototype = {
345  __proto__: MimeMessage.prototype.__proto__, // == HeaderHandlerBase
346};
347
348/**
349 * Creates a stream listener that will call k once done, passing it the string
350 * that has been read.
351 */
352function createStreamListener(k) {
353  return {
354    _data: "",
355    _stream: null,
356
357    QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener, Ci.nsIRequestObserver]),
358
359    // nsIRequestObserver
360    onStartRequest: function (aRequest, aContext) {},
361    onStopRequest: function (aRequest, aContext, aStatusCode) {
362      k(this._data);
363    },
364
365    // nsIStreamListener
366    onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
367      if (this._stream == null) {
368        this._stream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
369        this._stream.init(aInputStream);
370      }
371      this._data += this._stream.read(aCount);
372    }
373  };
374}
375
376/**
377 * @param aMsgHdr The message header whose headers you want
378 * @param k A function that takes a HeaderHandler object (see mimemsg.js).
379 *  Such an object has a get function, a has function. It has a header property,
380 *  whose keys are lowercased header names, and whose values are list of
381 *  strings corresponding to the multiple entries found for that header.
382 */
383function msgHdrGetHeaders(aMsgHdr, k) {
384  let uri = msgHdrGetUri(aMsgHdr);
385  let messageService = MailServices.messenger.messageServiceFromURI(uri);
386
387  let fallback = function ()
388  MsgHdrToMimeMessage(aMsgHdr, null, function (aMsgHdr, aMimeMsg) {
389    k(aMimeMsg);
390  }, true, {
391    partsOnDemand: true,
392  });
393
394  // This is intentionally disabled because there's a bug in Thunderbird that
395  // renders the supposedly-useful streamHeaders function unusable.
396  if (false && "streamHeaders" in messageService) {
397    try {
398      messageService.streamHeaders(uri, createStreamListener(function (aRawString) {
399        let re = /\r?\n\s+/g;
400        let str = aRawString.replace(re, " ");
401        let lines = str.split(/\r?\n/);
402        let obj = {};
403        for each(let [, line] in Iterator(lines)) {
404          let i = line.indexOf(":");
405          if (i < 0)
406            continue;
407          let k = line.substring(0, i).toLowerCase();
408          let v = line.substring(i + 1).trim();
409          if (!(k in obj))
410            obj[k] = [];
411          obj[k].push(v);
412        }
413        k(new HeaderHandler(obj));
414      }), null, true);
415    } catch (e) {
416      fallback();
417    }
418  } else {
419    fallback();
420  }
421}
Note: See TracBrowser for help on using the repository browser.