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

source: modules/vI_smartIdentityCollection.js @ 0ff671

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

made mailinglist-recognition more robust

  • Property mode set to 100644
File size: 18.9 KB
Line 
1/* ***** BEGIN LICENSE BLOCK *****
2    This program is free software; you can redistribute it and/or modify
3    it under the terms of the GNU General Public License as published by
4    the Free Software Foundation; either version 2 of the License, or
5    (at your option) any later version.
6
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
15
16    The Original Code is the Virtual Identity Extension.
17
18    The Initial Developer of the Original Code is Rene Ejury.
19    Portions created by the Initial Developer are Copyright (C) 2007
20    the Initial Developer. All Rights Reserved.
21
22    Contributor(s):
23 * ***** END LICENSE BLOCK ***** */
24
25var EXPORTED_SYMBOLS = ["smartIdentityCollection"]
26
27Components.utils.import("resource://v_identity/vI_log.js");
28Components.utils.import("resource://v_identity/vI_identityData.js");
29Components.utils.import("resource://v_identity/vI_rdfDatasource.js");
30Components.utils.import("resource://v_identity/vI_prefs.js");
31
32let Log = setupLogging("virtualIdentity.smartIdentityCollection");
33
34function smartIdentityCollection(currentWindow, msgHdr, preseletedID, currentIDisVID, newsgroup, recipients) {
35  this._currentWindow = currentWindow;
36  this._IDisVID = currentIDisVID;
37  this._preselectedID = preseletedID;
38  this._msgHdr = msgHdr;
39  this._newsgroup = newsgroup;
40  this._unicodeConverter.charset = "UTF-8";
41  this._recipients = recipients;
42  this._rdfDatasourceAccess = new rdfDatasourceAccess(this._currentWindow);
43  this._allIdentities = new identityCollection();
44};
45
46smartIdentityCollection.prototype = {
47  _currentWindow: null,
48
49  messenger: Components.classes["@mozilla.org/messenger;1"].createInstance()
50    .QueryInterface(Components.interfaces.nsIMessenger),
51  _unicodeConverter: Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
52    .createInstance(Components.interfaces.nsIScriptableUnicodeConverter),
53  _headerParser: Components.classes["@mozilla.org/messenger/headerparser;1"]
54    .getService(Components.interfaces.nsIMsgHeaderParser),
55
56  _msgComposeTypeReference: Components.interfaces.nsIMsgCompType,
57
58  _IDisVID: false,
59  _preselectedID: null,
60  _allIdentities: null,
61  _selectedValue: null,
62  _newsgroup: null,
63  _rdfDatasourceAccess: null,
64
65  // this function adds a timestamp to the current sender
66  __autoTimestamp: function () {
67    Log.debug("__autoTimestamp()");
68    if (this._IDisVID) {
69      Log.debug("Virtual Identity in use, aborting");
70      return;
71    }
72
73    var current_email = this._preselectedID.email.split("@");
74    var localpart = current_email[0];
75    var domain = current_email[1];
76
77    Log.debug("current email: " + current_email[0] + "@" + current_email[1]);
78
79    var autoString = vIprefs.get("autoString");
80    var formatString = vIprefs.get("autoTimeFormat");
81
82    var dateObj = new Date();
83    var dateString = "";
84    if (formatString == "") dateString = parseInt(dateObj.getTime() / 1000);
85    else try { //   you never know what the formatString will be...
86      dateString = dateObj.toLocaleFormat(formatString).replace(/\s+|[\x00-\x2a]|\x2c|\x2f|[\x3a-\x40]|[\x5b-\x5d]|\x60|\x7c|[\x7f-\xff]/g, "_");
87    } catch (e) {};
88
89    var new_email = autoString.replace(/%l/g, localpart).replace(/%d/g, domain).replace(/%t/g, dateString);
90    Log.debug("new email: " + new_email);
91
92    var newIdentity = new identityData(this._currentWindow, new_email,
93      this._preselectedID.fullName, this._preselectedID.key, this._preselectedID.smtpServerKey, null, null)
94
95    this._allIdentities.addWithoutDuplicates(newIdentity);
96    this._selectedValue = 0;
97  },
98
99  __ignoreID: function () {
100    Log.debug("checking " + vIprefs.get("idSelection_ignoreIDs") + " against " + this._preselectedID.key)
101      // check if usage if virtual Identities should be used at all for the currently selected ID
102    if (vIprefs.get("idSelection_ignoreIDs").indexOf(":" + this._preselectedID.key + ":") != -1) {
103      Log.debug("not using virtual Identites for ID " + this._preselectedID.key);
104      return true;
105    }
106    return false
107  },
108
109  NewMail: function () {
110    Log.debug("NewMail()");
111    if (this.__ignoreID()) return;
112    this._rdfDatasourceAccess.getVIdentityFromAllRecipients(this._allIdentities, this._recipients);
113    if (this._allIdentities.number == 0 && vIprefs.get("autoTimestamp")) this.__autoTimestamp();
114  },
115
116  _foundExistingIdentity: function () {
117    /* compare with existing Identities                                     */
118    for (var index = 0; index < this._allIdentities.number; index++) {
119      var existingID = this._allIdentities.identityDataCollection[index].isExistingIdentity(false);
120      if (existingID) {
121        this._allIdentities.identityDataCollection[index].id.key = existingID; // set found identity
122        // reorder list of Identities to prefer it on autoselect
123        // has to be done before Identities are added to the Menu
124        Log.debug("found existing Identity, reorder to prefer this one.");
125        var firstIdentity = this._allIdentities.identityDataCollection[index];
126        for (var i = index; index > 0; index--) {
127          this._allIdentities.identityDataCollection[index] = this._allIdentities.identityDataCollection[index - 1];
128        }
129        this._allIdentities.identityDataCollection[0] = firstIdentity;
130        return {
131          key: index
132        };
133      }
134    }
135    return null;
136  },
137
138  ReplyOnSent: function () {
139    Log.debug("ReplyOnSent() (rules like SmartDraft)");
140    this.__SmartDraftOrReplyOnSent();
141    this._rdfDatasourceAccess.getVIdentityFromAllRecipients(this._allIdentities, this._recipients);
142  },
143
144  Draft: function () {
145    Log.debug("Draft()");
146
147    this.__SmartDraftOrReplyOnSent();
148    this._rdfDatasourceAccess.getVIdentityFromAllRecipients(this._allIdentities, this._recipients);
149  },
150
151  __parseHeadersWithArray: function (header, identityCollection) {
152    var emails = {};
153    var fullNames = {};
154    var combinedNames = {};
155    var number = this._headerParser.parseHeadersWithArray(header, emails, fullNames, combinedNames);
156    for (var index = 0; index < number; index++) {
157      var newIdentity = new identityData(this._currentWindow, emails.value[index], fullNames.value[index],
158        null, NO_SMTP_TAG, null, null);
159      identityCollection.addWithoutDuplicates(newIdentity);
160    }
161  },
162
163  // this function checks if we have a draft-case and Smart-Draft should replace the Identity
164  __SmartDraftOrReplyOnSent: function () {
165    if (!vIprefs.get("smart_draft")) {
166      Log.debug("SmartDraft deactivated");
167      return;
168    }
169
170    Log.debug("__SmartDraftOrReplyOnSent()");
171
172    if (this._msgHdr) {
173      this.__parseHeadersWithArray(this._msgHdr.author, this._allIdentities)
174      Log.debug("sender '" + this._allIdentities.identityDataCollection[0].combinedName + "'");
175    } else Log.debug("__SmartDraftOrReplyOnSent: No Header found, shouldn't happen");
176  },
177
178  __filterAddresses: function () {
179    var returnIdentities = new identityCollection();
180
181    var filterList =
182      this._unicodeConverter.ConvertToUnicode(vIprefs.get("smart_reply_filter")).split(/\n/)
183    if (filterList.length == 0) filterList[0] == ""
184
185    for (var i = 0; i < filterList.length; i++) {
186      const filterType = {
187        None: 0,
188        RegExp: 1,
189        StrCmp: 2
190      }
191      var recentfilterType;
192      var skipRegExp = false;
193      if (filterList.length <= 1 && filterList[0] == "") {
194        Log.debug("no filters configured");
195        recentfilterType = filterType.None;
196      } else if (/^[+-]?\/(.*)\/$/.exec(filterList[i])) {
197        Log.debug("filter emails with RegExp '" + filterList[i].replace(/\\/g, "\\\\") + "'");
198        recentfilterType = filterType.RegExp;
199      } else {
200        Log.debug("filter emails, compare with '" + filterList[i] + "'");
201        recentfilterType = filterType.StrCmp;
202      }
203      for (var j = 0; j < this._allIdentities.number; j++) { // check if recent email-address (pre-choosen identity) is found in
204        // copied and adapted from correctIdentity, thank you for the RegExp-idea!
205        var add_addr = false;
206        switch (recentfilterType) {
207        case filterType.None:
208          add_addr = true;
209          break;
210        case filterType.RegExp:
211          if (skipRegExp) break;
212          try {
213            /^[+-]?\/(.*)\/$/.exec(filterList[i]);
214            if (filterList[i][0] == "-") {
215              if (this._allIdentities.identityDataCollection[j].email.match(new RegExp(RegExp.$1, "i")))
216                this._allIdentities.dropIdentity(j--);
217            } else
218              add_addr = (this._allIdentities.identityDataCollection[j].email.match(new RegExp(RegExp.$1, "i")));
219          } catch (vErr) {
220            this.stringBundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
221              .getService(Components.interfaces.nsIStringBundleService)
222              .createBundle("chrome://v_identity/locale/v_identity.properties");
223            SmartReplyNotification.info(
224              this.stringBundle.GetStringFromName("vident.smartIdentity.ignoreRegExp") +
225              +filterList[i].replace(/\\/g, "\\\\") + " .");
226            skipRegExp = true;
227          }
228          break;
229        case filterType.StrCmp:
230          add_addr = (this._allIdentities.identityDataCollection[j].email.toLowerCase().indexOf(filterList[i].toLowerCase()) != -1)
231          break;
232        }
233        if (add_addr) returnIdentities.addWithoutDuplicates(this._allIdentities.identityDataCollection[j])
234      }
235    }
236    this._allIdentities.takeOver(returnIdentities);
237  },
238
239  __smartReplyCollectAddresses: function () {
240    // add emails from selected headers (stored by vI_getHeader.xul/js)
241    var reply_headers = this._unicodeConverter.ConvertToUnicode(vIprefs.get("smart_reply_headers")).split(/\n/)
242
243    for (var index = 0; index < reply_headers.length; index++) {
244      // ------------- prepare fields to read the stored header ----------------
245      var replyHeader_splitted = reply_headers[index].split(/:/)
246        // use first part (all before ':') as the header name
247      var replyHeaderName = replyHeader_splitted[0].toLowerCase()
248        // check second or third part for any number
249      var replyHeaderNumber = null;
250      if (replyHeader_splitted.length > 1) parseInt(replyHeader_splitted[1]);
251      if ((!replyHeaderNumber || isNaN(replyHeaderNumber)) && replyHeader_splitted.length > 2) replyHeaderNumber = parseInt(replyHeader_splitted[2]);
252      // check if Fullnames should be erased
253      var replyHeaderEmptyFullNames = ((replyHeader_splitted[1] && replyHeader_splitted[1].match(/@/)) ||
254        (replyHeader_splitted[2] && replyHeader_splitted[2].match(/@/)));
255
256      // create header name to find the value
257      var replyHeaderNameToRead = replyHeaderName
258      if (replyHeaderNumber && !isNaN(replyHeaderNumber)) replyHeaderNameToRead += ":" + replyHeaderNumber
259
260      // if mailing-list ignore to-header (usually the mailing list address)
261      if ((replyHeaderNameToRead == "to" || replyHeaderNameToRead == "x-original-to") && this._msgHdr.getStringProperty("vI_list-id")) {
262        Log.debug("header 'list-id' found (mailinglist), skipping header '" + replyHeaderNameToRead + "'");
263        continue;
264      }
265
266      // if mailing-list ignore to-header (usually the mailing list address)
267      if (replyHeaderNameToRead == "to" && this._msgHdr.getStringProperty("vI_list-id")) {
268        Log.debug("header 'list-id' found (mailinglist), skipping header 'to'");
269        continue;
270      }
271
272      // ------------- read the stored header -------------------------------
273      var value = this._unicodeConverter.ConvertToUnicode(this._msgHdr.getStringProperty("vI_" + replyHeaderNameToRead))
274        /*          let window3pane =  Components.classes['@mozilla.org/appshell/window-mediator;1']
275                         .getService(Components.interfaces.nsIWindowMediator)
276                         .getMostRecentWindow("mail:3pane");
277                   
278                    Log.debug("found stored header '" +
279                        replyHeaderNameToRead + "': '" + window3pane.virtualIdentityExtension.storedHeaders["vI_" + replyHeaderNameToRead] + "'");*/
280
281      Log.debug("reading header '" +
282        replyHeaderNameToRead + "': '" + value + "'");
283
284      // ------------- parse address-string to get a field of single email-addresses
285      var splitted = new identityCollection();
286      this.__parseHeadersWithArray(value, splitted);
287
288      // move found addresses step by step to this._allIdentities, and change values if requested
289      for (var i = 0; i < splitted.number; i++) {
290        // if there is no email than it makes no sense to use it as a sender
291        if (!splitted.identityDataCollection[i].email.match(/^.*@.*$/)) {
292          Log.debug("  skipping '" +
293            splitted.identityDataCollection[i].email + "', no email")
294          continue;
295        }
296
297        if (replyHeaderEmptyFullNames) splitted.identityDataCollection[i].fullName = ""
298
299        this._allIdentities.addWithoutDuplicates(splitted.identityDataCollection[i]);
300
301        Log.debug("  found '" +
302          splitted.identityDataCollection[i].combinedName + "'")
303      }
304    }
305  },
306
307  Reply: function () {
308    Log.debug("Reply()");
309
310    if (this._msgHdr && !this._newsgroup && !this._msgHdr.getStringProperty("vI_content_base")) {
311      //    RFC 2821 (http://www.ietf.org/rfc/rfc2821.txt) says:
312      //    "4.4 Trace Information
313      //    When an SMTP server receives a message for delivery or further
314      //    processing, it MUST insert trace ("time stamp" or "Received")
315      //    information at the beginning of the message content, as discussed in
316      //    section 4.1.1.4."
317      //    so it should be always possible to decide if Reply or Draft based on received headers
318      //    hidden option smart_detectByReceivedHeader will act as a switch for not RFC-compliant servers
319      // RFC-compliant
320      if (vIprefs.get("smart_detectByReceivedHeader")) {
321        if (!this._msgHdr.getStringProperty("vI_received")) { // mail was not received
322          Log.debug("reply on non-received (sent?) mail. Using SmartDraft.");
323          this.ReplyOnSent();
324          return;
325        }
326      }
327      // not RFC-compliant
328      else {
329        const MSG_FOLDER_FLAG_INBOX = 0x1000
330        const MSG_FOLDER_FLAG_SENTMAIL = 0x0200;
331
332        if (this._msgHdr && (this._msgHdr.folder.flags & MSG_FOLDER_FLAG_SENTMAIL)) {
333          if (this._msgHdr.folder.flags & MSG_FOLDER_FLAG_INBOX)
334            Log.debug("reply from Sent folder. Folder is INBOX, assuming Reply-Case.");
335          else {
336            Log.debug("reply from Sent folder. Using SmartDraft.");
337            this.ReplyOnSent();
338            return;
339          }
340        }
341      }
342    }
343
344    if (this.__ignoreID()) return;
345
346    var storageIdentities = new identityCollection();
347    this._rdfDatasourceAccess.getVIdentityFromAllRecipients(storageIdentities, this._recipients);
348
349    if (storageIdentities.number == 0 || !vIprefs.get("idSelection_storage_ignore_smart_reply"))
350      this.__SmartReply();
351    else Log.debug("SmartReply skipped, Identities in Storage found.");
352
353    // merge SmartReply-Identities and Storage-Identites
354    if (vIprefs.get("idSelection_storage_prefer_smart_reply")) {
355      this._allIdentities.mergeWithoutDuplicates(storageIdentities);
356    } else {
357      var smartIdentities = this._allIdentities;
358      this._allIdentities = storageIdentities;
359      this._allIdentities.mergeWithoutDuplicates(smartIdentities);
360    }
361
362    Log.debug("merged SmartReply & Storage, " + this._allIdentities.number + " address(es) left")
363  },
364
365  // this function checks if we have a reply-case and Smart-Reply should replace the Identity
366  __SmartReply: function () {
367    if (!vIprefs.get("smart_reply")) {
368      Log.debug("SmartReply deactivated");
369      return;
370    }
371    if (this._newsgroup && !vIprefs.get("smart_reply_for_newsgroups")) {
372      Log.debug("SmartReply, answering to a newsgroup, aborting");
373      return;
374    }
375
376    Log.debug("__SmartReply()");
377    Log.debug("----------------------------------------------------------")
378    if (this._msgHdr) {
379      /* first step: collect addresses */
380      this.__smartReplyCollectAddresses();
381      Log.debug("" + this._allIdentities.number + " address(es) after parsing, before filtering")
382
383      /* second step: filter (and sort) addresses */
384      this.__filterAddresses();
385
386      Log.debug("filtering done, " + this._allIdentities.number + " address(es) left")
387
388      /* set default FullName */
389      var smart_reply_defaultFullName = this._unicodeConverter.ConvertToUnicode(vIprefs.get("smart_reply_defaultFullName"))
390      if (smart_reply_defaultFullName != "") {
391        for (var index = 0; index < this._allIdentities.number; index++) {
392          if (this._allIdentities.identityDataCollection[index].fullName == "") {
393            this._allIdentities.identityDataCollection[index].fullName = smart_reply_defaultFullName
394            Log.debug("added default FullName '" +
395              smart_reply_defaultFullName + "' to '" + this._allIdentities.identityDataCollection[index].email + "'")
396          }
397        }
398      }
399
400      /* smart_reply_ignoreFullName: compare email with other Identities            */
401      /* if match replace FullName with existing one, keep identity in list by now      */
402      /* will not be added to the menu but probably choosen with __smartIdentitySelection   */
403      if (vIprefs.get("smart_reply_ignoreFullName")) {
404        Log.debug("compare with existing Identities (ignoring FullNames).")
405
406        for (var index = 0; index < this._allIdentities.number; index++) {
407          var idKey = this._allIdentities.identityDataCollection[index].isExistingIdentity(true);
408          if (idKey) {
409            var AccountManager = Components.classes["@mozilla.org/messenger/account-manager;1"]
410              .getService(Components.interfaces.nsIMsgAccountManager);
411            var newFullName = AccountManager.getIdentity(idKey).fullName;
412            this._allIdentities.identityDataCollection[index].fullName = newFullName;
413            Log.debug("replaced Fullname of '" + this._allIdentities.identityDataCollection[index].email + "' with '" + newFullName + "'");
414          }
415        }
416      }
417
418      /* smart_reply_searchBaseIdentity: compare email with other Identities          */
419      /* to find matching domain. Use first found as base identity (smtp etc) */
420      if (vIprefs.get("smart_reply_searchBaseIdentity")) {
421        Log.debug("compare domain name with existing accounts.")
422
423        for (var index = 0; index < this._allIdentities.number; index++) {
424          var idKey = this._allIdentities.identityDataCollection[index].hasMatchingDomainIdentity();
425          if (idKey) {
426            Log.debug("use id with matching domain as base ID");
427            this._allIdentities.identityDataCollection[index].id.key = idKey;
428          }
429        }
430      }
431
432    } else Log.debug("SmartReply skipped. No Header-information found.");
433
434    Log.debug("----------------------------------------------------------")
435  },
436
437
438};
Note: See TracBrowser for help on using the repository browser.