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 gContactSync.
 15  *
 16  * The Initial Developer of the Original Code is
 17  * Josh Geenen <gcontactsync@pirules.org>.
 18  * Portions created by the Initial Developer are Copyright (C) 2008-2009
 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 if (!com) var com = {}; // A generic wrapper variable
 38 // A wrapper for all GCS functions and variables
 39 if (!com.gContactSync) com.gContactSync = {};
 40 
 41 /**
 42  * MailList is an abstraction of a mailing list that facilitates getting the
 43  * cards contained within the actual list as well as accessing and modifying the
 44  * list and its properties.
 45  *
 46  * @param aList {Components.interfaces.nsIAbDirectory}      The actual nsIAbDirectory
 47  *                                       representation of a mailing list.
 48  * @param aParentDirectory {AddressBook} The parent directory (as an
 49  *                                       AddressBook object) containing this
 50  *                                       mailing list.
 51  * @param aNew             {boolean}     Set as true for new mailing lists where
 52  *                                       no attempt should be made to fetch the
 53  *                                       contacts contained in the list.
 54  * @constructor
 55  * @class
 56  */
 57 com.gContactSync.MailList = function gCS_MailList(aList, aParentDirectory, aNew) {
 58   if (!aParentDirectory ||
 59     !(aParentDirectory instanceof com.gContactSync.AddressBook ||
 60         aParentDirectory instanceof com.gContactSync.GAddressBook))
 61     throw "Error - invalid address book supplied to the MailList Constructor";
 62   this.mParent = aParentDirectory;
 63   this.mParent.checkList(aList, "MailList constructor");
 64   this.mList   = aList;
 65   this.mList.QueryInterface(Components.interfaces.nsIAbMDBDirectory);
 66   this.mNew    = aNew;
 67   if (!aNew)
 68     this.getAllContacts();
 69 };
 70 
 71 com.gContactSync.MailList.prototype = {
 72   /** The contacts in this mailing list (cached) */
 73   mContacts:       [],
 74   /** This is true whenever the contacts have to be fetched again */
 75   mContactsUpdate: false,
 76   /**
 77    * Sets the name of this list. The update method must be called in order for
 78    * the change to become permanent.
 79    * @param aName {string} The new name for the list.
 80    */
 81   setName: function MailList_setName(aName) {
 82     this.mList.dirName = aName;
 83   },
 84   /**
 85    * Returns the name of this list.
 86    * @returns {string} The name of this list.
 87    */
 88   getName: function MailList_getName() {
 89     return this.mList.dirName;
 90   },
 91   /**
 92    * Returns the card in this mail list, if any, with the same (not-null)
 93    * value for the GoogleID attribute, or, if the GoogleID is null, if the
 94    *         display name, primary, and second emails are the same.
 95    * @param aContact {TBContact} The contact being searched for.
 96    * @param aAttrs   {Array} The attributes whose values must be identical in
 97    *                         order for the contact to match.  The defaults are
 98    *                         DisplayName, PrimaryEmail, and SecondEmail.
 99    *                         This is only used if the contact doesn't have a
100    *                         GoogleID
101    * @returns {TBContact} The card in this list, if any, with the same, and
102    *                      non-null value for its GoogleID attribute, or, if the
103    *                      GoogleID is null, if the display name, primary, and
104    *                      second emails are the same.
105    */
106   hasContact: function MailList_hasContact(aContact, aAttrs) {
107     if (!(aContact instanceof com.gContactSync.TBContact)) {
108       throw "Invalid aContact sent to MailList.hasContact";
109     }
110     // get all of the cards in this list again, if necessary
111     if (this.mContactsUpdate || this.mContacts.length === 0) {
112       this.getAllContacts();
113     }
114     // the attributes to check
115     var attrs = aAttrs ? aAttrs : ["DisplayName", "PrimaryEmail", "SecondEmail"];
116     for (var i = 0, length = this.mContacts.length; i < length; i++) {
117       var contact    = this.mContacts[i],
118           aContactID = aContact.getID();
119       // if it is an old card (has id) compare IDs
120       if (aContactID) {
121         if (aContactID === contact.getID()) {
122           return contact;
123         }
124       }
125       // else check that display name, primary and second email are equal
126       else {
127         for (var j = 0; j < attrs.length; j++) {
128           var aContactVal = aContact.getValue(attrs[j]),
129               contactVal  = contact.getValue(attrs[j]);
130           // if a value is non-empty and the two are not equal then return false
131           if ((aContactVal || contactVal) && aContactVal !== contactVal) {
132             return false;
133           }
134         }
135         return contact;
136       }
137     }
138     return null;
139   },
140   /**
141    * Sets the nick name for this mailing list.  The update method must be
142    * called in order for the change to become permanent.
143    * @param aNickName {string} The new nick name for this mailing list.
144    */
145   setNickName: function MailList_setNickName(aNickName) {
146     this.mList.listNickName = aNickName;
147   },
148   /**
149    * Returns the nick name of this mailing list.
150    * @returns {string} The nick name of this mailing list.
151    */
152   getNickName: function MailList_getNickName() {
153     return this.mList.listNickName;
154   },
155   /**
156    * Sets the description for this mailing list.  The update method must be
157    * called in order for the change to become permanent.
158    * @param aDescription {string} The new description for this mailing list.
159    */
160   setDescription: function MailList_setDescription(aDescription) {
161     this.mList.description = aDescription;
162   },
163   /**
164    * Returns the description of this mailing list.
165    * @returns {string} The description of this mailing list.
166    */
167   getDescription: function MailList_getDescription() {
168     return this.mList.description;
169   },
170   /**
171    * Adds a contact to this mailing list without checking if it already exists.
172    * NOTE: If the contact does not have a primary e-mail address then this
173    * method will add a fake one.
174    * @param aContact {TBContact} The contact to add to this mailing list.
175    * @returns {TBContact}  The contact.
176    */
177   addContact: function MailList_addContact(aContact) {
178     if (!(aContact instanceof com.gContactSync.TBContact)) {
179       throw "Invalid aContact sent to AddressBook.addContact";
180     }
181     // Add a dummy e-mail address if necessary and ignore the preference
182     // If this was not done then the mailing list would break.
183     if (!(aContact.getValue("PrimaryEmail"))) {
184       aContact.setValue("PrimaryEmail", com.gContactSync.makeDummyEmail(aContact, true));
185       aContact.update(); // TODO is this necessary
186     }
187     try {
188       var realContact = new com.gContactSync.TBContact(this.mList.addCard(aContact.mContact),
189                                                        this);
190       this.mContacts.push(realContact);
191       return realContact;
192     }
193     catch (e) {
194       com.gContactSync.LOGGER.LOG_ERROR("Unable to add card to the mail list with URI: " +
195                        this.getURI(), e);
196     }
197     return null;
198   },
199   /**
200    * Returns the uniform resource identifier (URI) for this mailing list.
201    * @returns {string} The URI of this list.
202    */
203   getURI: function MailList_getURI() {
204     if (this.mList.URI)
205       return this.mList.URI;
206     return this.mList.getDirUri();
207   },
208   /**
209    * Returns an array of all of the cards in this mailing list.
210    * @returns {array} An array containing all of the cards in this mailing list.
211    */
212   getAllContacts: function MailList_getAllContacts() {
213     // NOTE: Sometimes hasMoreElements fails if mail lists aren't working
214     this.mContacts = [];
215     var iter = this.mList.childCards,
216         data;
217     if (iter instanceof Components.interfaces.nsISimpleEnumerator) { // Thunderbird 3
218       try {
219         while (iter.hasMoreElements()) {
220           data = iter.getNext();
221           if (data instanceof Components.interfaces.nsIAbCard)
222             this.mContacts.push(new com.gContactSync.TBContact(data, this));
223         }
224       }
225       catch (e) {
226         com.gContactSync.LOGGER.LOG_ERROR("A mailing list is not working:", e);
227         if (com.gContactSync.confirm(com.gContactSync.StringBundle.getStr("resetConfirm"))) {
228           if (this.mParent.reset()) {
229             com.gContactSync.alert(com.gContactSync.StringBundle.getStr("pleaseRestart"));
230           }
231         }
232         // Throw an error to stop the sync
233         throw com.gContactSync.StringBundle.getStr("mailListBroken");
234       }
235     }
236     else if (iter instanceof Components.interfaces.nsIEnumerator) { // TB 2
237       // use nsIEnumerator...
238       try {
239         iter.first();
240         do {
241           data = iter.currentItem();
242           if (data instanceof Components.interfaces.nsIAbCard)
243             this.mContacts.push(new com.gContactSync.TBContact(data, this));
244           iter.next();
245         } while (Components.lastResult === 0);
246       }
247       catch (ex) {
248         // TODO find a way to distinguish between the usual errors and the
249         // broken list errors
250         // error is expected when finished
251         com.gContactSync.LOGGER.VERBOSE_LOG("This error is expected:\n" + ex);
252       }
253     }
254     else {
255       com.gContactSync.LOGGER.LOG_ERROR("Could not iterate through an address book's contacts");
256       throw com.gContactSync.StringBundle.getStr("mailListBroken");
257     }
258     return this.mContacts;
259   },
260   /**
261    * Deletes all of the cards in the array of cards from this list.
262    * @param aContacts {array} The array of TBContacts to delete from this mailing list.
263    */
264   deleteContacts: function MailList_deleteContacts(aContacts) {
265     if (!(aContacts && aContacts.length > 0))
266       return;
267     var arr,
268         i = 0;
269     if (com.gContactSync.AbManager.mVersion === 3) { // TB 3
270       arr = Components.classes["@mozilla.org/array;1"]
271                       .createInstance(Components.interfaces.nsIMutableArray);
272       for (; i < aContacts.length; i++) {
273         if (aContacts[i] instanceof com.gContactSync.TBContact) {
274           arr.appendElement(aContacts[i].mContact, false);
275         }
276         else {
277           com.gContactSync.LOGGER.LOG_WARNING("Found an invalid contact sent " +
278                                               "MailList.deleteContacts");
279         }
280       }
281     }
282     else { // TB 2
283       arr =  Components.classes["@mozilla.org/supports-array;1"]
284                        .createInstance(Components.interfaces.nsISupportsArray);
285       for (; i < aContacts.length; i++) {
286         if (aContacts[i] instanceof com.gContactSync.TBContact) {
287           arr.AppendElement(aContacts[i].mContact, false);
288         }
289         else {
290           com.gContactSync.LOGGER.LOG_WARNING("Found an invalid contact sent " +
291                                               "MailList.deleteContacts");
292         }
293       }
294     }
295     try {
296       if (arr) { // make sure arr isn't null (mailnews bug 448165)
297         this.mContactsUpdate = true; // update mContacts when used
298         this.mList.deleteCards(arr);
299       }
300     }
301     catch (e) {
302       com.gContactSync.LOGGER.LOG_WARNING("Error while deleting cards from a mailing list", e);
303     }
304     this.mContacts = this.getAllContacts();
305   },
306   /**
307    * Deletes this mailing list from its parent address book.
308    */
309   remove: function MailList_delete() {
310     this.mParent.mDirectory.deleteDirectory(this.mList);
311     this.mContacts = [];
312     // make sure the functions don't do anything
313     for (var i in this) {
314       if (i instanceof Function)
315         i = function () {};
316     }
317   },
318   /**
319    * Updates this mail list (commits changes like renaming or changing the
320    * nickname)
321    */
322   update: function MailList_update() {
323     try {
324       if (com.gContactSync.AbManager.mVersion === 3)
325         this.mList.editMailListToDatabase(null);
326       else
327         this.mList.editMailListToDatabase(this.getURI(), null);
328     }
329     catch (e) {
330       com.gContactSync.LOGGER.LOG_WARNING("Unable to update mail list", e);
331     }
332   }
333 };
334