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