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 * Synchronizes a Thunderbird Address Book with Google Contacts. 43 * @class 44 */ 45 com.gContactSync.Sync = { 46 /** Google contacts that should be deleted */ 47 mContactsToDelete: [], 48 /** New contacts to add to Google */ 49 mContactsToAdd: [], 50 /** Contacts to update */ 51 mContactsToUpdate: [], 52 /** Groups to delete */ 53 mGroupsToDelete: [], 54 /** Groups to add */ 55 mGroupsToAdd: [], 56 /** Groups to update */ 57 mGroupsToUpdate: [], 58 /** Groups to add (URIs) */ 59 mGroupsToAddURI: [], 60 /** The current authentication token */ 61 mCurrentAuthToken: {}, 62 /** The current username */ 63 mCurrentUsername: {}, 64 /** The current address book being synchronized */ 65 mCurrentAb: {}, 66 /** Synchronized address book */ 67 mAddressBooks: [], 68 /** The index of the AB being synced */ 69 mIndex: 0, 70 /** The URI of a photo to be added to the newly created Google contact */ 71 mNewPhotoURI: {}, 72 /** Temporarily set to true when a backup is necessary for this account */ 73 mBackup: false, 74 /** Temporarily set to true when the first backup is necessary */ 75 mFirstBackup: false, 76 /** Summary data from the current sync */ 77 mCurrentSummary: {}, 78 /** Summary data from the entire synchronization */ 79 mOverallSummary: {}, 80 /** Commands to execute when offline during an HTTP Request */ 81 mOfflineFunction: function Sync_offlineFunc(httpReq) { 82 com.gContactSync.Overlay.setStatusBarText(com.gContactSync.StringBundle.getStr('offlineStatusText')); 83 com.gContactSync.Sync.finish(com.gContactSync.StringBundle.getStr('offlineStatusText')); 84 }, 85 /** True if a synchronization is scheduled */ 86 mSyncScheduled: false, 87 /** used to store groups for the account being synchronized */ 88 mGroups: {}, 89 /** stores the mail lists in the directory being synchronized */ 90 mLists: {}, 91 /** override for the contact feed URL. Intended for syncing one group only */ 92 mContactsUrl: null, 93 /** This should be set to true if the sync was run manually */ 94 mManualSync: false, 95 /** 96 * Performs the first steps of the sync process. 97 * @param aManualSync {boolean} Set this to true if the sync was run manually. 98 */ 99 begin: function Sync_begin(aManualSync) { 100 if (!com.gContactSync.gdata.isAuthValid()) { 101 com.gContactSync.alert(com.gContactSync.StringBundle.getStr("pleaseAuth")); 102 return; 103 } 104 // quit if still syncing. 105 if (com.gContactSync.Preferences.mSyncPrefs.synchronizing.value) { 106 return; 107 } 108 109 com.gContactSync.Sync.mManualSync = (aManualSync === true); 110 111 // Reset the overall summary 112 com.gContactSync.Sync.mOverallSummary = new com.gContactSync.SyncSummaryData(); 113 com.gContactSync.Sync.mCurrentSummary = new com.gContactSync.SyncSummaryData(); 114 115 com.gContactSync.Sync.mSyncScheduled = false; 116 com.gContactSync.Preferences.setSyncPref("synchronizing", true); 117 com.gContactSync.Sync.mBackup = false; 118 com.gContactSync.LOGGER.mErrorCount = 0; // reset the error count 119 com.gContactSync.Overlay.setStatusBarText(com.gContactSync.StringBundle.getStr("syncing")); 120 com.gContactSync.Sync.mIndex = 0; 121 com.gContactSync.Sync.mAddressBooks = com.gContactSync.GAbManager.getSyncedAddressBooks(true); 122 com.gContactSync.Sync.mCurrentAb = {}; 123 com.gContactSync.Sync.syncNextUser(); 124 }, 125 /** 126 * Synchronizes the next address book in com.gContactSync.Sync.mAddressBooks. 127 * If all ABs were synchronized, then this continues with com.gContactSync.Sync.finish(); 128 */ 129 syncNextUser: function Sync_syncNextUser() { 130 131 // Log some summary data if an AB was just synchronized 132 if (com.gContactSync.Sync.mCurrentAb && 133 com.gContactSync.Sync.mCurrentAb instanceof com.gContactSync.GAddressBook) { 134 com.gContactSync.Sync.mCurrentSummary.print(false); 135 } 136 137 // Add the current summary to the overall summary then reset the current 138 // summary 139 com.gContactSync.Sync.mOverallSummary.addSummary(com.gContactSync.Sync.mCurrentSummary); 140 com.gContactSync.Sync.mCurrentSummary = new com.gContactSync.SyncSummaryData(); 141 142 var obj = com.gContactSync.Sync.mAddressBooks[com.gContactSync.Sync.mIndex++]; 143 if (!obj) { 144 com.gContactSync.Sync.finish(); 145 return; 146 } 147 // make sure the user doesn't have to restart TB 148 if (com.gContactSync.Preferences.mSyncPrefs.needRestart.value) { 149 var restartStr = com.gContactSync.StringBundle.getStr("pleaseRestart"); 150 com.gContactSync.alert(restartStr); 151 com.gContactSync.Overlay.setStatusBarText(restartStr); 152 return; 153 } 154 com.gContactSync.Overlay.setStatusBarText(com.gContactSync.StringBundle.getStr("syncing")); 155 com.gContactSync.Sync.mCurrentUsername = obj.username; 156 com.gContactSync.LOGGER.LOG("Starting synchronization for " + com.gContactSync.Sync.mCurrentUsername + 157 " at: " + new Date().getTime() + " (" + Date() + ")\n"); 158 com.gContactSync.Sync.mCurrentAb = obj.ab; 159 com.gContactSync.Sync.mCurrentAuthToken = com.gContactSync.LoginManager.getAuthTokens()[com.gContactSync.Sync.mCurrentUsername]; 160 com.gContactSync.Sync.mContactsUrl = null; 161 com.gContactSync.Sync.mBackup = false; 162 com.gContactSync.LOGGER.VERBOSE_LOG("Found Address Book with name: " + 163 com.gContactSync.Sync.mCurrentAb.mDirectory.dirName + 164 "\n - URI: " + com.gContactSync.Sync.mCurrentAb.mURI + 165 "\n - Pref ID: " + com.gContactSync.Sync.mCurrentAb.getPrefId()); 166 if (com.gContactSync.Sync.mCurrentAb.mPrefs.Disabled === "true") { 167 com.gContactSync.LOGGER.LOG("*** NOTE: Synchronization was disabled for this address book ***"); 168 com.gContactSync.Sync.mCurrentAb = null; 169 com.gContactSync.Sync.syncNextUser(); 170 return; 171 } 172 // If an authentication token cannot be found for this username then 173 // offer to let the user login with that account 174 if (!com.gContactSync.Sync.mCurrentAuthToken) { 175 com.gContactSync.LOGGER.LOG_WARNING("Unable to find the auth token for: " + 176 com.gContactSync.Sync.mCurrentUsername); 177 if (com.gContactSync.confirm(com.gContactSync.StringBundle.getStr("noTokenFound") + 178 ": " + com.gContactSync.Sync.mCurrentUsername + 179 "\n" + com.gContactSync.StringBundle.getStr("ab") + 180 ": " + com.gContactSync.Sync.mCurrentAb.getName())) { 181 // Now let the user login 182 var prompt = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] 183 .getService(Components.interfaces.nsIPromptService) 184 .promptUsernameAndPassword, 185 username = {value: com.gContactSync.Sync.mCurrentUsername}, 186 password = {}, 187 // opens a username/password prompt 188 ok = prompt(window, com.gContactSync.StringBundle.getStr("loginTitle"), 189 com.gContactSync.StringBundle.getStr("loginText"), username, password, null, 190 {value: false}); 191 if (!ok) { 192 com.gContactSync.Sync.syncNextUser(); 193 return; 194 } 195 // Decrement the index so Sync.syncNextUser runs on this AB again 196 com.gContactSync.Sync.mIndex--; 197 // This is a primitive way of validating an e-mail address, but Google takes 198 // care of the rest. It seems to allow getting an auth token w/ only the 199 // username, but returns an error when trying to do anything w/ that token 200 // so this makes sure it is a full e-mail address. 201 if (username.value.indexOf("@") < 1) { 202 com.gContactSync.alertError(com.gContactSync.StringBundle.getStr("invalidEmail")); 203 com.gContactSync.Sync.syncNextUser(); 204 return; 205 } 206 // fix the username before authenticating 207 username.value = com.gContactSync.fixUsername(username.value); 208 var body = com.gContactSync.gdata.makeAuthBody(username.value, password.value); 209 var httpReq = new com.gContactSync.GHttpRequest("authenticate", null, null, body); 210 // if it succeeds and Google returns the auth token, store it and then start 211 // a new sync 212 httpReq.mOnSuccess = function reauth_onSuccess(httpReq) { 213 com.gContactSync.LoginManager.addAuthToken(username.value, 214 'GoogleLogin' + 215 httpReq.responseText.split("\n")[2]); 216 com.gContactSync.Sync.syncNextUser(); 217 }; 218 // if it fails, alert the user and prompt them to try again 219 httpReq.mOnError = function reauth_onError(httpReq) { 220 com.gContactSync.alertError(com.gContactSync.StringBundle.getStr('authErr')); 221 com.gContactSync.LOGGER.LOG_ERROR('Authentication Error - ' + 222 httpReq.status, 223 httpReq.responseText); 224 com.gContactSync.Sync.syncNextUser(); 225 }; 226 // if the user is offline, alert them and quit 227 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 228 httpReq.send(); 229 } 230 else 231 com.gContactSync.Sync.syncNextUser(); 232 return; 233 } 234 var lastBackup = parseInt(obj.ab.mPrefs.lastBackup, 10), 235 interval = com.gContactSync.Preferences.mSyncPrefs.backupInterval.value * 24 * 3600 * 1000, 236 prefix = ""; 237 com.gContactSync.LOGGER.VERBOSE_LOG(" - Last backup was at " + lastBackup + 238 ", interval is " + interval); 239 this.mFirstBackup = !lastBackup && interval >= 0; 240 this.mBackup = this.mFirstBackup || interval >= 0 && new Date().getTime() - lastBackup > interval; 241 prefix = this.mFirstBackup ? "init_" : ""; 242 if (this.mBackup) { 243 com.gContactSync.GAbManager.backupAB(com.gContactSync.Sync.mCurrentAb, 244 prefix, 245 ".bak"); 246 } 247 // getGroups must be called if the myContacts pref is set so it can find the 248 // proper group URL 249 if (com.gContactSync.Sync.mCurrentAb.mPrefs.syncGroups === "true" || 250 (com.gContactSync.Sync.mCurrentAb.mPrefs.myContacts !== "false" && 251 com.gContactSync.Sync.mCurrentAb.mPrefs.myContactsName !== "false")) { 252 com.gContactSync.Sync.getGroups(); 253 } 254 else { 255 com.gContactSync.Sync.getContacts(); 256 } 257 }, 258 /** 259 * Sends an HTTP Request to Google for a feed of all of the user's groups. 260 * Calls com.gContactSync.Sync.begin() when there is a successful response on an error other 261 * than offline. 262 */ 263 getGroups: function Sync_getGroups() { 264 com.gContactSync.LOGGER.LOG("***Beginning Group - Mail List Synchronization***"); 265 var httpReq = new com.gContactSync.GHttpRequest("getGroups", 266 com.gContactSync.Sync.mCurrentAuthToken, 267 null, 268 null, 269 com.gContactSync.Sync.mCurrentUsername); 270 httpReq.mOnSuccess = function getGroupsSuccess(httpReq) { 271 com.gContactSync.LOGGER.VERBOSE_LOG(com.gContactSync.serializeFromText(httpReq.responseText)); 272 com.gContactSync.Sync.syncGroups(httpReq.responseXML); 273 }; 274 httpReq.mOnError = function getGroupsError(httpReq) { 275 com.gContactSync.LOGGER.LOG_ERROR(httpReq.responseText); 276 // if there is an error, try to sync w/o groups 277 com.gContactSync.Sync.begin(); 278 }; 279 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 280 httpReq.send(); 281 }, 282 /** 283 * Sends an HTTP Request to Google for a feed of all the user's contacts. 284 * Calls com.gContactSync.Sync.sync with the response if successful or com.gContactSync.Sync.syncNextUser with the 285 * error. 286 */ 287 getContacts: function Sync_getContacts() { 288 com.gContactSync.LOGGER.LOG("***Beginning Contact Synchronization***"); 289 var httpReq; 290 if (com.gContactSync.Sync.mContactsUrl) { 291 httpReq = new com.gContactSync.GHttpRequest("getFromGroup", 292 com.gContactSync.Sync.mCurrentAuthToken, 293 null, 294 null, 295 com.gContactSync.Sync.mCurrentUsername, com.gContactSync.Sync.mContactsUrl); 296 } 297 else { 298 httpReq = new com.gContactSync.GHttpRequest("getAll", 299 com.gContactSync.Sync.mCurrentAuthToken, 300 null, 301 null, 302 com.gContactSync.Sync.mCurrentUsername); 303 } 304 httpReq.mOnSuccess = function getContactsSuccess(httpReq) { 305 // com.gContactSync.serializeFromText does not do anything if verbose 306 // logging is disabled so the serialization won't waste time 307 var backup = com.gContactSync.Sync.mBackup, 308 firstBackup = com.gContactSync.Sync.mFirstBackup, 309 feed = com.gContactSync.serializeFromText(httpReq.responseText, 310 backup); 311 com.gContactSync.LOGGER.VERBOSE_LOG(feed); 312 if (backup) { 313 com.gContactSync.gdata.backupFeed(feed, 314 com.gContactSync.Sync.mCurrentUsername, 315 (firstBackup ? "init_" : ""), 316 ".bak"); 317 } 318 com.gContactSync.Sync.sync2(httpReq.responseXML); 319 }; 320 httpReq.mOnError = function getContactsError(httpReq) { 321 com.gContactSync.LOGGER.LOG_ERROR('Error while getting all contacts', 322 httpReq.responseText); 323 com.gContactSync.Sync.syncNextUser(httpReq.responseText); 324 }; 325 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 326 httpReq.send(); 327 }, 328 /** 329 * Completes the synchronization process by writing the finish time to a file, 330 * writing the sync details to a different file, scheduling another sync, and 331 * writes the completion status to the status bar. 332 * 333 * @param aError {string} Optional. A string containing the error message. 334 * @param aStartOver {boolean} Also optional. True if the sync should be restarted. 335 */ 336 finish: function Sync_finish(aError, aStartOver) { 337 if (aError) 338 com.gContactSync.LOGGER.LOG_ERROR("Error during sync", aError); 339 if (com.gContactSync.LOGGER.mErrorCount > 0) { 340 // if there was an error, display the error message unless the user is 341 // offline 342 if (com.gContactSync.Overlay.getStatusBarText() !== aError) 343 com.gContactSync.Overlay.setStatusBarText(com.gContactSync.StringBundle.getStr("errDuringSync")); 344 } 345 else { 346 com.gContactSync.Overlay.writeTimeToStatusBar(); 347 com.gContactSync.LOGGER.LOG("Finished Synchronization at: " + Date()); 348 } 349 350 // Print a summary and alert the user if it was a manual sync and 351 // alertSummary is set to true. 352 com.gContactSync.Sync.mOverallSummary.print( 353 com.gContactSync.Sync.mManualSync && 354 com.gContactSync.Preferences.mSyncPrefs.alertSummary.value); 355 356 // reset some variables 357 com.gContactSync.ContactConverter.mCurrentCard = {}; 358 com.gContactSync.Preferences.setSyncPref("synchronizing", false); 359 com.gContactSync.Sync.mCurrentAb = {}; 360 com.gContactSync.Sync.mContactsUrl = null; 361 com.gContactSync.Sync.mCurrentUsername = {}; 362 com.gContactSync.Sync.mCurrentAuthToken = {}; 363 // refresh the ab results pane 364 // https://www.mozdev.org/bugs/show_bug.cgi?id=19733 365 try { 366 if (SetAbView !== undefined) { 367 SetAbView(GetSelectedDirectory(), false); 368 } 369 370 // select the first card, if any 371 if (gAbView && gAbView.getCardFromRow(0)) 372 SelectFirstCard(); 373 } 374 catch (e) {} 375 // start over, if necessary, or schedule the next synchronization 376 if (aStartOver) 377 com.gContactSync.Sync.begin(); 378 else 379 com.gContactSync.Sync.schedule(com.gContactSync.Preferences.mSyncPrefs.refreshInterval.value * 60000); 380 }, 381 /** 382 * Does the actual synchronization of contacts and modifies the AB as it goes. 383 * Initializes arrays of Google contacts to add, remove, or update. 384 * @param aAtom {XML} The ATOM/XML feed of contacts. 385 */ 386 sync2: function Sync_sync2(aAtom) { 387 // get the address book 388 var ab = com.gContactSync.Sync.mCurrentAb, 389 // get all the contacts from the feed and the cards from the address book 390 googleContacts = aAtom.getElementsByTagName('entry'), 391 abCards = ab.getAllContacts(), 392 // get and log the last sync time (milliseconds since 1970 UTC) 393 lastSync = parseInt(ab.mPrefs.lastSync, 10), 394 cardsToDelete = [], 395 maxContacts = com.gContactSync.Preferences.mSyncPrefs.maxContacts.value, 396 // if there are more contacts than returned, increase the pref 397 newMax; 398 if (isNaN(lastSync)) { 399 com.gContactSync.LOGGER.LOG_WARNING("lastSync was NaN, setting to 0"); 400 lastSync = 0; 401 } 402 // mark the AB as not having been reset if it gets this far 403 com.gContactSync.Sync.mCurrentAb.savePref("reset", false); 404 405 // have to update the lists or TB 2 won't work properly 406 com.gContactSync.Sync.mLists = ab.getAllLists(); 407 com.gContactSync.LOGGER.LOG("Last sync was at: " + lastSync + 408 " (" + new Date(lastSync) + ")"); 409 if ((newMax = com.gContactSync.gdata.contacts.getNumberOfContacts(aAtom)) >= maxContacts.value) { 410 com.gContactSync.Preferences.setPref(com.gContactSync.Preferences.mSyncBranch, maxContacts.label, 411 maxContacts.type, newMax + 50); 412 com.gContactSync.Sync.finish("Max Contacts too low...resynchronizing", true); 413 return; 414 } 415 com.gContactSync.Sync.mContactsToAdd = []; 416 com.gContactSync.Sync.mContactsToDelete = []; 417 com.gContactSync.Sync.mContactsToUpdate = []; 418 var gContact, 419 // get the strings outside of the loop so they are only found once 420 found = " * Found a match, last modified:", 421 bothChanged = " * Conflict detected: the contact has been updated in " + 422 "both Google and Thunderbird", 423 bothGoogle = " * The contact from Google will be updated", 424 bothTB = " * The card from Thunderbird will be updated", 425 gContacts = {}; 426 // Step 1: get all contacts from Google into GContact objects in an object 427 // keyed by ID. 428 for (var i = 0, length = googleContacts.length; i < length; i++) { 429 gContact = new com.gContactSync.GContact(googleContacts[i]); 430 gContact.lastModified = gContact.getLastModifiedDate(); 431 gContact.id = gContact.getID(true); 432 gContacts[gContact.id] = gContact; 433 } 434 // re-initialize the contact converter (in case a pref changed) 435 com.gContactSync.ContactConverter.init(); 436 437 // Step 2: iterate through TB Contacts and check for matches 438 for (i = 0, length = abCards.length; i < length; i++) { 439 var tbContact = abCards[i], 440 id = tbContact.getID(), 441 tbCardDate = tbContact.getValue("LastModifiedDate"); 442 com.gContactSync.LOGGER.LOG(tbContact.getName() + ": " + id); 443 tbContact.id = id; 444 // no ID = new contact 445 if (!id) { 446 if (ab.mPrefs.readOnly === "true") { 447 com.gContactSync.LOGGER.LOG(" * The contact is new. " + 448 "Ignoring since read-only mode is on."); 449 this.mCurrentSummary.mLocal.mIgnored++; 450 } 451 else { 452 // this.mCurrentSummary.mRemote.mAdded++; This is done after the contact is 453 // successfully added 454 com.gContactSync.LOGGER.LOG(" * This contact is new and will be added to Google."); 455 this.mCurrentSummary.mRemote.mAdded++; 456 com.gContactSync.Sync.mContactsToAdd.push(tbContact); 457 } 458 } 459 // if there is a matching Google Contact 460 else if (gContacts[id]) { 461 gContact = gContacts[id]; 462 // remove it from gContacts 463 gContacts[id] = null; 464 // note that this returns 0 if readOnly is set 465 gCardDate = ab.mPrefs.writeOnly !== "true" ? gContact.lastModified : 0; 466 // 4 options 467 // if both were updated 468 com.gContactSync.LOGGER.LOG(found + 469 "\n - Google: " + gCardDate + 470 " (" + new Date(gCardDate) + ")" + 471 "\n - Thunderbird: " + (tbCardDate * 1000) + 472 " (" + new Date(tbCardDate * 1000) + ")"); 473 com.gContactSync.LOGGER.VERBOSE_LOG(" * Google ID: " + id); 474 // If there is a conflict, looks at the updateGoogleInConflicts 475 // preference and updates Google if it's true, or Thunderbird if false 476 if (gCardDate > lastSync && tbCardDate > lastSync / 1000) { 477 com.gContactSync.LOGGER.LOG(bothChanged); 478 this.mCurrentSummary.mConflicted++; 479 if (ab.mPrefs.writeOnly === "true" || ab.mPrefs.updateGoogleInConflicts === "true") { 480 com.gContactSync.LOGGER.LOG(bothGoogle); 481 var toUpdate = {}; 482 toUpdate.gContact = gContact; 483 toUpdate.abCard = tbContact; 484 this.mCurrentSummary.mRemote.mUpdated++; 485 com.gContactSync.Sync.mContactsToUpdate.push(toUpdate); 486 } 487 // update Thunderbird if writeOnly is off and updateGoogle is off 488 else { 489 com.gContactSync.LOGGER.LOG(bothTB); 490 this.mCurrentSummary.mLocal.mUpdated++; 491 com.gContactSync.ContactConverter.makeCard(gContact, tbContact); 492 } 493 } 494 // if the contact from Google is newer update the TB card 495 else if (gCardDate > lastSync) { 496 com.gContactSync.LOGGER.LOG(" * The contact from Google is newer...Updating the" + 497 " contact from Thunderbird"); 498 this.mCurrentSummary.mLocal.mUpdated++; 499 com.gContactSync.ContactConverter.makeCard(gContact, tbContact); 500 } 501 // if the TB card is newer update Google 502 else if (tbCardDate > lastSync / 1000) { 503 com.gContactSync.LOGGER.LOG(" * The contact from Thunderbird is newer...Updating the" + 504 " contact from Google"); 505 var toUpdate = {}; 506 toUpdate.gContact = gContact; 507 toUpdate.abCard = tbContact; 508 this.mCurrentSummary.mRemote.mUpdated++; 509 com.gContactSync.Sync.mContactsToUpdate.push(toUpdate); 510 } 511 // otherwise nothing needs to be done 512 else { 513 com.gContactSync.LOGGER.LOG(" * Neither contact has changed"); 514 this.mCurrentSummary.mNotChanged++; 515 } 516 } 517 // if there isn't a match, but the card is new, add it to Google 518 else if (tbContact.getValue("LastModifiedDate") > lastSync / 1000 || 519 isNaN(lastSync)) { 520 com.gContactSync.LOGGER.LOG(" * Contact is new, adding to Google."); 521 this.mCurrentSummary.mRemote.mAdded++; 522 com.gContactSync.Sync.mContactsToAdd.push(tbContact); 523 } 524 // Otherwise, delete the contact from the address book if writeOnly 525 // mode isn't on 526 else if (ab.mPrefs.writeOnly !== "true") { 527 com.gContactSync.LOGGER.LOG(" * Contact deleted from Google, " + 528 "deleting local copy"); 529 this.mCurrentSummary.mLocal.mRemoved++; 530 cardsToDelete.push(tbContact); 531 } else { 532 this.mCurrentSummary.mRemote.mIgnored++; 533 com.gContactSync.LOGGER.LOG(" * Contact deleted from Google, ignoring" + 534 " since write-only mode is enabled"); 535 } 536 } 537 538 // STEP 3: Check for old Google contacts to delete and new contacts to add to TB 539 com.gContactSync.LOGGER.LOG("**Looking for unmatched Google contacts**"); 540 for (var id in gContacts) { 541 var gContact = gContacts[id]; 542 if (gContact) { 543 544 // If writeOnly is on, then set the last modified date to 1 so TB grabs 545 // all the contacts from Google during the first sync. 546 var gCardDate = ab.mPrefs.writeOnly != "true" ? gContact.lastModified : 1; 547 com.gContactSync.LOGGER.LOG(gContact.getName() + " - " + gCardDate + 548 "\n" + id); 549 if (gCardDate > lastSync || isNaN(lastSync)) { 550 com.gContactSync.LOGGER.LOG(" * The contact is new and will be added to Thunderbird"); 551 this.mCurrentSummary.mLocal.mAdded++; 552 var newCard = ab.newContact(); 553 com.gContactSync.ContactConverter.makeCard(gContact, newCard); 554 } 555 else if (ab.mPrefs.readOnly != "true") { 556 com.gContactSync.LOGGER.LOG(" * The contact is old and will be deleted"); 557 this.mCurrentSummary.mLocal.mRemoved++; 558 com.gContactSync.Sync.mContactsToDelete.push(gContact); 559 } 560 else { 561 com.gContactSync.LOGGER.LOG (" * The contact was deleted in Thunderbird. " + 562 "Ignoring since read-only mode is on."); 563 this.mCurrentSummary.mLocal.mIgnored++; 564 } 565 } 566 } 567 var threshold = com.gContactSync.Preferences.mSyncPrefs 568 .confirmDeleteThreshold.value; 569 // Request permission from the user to delete > threshold contacts from a 570 // single source 571 // If the user clicks Cancel the AB is disabled 572 if (threshold > -1 && 573 (cardsToDelete.length >= threshold || 574 com.gContactSync.Sync.mContactsToDelete.length >= threshold) && 575 !com.gContactSync.Sync.requestDeletePermission(cardsToDelete.length, 576 com.gContactSync.Sync.mContactsToDelete.length)) { 577 // If canceled here then reset most remote counts and local deleted to 0 578 this.mCurrentSummary.mLocal.mRemoved = 0; 579 this.mCurrentSummary.mRemote.mAdded = 0; 580 this.mCurrentSummary.mRemote.mRemoved = 0; 581 this.mCurrentSummary.mRemote.mUpdated = 0; 582 com.gContactSync.Sync.syncNextUser(); 583 return; 584 } 585 // delete the old contacts from Thunderbird 586 if (cardsToDelete.length > 0) { 587 ab.deleteContacts(cardsToDelete); 588 } 589 590 com.gContactSync.LOGGER.LOG("***Deleting contacts from Google***"); 591 // delete contacts from Google 592 com.gContactSync.Sync.processDeleteQueue(); 593 }, 594 /** 595 * Shows a confirmation dialog asking the user to give gContactSync permission 596 * to delete the specified number of contacts from Google and Thunderbird. 597 * If the user clicks Cancel then synchronization with the current address 598 * book is disabled. 599 * @param {int} The number of contacts about to be deleted from Thunderbird. 600 * @param {int} The number of contacts about to be deleted from Google. 601 * @returns {boolean} True if the user clicked OK, false if Cancel. 602 */ 603 requestDeletePermission: function Sync_requestDeletePermission(aNumTB, aNumGoogle) { 604 var warning = com.gContactSync.StringBundle.getStr("confirmDelete1") + 605 " '" + com.gContactSync.Sync.mCurrentAb.getName() + "'" + 606 "\nThunderbird: " + aNumTB + 607 "\nGoogle: " + aNumGoogle + 608 "\n" + com.gContactSync.StringBundle.getStr("confirmDelete2"); 609 com.gContactSync.LOGGER.LOG("Requesting permission to delete " + 610 "TB: " + aNumTB + ", Google: " + aNumGoogle + 611 " contacts..."); 612 if (!com.gContactSync.confirm(warning)) { 613 com.gContactSync.LOGGER.LOG(" * Permission denied, disabling AB"); 614 com.gContactSync.Sync.mCurrentAb.savePref("Disabled", true); 615 com.gContactSync.alert(com.gContactSync.StringBundle.getStr("deleteCancel")); 616 return false; 617 } 618 com.gContactSync.LOGGER.LOG(" * Permission granted"); 619 return true; 620 }, 621 /** 622 * Deletes all contacts from Google included in the mContactsToDelete 623 * array one at a time to avoid timing conflicts. Calls 624 * com.gContactSync.Sync.processAddQueue() when finished. 625 */ 626 processDeleteQueue: function Sync_processDeleteQueue() { 627 var ab = com.gContactSync.Sync.mCurrentAb; 628 if (!com.gContactSync.Sync.mContactsToDelete 629 || com.gContactSync.Sync.mContactsToDelete.length == 0 630 || ab.mPrefs.readOnly == "true") { 631 com.gContactSync.LOGGER.LOG("***Adding contacts to Google***"); 632 com.gContactSync.Sync.processAddQueue(); 633 return; 634 } 635 // TODO if com.gContactSync.Sync.mContactsUrl is set should the contact just 636 // be removed from that group or completely removed? 637 com.gContactSync.Overlay.setStatusBarText(com.gContactSync.StringBundle.getStr("deleting") + " " + 638 com.gContactSync.Sync.mContactsToDelete.length + " " + 639 com.gContactSync.StringBundle.getStr("remaining")); 640 var contact = com.gContactSync.Sync.mContactsToDelete.shift(); 641 var editURL = contact.getValue("EditURL").value; 642 com.gContactSync.LOGGER.LOG(" * " + contact.getName() + " - " + editURL); 643 644 var httpReq = new com.gContactSync.GHttpRequest("delete", 645 com.gContactSync.Sync.mCurrentAuthToken, 646 editURL, null, 647 com.gContactSync.Sync.mCurrentUsername); 648 httpReq.addHeaderItem("If-Match", "*"); 649 httpReq.mOnSuccess = com.gContactSync.Sync.processDeleteQueue; 650 httpReq.mOnError = function processDeleteError(httpReq) { 651 com.gContactSync.LOGGER.LOG_ERROR('Error while deleting contact', 652 httpReq.responseText); 653 com.gContactSync.Sync.processDeleteQueue(); 654 }; 655 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 656 httpReq.send(); 657 }, 658 /** 659 * Adds all cards to Google included in the mContactsToAdd array one at a 660 * time to avoid timing conflicts. Calls 661 * com.gContactSync.Sync.processUpdateQueue() when finished. 662 */ 663 processAddQueue: function Sync_processAddQueue() { 664 var ab = com.gContactSync.Sync.mCurrentAb; 665 // if all contacts were added then update all necessary contacts 666 if (!com.gContactSync.Sync.mContactsToAdd 667 || com.gContactSync.Sync.mContactsToAdd.length == 0 668 || ab.mPrefs.readOnly == "true") { 669 com.gContactSync.LOGGER.LOG("***Updating contacts from Google***"); 670 com.gContactSync.Sync.processUpdateQueue(); 671 return; 672 } 673 com.gContactSync.Overlay.setStatusBarText(com.gContactSync.StringBundle.getStr("adding") + " " + 674 com.gContactSync.Sync.mContactsToAdd.length + " " + 675 com.gContactSync.StringBundle.getStr("remaining")); 676 var cardToAdd = com.gContactSync.Sync.mContactsToAdd.shift(); 677 com.gContactSync.LOGGER.LOG("\n" + cardToAdd.getName()); 678 // get the XML representation of the card 679 // NOTE: cardToAtomXML adds the contact to the current group, if any 680 var gcontact = com.gContactSync.ContactConverter.cardToAtomXML(cardToAdd); 681 var xml = gcontact.xml; 682 var string = com.gContactSync.serialize(xml); 683 if (com.gContactSync.Preferences.mSyncPrefs.verboseLog.value) 684 com.gContactSync.LOGGER.LOG(" * XML of contact being added:\n" + string + "\n"); 685 var httpReq = new com.gContactSync.GHttpRequest("add", 686 com.gContactSync.Sync.mCurrentAuthToken, 687 null, 688 string, 689 com.gContactSync.Sync.mCurrentUsername); 690 this.mNewPhotoURI = com.gContactSync.Preferences.mSyncPrefs.sendPhotos ? 691 gcontact.mNewPhotoURI : null; 692 /* When the contact is successfully created: 693 * 1. Get the card from which the contact was made 694 * 2. Get a GContact object for the new contact 695 * 3. Set the card's GoogleID attribute to match the new contact's ID 696 * 4. Update the card in the address book 697 * 5. Set the new contact's photo, if necessary 698 * 6. Call this method again 699 */ 700 var onCreated = function contactCreated(httpReq) { 701 var ab = com.gContactSync.Sync.mCurrentAb, 702 contact = com.gContactSync.ContactConverter.mCurrentCard, 703 gcontact = new com.gContactSync.GContact(httpReq.responseXML); 704 contact.setValue('GoogleID', gcontact.getID(true)); 705 contact.update(); 706 // if photos are allowed to be uploaded to Google then do so 707 if (com.gContactSync.Preferences.mSyncPrefs.sendPhotos) { 708 gcontact.setPhoto(com.gContactSync.Sync.mNewPhotoURI); 709 } 710 // reset the new photo URI variable 711 com.gContactSync.Sync.mNewPhotoURI = null; 712 com.gContactSync.Sync.processAddQueue(); 713 } 714 httpReq.mOnCreated = onCreated; 715 httpReq.mOnError = function contactCreatedError(httpReq) { 716 com.gContactSync.LOGGER.LOG_ERROR('Error while adding contact', 717 httpReq.responseText); 718 com.gContactSync.Sync.processAddQueue(); 719 }; 720 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 721 httpReq.send(); 722 }, 723 /** 724 * Updates all cards to Google included in the mContactsToUpdate array one at 725 * a time to avoid timing conflicts. Calls 726 * com.gContactSync.Sync.syncNextUser() when done. 727 */ 728 processUpdateQueue: function Sync_processUpdateQueue() { 729 var ab = com.gContactSync.Sync.mCurrentAb; 730 if (!com.gContactSync.Sync.mContactsToUpdate 731 || com.gContactSync.Sync.mContactsToUpdate.length == 0 732 || ab.mPrefs.readOnly == "true") { 733 // set the previous address book's last sync date (if it exists) 734 if (com.gContactSync.Sync.mCurrentAb && 735 com.gContactSync.Sync.mCurrentAb.setLastSyncDate) { 736 com.gContactSync.Sync.mCurrentAb.setLastSyncDate((new Date()).getTime()); 737 } 738 if (com.gContactSync.Sync.mAddressBooks[com.gContactSync.Sync.mIndex]) { 739 var delay = com.gContactSync.Preferences.mSyncPrefs.accountDelay.value; 740 com.gContactSync.LOGGER.LOG("**About to wait " + delay + 741 " ms before synchronizing the next account**"); 742 com.gContactSync.Overlay.setStatusBarText(com.gContactSync.StringBundle.getStr("waiting")); 743 setTimeout(com.gContactSync.Sync.syncNextUser, delay); 744 } 745 else { 746 com.gContactSync.Sync.syncNextUser(); 747 } 748 return; 749 } 750 com.gContactSync.Overlay.setStatusBarText(com.gContactSync.StringBundle.getStr("updating") + " " + 751 com.gContactSync.Sync.mContactsToUpdate.length + " " + 752 com.gContactSync.StringBundle.getStr("remaining")); 753 var obj = com.gContactSync.Sync.mContactsToUpdate.shift(); 754 var gContact = obj.gContact; 755 var abCard = obj.abCard; 756 757 var editURL = gContact.getValue("EditURL").value; 758 com.gContactSync.LOGGER.LOG("\nUpdating " + gContact.getName()); 759 var xml = com.gContactSync.ContactConverter.cardToAtomXML(abCard, gContact).xml; 760 761 var string = com.gContactSync.serialize(xml); 762 if (com.gContactSync.Preferences.mSyncPrefs.verboseLog.value) 763 com.gContactSync.LOGGER.LOG(" * XML of contact being updated:\n" + string + "\n"); 764 var httpReq = new com.gContactSync.GHttpRequest("update", 765 com.gContactSync.Sync.mCurrentAuthToken, 766 editURL, 767 string, 768 com.gContactSync.Sync.mCurrentUsername); 769 httpReq.addHeaderItem("If-Match", "*"); 770 httpReq.mOnSuccess = com.gContactSync.Sync.processUpdateQueue; 771 httpReq.mOnError = function processUpdateError(httpReq) { 772 com.gContactSync.LOGGER.LOG_ERROR('Error while updating contact', 773 httpReq.responseText); 774 com.gContactSync.Sync.processUpdateQueue(); 775 }; 776 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 777 httpReq.send(); 778 }, 779 /** 780 * Syncs all contact groups with mailing lists. 781 * @param aAtom {XML} The ATOM/XML feed of Groups. 782 */ 783 syncGroups: function Sync_syncGroups(aAtom) { 784 // reset the groups object 785 com.gContactSync.Sync.mGroups = {}; 786 com.gContactSync.Sync.mLists = {}; 787 com.gContactSync.Sync.mGroupsToAdd = []; 788 com.gContactSync.Sync.mGroupsToDelete = []; 789 com.gContactSync.Sync.mGroupsToUpdate = []; 790 // if there wasn't an error, setup groups 791 if (aAtom) { 792 var ab = com.gContactSync.Sync.mCurrentAb; 793 var ns = com.gContactSync.gdata.namespaces.ATOM; 794 var lastSync = parseInt(ab.mPrefs.lastSync, 10); 795 var myContacts = ab.mPrefs.myContacts == "true" && ab.mPrefs.myContactsName; 796 var arr = aAtom.getElementsByTagNameNS(ns.url, "entry"); 797 var noCatch = false; 798 // get the mailing lists if not only synchronizing my contacts 799 if (!myContacts) { 800 com.gContactSync.LOGGER.VERBOSE_LOG("***Getting all mailing lists***"); 801 com.gContactSync.Sync.mLists = ab.getAllLists(true); 802 com.gContactSync.LOGGER.VERBOSE_LOG("***Getting all contact groups***"); 803 for (var i = 0; i < arr.length; i++) { 804 try { 805 var group = new com.gContactSync.Group(arr[i]); 806 // add the ID to mGroups by making a new property with the ID as the 807 // name and the title as the value for easy lookup for contacts 808 var id = group.getID(); 809 var title = group.getTitle(); 810 var modifiedDate = group.getLastModifiedDate(); 811 com.gContactSync.LOGGER.LOG(" * " + title + " - " + id + 812 " last modified: " + modifiedDate); 813 var list = com.gContactSync.Sync.mLists[id]; 814 com.gContactSync.Sync.mGroups[id] = group; 815 if (modifiedDate < lastSync) { // it's an old group 816 if (list) { 817 list.matched = true; 818 // if the name is different, update the group's title 819 var listName = list.getName(); 820 com.gContactSync.LOGGER.LOG(" - Matched with mailing list " + listName); 821 if (listName != title) { 822 // You cannot rename system groups...so change the name back 823 // In the future system groups will be localized, so this 824 // must be ignored. 825 if (group.isSystemGroup()) { 826 // If write-only is on then ignore the name change 827 if (ab.mPrefs.writeOnly != "true") 828 list.setName(title); 829 com.gContactSync.LOGGER.LOG_WARNING(" - A system group was renamed in Thunderbird"); 830 } 831 else if (ab.mPrefs.readOnly == "true") { 832 com.gContactSync.LOGGER.LOG(" - The mailing list's name has changed. " + 833 "Ignoring since read-only mode is on."); 834 } 835 else { 836 com.gContactSync.LOGGER.LOG(" - Going to rename the group to " + listName); 837 group.setTitle(listName); 838 com.gContactSync.Sync.mGroupsToUpdate.push(group); 839 } 840 } 841 } 842 else { 843 if (ab.mPrefs.readOnly == "true") { 844 com.gContactSync.LOGGER.LOG(" - A mailing list was deleted. " + 845 "Ignoring since read-only mode is on."); 846 } 847 else { 848 // System groups cannot be deleted. 849 // This would be difficult to recover from, so stop 850 // synchronization and reset the AB 851 if (group.isSystemGroup()) { 852 noCatch = true; // don't catch this error 853 com.gContactSync.LOGGER.LOG_ERROR(" - A system group was deleted from Thunderbird"); 854 var restartStr = com.gContactSync.StringBundle.getStr("pleaseRestart"); 855 if (com.gContactSync.confirm(com.gContactSync.StringBundle.getStr("resetConfirm"))) { 856 ab.reset(); 857 com.gContactSync.Overlay.setStatusBarText(restartStr); 858 com.gContactSync.alert(restartStr); 859 com.gContactSync.Preferences.setSyncPref("needRestart", true); 860 } 861 // Throw an error to stop the sync 862 throw "A system group was deleted from Thunderbird"; 863 } 864 else { 865 com.gContactSync.Sync.mGroupsToDelete.push(group); 866 com.gContactSync.LOGGER.LOG(" - Didn't find a matching mail list. It will be deleted"); 867 } 868 } 869 } 870 } 871 else { // it is new or updated 872 if (list) { // the group has been updated 873 com.gContactSync.LOGGER.LOG(" - Matched with mailing list " + listName); 874 // if the name changed, update the mail list's name 875 if (list.getName() != title) { 876 if (ab.mPrefs.writeOnly == "true") { 877 com.gContactSync.LOGGER.VERBOSE_LOG(" - The group was renamed, but write-only mode was enabled"); 878 } 879 else { 880 com.gContactSync.LOGGER.LOG(" - The group's name changed, updating the list"); 881 list.setName(title); 882 list.update(); 883 } 884 } 885 list.matched = true; 886 } 887 else { // the group is new 888 if (ab.mPrefs.writeOnly == "true") { 889 com.gContactSync.LOGGER.VERBOSE_LOG(" - The group is new, but write-only mode was enabled"); 890 } 891 else { 892 // make a new mailing list with the same name 893 com.gContactSync.LOGGER.LOG(" - The group is new"); 894 var list = ab.addList(title, id); 895 com.gContactSync.LOGGER.VERBOSE_LOG(" - List added to address book"); 896 } 897 } 898 } 899 } 900 catch (e) { 901 if (noCatch) throw e; 902 com.gContactSync.LOGGER.LOG_ERROR("Error while syncing groups: " + e); 903 } 904 } 905 com.gContactSync.LOGGER.LOG("***Looking for unmatched mailing lists***"); 906 for (var i in com.gContactSync.Sync.mLists) { 907 var list = com.gContactSync.Sync.mLists[i]; 908 if (list && !list.matched) { 909 // if it is new, make a new group in Google 910 if (i.indexOf("http://www.google.com/m8/feeds/groups/") == -1) { 911 com.gContactSync.LOGGER.LOG("-Found new list named " + list.getName()); 912 com.gContactSync.LOGGER.VERBOSE_LOG(" * The URI is: " + list.getURI()); 913 if (ab.mPrefs.readOnly == "true") { 914 com.gContactSync.LOGGER.LOG(" * Ignoring since read-only mode is on"); 915 } 916 else { 917 com.gContactSync.LOGGER.LOG(" * It will be added to Google"); 918 com.gContactSync.Sync.mGroupsToAdd.push(list); 919 } 920 } 921 // if it is old, delete it 922 else { 923 com.gContactSync.LOGGER.LOG("-Found an old list named " + list.getName()); 924 com.gContactSync.LOGGER.VERBOSE_LOG(" * The URI is: " + list.getURI()); 925 if (ab.mPrefs.writeOnly == "true") { 926 com.gContactSync.LOGGER.VERBOSE_LOG(" * Write-only mode was enabled so no action will be taken"); 927 } 928 else { 929 com.gContactSync.LOGGER.LOG(" * It will be deleted from Thunderbird"); 930 list.remove(); 931 } 932 } 933 } 934 } 935 } 936 else { 937 var groupName = ab.mPrefs.myContactsName.toLowerCase(); 938 com.gContactSync.LOGGER.LOG("Only synchronizing the '" + 939 ab.mPrefs.myContactsName + "' group."); 940 var group, id, sysId, title; 941 var foundGroup = false; 942 for (var i = 0; i < arr.length; i++) { 943 try { 944 group = new com.gContactSync.Group(arr[i]); 945 // add the ID to mGroups by making a new property with the ID as the 946 // name and the title as the value for easy lookup for contacts 947 // Note: If someone wants to sync a group with the same name as a 948 // system group then this method won't work because system groups 949 // are first. 950 id = group.getID(); 951 sysId = group.getSystemId(); 952 title = group.getTitle(); 953 com.gContactSync.LOGGER.VERBOSE_LOG(" - Found a group named '" 954 + title + "' with ID '" 955 + id + "'"); 956 title = title ? title.toLowerCase() : ""; 957 sysId = sysId ? sysId.toLowerCase() : ""; 958 if (sysId == groupName || title == groupName) { 959 foundGroup = true; 960 break; 961 } 962 } 963 catch (e) {com.gContactSync.alertError(e);} 964 } 965 if (foundGroup) { 966 com.gContactSync.LOGGER.LOG(" * Found the group to synchronize: " + id); 967 com.gContactSync.Sync.mContactsUrl = id; 968 return com.gContactSync.Sync.getContacts(); 969 } 970 else { 971 var msg = " * Could not find the group '" + groupName + "' to synchronize." 972 com.gContactSync.LOGGER.LOG_ERROR(msg); 973 return com.gContactSync.Sync.syncNextUser(); 974 } 975 } 976 } 977 com.gContactSync.LOGGER.LOG("***Deleting old groups from Google***"); 978 return com.gContactSync.Sync.deleteGroups(); 979 }, 980 /** 981 * Deletes all of the groups in mGroupsToDelete one at a time to avoid timing 982 * issues. Calls com.gContactSync.Sync.addGroups() when finished. 983 */ 984 deleteGroups: function Sync_deleteGroups() { 985 var ab = com.gContactSync.Sync.mCurrentAb; 986 if (com.gContactSync.Sync.mGroupsToDelete.length == 0 987 || ab.mPrefs.readOnly == "true") { 988 com.gContactSync.LOGGER.LOG("***Adding new groups to Google***"); 989 com.gContactSync.Sync.addGroups(); 990 return; 991 } 992 var group = com.gContactSync.Sync.mGroupsToDelete.shift(); 993 com.gContactSync.LOGGER.LOG("-Deleting group: " + group.getTitle()); 994 var httpReq = new com.gContactSync.GHttpRequest("delete", 995 com.gContactSync.Sync.mCurrentAuthToken, 996 group.getEditURL(), 997 null, 998 com.gContactSync.Sync.mCurrentUsername); 999 httpReq.mOnSuccess = com.gContactSync.Sync.deleteGroups; 1000 httpReq.mOnError = function deleteGroupsError(httpReq) { 1001 com.gContactSync.LOGGER.LOG_ERROR('Error while deleting group', 1002 httpReq.responseText); 1003 com.gContactSync.Sync.deleteGroups(); 1004 }; 1005 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 1006 httpReq.addHeaderItem("If-Match", "*"); 1007 httpReq.send(); 1008 }, 1009 /** 1010 * The first part of adding a group involves creating the XML representation 1011 * of the mail list and then calling com.gContactSync.Sync.addGroups2() upon successful 1012 * creation of a group. 1013 */ 1014 addGroups: function Sync_addGroups() { 1015 var ab = com.gContactSync.Sync.mCurrentAb; 1016 if (com.gContactSync.Sync.mGroupsToAdd.length == 0 1017 || ab.mPrefs.readOnly == "true") { 1018 com.gContactSync.LOGGER.LOG("***Updating groups from Google***"); 1019 com.gContactSync.Sync.updateGroups(); 1020 return; 1021 } 1022 var list = com.gContactSync.Sync.mGroupsToAdd[0]; 1023 var group = new com.gContactSync.Group(null, list.getName()); 1024 com.gContactSync.LOGGER.LOG("-Adding group: " + group.getTitle()); 1025 var body = com.gContactSync.serialize(group.xml); 1026 if (com.gContactSync.Preferences.mSyncPrefs.verboseLog.value) 1027 com.gContactSync.LOGGER.VERBOSE_LOG(" * XML feed of new group:\n" + body); 1028 var httpReq = new com.gContactSync.GHttpRequest("addGroup", 1029 com.gContactSync.Sync.mCurrentAuthToken, 1030 null, 1031 body, 1032 com.gContactSync.Sync.mCurrentUsername); 1033 httpReq.mOnCreated = com.gContactSync.Sync.addGroups2; 1034 httpReq.mOnError = function addGroupError(httpReq) { 1035 com.gContactSync.LOGGER.LOG_ERROR('Error while adding group', 1036 httpReq.responseText); 1037 com.gContactSync.Sync.mGroupsToAddURI.shift() 1038 com.gContactSync.Sync.addGroups(); 1039 }; 1040 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 1041 httpReq.send(); 1042 }, 1043 /** 1044 * The second part of adding a group involves updating the list from which 1045 * this group was created so the two can be matched during the next sync. 1046 * @param aResponse {XMLHttpRequest} The HTTP request. 1047 */ 1048 addGroups2: function Sync_addGroups2(aResponse) { 1049 var group = new com.gContactSync.Group(aResponse.responseXML 1050 .getElementsByTagNameNS(com.gContactSync.gdata.namespaces.ATOM.url, 1051 "entry")[0]); 1052 if (com.gContactSync.Preferences.mSyncPrefs.verboseLog.value) 1053 com.gContactSync.LOGGER.LOG(com.gContactSync.serializeFromText(aResponse.responseText)); 1054 var list = com.gContactSync.Sync.mGroupsToAdd.shift(); 1055 var id = group.getID(); 1056 list.setNickName(id); 1057 if (list.update) 1058 list.update(); 1059 com.gContactSync.Sync.mLists[id] = list; 1060 com.gContactSync.Sync.addGroups(); 1061 }, 1062 /** 1063 * Updates all groups in mGroupsToUpdate one at a time to avoid timing issues 1064 * and calls com.gContactSync.Sync.getContacts() when finished. 1065 */ 1066 updateGroups: function Sync_updateGroups() { 1067 var ab = com.gContactSync.Sync.mCurrentAb; 1068 if (com.gContactSync.Sync.mGroupsToUpdate.length == 0 1069 || ab.mPrefs.readOnly == "true") { 1070 com.gContactSync.Sync.getContacts(); 1071 return; 1072 } 1073 var group = com.gContactSync.Sync.mGroupsToUpdate.shift(); 1074 com.gContactSync.LOGGER.LOG("-Updating group: " + group.getTitle()); 1075 var body = com.gContactSync.serialize(group.xml); 1076 if (com.gContactSync.Preferences.mSyncPrefs.verboseLog.value) 1077 com.gContactSync.LOGGER.VERBOSE_LOG(" * XML feed of group: " + body); 1078 var httpReq = new com.gContactSync.GHttpRequest("update", 1079 com.gContactSync.Sync.mCurrentAuthToken, 1080 group.getEditURL(), 1081 body, 1082 com.gContactSync.Sync.mCurrentUsername); 1083 httpReq.mOnSuccess = com.gContactSync.Sync.updateGroups; 1084 httpReq.mOnError = function updateGroupError(httpReq) { 1085 com.gContactSync.LOGGER.LOG_ERROR("Error while updating group", 1086 httpReq.responseText); 1087 com.gContactSync.Sync.updateGroups(); 1088 }; 1089 httpReq.mOnOffline = com.gContactSync.Sync.mOfflineFunction; 1090 httpReq.addHeaderItem("If-Match", "*"); 1091 httpReq.send(); 1092 }, 1093 /** 1094 * Schedules another sync after the given delay if one is not already scheduled, 1095 * there isn't a sync currently running, if the delay is greater than 0, and 1096 * finally if the auto sync pref is set to true. 1097 * @param aDelay {integer} The duration of time to wait before synchronizing 1098 * again. 1099 */ 1100 schedule: function Sync_schedule(aDelay) { 1101 // only schedule a sync if the delay is greater than 0, a sync is not 1102 // already scheduled, and autosyncing is enabled 1103 if (aDelay && !com.gContactSync.Preferences.mSyncPrefs.synchronizing.value && 1104 !com.gContactSync.Sync.mSyncScheduled && aDelay > 0 && 1105 com.gContactSync.Preferences.mSyncPrefs.autoSync.value) { 1106 com.gContactSync.Sync.mSyncScheduled = true; 1107 com.gContactSync.LOGGER.VERBOSE_LOG("Next sync in: " + aDelay + " milliseconds"); 1108 setTimeout(com.gContactSync.Sync.begin, aDelay); 1109 } 1110 } 1111 }; 1112