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

source: modules/vI_smartIdentityCollection.js

ng_0.9 0.10.3
Last change on this file was 4c815c, checked in by rene <rene@…>, 4 years ago

removed old datetime formatting comment

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