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

source: modules/vI_smartIdentityCollection.js @ 190c29

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

added stftime (replacement for dateObj.toLocaleFormat)

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