1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is gContactSync. 15 * 16 * The Initial Developer of the Original Code is 17 * Josh Geenen <gcontactsync@pirules.org>. 18 * Portions created by the Initial Developer are Copyright (C) 2008-2010 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * 23 * Alternatively, the contents of this file may be used under the terms of 24 * either the GNU General Public License Version 2 or later (the "GPL"), or 25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 26 * in which case the provisions of the GPL or the LGPL are applicable instead 27 * of those above. If you wish to allow use of your version of this file only 28 * under the terms of either the GPL or the LGPL, and not to allow others to 29 * use your version of this file under the terms of the MPL, indicate your 30 * decision by deleting the provisions above and replace them with the notice 31 * and other provisions required by the GPL or the LGPL. If you do not delete 32 * the provisions above, a recipient may use your version of this file under 33 * the terms of any one of the MPL, the GPL or the LGPL. 34 * 35 * ***** END LICENSE BLOCK ***** */ 36 37 if (!com) var com = {}; // A generic wrapper variable 38 // A wrapper for all GCS functions and variables 39 if (!com.gContactSync) com.gContactSync = {}; 40 41 /** 42 * An object that can obtain address books by the name or URI, find the synced 43 * address books, and edit cards. 44 * @class 45 */ 46 com.gContactSync.AbManager = { 47 /** The version of Thunderbird */ 48 mVersion: Components.classes["@mozilla.org/abmanager;1"] ? 3 : 2, 49 /** True if the changes started in Bug 413260 have been applied */ 50 mBug413260: Components.classes["@mozilla.org/addressbook/cardproperty;1"] 51 .createInstance(Components.interfaces.nsIAbCard) 52 .getProperty !== undefined, 53 /** attributes that can be set by getCardValue and setCardValue */ 54 mBasicAttributes: [ 55 "DisplayName", "Notes", "CellularNumber", "HomePhone", "WorkPhone", 56 "PagerNumber", "FaxNumber", "_AimScreenName", "PrimaryEmail", "SecondEmail", 57 "Company", "JobTitle", "HomeAddress", "WorkAddress", "NickName", "LastName", 58 "FirstName", "HomeAddress2", "HomeCity", "HomeState", "HomeZipCode", 59 "HomeCountry", "WorkAddress2", "WorkCity", "WorkState", "WorkZipCode", 60 "WorkCountry", "WebPage1", "WebPage2", "Department", "Custom1", "Custom2", 61 "Custom3", "Custom4", "WorkPhoneType", "HomePhoneType", "CellularNumberType", 62 "FaxNumberType", "PagerNumberType" 63 ], 64 /** 65 * Returns true if the given attribute is able to be set/obtained through the 66 * setCardValue and getCardValue functions of nsIAbCard. 67 * @param aAttribute The attribute to check. 68 * @returns True if aAttribute is usable with set/getCardValue. 69 */ 70 isRegularAttribute: function AbManager_isRegularAttribute(aAttribute) { 71 return this.mBasicAttributes.indexOf(aAttribute) !== -1; 72 }, 73 /** 74 * Checks the validity of a directory and returns false if it is invalid. 75 * @param aDirectory {nsIAbDirectory} The directory to check. 76 */ 77 isDirectoryValid: function AbManager_isDirectoryValid(aDirectory) { 78 return aDirectory && 79 aDirectory instanceof Components.interfaces.nsIAbDirectory && 80 aDirectory.dirName !== ""; 81 }, 82 /** 83 * Checks the validity of a card and throws an error if the card is invalid. 84 * @param aCard {nsIAbCard} An object that should be an instance of nsIAbCard 85 * @param aMethodName {string} The name of the method calling checkCard (used 86 * when throwing the error) 87 */ 88 checkCard: function AbManager_checkCard(aCard, aMethodName) { 89 var card = aCard && aCard.mCard ? aCard.mCard : aCard; 90 if (!card || (!(card instanceof Components.interfaces.nsIAbCard) && 91 !(Components.interfaces.nsIAbMDBCard && card instanceof Components.interfaces.nsIAbMDBCard))) { 92 throw "Invalid card: " + aCard + "passed to the '" + aMethodName + 93 "' method." + com.gContactSync.StringBundle.getStr("pleaseReport"); 94 } 95 }, 96 /** 97 * Returns the value of the specifiec property in the given card, or throws an 98 * error if it is not present or blank. 99 * @param aCard {nsIAbCard} The card to get the value from. 100 * @param aAttrName {string} The name of the attribute to get. 101 */ 102 getCardValue: function AbManager_getCardValue(aCard, aAttrName) { 103 this.checkCard(aCard, "getCardValue"); 104 if (this.mBug413260) // if the patch for Bug 413260 is applied 105 return aCard.getProperty(aAttrName, null); 106 else { 107 if (aAttrName === "LastModifiedDate") 108 return aCard.lastModifiedDate; // workaround for lastModifiedDate bug 109 var value; 110 if (this.isRegularAttribute(aAttrName)) 111 try { return aCard.getCardValue(aAttrName); } 112 catch (e) { com.gContactSync.LOGGER.LOG_WARNING("Error in getCardValue: " + e); } 113 else if (Components.interfaces.nsIAbMDBCard && aCard instanceof Components.interfaces.nsIAbMDBCard) 114 return this.getMDBCardValue(aCard, aAttrName); 115 else 116 com.gContactSync.LOGGER.LOG_WARNING("Couldn't get the value " + aAttrName + " of the card " 117 + aCard); 118 } 119 return null; 120 }, 121 /** 122 * Returns an object with a property for each of the e-mail addresses of this 123 * card as recognized by gContactSync (PrimaryEmail, SecondEmail, ThirdEmail, 124 * and FourthEmail) 125 * @param aCard {nsIAbCard} The card from which the e-mail addresses are 126 * obtained. 127 * @returns An object with the card's e-mail addresses. 128 */ 129 getCardEmailAddresses: function AbManager_getCardEmailAddresses(aCard) { 130 this.checkCard(aCard, "getCardEmailAddresses"); 131 var primaryEmail = this.getCardValue(aCard, "PrimaryEmail"); 132 var addresses = []; 133 if (primaryEmail) 134 addresses[primaryEmail] = true; 135 var secondEmail = this.getCardValue(aCard, "SecondEmail"); 136 if (secondEmail) 137 addresses[secondEmail] = true; 138 var thirdEmail = this.getCardValue(aCard, "ThirdEmail"); 139 if (thirdEmail) 140 addresses[thirdEmail] = true; 141 var fourthEmail = this.getCardValue(aCard, "FourthEmail"); 142 if (fourthEmail) 143 addresses[fourthEmail] = true; 144 return addresses; 145 }, 146 /** 147 * Returns true if the card has at least one e-mail address identical to one 148 * in aAddresses. 149 * @param aCard {nsIAbCard} The card from which the e-mail addresses are 150 * obtained. 151 * @param aAddresses An object with the card's e-mail addresses as returned by 152 * AbManager.getCardEmailAddresses. 153 * @returns {boolean} True if the card has at least one e-mail address in 154 * common with aAddresses. 155 */ 156 cardHasEmailAddress: function AbManager_cardHasEmailAddress(aCard, aAddresses) { 157 this.checkCard(aCard, "getCardEmailAddresses"); 158 if (!aAddresses) 159 return false; 160 var cardAddresses = this.getCardEmailAddresses(aCard); 161 for (var i in cardAddresses) { 162 if (aAddresses[i]) 163 return true; 164 } 165 return false; 166 }, 167 /** 168 * Sets the value of the specifiec property in the given card but does not 169 * update the card in the database. 170 * @param aCard {nsIAbCard} The card to get the value from. 171 * @param aAttrName {string} The name of the attribute to set. 172 * @param aValue {string} The value to set for the attribute. 173 */ 174 setCardValue: function AbManager_setCardValue(aCard, aAttrName, aValue) { 175 this.checkCard(aCard, "setCardValue"); 176 if (!aValue) 177 aValue = ""; 178 // make sure the last modified date is in milliseconds since 1/1/1970 UTC 179 // and not in microseconds 180 if (aAttrName == "LastModifiedDate" && parseInt(aValue, 10) > 2147483647) { 181 com.gContactSync.LOGGER.LOG_WARNING("Had to adjust last modified date from " + aValue); 182 aValue = aValue/1000; 183 } 184 if (this.mBug413260) { // if the patch for Bug 413260 is applied 185 if (aAttrName == "PreferMailFormat") { 186 switch (aValue) { 187 case "plaintext": 188 case "text": 189 case "1": 190 aValue = 1; 191 break; 192 case "html": 193 case "2": 194 aValue = 2; 195 break; 196 default: // if it is anything else set as unknown 197 aValue = 0; 198 } 199 } 200 aCard.setProperty(aAttrName, aValue); 201 } 202 else { 203 // workaround a last modified date bug 204 if (aAttrName == "LastModifiedDate") 205 try { 206 if (aValue == "") 207 aValue = 0; 208 aCard.lastModifiedDate = aValue; 209 } catch (e) { com.gContactSync.LOGGER.LOG_WARNING("Invalid lastModifiedDate"); } 210 else if (aAttrName == "AllowRemoteContent") { 211 // AllowRemoteContent may be 1/0 if the patch or true/false otherwise 212 var value = aValue == "1" || (aValue != "0" && aValue); 213 aCard.allowRemoteContent = value; 214 } 215 else if (aAttrName == "PreferMailFormat") { 216 // can be a 0/1/2 or unknown/plaintext/html 217 var value; 218 switch (aValue) { 219 case "plaintext": 220 case "text": 221 case "1": 222 value = 1; 223 break; 224 case "html": 225 case "2": 226 value = 2; 227 break; 228 default: // if it is anything else set as unknown 229 value = 0; 230 } 231 aCard.preferMailFormat = value; 232 } 233 else if (this.isRegularAttribute(aAttrName)) 234 try { aCard.setCardValue(aAttrName, aValue); } 235 catch (e) { com.gContactSync.LOGGER.LOG_WARNING("Error in setCardValue: " + e); } 236 else if (Components.interfaces.nsIAbMDBCard && aCard instanceof Components.interfaces.nsIAbMDBCard) 237 this.setMDBCardValue(aCard, aAttrName, aValue); 238 else 239 com.gContactSync.LOGGER.LOG_WARNING("Couldn't set the value " + aAttrName + " of the card " 240 + aCard); 241 } 242 }, 243 /** 244 * Sets the requested value of an MDB card's attribute. Performs a 245 * QueryInterface if necessary. 246 * @param aCard {nsIAbCard} The MDB card to set the value for. 247 * @param aAttrName {string} The name of the attribute whose value is set. 248 * @param aValue {string} The value to set for aAttrName. 249 * 250 * @returns {boolean} True if the attribute was set to the given value. 251 */ 252 setMDBCardValue: function AbManager_setMDBCardValue(aCard, aAttrName, aValue) { 253 try { 254 aCard.setStringAttribute(aAttrName, aValue); 255 return true; 256 } 257 catch (e) { 258 com.gContactSync.LOGGER.LOG_WARNING("Error in setMDBCardValue: " + e + "\n" + aAttrName + 259 "\n" + aValue); 260 } 261 return false; 262 }, 263 /** 264 * Returns the requested value of an MDB card's attribute. Performs a 265 * QueryInterface if necessary. 266 * @param aCard {nsIAbCard} The MDB card to get the value from. 267 * @param aAttrName {string} The name of the attribute whose value is returned. 268 * @returns {string} The value of aCard's attribute aAttrName. 269 */ 270 getMDBCardValue: function AbManager_getMDBCardValue(aCard, aAttrName) { 271 try { 272 return aCard.getStringAttribute(aAttrName); 273 } 274 catch (e) { 275 com.gContactSync.LOGGER.LOG_WARNING("Error in getMDBCardValue: " + e + "\n" + aAttrName); 276 } 277 return null; 278 }, 279 /** 280 * Returns the address book with the given URI, if found. Does not attempt 281 * to make a new address book if not found and returns null. 282 * @returns {nsIAbDirectory} The Address Book with the given URI 283 */ 284 getAbByURI: function AbManager_getAbByURI(aURI) { 285 if (!aURI) { 286 com.gContactSync.LOGGER.LOG_WARNING("Invalid aURI supplied to the 'getAbByURI' method" + 287 com.gContactSync.StringBundle.getStr("pleaseReport")); 288 return null; 289 } 290 try { 291 var dir; 292 if (Components.classes["@mozilla.org/abmanager;1"]) 293 dir = Components.classes["@mozilla.org/abmanager;1"] 294 .getService(Components.interfaces.nsIAbManager) 295 .getDirectory(aURI) 296 .QueryInterface(Components.interfaces.nsIAbDirectory); 297 else 298 dir = Components.classes["@mozilla.org/rdf/rdf-service;1"] 299 .getService(Components.interfaces.nsIRDFService) 300 .GetResource(aURI) 301 .QueryInterface(Components.interfaces.nsIAbDirectory); 302 // checks that the directory exists and is valid. returns null if not. 303 if (!this.isDirectoryValid(dir)) 304 return null; 305 return dir; 306 } 307 catch (e) { com.gContactSync.LOGGER.LOG_ERROR("Error in getAbByURI", e); } 308 return null; 309 }, 310 /** 311 * Returns the Address Book if it can be found. If it cannot be found 312 * it tries once to make it and return the newly made address book. 313 * @param aDirName {string} The name of the address book 314 * @param aDontMakeAb {boolean} True if the address book shouldn't be created 315 * if not found. 316 * @returns {nsIAbDirectory} The Address Book with the name given 317 */ 318 getAbByName: function AbManager_getAbByName(aDirName, aDontMakeAb) { 319 if (!aDirName || aDirName.length == 0) 320 throw "Invalid aDirName passed to the 'getAbByName' method." + 321 com.gContactSync.StringBundle.getStr("pleaseReport"); 322 var iter, data; 323 if (Components.classes["@mozilla.org/abmanager;1"]) { // TB 3 324 var abManager = Components.classes["@mozilla.org/abmanager;1"] 325 .getService(Components.interfaces.nsIAbManager); 326 iter = abManager.directories; 327 } 328 else { // TB 2 329 // obtain the main directory through the RDF service 330 var dir = Components.classes["@mozilla.org/rdf/rdf-service;1"] 331 .getService(Components.interfaces.nsIRDFService) 332 .GetResource("moz-abdirectory://") 333 .QueryInterface(Components.interfaces.nsIAbDirectory); 334 iter = dir.childNodes; 335 } 336 while (iter.hasMoreElements()) { 337 data = iter.getNext(); 338 if (data instanceof Components.interfaces.nsIAbDirectory) 339 if (data.dirName == aDirName) 340 return data; 341 } 342 iter = null; 343 if (aDontMakeAb) 344 return null; 345 // the AB doesn't exist, so make one: 346 // TODO - this should be in its own method 347 if (Components.classes["@mozilla.org/addressbook/properties;1"]) { // TB 2 348 // setup the "properties" of the new address book 349 var properties = Components.classes["@mozilla.org/addressbook/properties;1"] 350 .createInstance(Components.interfaces.nsIAbDirectoryProperties); 351 properties.description = aDirName; 352 properties.dirType = 2; // address book 353 dir.createNewDirectory(properties); 354 iter = dir.childNodes; 355 } 356 else if (abManager) { // TB 3 357 abManager.newAddressBook(aDirName, "moz-abmdbdirectory://", 2); 358 iter = abManager.directories; 359 } 360 else if (Components.classes["@mozilla.org/addressbook;1"]) { // Postbox 361 var addressbook = Components.classes["@mozilla.org/addressbook;1"] 362 .createInstance(Components.interfaces.nsIAddressBook); 363 addressbook.newAddressBook(aDirName, "", 2); 364 iter = dir.childNodes; 365 } 366 else { 367 com.gContactSync.LOGGER.LOG_WARNING("Unable to determine how to create a directory"); 368 alert("error"); 369 return null; 370 } 371 if (!iter) { 372 com.gContactSync.LOGGER.LOG_WARNING("iter is invalid in getAbByName"); 373 return null; 374 } 375 while (iter.hasMoreElements()) { 376 data = iter.getNext(); 377 if (data instanceof Components.interfaces.nsIAbDirectory) 378 if (data.dirName == aDirName) { 379 var ab = new com.gContactSync.GAddressBook(data); 380 ab.setLastSyncDate(0); 381 return data; 382 } 383 }// end of while loop 384 return null; 385 }, 386 /** 387 * Deletes the Address Book with the given URI. 388 * This does NOT provide any confirmation dialog. 389 * Note: This will not work in Thunderbird 2 with mailing lists. 390 * This will not allow deleting the PAB or CAB and will show a popup 391 * if there is an attempt to delete one of those ABs. 392 * @param aURI {string} The URI of the address book to delete. 393 */ 394 deleteAB: function AbManager_deleteAB(aURI) { 395 if (!aURI) { 396 com.gContactSync.LOGGER.LOG_ERROR("Invalid URI passed to AbManager.deleteAB"); 397 return false; 398 } 399 if (aURI.indexOf("abook.mab") != -1 || aURI.indexOf("history.mab") != -1) { 400 com.gContactSync.alertError(com.gContactSync.StringBundle.getStr("deletePAB")); 401 com.gContactSync.LOGGER.LOG_WARNING("Attempt made to delete the PAB or CAB. URI: " + aURI); 402 return false; 403 } 404 com.gContactSync.LOGGER.VERBOSE_LOG("Deleting address book with the URI " + aURI); 405 // In TB 3 just use the AbManager to delete the AB 406 if (Components.classes["@mozilla.org/abmanager;1"]) { 407 var abManager = Components.classes["@mozilla.org/abmanager;1"] 408 .getService(Components.interfaces.nsIAbManager); 409 if (!abManager) { 410 com.gContactSync.LOGGER.LOG_ERROR("Unable to get the AB Manager service"); 411 return false; 412 } 413 abManager.deleteAddressBook(aURI); 414 } 415 // TB 2 requires a bit more work 416 else { 417 // First create an array of parent resources 418 var parentArray = Components.classes["@mozilla.org/supports-array;1"] 419 .createInstance(Components.interfaces.nsISupportsArray); 420 if (!parentArray) { 421 com.gContactSync.LOGGER.LOG_ERROR("Unable to get an nsISupportsArray"); 422 return false; 423 } 424 var parentId = "moz-abdirectory://"; 425 var parentDir = GetDirectoryFromURI(parentId); 426 parentArray.AppendElement(parentDir); 427 428 // Next create an array of the resources to delete 429 var resourceArray = Components.classes["@mozilla.org/supports-array;1"] 430 .createInstance(Components.interfaces.nsISupportsArray); 431 if (!resourceArray) { 432 com.gContactSync.LOGGER.LOG_ERROR("Unable to get an nsISupportsArray"); 433 return false; 434 } 435 var selectedABResource = GetDirectoryFromURI(aURI) 436 .QueryInterface(Components.interfaces.nsIRDFResource); 437 if (!selectedABResource) { 438 com.gContactSync.LOGGER.LOG_ERROR("Unable to get an nsISupportsArray"); 439 return false; 440 } 441 resourceArray.AppendElement(selectedABResource); 442 443 // Get the directory tree 444 var dirTree = GetDirTree(); 445 if (!dirTree) { 446 com.gContactSync.LOGGER.LOG_ERROR("Unable to get the directory tree"); 447 return false; 448 } 449 450 // Finally delete the address book 451 top.addressbook.deleteAddressBooks(dirTree.database, parentArray, resourceArray); 452 } 453 return true; 454 } 455 }; 456