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

source: modules/stdlib/msgHdrUtils.js @ f440ee

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

expression closures are deprecated

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