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-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 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 * An extension of AddressBook that adds functionality specific to gContactSync.
 43 * @param aDirectory {nsIAbDirectory} The actual directory.
 44 * @param aNoPrefs   {boolean}        Set this to true to skip fetching the
 45 *                                    preferences.
 46 * @constructor
 47 * @class
 48 * @extends com.gContactSync.AddressBook
 49 */
 50 com.gContactSync.GAddressBook = function gCS_GAddressBook(aDirectory, aNoPrefs) {
 51   // call the AddressBook constructor using this object
 52   com.gContactSync.AddressBook.call(this, aDirectory);
 53 
 54   // Preferences for this address book
 55   // If these aren't set the global preference with the same name, if any, is used
 56   // NOTE: All these preferences are converted to strings
 57   this.mPrefs = {
 58     Plugin:         "", // The name of the plugin to use
 59     Username:       "", // The username of the acct synced with
 60     Disabled:       "", // Temporarily disable synchronization with this AB
 61     // NOTE: These three prefs aren't combined into a single pref for backwards
 62     // compatibility with 0.2.x
 63     myContacts:     "", // true if only one group should be synced
 64     myContactsName: "", // The name of the group to sync
 65     syncGroups:     "", // Synchronize groups
 66     // NOTE: These two prefs aren't combined into a single pref for backwards
 67     // compatibility with 0.2.x
 68     readOnly:       "", // Fetch updates from Google but don't send any changes
 69     writeOnly:      "", // Send changes to the server, but don't fetch any changes
 70     updateGoogleInConflicts: "", // If a contact was updated in Google and TB then
 71                                  // this pref determines which contact to update
 72     lastSync:       "", // The last time this AB was synchronized
 73     lastBackup:     "", // The last time this AB was backed up
 74     reset:          ""  // Whether this AB has been reset since the last sync
 75   };
 76   if (!aNoPrefs)
 77     this.getPrefs();
 78 };
 79 
 80 // Copy the AB prototype (methods and member variables)
 81 com.gContactSync.GAddressBook.prototype = com.gContactSync.AddressBook.prototype;
 82 
 83 // A prefix for all preferences used to prevent conflicts with other extensions
 84 com.gContactSync.GAddressBook.prototype.prefPrefix = "gContactSync";
 85 
 86 /**
 87  * Fetches all of this directory's preferences.  If the directory does not have
 88  * any given preferences this function will use the global preference's value,
 89  * if any.
 90  */
 91 com.gContactSync.GAddressBook.prototype.getPrefs = function GAddressBook_getPrefs() {
 92   com.gContactSync.LOGGER.VERBOSE_LOG("\nGetting Prefs for AB '" + this.getName() + "':");
 93   var i, val, pref;
 94   for (i in this.mPrefs) {
 95     var isLastSync = (i === "lastSync")
 96     // all prefs except lastSync have the prefPrefix in from of them
 97     val = this.getStringPref(isLastSync ? i : this.prefPrefix + i);
 98     // getStringPref returns 0 iff the pref doesn't exist
 99     // if the pref doesn't exist, then use the global gContactSync pref
100     // AND set this AB's pref so this doesn't fall through next time
101     // this behavior is mostly for backwards compatibility
102     if (val === 0) {
103       com.gContactSync.LOGGER.VERBOSE_LOG("getPrefs fell through on " + i);
104       pref = com.gContactSync.Preferences.mSyncPrefs[i];
105       val  = pref ? String(pref.value) : "";
106       this.savePref(i, val);
107     }
108     // Bug 22817 - Unexpected behavior when lastSync is NaN
109     // Make sure that lastSync isn't set to NaN
110     else if (isLastSync && isNaN(val)) {
111       val = 0;
112     }
113     com.gContactSync.LOGGER.VERBOSE_LOG(" * " + i + " = " + val);
114     this.mPrefs[i] = val;
115   }
116   com.gContactSync.LOGGER.VERBOSE_LOG("\n");
117 };
118 
119 /**
120  * Save the value of a given preference for this address book.
121  *
122  * @param aName  {string} The name of the preference to set.
123  * @param aValue {string} The value to set the preference to.
124  */
125 com.gContactSync.GAddressBook.prototype.savePref = function GAddressBook_savePref(aName, aValue) {
126   com.gContactSync.LOGGER.VERBOSE_LOG(" * Setting pref '" + aName + "' to value '" + aValue + "'");
127   // all prefs except lastSync have the prefPrefix in from of them
128   this.setStringPref((aName === "lastSync" ? aName : this.prefPrefix + aName), aValue);
129   // in theory (and in testing) the preferences listener should already take
130   // care of setting the preference in this.mPrefs...
131   this.mPrefs[aName] = aValue;
132 };
133  
134 /**
135  * Sets the last time this address book was synchronized, in milliseconds
136  * since the epoch.
137  * @param aLastSync {integer} The last sync time.
138  */
139 com.gContactSync.GAddressBook.prototype.setLastSyncDate = function GAddressBook_setLastSyncDate(aLastSync) {
140   this.setStringPref("lastSync", aLastSync);
141   // in theory (and in testing) the preferences listener should already take
142   // care of setting the preference in this.mPrefs...
143   this.mPrefs.lastSync = aLastSync;
144 };
145  
146 /**
147  * 'Resets' this address book making it appear to be brand new and never
148  * synchronized.
149  * The username is NOT erased.
150  * 
151  * This includes:
152  *   - Creating a backup
153  *   - Deleting all mailing lists
154  *   - Deleting all contacts
155  *   - Setting primary to true
156  *   - Setting the last sync date to 0
157  * @returns {boolean} True if the AB was reset, false otherwise.
158  */
159 com.gContactSync.GAddressBook.prototype.reset = function GAddressBook_reset() {
160   com.gContactSync.LOGGER.LOG("Resetting the " + this.getName() + " directory.");
161   var lists, i, dt;
162   if (this.mPrefs.reset === "true") {
163     com.gContactSync.LOGGER.LOG_WARNING("An attempt was made to reset an AB which was already reset.  Ignoring request.");
164     return false;
165   }
166   dt = new Date().toLocaleFormat("%Y_%m_%d_");
167   com.gContactSync.GAbManager.backupAB(this, "reset_" + dt, ".bak");
168   try {
169     lists = this.getAllLists(true);
170   }
171   catch (e) {
172     com.gContactSync.LOGGER.LOG_ERROR("Unable to get all lists", e);
173     lists = {};
174   }
175   com.gContactSync.LOGGER.VERBOSE_LOG(" * Deleting all lists");
176   for (i in lists) {
177     if (lists[i] instanceof com.gContactSync.GMailList) {
178       com.gContactSync.LOGGER.VERBOSE_LOG("  - Deleting list " + lists[i].getName());
179       lists[i].remove();
180     }
181   }
182   com.gContactSync.LOGGER.VERBOSE_LOG(" * Finished deleting lists");
183   com.gContactSync.LOGGER.VERBOSE_LOG(" * Deleting all contacts");
184   this.deleteContacts(this.getAllContacts());
185   com.gContactSync.LOGGER.VERBOSE_LOG(" * Setting Last Sync Date to 0");
186   this.setLastSyncDate(0);
187   com.gContactSync.LOGGER.LOG("Finished resetting the directory.");
188   // mark the AB as having been reset
189   this.savePref("reset", true);
190   return true;
191 };
192 
193 /**
194  * Updates the LastModifiedDate of every contact in this address book so
195  * it gets updated during the next sync.
196  */
197 com.gContactSync.GAddressBook.prototype.replaceToServer = function GAddressBook_replaceToServer() {
198   var contacts = this.getAllContacts(),
199       time     = (new Date()).getTime();
200   
201   // Set the LastModifiedDate to right now so each contact will get sent to
202   // Google during the next sync.  Also update the card in the AB.
203   for (var i = 0; i < contacts.length; i++) {
204     contacts[i].setValue("LastModifiedDate", time, true);
205   }
206 };
207 
208 /**
209  * Returns a new GMailList object given the same parameters as the GMailList
210  * constructor.
211  *
212  * See the GMailList constructor for the most recent comments.
213  *
214  * @param aList {Ci.nsIAbDirectory}       The actual nsIAbDirectory
215  *                                        representation of a mailing list.
216  * @param aParentDirectory {GAddressBook} The parent directory (as an
217  *                                        AddressBook object) containing this
218  *                                        mailing list.
219  * @param aNew             {boolean}      Set as true for new mailing lists where
220  *                                        no attempt should be made to fetch the
221  *                                        contacts contained in the list.
222  * @returns {GMailList} A new GMailList.
223  */
224 com.gContactSync.GAddressBook.prototype.newListObj = function GAddressBook_newListObj(aList, aParentDirectory, aNew) {
225   return new com.gContactSync.GMailList(aList, aParentDirectory, aNew);
226 };
227 
228 /**
229  * Returns an an object containing GMailList objects whose attribute name is
230  * the name of the mail list.
231  * @param skipGetCards {boolean} True to skip getting the cards of each list.
232  * @returns {object} An object containing GMailList objects.
233  */
234 com.gContactSync.GAddressBook.prototype.getAllLists = function GAddressBook_getAllLists(skipGetCards) {
235   // same in Thunderbird 2 and 3
236   com.gContactSync.LOGGER.VERBOSE_LOG("Searching for mailing lists:");
237   var iter = this.mDirectory.childNodes,
238       obj  = {},
239       list,
240       id,
241       data;
242   while (iter.hasMoreElements()) {
243     data = iter.getNext();
244     if (data instanceof Components.interfaces.nsIAbDirectory && data.isMailList) {
245       list    = this.newListObj(data, this, skipGetCards);
246       id      = list.getGroupID();
247       obj[id] = list;
248       com.gContactSync.LOGGER.VERBOSE_LOG(" * " + list.getName() + " - " + id);
249     }
250   }
251   return obj;
252 };
253