summaryrefslogtreecommitdiffstats
path: root/mailnews/addrbook
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
commit302bf1b523012e11b60425d6eee1221ebc2724eb (patch)
treeb191a895f8716efcbe42f454f37597a545a6f421 /mailnews/addrbook
parent21b3f6247403c06f85e1f45d219f87549862198f (diff)
downloadUXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.gz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.lz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.xz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.zip
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
Diffstat (limited to 'mailnews/addrbook')
-rw-r--r--mailnews/addrbook/content/abAddressBookNameDialog.js72
-rw-r--r--mailnews/addrbook/content/abAddressBookNameDialog.xul26
-rw-r--r--mailnews/addrbook/content/abDragDrop.js424
-rw-r--r--mailnews/addrbook/content/abEditCardDialog.xul17
-rw-r--r--mailnews/addrbook/content/abMailListDialog.js613
-rw-r--r--mailnews/addrbook/content/abNewCardDialog.xul33
-rw-r--r--mailnews/addrbook/content/abResultsPane.js502
-rw-r--r--mailnews/addrbook/content/abResultsPaneOverlay.xul90
-rw-r--r--mailnews/addrbook/content/addrbookWidgets.xml439
-rw-r--r--mailnews/addrbook/content/print.css94
-rw-r--r--mailnews/addrbook/moz.build9
-rw-r--r--mailnews/addrbook/prefs/content/pref-directory-add.js394
-rw-r--r--mailnews/addrbook/prefs/content/pref-directory-add.xul152
-rw-r--r--mailnews/addrbook/prefs/content/pref-editdirectories.js142
-rw-r--r--mailnews/addrbook/prefs/content/pref-editdirectories.xul43
-rw-r--r--mailnews/addrbook/public/moz.build47
-rw-r--r--mailnews/addrbook/public/nsAbBaseCID.h445
-rw-r--r--mailnews/addrbook/public/nsIAbAddressCollector.idl58
-rw-r--r--mailnews/addrbook/public/nsIAbAutoCompleteResult.idl36
-rw-r--r--mailnews/addrbook/public/nsIAbBooleanExpression.idl122
-rw-r--r--mailnews/addrbook/public/nsIAbCard.idl358
-rw-r--r--mailnews/addrbook/public/nsIAbCollection.idl92
-rw-r--r--mailnews/addrbook/public/nsIAbDirFactory.idl35
-rw-r--r--mailnews/addrbook/public/nsIAbDirFactoryService.idl28
-rw-r--r--mailnews/addrbook/public/nsIAbDirSearchListener.idl15
-rw-r--r--mailnews/addrbook/public/nsIAbDirectory.idl296
-rw-r--r--mailnews/addrbook/public/nsIAbDirectoryQuery.idl164
-rw-r--r--mailnews/addrbook/public/nsIAbDirectoryQueryProxy.idl14
-rw-r--r--mailnews/addrbook/public/nsIAbDirectorySearch.idl53
-rw-r--r--mailnews/addrbook/public/nsIAbItem.idl90
-rw-r--r--mailnews/addrbook/public/nsIAbLDAPAttributeMap.idl194
-rw-r--r--mailnews/addrbook/public/nsIAbLDAPCard.idl56
-rw-r--r--mailnews/addrbook/public/nsIAbLDAPDirectory.idl112
-rw-r--r--mailnews/addrbook/public/nsIAbLDAPReplicationData.idl68
-rw-r--r--mailnews/addrbook/public/nsIAbLDAPReplicationQuery.idl67
-rw-r--r--mailnews/addrbook/public/nsIAbLDAPReplicationService.idl32
-rw-r--r--mailnews/addrbook/public/nsIAbLDIFService.idl40
-rw-r--r--mailnews/addrbook/public/nsIAbListener.idl90
-rw-r--r--mailnews/addrbook/public/nsIAbMDBDirectory.idl71
-rw-r--r--mailnews/addrbook/public/nsIAbManager.idl190
-rw-r--r--mailnews/addrbook/public/nsIAbView.idl109
-rw-r--r--mailnews/addrbook/public/nsIAddbookUrl.idl19
-rw-r--r--mailnews/addrbook/public/nsIAddrDBAnnouncer.idl35
-rw-r--r--mailnews/addrbook/public/nsIAddrDBListener.idl36
-rw-r--r--mailnews/addrbook/public/nsIAddrDatabase.idl311
-rw-r--r--mailnews/addrbook/public/nsIMsgVCardService.idl29
-rw-r--r--mailnews/addrbook/src/moz.build93
-rw-r--r--mailnews/addrbook/src/nsAbAddressCollector.cpp331
-rw-r--r--mailnews/addrbook/src/nsAbAddressCollector.h44
-rw-r--r--mailnews/addrbook/src/nsAbAutoCompleteMyDomain.js58
-rw-r--r--mailnews/addrbook/src/nsAbAutoCompleteSearch.js466
-rw-r--r--mailnews/addrbook/src/nsAbBSDirectory.cpp323
-rw-r--r--mailnews/addrbook/src/nsAbBSDirectory.h50
-rw-r--r--mailnews/addrbook/src/nsAbBoolExprToLDAPFilter.cpp246
-rw-r--r--mailnews/addrbook/src/nsAbBoolExprToLDAPFilter.h45
-rw-r--r--mailnews/addrbook/src/nsAbBooleanExpression.cpp132
-rw-r--r--mailnews/addrbook/src/nsAbBooleanExpression.h43
-rw-r--r--mailnews/addrbook/src/nsAbCardProperty.cpp1193
-rw-r--r--mailnews/addrbook/src/nsAbCardProperty.h59
-rw-r--r--mailnews/addrbook/src/nsAbContentHandler.cpp184
-rw-r--r--mailnews/addrbook/src/nsAbContentHandler.h26
-rw-r--r--mailnews/addrbook/src/nsAbDirFactoryService.cpp54
-rw-r--r--mailnews/addrbook/src/nsAbDirFactoryService.h23
-rw-r--r--mailnews/addrbook/src/nsAbDirProperty.cpp593
-rw-r--r--mailnews/addrbook/src/nsAbDirProperty.h72
-rw-r--r--mailnews/addrbook/src/nsAbDirectoryQuery.cpp528
-rw-r--r--mailnews/addrbook/src/nsAbDirectoryQuery.h113
-rw-r--r--mailnews/addrbook/src/nsAbDirectoryQueryProxy.cpp33
-rw-r--r--mailnews/addrbook/src/nsAbDirectoryQueryProxy.h27
-rw-r--r--mailnews/addrbook/src/nsAbLDAPAttributeMap.js247
-rw-r--r--mailnews/addrbook/src/nsAbLDAPAutoCompleteSearch.js325
-rw-r--r--mailnews/addrbook/src/nsAbLDAPCard.cpp297
-rw-r--r--mailnews/addrbook/src/nsAbLDAPCard.h30
-rw-r--r--mailnews/addrbook/src/nsAbLDAPChangeLogData.cpp542
-rw-r--r--mailnews/addrbook/src/nsAbLDAPChangeLogData.h57
-rw-r--r--mailnews/addrbook/src/nsAbLDAPChangeLogQuery.cpp180
-rw-r--r--mailnews/addrbook/src/nsAbLDAPChangeLogQuery.h28
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirFactory.cpp79
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirFactory.h23
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirectory.cpp948
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirectory.h75
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirectoryModify.cpp372
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirectoryModify.h31
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp610
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirectoryQuery.h44
-rw-r--r--mailnews/addrbook/src/nsAbLDAPListenerBase.cpp358
-rw-r--r--mailnews/addrbook/src/nsAbLDAPListenerBase.h54
-rw-r--r--mailnews/addrbook/src/nsAbLDAPReplicationData.cpp489
-rw-r--r--mailnews/addrbook/src/nsAbLDAPReplicationData.h66
-rw-r--r--mailnews/addrbook/src/nsAbLDAPReplicationQuery.cpp153
-rw-r--r--mailnews/addrbook/src/nsAbLDAPReplicationQuery.h44
-rw-r--r--mailnews/addrbook/src/nsAbLDAPReplicationService.cpp136
-rw-r--r--mailnews/addrbook/src/nsAbLDAPReplicationService.h33
-rw-r--r--mailnews/addrbook/src/nsAbLDIFService.cpp868
-rw-r--r--mailnews/addrbook/src/nsAbLDIFService.h37
-rw-r--r--mailnews/addrbook/src/nsAbMDBCard.cpp55
-rw-r--r--mailnews/addrbook/src/nsAbMDBCard.h26
-rw-r--r--mailnews/addrbook/src/nsAbMDBDirFactory.cpp118
-rw-r--r--mailnews/addrbook/src/nsAbMDBDirFactory.h24
-rw-r--r--mailnews/addrbook/src/nsAbMDBDirProperty.cpp145
-rw-r--r--mailnews/addrbook/src/nsAbMDBDirProperty.h40
-rw-r--r--mailnews/addrbook/src/nsAbMDBDirectory.cpp1125
-rw-r--r--mailnews/addrbook/src/nsAbMDBDirectory.h104
-rw-r--r--mailnews/addrbook/src/nsAbManager.cpp1422
-rw-r--r--mailnews/addrbook/src/nsAbManager.h71
-rw-r--r--mailnews/addrbook/src/nsAbOSXCard.h47
-rw-r--r--mailnews/addrbook/src/nsAbOSXCard.mm401
-rw-r--r--mailnews/addrbook/src/nsAbOSXDirFactory.cpp50
-rw-r--r--mailnews/addrbook/src/nsAbOSXDirFactory.h21
-rw-r--r--mailnews/addrbook/src/nsAbOSXDirectory.h126
-rw-r--r--mailnews/addrbook/src/nsAbOSXDirectory.mm1374
-rw-r--r--mailnews/addrbook/src/nsAbOSXUtils.h36
-rw-r--r--mailnews/addrbook/src/nsAbOSXUtils.mm117
-rw-r--r--mailnews/addrbook/src/nsAbOutlookDirFactory.cpp87
-rw-r--r--mailnews/addrbook/src/nsAbOutlookDirFactory.h22
-rw-r--r--mailnews/addrbook/src/nsAbOutlookDirectory.cpp1539
-rw-r--r--mailnews/addrbook/src/nsAbOutlookDirectory.h152
-rw-r--r--mailnews/addrbook/src/nsAbQueryStringToExpression.cpp337
-rw-r--r--mailnews/addrbook/src/nsAbQueryStringToExpression.h49
-rw-r--r--mailnews/addrbook/src/nsAbUtils.h140
-rw-r--r--mailnews/addrbook/src/nsAbView.cpp1451
-rw-r--r--mailnews/addrbook/src/nsAbView.h83
-rw-r--r--mailnews/addrbook/src/nsAbWinHelper.cpp1003
-rw-r--r--mailnews/addrbook/src/nsAbWinHelper.h156
-rw-r--r--mailnews/addrbook/src/nsAddbookProtocolHandler.cpp323
-rw-r--r--mailnews/addrbook/src/nsAddbookProtocolHandler.h45
-rw-r--r--mailnews/addrbook/src/nsAddbookUrl.cpp282
-rw-r--r--mailnews/addrbook/src/nsAddbookUrl.h39
-rw-r--r--mailnews/addrbook/src/nsAddrDatabase.cpp3335
-rw-r--r--mailnews/addrbook/src/nsAddrDatabase.h439
-rw-r--r--mailnews/addrbook/src/nsAddrbook.manifest12
-rw-r--r--mailnews/addrbook/src/nsDirPrefs.cpp1452
-rw-r--r--mailnews/addrbook/src/nsDirPrefs.h86
-rw-r--r--mailnews/addrbook/src/nsMapiAddressBook.cpp147
-rw-r--r--mailnews/addrbook/src/nsMapiAddressBook.h54
-rw-r--r--mailnews/addrbook/src/nsMsgVCardService.cpp77
-rw-r--r--mailnews/addrbook/src/nsMsgVCardService.h24
-rw-r--r--mailnews/addrbook/src/nsVCard.cpp1571
-rw-r--r--mailnews/addrbook/src/nsVCard.h64
-rw-r--r--mailnews/addrbook/src/nsVCardObj.cpp1330
-rw-r--r--mailnews/addrbook/src/nsVCardObj.h396
-rw-r--r--mailnews/addrbook/src/nsWabAddressBook.cpp128
-rw-r--r--mailnews/addrbook/src/nsWabAddressBook.h57
143 files changed, 37406 insertions, 0 deletions
diff --git a/mailnews/addrbook/content/abAddressBookNameDialog.js b/mailnews/addrbook/content/abAddressBookNameDialog.js
new file mode 100644
index 000000000..a62659cc3
--- /dev/null
+++ b/mailnews/addrbook/content/abAddressBookNameDialog.js
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource:///modules/mailServices.js");
+
+var gOkButton;
+var gNameInput;
+var gDirectory = null;
+
+var kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
+var kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
+var kAllDirectoryRoot = "moz-abdirectory://";
+var kPABDirectory = 2; // defined in nsDirPrefs.h
+
+function abNameOnLoad()
+{
+ // Get the document elements.
+ gOkButton = document.documentElement.getButton('accept');
+ gNameInput = document.getElementById('name');
+
+ // look in arguments[0] for parameters to see if we have a directory or not
+ if ("arguments" in window && window.arguments[0] &&
+ "selectedDirectory" in window.arguments[0]) {
+ gDirectory = window.arguments[0].selectedDirectory;
+ gNameInput.value = gDirectory.dirName;
+ }
+
+ // Work out the window title (if we have a directory specified, then it's a
+ // rename).
+ var bundle = document.getElementById("bundle_addressBook");
+
+ if (gDirectory) {
+ let oldListName = gDirectory.dirName;
+ document.title = bundle.getFormattedString("addressBookTitleEdit", [oldListName]);
+ } else {
+ document.title = bundle.getString("addressBookTitleNew");
+ }
+
+ if (gDirectory &&
+ (gDirectory.URI == kCollectedAddressbookURI ||
+ gDirectory.URI == kPersonalAddressbookURI ||
+ gDirectory.URI == kAllDirectoryRoot + "?")) {
+ // Address book name is not editable, therefore disable the field and
+ // only have an ok button that doesn't do anything.
+ gNameInput.readOnly = true;
+ document.documentElement.buttons = "accept";
+ document.documentElement.removeAttribute("ondialogaccept");
+ } else {
+ gNameInput.focus();
+ abNameDoOkEnabling();
+ }
+}
+
+function abNameOKButton()
+{
+ var newName = gNameInput.value.trim();
+
+ // Either create a new directory or update an existing one depending on what
+ // we were given when we started.
+ if (gDirectory)
+ gDirectory.dirName = newName;
+ else
+ MailServices.ab.newAddressBook(newName, "", kPABDirectory);
+
+ return true;
+}
+
+function abNameDoOkEnabling()
+{
+ gOkButton.disabled = gNameInput.value.trim() == "";
+}
diff --git a/mailnews/addrbook/content/abAddressBookNameDialog.xul b/mailnews/addrbook/content/abAddressBookNameDialog.xul
new file mode 100644
index 000000000..f707cd597
--- /dev/null
+++ b/mailnews/addrbook/content/abAddressBookNameDialog.xul
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/dialogs.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/abAddressBookNameDialog.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 36em;"
+ onload="abNameOnLoad();"
+ ondialogaccept="return abNameOKButton();">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_addressBook"
+ src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+ </stringbundleset>
+
+ <script type="application/javascript" src="chrome://messenger/content/addressbook/abAddressBookNameDialog.js"/>
+
+ <hbox align="center">
+ <label control="name" value="&name.label;" accesskey="&name.accesskey;"/>
+ <textbox id="name" oninput="abNameDoOkEnabling();" flex="1"/>
+ </hbox>
+</dialog>
diff --git a/mailnews/addrbook/content/abDragDrop.js b/mailnews/addrbook/content/abDragDrop.js
new file mode 100644
index 000000000..6160ec530
--- /dev/null
+++ b/mailnews/addrbook/content/abDragDrop.js
@@ -0,0 +1,424 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/PluralForm.jsm");
+
+// Returns the load context for the current window
+function getLoadContext() {
+ return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsILoadContext);
+}
+
+var abFlavorDataProvider = {
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIFlavorDataProvider]),
+
+ getFlavorData: function(aTransferable, aFlavor, aData, aDataLen)
+ {
+ if (aFlavor == "application/x-moz-file-promise")
+ {
+ var primitive = {};
+ aTransferable.getTransferData("text/vcard", primitive, {});
+ var vCard = primitive.value.QueryInterface(Components.interfaces.nsISupportsString).data;
+ aTransferable.getTransferData("application/x-moz-file-promise-dest-filename", primitive, {});
+ var leafName = primitive.value.QueryInterface(Components.interfaces.nsISupportsString).data;
+ aTransferable.getTransferData("application/x-moz-file-promise-dir", primitive, {});
+ var localFile = primitive.value.QueryInterface(Components.interfaces.nsIFile).clone();
+ localFile.append(leafName);
+
+ var ofStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
+ ofStream.init(localFile, -1, -1, 0);
+ var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
+ converter.init(ofStream, null, 0, 0);
+ converter.writeString(vCard);
+ converter.close();
+
+ aData.value = localFile;
+ }
+ }
+};
+
+var abResultsPaneObserver = {
+ onDragStart: function (aEvent, aXferData, aDragAction)
+ {
+ var selectedRows = GetSelectedRows();
+
+ if (!selectedRows)
+ return;
+
+ var selectedAddresses = GetSelectedAddresses();
+
+ aXferData.data = new TransferData();
+ aXferData.data.addDataForFlavour("moz/abcard", selectedRows);
+ aXferData.data.addDataForFlavour("text/x-moz-address", selectedAddresses);
+ aXferData.data.addDataForFlavour("text/unicode", selectedAddresses);
+
+ let srcDirectory = getSelectedDirectory();
+ // The default allowable actions are copy, move and link, so we need
+ // to restrict them here.
+ if (!srcDirectory.readOnly)
+ // Only allow copy & move from read-write directories.
+ aDragAction.action = Components.interfaces.
+ nsIDragService.DRAGDROP_ACTION_COPY |
+ Components.interfaces.
+ nsIDragService.DRAGDROP_ACTION_MOVE;
+ else
+ // Only allow copy from read-only directories.
+ aDragAction.action = Components.interfaces.
+ nsIDragService.DRAGDROP_ACTION_COPY;
+
+ var card = GetSelectedCard();
+ if (card && card.displayName) {
+ let vCard = card.translateTo("vcard");
+ aXferData.data.addDataForFlavour("text/vcard", decodeURIComponent(vCard));
+ aXferData.data.addDataForFlavour("application/x-moz-file-promise-dest-filename", card.displayName + ".vcf");
+ aXferData.data.addDataForFlavour("application/x-moz-file-promise-url", "data:text/vcard," + vCard);
+ aXferData.data.addDataForFlavour("application/x-moz-file-promise", abFlavorDataProvider);
+ }
+ },
+
+ onDrop: function (aEvent, aXferData, aDragSession)
+ {
+ },
+
+ onDragExit: function (aEvent, aDragSession)
+ {
+ },
+
+ onDragOver: function (aEvent, aFlavour, aDragSession)
+ {
+ },
+
+ getSupportedFlavours: function ()
+ {
+ return null;
+ }
+};
+
+
+var dragService = Components.classes["@mozilla.org/widget/dragservice;1"]
+ .getService(Components.interfaces.nsIDragService);
+
+var abDirTreeObserver = {
+ /**
+ * canDrop - determine if the tree will accept the dropping of a item
+ * onto it.
+ *
+ * Note 1: We don't allow duplicate mailing list names, therefore copy
+ * is not allowed for mailing lists.
+ * Note 2: Mailing lists currently really need a card in the parent
+ * address book, therefore only moving to an address book is allowed.
+ *
+ * The possibilities:
+ *
+ * anything -> same place = Not allowed
+ * anything -> read only directory = Not allowed
+ * mailing list -> mailing list = Not allowed
+ * (we currently do not support recursive lists)
+ * address book card -> different address book = MOVE or COPY
+ * address book card -> mailing list = COPY only
+ * (cards currently have to exist outside list for list to work correctly)
+ * mailing list -> different address book = MOVE only
+ * (lists currently need to have unique names)
+ * card in mailing list -> parent mailing list = Not allowed
+ * card in mailing list -> other mailing list = MOVE or COPY
+ * card in mailing list -> other address book = MOVE or COPY
+ * read only directory item -> anywhere = COPY only
+ */
+ canDrop: function(index, orientation, dataTransfer)
+ {
+ if (orientation != Components.interfaces.nsITreeView.DROP_ON)
+ return false;
+ if (!dataTransfer.types.includes("moz/abcard")) {
+ return false;
+ }
+
+ let targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
+
+ let srcURI = getSelectedDirectoryURI();
+
+ // We cannot allow copy/move to "All Address Books".
+ if (targetURI == kAllDirectoryRoot + "?")
+ return false;
+
+ // The same place case
+ if (targetURI == srcURI)
+ return false;
+
+ // determine if we dragging from a mailing list on a directory x to the parent (directory x).
+ // if so, don't allow the drop
+ if (srcURI.startsWith(targetURI))
+ return false;
+
+ // check if we can write to the target directory
+ // e.g. LDAP is readonly currently
+ var targetDirectory = GetDirectoryFromURI(targetURI);
+
+ if (targetDirectory.readOnly)
+ return false;
+
+ var dragSession = dragService.getCurrentSession();
+ if (!dragSession)
+ return false;
+
+ // XXX Due to bug 373125/bug 349044 we can't specify a default action,
+ // so we default to move and this means that the user would have to press
+ // ctrl to copy which most users don't realise.
+ //
+ // If target directory is a mailing list, then only allow copies.
+ // if (targetDirectory.isMailList &&
+ // dragSession.dragAction != Components.interfaces.
+ // nsIDragService.DRAGDROP_ACTION_COPY)
+ //return false;
+
+ var srcDirectory = GetDirectoryFromURI(srcURI);
+
+ // Only allow copy from read-only directories.
+ if (srcDirectory.readOnly &&
+ dragSession.dragAction != Components.interfaces.
+ nsIDragService.DRAGDROP_ACTION_COPY)
+ return false;
+
+ // Go through the cards checking to see if one of them is a mailing list
+ // (if we are attempting a copy) - we can't copy mailing lists as
+ // that would give us duplicate names which isn't allowed at the
+ // moment.
+ var draggingMailList = false;
+
+ // The data contains the a string of "selected rows", eg.: "1,2".
+ var rows = dataTransfer.getData("moz/abcard").split(",").map(j => parseInt(j, 10));
+
+ for (var j = 0; j < rows.length; j++)
+ {
+ if (gAbView.getCardFromRow(rows[j]).isMailList)
+ {
+ draggingMailList = true;
+ break;
+ }
+ }
+
+ // The rest of the cases - allow cards for copy or move, but only allow
+ // move of mailing lists if we're not going into another mailing list.
+ if (draggingMailList &&
+ (targetDirectory.isMailList ||
+ dragSession.dragAction == Components.interfaces.
+ nsIDragService.DRAGDROP_ACTION_COPY))
+ {
+ return false;
+ }
+
+ dragSession.canDrop = true;
+ return true;
+ },
+
+ /**
+ * onDrop - we don't need to check again for correctness as the
+ * tree view calls canDrop just before calling onDrop.
+ *
+ */
+ onDrop: function(index, orientation, dataTransfer)
+ {
+ var dragSession = dragService.getCurrentSession();
+ if (!dragSession)
+ return;
+ if (!dataTransfer.types.includes("moz/abcard")) {
+ return;
+ }
+
+ let targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
+ let srcURI = getSelectedDirectoryURI();
+
+ // The data contains the a string of "selected rows", eg.: "1,2".
+ var rows = dataTransfer.getData("moz/abcard").split(",").map(j => parseInt(j, 10));
+ var numrows = rows.length;
+
+ var result;
+ // needToCopyCard is used for whether or not we should be creating
+ // copies of the cards in a mailing list in a different address book
+ // - it's not for if we are moving or not.
+ var needToCopyCard = true;
+ if (srcURI.length > targetURI.length) {
+ result = srcURI.split(targetURI);
+ if (result[0] != srcURI) {
+ // src directory is a mailing list on target directory, no need to copy card
+ needToCopyCard = false;
+ }
+ }
+ else {
+ result = targetURI.split(srcURI);
+ if (result[0] != targetURI) {
+ // target directory is a mailing list on src directory, no need to copy card
+ needToCopyCard = false;
+ }
+ }
+
+ // if we still think we have to copy the card,
+ // check if srcURI and targetURI are mailing lists on same directory
+ // if so, we don't have to copy the card
+ if (needToCopyCard) {
+ var targetParentURI = GetParentDirectoryFromMailingListURI(targetURI);
+ if (targetParentURI && (targetParentURI == GetParentDirectoryFromMailingListURI(srcURI)))
+ needToCopyCard = false;
+ }
+
+ var directory = GetDirectoryFromURI(targetURI);
+
+ // Only move if we are not transferring to a mail list
+ var actionIsMoving = (dragSession.dragAction & dragSession.DRAGDROP_ACTION_MOVE) && !directory.isMailList;
+
+ let cardsToCopy = [];
+ for (let j = 0; j < numrows; j++) {
+ cardsToCopy.push(gAbView.getCardFromRow(rows[j]));
+ }
+ for (let card of cardsToCopy) {
+ if (card.isMailList) {
+ // This check ensures we haven't slipped through by mistake
+ if (needToCopyCard && actionIsMoving) {
+ directory.addMailList(GetDirectoryFromURI(card.mailListURI));
+ }
+ } else {
+ let srcDirectory = null;
+ if (srcURI == (kAllDirectoryRoot + "?") && actionIsMoving) {
+ let dirId = card.directoryId.substring(0, card.directoryId.indexOf("&"));
+ srcDirectory = MailServices.ab.getDirectoryFromId(dirId);
+ }
+
+ directory.dropCard(card, needToCopyCard);
+
+ // This is true only if srcURI is "All ABs" and action is moving.
+ if (srcDirectory) {
+ let cardArray =
+ Components.classes["@mozilla.org/array;1"]
+ .createInstance(Components.interfaces.nsIMutableArray);
+ cardArray.appendElement(card, false);
+ srcDirectory.deleteCards(cardArray);
+ }
+ }
+ }
+
+ var cardsTransferredText;
+
+ // If we are moving, but not moving to a directory, then delete the
+ // selected cards and display the appropriate text
+ if (actionIsMoving && srcURI != (kAllDirectoryRoot + "?")) {
+ // If we have moved the cards, then delete them as well.
+ gAbView.deleteSelectedCards();
+ }
+
+ if (actionIsMoving) {
+ cardsTransferredText = PluralForm.get(numrows,
+ gAddressBookBundle.getFormattedString("contactsMoved", [numrows]));
+ } else {
+ cardsTransferredText = PluralForm.get(numrows,
+ gAddressBookBundle.getFormattedString("contactsCopied", [numrows]));
+ }
+
+ if (srcURI == kAllDirectoryRoot + "?") {
+ SetAbView(srcURI);
+ }
+
+ document.getElementById("statusText").label = cardsTransferredText;
+ },
+
+ onToggleOpenState: function()
+ {
+ },
+
+ onCycleHeader: function(colID, elt)
+ {
+ },
+
+ onCycleCell: function(row, colID)
+ {
+ },
+
+ onSelectionChanged: function()
+ {
+ },
+
+ onPerformAction: function(action)
+ {
+ },
+
+ onPerformActionOnRow: function(action, row)
+ {
+ },
+
+ onPerformActionOnCell: function(action, row, colID)
+ {
+ }
+}
+
+function DragAddressOverTargetControl(event)
+{
+ var dragSession = gDragService.getCurrentSession();
+
+ if (!dragSession.isDataFlavorSupported("text/x-moz-address"))
+ return;
+
+ var trans = Components.classes["@mozilla.org/widget/transferable;1"]
+ .createInstance(Components.interfaces.nsITransferable);
+ trans.init(getLoadContext());
+ trans.addDataFlavor("text/x-moz-address");
+
+ var canDrop = true;
+
+ for ( var i = 0; i < dragSession.numDropItems; ++i )
+ {
+ dragSession.getData ( trans, i );
+ var dataObj = new Object();
+ var bestFlavor = new Object();
+ var len = new Object();
+ try
+ {
+ trans.getAnyTransferData ( bestFlavor, dataObj, len );
+ }
+ catch (ex)
+ {
+ canDrop = false;
+ break;
+ }
+ }
+ dragSession.canDrop = canDrop;
+}
+
+function DropAddressOverTargetControl(event)
+{
+ var dragSession = gDragService.getCurrentSession();
+
+ var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
+ trans.addDataFlavor("text/x-moz-address");
+
+ for ( var i = 0; i < dragSession.numDropItems; ++i )
+ {
+ dragSession.getData ( trans, i );
+ var dataObj = new Object();
+ var bestFlavor = new Object();
+ var len = new Object();
+
+ // Ensure we catch any empty data that may have slipped through
+ try
+ {
+ trans.getAnyTransferData ( bestFlavor, dataObj, len);
+ }
+ catch (ex)
+ {
+ continue;
+ }
+
+ if ( dataObj )
+ dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
+ if ( !dataObj )
+ continue;
+
+ // pull the address out of the data object
+ var address = dataObj.data.substring(0, len.value);
+ if (!address)
+ continue;
+
+ DropRecipient(address);
+ }
+}
diff --git a/mailnews/addrbook/content/abEditCardDialog.xul b/mailnews/addrbook/content/abEditCardDialog.xul
new file mode 100644
index 000000000..0a21f3b83
--- /dev/null
+++ b/mailnews/addrbook/content/abEditCardDialog.xul
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/addressbook/abCardOverlay.xul"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="abcardWindow"
+ onload="OnLoadEditCard()"
+ ondialogaccept="return EditCardOKButton();">
+
+ <stringbundleset id="stringbundleset"/>
+ <vbox id="editcard"/>
+</dialog>
diff --git a/mailnews/addrbook/content/abMailListDialog.js b/mailnews/addrbook/content/abMailListDialog.js
new file mode 100644
index 000000000..ee94d39b7
--- /dev/null
+++ b/mailnews/addrbook/content/abMailListDialog.js
@@ -0,0 +1,613 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+top.MAX_RECIPIENTS = 1;
+var inputElementType = "";
+
+var gListCard;
+var gEditList;
+var oldListName = "";
+var gLoadListeners = [];
+var gSaveListeners = [];
+
+try
+{
+ var gDragService = Components.classes["@mozilla.org/widget/dragservice;1"]
+ .getService(Components.interfaces.nsIDragService);
+}
+catch (e)
+{
+}
+
+// Returns the load context for the current window
+function getLoadContext() {
+ return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsILoadContext);
+}
+
+function awHandleKeyPress(element, event)
+{
+ // allow dialog to close on enter if focused textbox has no value
+ if (element.value != "" && event.keyCode == KeyEvent.DOM_VK_RETURN) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+}
+
+function mailingListExists(listname)
+{
+ if (MailServices.ab.mailListNameExists(listname))
+ {
+ Services.prompt.alert(window,
+ gAddressBookBundle.getString("mailListNameExistsTitle"),
+ gAddressBookBundle.getString("mailListNameExistsMessage"));
+ return true;
+ }
+ return false;
+}
+
+function GetListValue(mailList, doAdd)
+{
+ var listname = document.getElementById("ListName").value.trim();
+
+ if (listname.length == 0)
+ {
+ var alertText = gAddressBookBundle.getString("emptyListName");
+ alert(alertText);
+ return false;
+ }
+ else
+ {
+ var canonicalNewListName = listname.toLowerCase();
+ var canonicalOldListName = oldListName.toLowerCase();
+ if (doAdd)
+ {
+ if (mailingListExists(canonicalNewListName))
+ return false;
+ }
+ else if (canonicalOldListName != canonicalNewListName)
+ {
+ if (mailingListExists(canonicalNewListName))
+ return false;
+ }
+ }
+
+ mailList.isMailList = true;
+ mailList.dirName = listname;
+ mailList.listNickName = document.getElementById('ListNickName').value;
+ mailList.description = document.getElementById('ListDescription').value;
+
+ var oldTotal = mailList.addressLists.length;
+ var i = 1;
+ var pos = 0;
+ var inputField, fieldValue, cardproperty;
+ while ((inputField = awGetInputElement(i)))
+ {
+
+ fieldValue = inputField.value;
+
+ if (doAdd || (!doAdd && pos >= oldTotal))
+ cardproperty = Components.classes["@mozilla.org/addressbook/cardproperty;1"].createInstance();
+ else
+ cardproperty = mailList.addressLists.queryElementAt(pos, Components.interfaces.nsIAbCard);
+
+ if (fieldValue == "")
+ {
+ if (!doAdd && cardproperty)
+ try
+ {
+ mailList.addressLists.removeElementAt(pos);
+ --oldTotal;
+ }
+ catch(ex)
+ {
+ // Ignore attempting to remove an item
+ // at a position greater than the number
+ // of elements in the addressLists attribute
+ }
+ }
+ else if (cardproperty)
+ {
+ cardproperty = cardproperty.QueryInterface(Components.interfaces.nsIAbCard);
+ if (cardproperty)
+ {
+ let addrObjects = MailServices.headerParser
+ .makeFromDisplayAddress(fieldValue, {});
+ for (let j = 0; j < addrObjects.length; j++)
+ {
+ if (j > 0)
+ {
+ cardproperty = Components.classes["@mozilla.org/addressbook/cardproperty;1"].createInstance();
+ cardproperty = cardproperty.QueryInterface(Components.interfaces.nsIAbCard);
+ }
+ cardproperty.primaryEmail = addrObjects[j].email;
+ cardproperty.displayName = addrObjects[j].name || addrObjects[j].email;
+
+ if (doAdd || (doAdd == false && pos >= oldTotal))
+ mailList.addressLists.appendElement(cardproperty, false);
+ }
+ pos++;
+ }
+ }
+ i++;
+ }
+
+ --i;
+
+ if (doAdd == false && i < oldTotal)
+ {
+ for (var j = i; j < oldTotal; j++)
+ mailList.addressLists.removeElementAt(j);
+ }
+ return true;
+}
+
+function MailListOKButton()
+{
+ var popup = document.getElementById('abPopup');
+ if (popup)
+ {
+ var uri = popup.getAttribute('value');
+
+ // FIX ME - hack to avoid crashing if no ab selected because of blank option bug from template
+ // should be able to just remove this if we are not seeing blank lines in the ab popup
+ if (!uri)
+ return false; // don't close window
+ // -----
+
+ //Add mailing list to database
+ var mailList = Components.classes["@mozilla.org/addressbook/directoryproperty;1"].createInstance();
+ mailList = mailList.QueryInterface(Components.interfaces.nsIAbDirectory);
+
+ if (GetListValue(mailList, true))
+ {
+ var parentDirectory = GetDirectoryFromURI(uri);
+ mailList = parentDirectory.addMailList(mailList);
+ NotifySaveListeners(mailList);
+ }
+ else
+ return false;
+ }
+
+ return true; // close the window
+}
+
+function OnLoadNewMailList()
+{
+ var selectedAB = null;
+
+ InitCommonJS();
+
+ if ("arguments" in window && window.arguments[0])
+ {
+ var abURI = window.arguments[0].selectedAB;
+ if (abURI && abURI != kAllDirectoryRoot + "?") {
+ var directory = GetDirectoryFromURI(abURI);
+ if (directory.isMailList) {
+ var parentURI = GetParentDirectoryFromMailingListURI(abURI);
+ if (parentURI) {
+ selectedAB = parentURI;
+ }
+ }
+ else if (directory.readOnly) {
+ selectedAB = kPersonalAddressbookURI;
+ }
+ else {
+ selectedAB = abURI;
+ }
+ }
+ }
+
+ if (!selectedAB)
+ selectedAB = kPersonalAddressbookURI;
+
+ // set popup with address book names
+ var abPopup = document.getElementById('abPopup');
+ abPopup.value = selectedAB;
+
+ AppendNewRowAndSetFocus();
+ awFitDummyRows(1);
+
+ document.addEventListener("keypress", awDocumentKeyPress, true);
+
+ // focus on first name
+ var listName = document.getElementById('ListName');
+ if (listName)
+ setTimeout( function(firstTextBox) { firstTextBox.focus(); }, 0, listName );
+
+ NotifyLoadListeners(directory);
+}
+
+function EditListOKButton()
+{
+ //edit mailing list in database
+ if (GetListValue(gEditList, false))
+ {
+ if (gListCard) {
+ // modify the list card (for the results pane) from the mailing list
+ gListCard.displayName = gEditList.dirName;
+ gListCard.lastName = gEditList.dirName;
+ gListCard.setProperty("NickName", gEditList.listNickName);
+ gListCard.setProperty("Notes", gEditList.description);
+ }
+
+ NotifySaveListeners(gEditList);
+ gEditList.editMailListToDatabase(gListCard);
+
+ window.arguments[0].refresh = true;
+ return true; // close the window
+ }
+
+ return false;
+}
+
+function OnLoadEditList()
+{
+ InitCommonJS();
+
+ gListCard = window.arguments[0].abCard;
+ var listUri = window.arguments[0].listURI;
+
+ gEditList = GetDirectoryFromURI(listUri);
+
+ document.getElementById('ListName').value = gEditList.dirName;
+ document.getElementById('ListNickName').value = gEditList.listNickName;
+ document.getElementById('ListDescription').value = gEditList.description;
+ oldListName = gEditList.dirName;
+
+ document.title = gAddressBookBundle.getFormattedString("mailingListTitleEdit", [oldListName]);
+
+ if (gEditList.addressLists)
+ {
+ let total = gEditList.addressLists.length;
+ if (total)
+ {
+ let listbox = document.getElementById('addressingWidget');
+ let newListBoxNode = listbox.cloneNode(false);
+ let templateNode = listbox.querySelector("listitem");
+
+ top.MAX_RECIPIENTS = 0;
+ for (let i = 0; i < total; i++)
+ {
+ let card = gEditList.addressLists.queryElementAt(i, Components.interfaces.nsIAbCard);
+ let address = MailServices.headerParser.makeMailboxObject(
+ card.displayName, card.primaryEmail).toString();
+ SetInputValue(address, newListBoxNode, templateNode);
+ }
+ listbox.parentNode.replaceChild(newListBoxNode, listbox);
+ }
+ }
+
+ // Is this directory read-only? If so, we now need to set all the fields to
+ // read-only.
+ if (gEditList.readOnly) {
+ const kMailListFields = [ 'ListName', 'ListNickName', 'ListDescription' ];
+
+ for (let i = 0; i < kMailListFields.length; ++i)
+ document.getElementById(kMailListFields[i]).readOnly = true;
+
+ document.documentElement.buttons = "accept";
+ document.documentElement.removeAttribute("ondialogaccept");
+
+ // Getting a sane read-only implementation for the addressing widget would
+ // basically need a separate dialog. Given I'm not sure about the future of
+ // the mailing list dialog in its current state, let's just disable it
+ // completely.
+ document.getElementById("addressingWidget").disabled = true;
+ }
+
+ document.addEventListener("keypress", awDocumentKeyPress, true);
+
+ // workaround for bug 118337 - for mailing lists that have more rows than fits inside
+ // the display, the value of the textbox inside the new row isn't inherited into the input -
+ // the first row then appears to be duplicated at the end although it is actually empty.
+ // see awAppendNewRow which copies first row and clears it
+ setTimeout(AppendLastRow, 0);
+ NotifyLoadListeners(gEditList);
+}
+
+function AppendLastRow()
+{
+ AppendNewRowAndSetFocus();
+ awFitDummyRows(1);
+
+ // focus on first name
+ var listName = document.getElementById('ListName');
+ if (listName)
+ listName.focus();
+}
+
+function AppendNewRowAndSetFocus()
+{
+ var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
+ if (lastInput && lastInput.value)
+ awAppendNewRow(true);
+ else
+ awSetFocus(top.MAX_RECIPIENTS, lastInput);
+}
+
+function SetInputValue(inputValue, parentNode, templateNode)
+{
+ top.MAX_RECIPIENTS++;
+
+ var newNode = templateNode.cloneNode(true);
+ parentNode.appendChild(newNode); // we need to insert the new node before we set the value of the select element!
+
+ var input = newNode.getElementsByTagName(awInputElementName());
+ if (input && input.length == 1)
+ {
+ //We need to set the value using both setAttribute and .value else we will
+ // lose the content when the field is not visible. See bug 37435
+ input[0].setAttribute("value", inputValue);
+ input[0].value = inputValue;
+ input[0].setAttribute("id", "addressCol1#" + top.MAX_RECIPIENTS);
+ }
+}
+
+function awNotAnEmptyArea(event)
+{
+ //This is temporary until i figure out how to ensure to always having an empty space after the last row
+
+ var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
+ if (lastInput && lastInput.value)
+ awAppendNewRow(false);
+
+ event.stopPropagation();
+}
+
+function awClickEmptySpace(target, setFocus)
+{
+ if (target == null ||
+ (target.localName != "listboxbody" &&
+ target.localName != "listcell" &&
+ target.localName != "listitem"))
+ return;
+
+ var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
+
+ if (lastInput && lastInput.value)
+ awAppendNewRow(setFocus);
+ else
+ if (setFocus)
+ awSetFocus(top.MAX_RECIPIENTS, lastInput);
+}
+
+function awReturnHit(inputElement)
+{
+ var row = awGetRowByInputElement(inputElement);
+ if (inputElement.value)
+ {
+ var nextInput = awGetInputElement(row+1);
+ if (!nextInput)
+ awAppendNewRow(true);
+ else
+ awSetFocus(row+1, nextInput);
+ }
+}
+
+function awDeleteRow(rowToDelete)
+{
+ /* When we delete a row, we must reset the id of others row in order to not break the sequence */
+ var maxRecipients = top.MAX_RECIPIENTS;
+ awRemoveRow(rowToDelete);
+
+ var numberOfCols = awGetNumberOfCols();
+ for (var row = rowToDelete + 1; row <= maxRecipients; row ++)
+ for (var col = 1; col <= numberOfCols; col++)
+ awGetElementByCol(row, col).setAttribute("id", "addressCol" + (col) + "#" + (row-1));
+
+ awTestRowSequence();
+}
+
+function awInputChanged(inputElement)
+{
+// AutoCompleteAddress(inputElement);
+
+ //Do we need to add a new row?
+ var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
+ if (lastInput && lastInput.value && !top.doNotCreateANewRow)
+ awAppendNewRow(false);
+ top.doNotCreateANewRow = false;
+}
+
+function awInputElementName()
+{
+ if (inputElementType == "")
+ inputElementType = document.getElementById("addressCol1#1").localName;
+ return inputElementType;
+}
+
+function awAppendNewRow(setFocus)
+{
+ var body = document.getElementById("addressingWidget");
+ var listitem1 = awGetListItem(1);
+
+ if (body && listitem1)
+ {
+ var nextDummy = awGetNextDummyRow();
+ var newNode = listitem1.cloneNode(true);
+ if (nextDummy)
+ body.replaceChild(newNode, nextDummy);
+ else
+ body.appendChild(newNode);
+
+ top.MAX_RECIPIENTS++;
+
+ var input = newNode.getElementsByTagName(awInputElementName());
+ if (input && input.length == 1)
+ {
+ input[0].setAttribute("value", "");
+ input[0].setAttribute("id", "addressCol1#" + top.MAX_RECIPIENTS);
+
+ if (input[0].getAttribute('focused') != '')
+ input[0].removeAttribute('focused');
+ }
+ // focus on new input widget
+ if (setFocus && input )
+ awSetFocus(top.MAX_RECIPIENTS, input[0]);
+ }
+}
+
+
+// functions for accessing the elements in the addressing widget
+
+function awGetInputElement(row)
+{
+ return document.getElementById("addressCol1#" + row);
+}
+
+
+function _awSetFocus()
+{
+ var listbox = document.getElementById('addressingWidget');
+ try
+ {
+ var theNewRow = awGetListItem(top.awRow);
+
+ listbox.ensureElementIsVisible(theNewRow);
+ top.awInputElement.focus();
+ }
+ catch(ex)
+ {
+ top.awFocusRetry ++;
+ if (top.awFocusRetry < 8)
+ {
+ dump("_awSetFocus failed, try it again...\n");
+ setTimeout(_awSetFocus, 0);
+ }
+ else
+ dump("_awSetFocus failed, forget about it!\n");
+ }
+}
+
+function awTabFromRecipient(element, event)
+{
+ //If we are the last element in the listbox, we don't want to create a new row.
+ if (element == awGetInputElement(top.MAX_RECIPIENTS))
+ top.doNotCreateANewRow = true;
+}
+
+function DragOverAddressListTree(event)
+{
+ var validFlavor = false;
+ var dragSession = gDragService.getCurrentSession();
+
+ // XXX add support for other flavors here
+ if (dragSession.isDataFlavorSupported("text/x-moz-address")) {
+ dragSession.canDrop = true;
+ }
+}
+
+function DropOnAddressListTree(event)
+{
+ let dragSession = gDragService.getCurrentSession();
+ let trans;
+
+ try {
+ trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
+ trans.init(getLoadContext());
+ trans.addDataFlavor("text/x-moz-address");
+ }
+ catch (ex) {
+ return;
+ }
+
+ for (let i = 0; i < dragSession.numDropItems; ++i)
+ {
+ dragSession.getData(trans, i);
+ let dataObj = new Object();
+ let bestFlavor = new Object();
+ let len = new Object();
+ trans.getAnyTransferData(bestFlavor, dataObj, len);
+ if (dataObj)
+ dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
+ if (!dataObj)
+ continue;
+
+ // pull the URL out of the data object
+ let address = dataObj.data.substring(0, len.value);
+ if (!address)
+ continue;
+
+ DropListAddress(event.target, address);
+ }
+}
+
+function DropListAddress(target, address)
+{
+ // Set focus on a new available, visible row.
+ awClickEmptySpace(target, true);
+ if (top.MAX_RECIPIENTS == 0)
+ top.MAX_RECIPIENTS = 1;
+
+ // Break apart the MIME-ready header address into individual addressees to
+ // add to the dialog.
+ let addresses = {}, names = {}, fullNames = {};
+ MailServices.headerParser.parseHeadersWithArray(address, addresses, names,
+ fullNames);
+ for (let full of fullNames.value)
+ {
+ let lastInput = awGetInputElement(top.MAX_RECIPIENTS);
+ lastInput.value = full;
+ awAppendNewRow(true);
+ }
+}
+
+/* Allows extensions to register a listener function for
+ * when a mailing list is loaded. The listener function
+ * should take two parameters - the first being the
+ * mailing list being loaded, the second one being the
+ * current window document.
+ */
+function RegisterLoadListener(aListener)
+{
+ gLoadListeners.push(aListener);
+}
+
+/* Allows extensions to unload a load listener function.
+ */
+function UnregisterLoadListener(aListener)
+{
+ var fIndex = gLoadListeners.indexOf(aListener);
+ if (fIndex != -1)
+ gLoadListeners.splice(fIndex, 1);
+}
+
+/* Allows extensions to register a listener function for
+ * when a mailing list is saved. Like a load listener,
+ * the save listener should take two parameters: the first
+ * being a copy of the mailing list that is being saved,
+ * and the second being the current window document.
+ */
+function RegisterSaveListener(aListener)
+{
+ gSaveListeners.push(aListener);
+}
+
+/* Allows extensions to unload a save listener function.
+ */
+function UnregisterSaveListener(aListener)
+{
+ var fIndex = gSaveListeners.indexOf(aListener);
+ if (fIndex != -1)
+ gSaveListeners.splice(fIndex, 1);
+}
+
+/* Notifies all load listeners.
+ */
+function NotifyLoadListeners(aMailingList)
+{
+ for (let i = 0; i < gLoadListeners.length; i++)
+ gLoadListeners[i](aMailingList, document);
+}
+
+/* Notifies all save listeners.
+ */
+function NotifySaveListeners(aMailingList)
+{
+ for (let i = 0; i < gSaveListeners.length; i++)
+ gSaveListeners[i](aMailingList, document);
+}
+
diff --git a/mailnews/addrbook/content/abNewCardDialog.xul b/mailnews/addrbook/content/abNewCardDialog.xul
new file mode 100644
index 000000000..4f4d1a49b
--- /dev/null
+++ b/mailnews/addrbook/content/abNewCardDialog.xul
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/addressbook/abCardOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/abNewCardDialog.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="abcardWindow"
+ onload="OnLoadNewCard()"
+ ondialogaccept="return NewCardOKButton();">
+
+ <stringbundleset id="stringbundleset"/>
+
+ <hbox align="center">
+
+ <label id="abPopupLabel" control="abPopup" value="&chooseAddressBook.label;" accesskey="&chooseAddressBook.accesskey;"/>
+
+ <menulist id="abPopup">
+ <menupopup id="abPopup-menupopup" class="addrbooksPopup" writable="true"/>
+ </menulist>
+
+ </hbox>
+
+ <spacer style="height:1em"/>
+
+ <vbox id="editcard"/>
+
+</dialog>
diff --git a/mailnews/addrbook/content/abResultsPane.js b/mailnews/addrbook/content/abResultsPane.js
new file mode 100644
index 000000000..58a1771cb
--- /dev/null
+++ b/mailnews/addrbook/content/abResultsPane.js
@@ -0,0 +1,502 @@
+/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ; js-indent-level: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Use of items in this file require:
+ *
+ * getSelectedDirectoryURI()
+ * returns the URI of the selected directory
+ * AbResultsPaneDoubleClick(card)
+ * Is called when the results pane is double-clicked, with the clicked card.
+ * AbEditCard(card)
+ * Is called when a card is to be edited, with the card as the parameter.
+ *
+ * The following function is only required if ResultsPaneController is used:
+ *
+ * goSetMenuValue()
+ * Core function in globalOverlay.js
+ */
+
+// List/card selections in the results pane.
+var kNothingSelected = 0;
+var kListsAndCards = 1;
+var kMultipleListsOnly = 2;
+var kSingleListOnly = 3;
+var kCardsOnly = 4;
+
+// Global Variables
+
+// gAbView holds an object with an nsIAbView interface
+var gAbView = null;
+// Holds a reference to the "abResultsTree" document element. Initially
+// set up by SetAbView.
+var gAbResultsTree = null;
+
+function SetAbView(aURI)
+{
+ // If we don't have a URI, just clear the view and leave everything else
+ // alone.
+ if (!aURI) {
+ gAbView.clearView();
+ return;
+ }
+
+ // If we do have a URI, we want to allow updating the review even if the
+ // URI is the same, as the search results may be different.
+
+ var sortColumn = kDefaultSortColumn;
+ var sortDirection = kDefaultAscending;
+
+ if (!gAbResultsTree) {
+ gAbResultsTree = document.getElementById("abResultsTree");
+ gAbResultsTree.controllers.appendController(ResultsPaneController);
+ }
+
+ if (gAbView) {
+ sortColumn = gAbView.sortColumn;
+ sortDirection = gAbView.sortDirection;
+ }
+ else {
+ if (gAbResultsTree.hasAttribute("sortCol"))
+ sortColumn = gAbResultsTree.getAttribute("sortCol");
+ var sortColumnNode = document.getElementById(sortColumn);
+ if (sortColumnNode && sortColumnNode.hasAttribute("sortDirection"))
+ sortDirection = sortColumnNode.getAttribute("sortDirection");
+ }
+
+ var directory = GetDirectoryFromURI(aURI);
+
+ if (!gAbView)
+ gAbView = Components.classes["@mozilla.org/addressbook/abview;1"]
+ .createInstance(Components.interfaces.nsIAbView);
+
+ var actualSortColumn = gAbView.setView(directory, GetAbViewListener(),
+ sortColumn, sortDirection);
+
+ gAbResultsTree.treeBoxObject.view =
+ gAbView.QueryInterface(Components.interfaces.nsITreeView);
+
+ UpdateSortIndicators(actualSortColumn, sortDirection);
+
+ // If the selected address book is LDAP and the search box is empty,
+ // inform the user of the empty results pane.
+ let abResultsTree = document.getElementById("abResultsTree");
+ let cardViewOuterBox = document.getElementById("CardViewOuterBox");
+ let blankResultsPaneMessageBox = document.getElementById("blankResultsPaneMessageBox");
+ if (aURI.startsWith("moz-abldapdirectory://") && !aURI.includes("?")) {
+ if (abResultsTree)
+ abResultsTree.hidden = true;
+ if (cardViewOuterBox)
+ cardViewOuterBox.hidden = true;
+ if (blankResultsPaneMessageBox)
+ blankResultsPaneMessageBox.hidden = false;
+ } else {
+ if (abResultsTree)
+ abResultsTree.hidden = false;
+ if (cardViewOuterBox)
+ cardViewOuterBox.hidden = false;
+ if (blankResultsPaneMessageBox)
+ blankResultsPaneMessageBox.hidden = true;
+ }
+}
+
+function CloseAbView()
+{
+ if (gAbView)
+ gAbView.clearView();
+}
+
+function GetOneOrMoreCardsSelected()
+{
+ return (gAbView && (gAbView.selection.getRangeCount() > 0));
+}
+
+function GetSelectedAddresses()
+{
+ return GetAddressesForCards(GetSelectedAbCards());
+}
+
+function GetNumSelectedCards()
+{
+ try {
+ return gAbView.selection.count;
+ }
+ catch (ex) {
+ }
+
+ // if something went wrong, return 0 for the count.
+ return 0;
+}
+
+function GetSelectedCardTypes()
+{
+ var cards = GetSelectedAbCards();
+ if (!cards) {
+ Components.utils.reportError("ERROR: GetSelectedCardTypes: |cards| is null.");
+ return kNothingSelected; // no view
+ }
+ var count = cards.length;
+ if (count == 0)
+ return kNothingSelected; // nothing selected
+
+ var mailingListCnt = 0;
+ var cardCnt = 0;
+ for (let i = 0; i < count; i++) {
+ // We can assume no values from GetSelectedAbCards will be null.
+ if (cards[i].isMailList)
+ mailingListCnt++;
+ else
+ cardCnt++;
+ }
+
+ return (mailingListCnt == 0) ? kCardsOnly :
+ (cardCnt > 0) ? kListsAndCards :
+ (mailingListCnt == 1) ? kSingleListOnly :
+ kMultipleListsOnly;
+}
+
+// NOTE, will return -1 if more than one card selected, or no cards selected.
+function GetSelectedCardIndex()
+{
+ if (!gAbView)
+ return -1;
+
+ var treeSelection = gAbView.selection;
+ if (treeSelection.getRangeCount() == 1) {
+ var start = new Object;
+ var end = new Object;
+ treeSelection.getRangeAt(0, start, end);
+ if (start.value == end.value)
+ return start.value;
+ }
+
+ return -1;
+}
+
+// NOTE, returns the card if exactly one card is selected, null otherwise
+function GetSelectedCard()
+{
+ var index = GetSelectedCardIndex();
+ return (index == -1) ? null : gAbView.getCardFromRow(index);
+}
+
+/**
+ * Return a (possibly empty) list of cards
+ *
+ * It pushes only non-null/empty element, if any, into the returned list.
+ */
+function GetSelectedAbCards()
+{
+ var abView = gAbView;
+
+ // if sidebar is open, and addressbook panel is open and focused,
+ // then use the ab view from sidebar (gCurFrame is from sidebarOverlay.js)
+ if (document.getElementById("sidebar-box")) {
+ const abPanelUrl =
+ "chrome://messenger/content/addressbook/addressbook-panel.xul";
+ if (gCurFrame &&
+ gCurFrame.getAttribute("src") == abPanelUrl &&
+ document.commandDispatcher.focusedWindow == gCurFrame.contentDocument.defaultView)
+ abView = gCurFrame.contentDocument.defaultView.gAbView;
+ }
+
+ if (!abView)
+ return [];
+
+ let cards = [];
+ var count = abView.selection.getRangeCount();
+ var current = 0;
+ for (let i = 0; i < count; ++i) {
+ let start = {};
+ let end = {};
+
+ abView.selection.getRangeAt(i, start, end);
+
+ for (let j = start.value; j <= end.value; ++j) {
+ // avoid inserting null element into the list. GetRangeAt() may be buggy.
+ let tmp = abView.getCardFromRow(j);
+ if (tmp) {
+ cards.push(tmp);
+ }
+ }
+ }
+ return cards;
+}
+
+// XXX todo
+// an optimization might be to make this return
+// the selected ranges, which would be faster
+// when the user does large selections, but for now, let's keep it simple.
+function GetSelectedRows()
+{
+ var selectedRows = "";
+
+ if (!gAbView)
+ return selectedRows;
+
+ var rangeCount = gAbView.selection.getRangeCount();
+ for (let i = 0; i < rangeCount; ++i) {
+ var start = new Object;
+ var end = new Object;
+ gAbView.selection.getRangeAt(i, start, end);
+ for (let j = start.value;j <= end.value; ++j) {
+ if (selectedRows)
+ selectedRows += ",";
+ selectedRows += j;
+ }
+ }
+
+ return selectedRows;
+}
+
+function AbSwapFirstNameLastName()
+{
+ if (gAbView)
+ gAbView.swapFirstNameLastName();
+}
+
+function AbEditSelectedCard()
+{
+ AbEditCard(GetSelectedCard());
+}
+
+function AbResultsPaneOnClick(event)
+{
+ // we only care about button 0 (left click) events
+ if (event.button != 0) return;
+
+ // all we need to worry about here is double clicks
+ // and column header clicks.
+ //
+ // we get in here for clicks on the "treecol" (headers)
+ // and the "scrollbarbutton" (scrollbar buttons)
+ // we don't want those events to cause a "double click"
+
+ var t = event.originalTarget;
+
+ if (t.localName == "treecol") {
+ var sortDirection;
+ var currentDirection = t.getAttribute("sortDirection");
+
+ // Revert the sort order. If none is set, use Ascending.
+ sortDirection = currentDirection == kDefaultAscending ?
+ kDefaultDescending : kDefaultAscending;
+
+ SortAndUpdateIndicators(t.id, sortDirection);
+ }
+ else if (t.localName == "treechildren") {
+ // figure out what row the click was in
+ var row = gAbResultsTree.treeBoxObject.getRowAt(event.clientX,
+ event.clientY);
+ if (row == -1)
+ return;
+
+ if (event.detail == 2)
+ AbResultsPaneDoubleClick(gAbView.getCardFromRow(row));
+ }
+}
+
+function AbSortAscending()
+{
+ var sortColumn = gAbResultsTree.getAttribute("sortCol");
+ SortAndUpdateIndicators(sortColumn, kDefaultAscending);
+}
+
+function AbSortDescending()
+{
+ var sortColumn = gAbResultsTree.getAttribute("sortCol");
+ SortAndUpdateIndicators(sortColumn, kDefaultDescending);
+}
+
+function SortResultPane(sortColumn)
+{
+ var sortDirection = kDefaultAscending;
+ if (gAbView)
+ sortDirection = gAbView.sortDirection;
+
+ SortAndUpdateIndicators(sortColumn, sortDirection);
+}
+
+function SortAndUpdateIndicators(sortColumn, sortDirection)
+{
+ UpdateSortIndicators(sortColumn, sortDirection);
+
+ if (gAbView)
+ gAbView.sortBy(sortColumn, sortDirection);
+}
+
+function UpdateSortIndicators(colID, sortDirection)
+{
+ var sortedColumn = null;
+
+ // set the sort indicator on the column we are sorted by
+ if (colID) {
+ sortedColumn = document.getElementById(colID);
+ if (sortedColumn) {
+ sortedColumn.setAttribute("sortDirection",sortDirection);
+ gAbResultsTree.setAttribute("sortCol", colID);
+ }
+ }
+
+ // remove the sort indicator from all the columns
+ // except the one we are sorted by
+ var currCol = gAbResultsTree.firstChild.firstChild;
+ while (currCol) {
+ if (currCol != sortedColumn && currCol.localName == "treecol")
+ currCol.removeAttribute("sortDirection");
+ currCol = currCol.nextSibling;
+ }
+}
+
+function InvalidateResultsPane()
+{
+ if (gAbResultsTree)
+ gAbResultsTree.treeBoxObject.invalidate();
+}
+
+// Controller object for Results Pane
+var ResultsPaneController =
+{
+ supportsCommand: function(command)
+ {
+ switch (command) {
+ case "cmd_selectAll":
+ case "cmd_delete":
+ case "button_delete":
+ case "cmd_properties":
+ case "cmd_newlist":
+ case "cmd_newCard":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(command)
+ {
+ switch (command) {
+ case "cmd_selectAll":
+ return true;
+ case "cmd_delete":
+ case "button_delete":
+ var numSelected;
+ var enabled = false;
+ if (gAbView && gAbView.selection) {
+ if (gAbView.directory)
+ enabled = !gAbView.directory.readOnly;
+ numSelected = gAbView.selection.count;
+ }
+ else
+ numSelected = 0;
+
+ // fix me, don't update on isCommandEnabled
+ if (command == "cmd_delete") {
+ switch (GetSelectedCardTypes()) {
+ case kSingleListOnly:
+ goSetMenuValue(command, "valueList");
+ break;
+ case kMultipleListsOnly:
+ goSetMenuValue(command, "valueLists");
+ break;
+ case kListsAndCards:
+ goSetMenuValue(command, "valueItems");
+ break;
+ case kCardsOnly:
+ default:
+ if (numSelected < 2)
+ goSetMenuValue(command, "valueCard");
+ else
+ goSetMenuValue(command, "valueCards");
+ break;
+ }
+ }
+ return (enabled && (numSelected > 0));
+ case "cmd_properties":
+ // Temporary fix for SeaMonkey (see bug 1318852).
+ // goSetLabelAccesskeyTooltiptext() is only defined in mail/.
+ // This will be removed in due course and therefore the
+ // block wasn't indented.
+ if (typeof goSetLabelAccesskeyTooltiptext == "function") {
+ let labelAttr = "valueGeneric";
+ let accKeyAttr = "valueGenericAccessKey";
+ let tooltipTextAttr = "valueGenericTooltipText";
+ switch (GetSelectedCardTypes()) {
+ // Set cmd_properties UI according to the type of the selected item(s),
+ // even with multiple selections for which cmd_properties is
+ // not yet available and hence disabled.
+ case kMultipleListsOnly:
+ case kSingleListOnly:
+ labelAttr = "valueMailingList";
+ accKeyAttr = "valueMailingListAccessKey";
+ tooltipTextAttr = "valueMailingListTooltipText";
+ break;
+ case kCardsOnly:
+ labelAttr = "valueContact";
+ accKeyAttr = "valueContactAccessKey";
+ tooltipTextAttr = "valueContactTooltipText";
+ break;
+ case kListsAndCards:
+ default:
+ //use generic set of attributes declared above
+ break;
+ }
+ // This code is shared between main AB and composition's contacts sidebar.
+ // Note that in composition, there's no cmd_properties-button (yet);
+ // the resulting dump() should be ignored.
+ goSetLabelAccesskeyTooltiptext("cmd_properties-button", null, null,
+ tooltipTextAttr);
+ goSetLabelAccesskeyTooltiptext("cmd_properties-contextMenu",
+ labelAttr, accKeyAttr);
+ goSetLabelAccesskeyTooltiptext("cmd_properties-menu",
+ labelAttr, accKeyAttr);
+ }
+ // While "Edit Contact" dialogue is still modal (bug 115904, bug 135126),
+ // only enable "Properties" button for single selection; then fix bug 119999.
+ return (GetNumSelectedCards() == 1);
+ case "cmd_newlist":
+ case "cmd_newCard":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(command)
+ {
+ switch (command) {
+ case "cmd_selectAll":
+ if (gAbView)
+ gAbView.selectAll();
+ break;
+ case "cmd_delete":
+ case "button_delete":
+ AbDelete();
+ break;
+ case "cmd_properties":
+ AbEditSelectedCard();
+ break;
+ case "cmd_newlist":
+ AbNewList();
+ break;
+ case "cmd_newCard":
+ AbNewCard();
+ break;
+ }
+ },
+
+ onEvent: function(event)
+ {
+ // on blur events set the menu item texts back to the normal values
+ if (event == "blur")
+ goSetMenuValue("cmd_delete", "valueDefault");
+ }
+};
+
+function SelectFirstCard()
+{
+ if (gAbView && gAbView.selection && (gAbView.selection.count > 0))
+ gAbView.selection.select(0);
+}
diff --git a/mailnews/addrbook/content/abResultsPaneOverlay.xul b/mailnews/addrbook/content/abResultsPaneOverlay.xul
new file mode 100644
index 000000000..6e6d9dbc9
--- /dev/null
+++ b/mailnews/addrbook/content/abResultsPaneOverlay.xul
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/addressbook/abResultsPane.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/addressbook/abResultsPaneOverlay.dtd">
+
+<overlay
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script type="application/javascript" src="chrome://messenger/content/addressbook/abResultsPane.js"/>
+<script type="application/javascript" src="chrome://global/content/nsDragAndDrop.js"/>
+<script type="application/javascript" src="chrome://messenger/content/addressbook/abDragDrop.js"/>
+
+<tree id="abResultsTree" flex="1" enableColumnDrag="true" class="plain focusring"
+ onclick="AbResultsPaneOnClick(event);"
+ onselect="this.view.selectionChanged(); document.commandDispatcher.updateCommands('addrbook-select');"
+ sortCol="GeneratedName"
+ persist="sortCol height">
+
+ <treecols id="abResultsTreeCols">
+ <!-- these column ids must match up to the mork column names, except for GeneratedName, see nsIAddrDatabase.idl -->
+ <treecol id="GeneratedName"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&GeneratedName.label;" primary="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="PrimaryEmail"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&PrimaryEmail.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="_AimScreenName"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&ScreenName.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Company"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&Company.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="NickName"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&NickName.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="SecondEmail"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&SecondEmail.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Department"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&Department.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="JobTitle"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&JobTitle.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="CellularNumber"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&CellularNumber.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="PagerNumber"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&PagerNumber.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="FaxNumber"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&FaxNumber.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="HomePhone"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&HomePhone.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="WorkPhone"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&WorkPhone.label;"/>
+
+ <!-- LOCALIZATION NOTE: _PhoneticName may be enabled for Japanese builds. -->
+ <!--
+ <splitter class="tree-splitter"/>
+ <treecol id="_PhoneticName"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&_PhoneticName.label;" hidden="true"/>
+ -->
+
+ </treecols>
+ <treechildren ondragstart="nsDragAndDrop.startDrag(event, abResultsPaneObserver);"/>
+</tree>
+
+</overlay>
+
diff --git a/mailnews/addrbook/content/addrbookWidgets.xml b/mailnews/addrbook/content/addrbookWidgets.xml
new file mode 100644
index 000000000..2481d5cd9
--- /dev/null
+++ b/mailnews/addrbook/content/addrbookWidgets.xml
@@ -0,0 +1,439 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<bindings id="addrbookBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="addrbooks-menupopup"
+ extends="chrome://global/content/bindings/popup.xml#popup">
+ <implementation implements="nsIAbListener">
+ <!-- A cache of nsIAbDirectory objects. -->
+ <field name="_directories">[]</field>
+
+ <!-- Represents the nsIAbDirectory attribute used as the value of the
+ parent menulist. Defaults to URI but can be e.g. dirPrefId -->
+ <field name="_value">this.getAttribute("value") || "URI"</field>
+
+ <constructor>
+ <![CDATA[
+ Components.utils.import("resource:///modules/mailServices.js");
+ // Init the address book cache.
+ const nsIAbDirectory = Components.interfaces.nsIAbDirectory;
+ let directories = MailServices.ab.directories;
+ while (directories && directories.hasMoreElements()) {
+ var ab = directories.getNext();
+ if (ab instanceof nsIAbDirectory && this._matches(ab))
+ this._directories.push(ab);
+ }
+
+ this._directories.sort(this._compare);
+
+ // Now create menuitems for all displayed directories.
+ var menulist = this.parentNode;
+ var value = this._value;
+ this._directories.forEach(function (ab) {
+ menulist.appendItem(ab.dirName, ab[value]);
+ });
+ if (this.hasAttribute("none")) {
+ // Create a dummy menuitem representing no selection.
+ this._directories.unshift(null);
+ menulist.insertItemAt(0, this.getAttribute("none"), "");
+ }
+
+ // Attempt to select the persisted or otherwise first directory.
+ menulist.value = menulist.value;
+ if (!menulist.selectedItem && this.hasChildNodes())
+ menulist.selectedIndex = 0;
+
+ const nsIAbListener = Components.interfaces.nsIAbListener;
+ // Add a listener so we can update correctly if the list should change
+ MailServices.ab.addAddressBookListener(this,
+ nsIAbListener.itemAdded |
+ nsIAbListener.directoryRemoved |
+ nsIAbListener.itemChanged);
+ ]]>
+ </constructor>
+
+ <destructor>
+ <![CDATA[
+ Components.utils.import("resource:///modules/mailServices.js");
+ MailServices.ab.removeAddressBookListener(this);
+
+ // Empty out anything in the list.
+ while (this.hasChildNodes())
+ this.lastChild.remove();
+ ]]>
+ </destructor>
+
+ <!-- nsIAbListener methods -->
+ <method name="onItemAdded">
+ <parameter name="aParentDir"/>
+ <parameter name="aItem"/>
+ <body><![CDATA[
+ // Are we interested in this new directory?
+ if (aItem instanceof Components.interfaces.nsIAbDirectory &&
+ !aItem.isMailList && this._matches(aItem)) {
+ this._directories.push(aItem);
+ this._directories.sort(this._compare);
+ // Insert the new menuitem at the position to which it was sorted.
+ this.parentNode.insertItemAt(this._directories.indexOf(aItem),
+ aItem.dirName, aItem[this._value]);
+ }
+ ]]></body>
+ </method>
+
+ <method name="onItemRemoved">
+ <parameter name="aParentDir"/>
+ <parameter name="aItem"/>
+ <body><![CDATA[
+ if (aItem instanceof Components.interfaces.nsIAbDirectory &&
+ !aItem.isMailList) {
+ // Find the item in the list to remove
+ // We can't use indexOf here because we need loose equality
+ for (var index = this._directories.length; --index >= 0; )
+ if (this._directories[index] == aItem)
+ break;
+ if (index != -1)
+ // Are we removing the selected directory?
+ if (this.parentNode.selectedItem ==
+ this.removeChild(this.childNodes[index]))
+ // If so, try to select the first directory, if available.
+ if (this.hasChildNodes())
+ this.firstChild.doCommand();
+ else
+ this.parentNode.selectedItem = null;
+ }
+ ]]></body>
+ </method>
+
+ <method name="onItemPropertyChanged">
+ <parameter name="aItem"/>
+ <parameter name="aProperty"/>
+ <parameter name="aOldValue"/>
+ <parameter name="aNewValue"/>
+ <body><![CDATA[
+ if (aItem instanceof Components.interfaces.nsIAbDirectory &&
+ !aItem.isMailList) {
+ // Find the item in the list to rename.
+ // We can't use indexOf here because we need loose equality
+ for (var oldIndex = this._directories.length; --oldIndex >= 0; )
+ if (this._directories[oldIndex] == aItem)
+ break;
+ if (oldIndex != -1) {
+ // Cache the matching item so that we can use indexOf next time.
+ aItem = this._directories[oldIndex];
+ var child = this.childNodes[oldIndex];
+ child.label = aItem.dirName;
+ this._directories.sort(this._compare);
+ // Reorder the menuitems if renaming changed the directory index.
+ var newIndex = this._directories.indexOf(aItem);
+ if (newIndex < oldIndex)
+ this.insertBefore(child, this.childNodes[newIndex]);
+ else if (newIndex > oldIndex)
+ this.insertBefore(child, this.childNodes[newIndex].nextSibling);
+ }
+ }
+ ]]></body>
+ </method>
+
+ <!-- Private methods -->
+ <!-- Tests to see whether this directory should display in the list. -->
+ <method name="_matches">
+ <parameter name="ab"/>
+ <body><![CDATA[
+ // This condition is used for instance when creating cards
+ if (this.getAttribute("writable") == "true" && ab.readOnly)
+ return false;
+
+ // This condition is used for instance when creating mailing lists
+ if (this.getAttribute("supportsmaillists") == "true" &&
+ !ab.supportsMailingLists)
+ return false;
+
+ return this.getAttribute(ab.isRemote ? "localonly" : "remoteonly") != "true";
+ ]]></body>
+ </method>
+
+ <!-- Used to sort directories in order -->
+ <method name="_compare">
+ <parameter name="a"/>
+ <parameter name="b"/>
+ <body><![CDATA[
+ // Null at the very top.
+ if (!a)
+ return -1;
+
+ if (!b)
+ return 1;
+
+ // Personal at the top.
+ const kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
+ if (a.URI == kPersonalAddressbookURI)
+ return -1;
+
+ if (b.URI == kPersonalAddressbookURI)
+ return 1;
+
+ // Collected at the bottom.
+ const kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
+ if (a.URI == kCollectedAddressbookURI)
+ return 1;
+
+ if (b.URI == kCollectedAddressbookURI)
+ return -1;
+
+ // Sort books of the same type by name.
+ if (a.dirType == b.dirType)
+ return a.dirName.localeCompare(b.dirName);
+
+ // If one of the dirTypes is PAB and the other is something else,
+ // then the other will go below the one of type PAB.
+ const PABDirectory = 2;
+ if (a.dirType == PABDirectory)
+ return -1;
+
+ if (b.dirType == PABDirectory)
+ return 1;
+
+ // Sort anything else by the dir type.
+ return a.dirType - b.dirType;
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="map-list"
+ extends="chrome://global/content/bindings/popup.xml#popup">
+ <implementation>
+ <property name="mapURL" readonly="true">
+ <getter><![CDATA[
+ return this._createMapItURL();
+ ]]></getter>
+ </property>
+
+ <constructor>
+ <![CDATA[
+ this._setWidgetDisabled(true);
+ ]]>
+ </constructor>
+
+ <!--
+ Initializes the necessary address data from an addressbook card.
+ @param aCard A nsIAbCard to get the address data from.
+ @param aAddrPrefix A prefix of the card properties to use. Use "Home" or "Work".
+ -->
+ <method name="initMapAddressFromCard">
+ <parameter name="aCard"/>
+ <parameter name="aAddrPrefix"/>
+ <body><![CDATA[
+ let mapItURLFormat = this._getMapURLPref(0);
+ let doNotShowMap = !mapItURLFormat || !aAddrPrefix || !aCard;
+ this._setWidgetDisabled(doNotShowMap);
+ if (doNotShowMap)
+ return;
+
+ this.setAttribute("map_address1", aCard.getProperty(aAddrPrefix + "Address"));
+ this.setAttribute("map_address2", aCard.getProperty(aAddrPrefix + "Address2"));
+ this.setAttribute("map_city" , aCard.getProperty(aAddrPrefix + "City"));
+ this.setAttribute("map_state" , aCard.getProperty(aAddrPrefix + "State"));
+ this.setAttribute("map_zip" , aCard.getProperty(aAddrPrefix + "ZipCode"));
+ this.setAttribute("map_country" , aCard.getProperty(aAddrPrefix + "Country"));
+ ]]></body>
+ </method>
+
+ <!--
+ Initializes the necessary address data from passed in values.
+ -->
+ <method name="initMapAddress">
+ <parameter name="aAddr1"/>
+ <parameter name="aAddr2"/>
+ <parameter name="aCity"/>
+ <parameter name="aState"/>
+ <parameter name="aZip"/>
+ <parameter name="aCountry"/>
+ <body><![CDATA[
+ let mapItURLFormat = this._getMapURLPref(0);
+ let doNotShowMap = !mapItURLFormat || !(aAddr1 + aAddr2 + aCity + aState + aZip + aCountry);
+ this._setWidgetDisabled(doNotShowMap);
+ if (doNotShowMap)
+ return;
+
+ this.setAttribute("map_address1", aAddr1);
+ this.setAttribute("map_address2", aAddr2);
+ this.setAttribute("map_city" , aCity);
+ this.setAttribute("map_state" , aState);
+ this.setAttribute("map_zip" , aZip);
+ this.setAttribute("map_country" , aCountry);
+ ]]></body>
+ </method>
+
+ <!--
+ Sets the disabled/enabled state of the parent widget (e.g. a button).
+ -->
+ <method name="_setWidgetDisabled">
+ <parameter name="aDisabled"/>
+ <body><![CDATA[
+ this.parentNode.disabled = aDisabled;
+ ]]></body>
+ </method>
+
+ <!--
+ Returns the Map service URL from localized pref. Returns null if there
+ is none at the given index.
+ @param aIndex The index of the service to return. 0 is the default service.
+ -->
+ <method name="_getMapURLPref">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ let url = null;
+ if (!aIndex) {
+ url = Services.prefs.getComplexValue("mail.addr_book.mapit_url.format",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ } else {
+ try {
+ url = Services.prefs.getComplexValue("mail.addr_book.mapit_url." + aIndex + ".format",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ } catch (e) { }
+ }
+
+ return url;
+ ]]></body>
+ </method>
+
+ <!--
+ Builds menuitem elements representing map services defined in prefs
+ and attaches them to the specified button.
+ -->
+ <method name="_listMapServices">
+ <body><![CDATA[
+ let index = 1;
+ let itemFound = true;
+ let defaultFound = false;
+ const kUserIndex = 100;
+ let aMapList = this;
+ while (aMapList.hasChildNodes()) {
+ aMapList.lastChild.remove();
+ }
+
+ let defaultUrl = this._getMapURLPref(0);
+
+ // Creates the menuitem with supplied data.
+ function addMapService(aUrl, aName) {
+ let item = document.createElement("menuitem");
+ item.setAttribute("url", aUrl);
+ item.setAttribute("label", aName);
+ item.setAttribute("type", "radio");
+ item.setAttribute("name", "mapit_service");
+ if (aUrl == defaultUrl)
+ item.setAttribute("checked", "true");
+ aMapList.appendChild(item);
+ }
+
+ // Generates a useful generic name by cutting out only the host address.
+ function generateName(aUrl) {
+ return new URL(aUrl).hostname;
+ }
+
+ // Add all defined map services as menuitems.
+ while (itemFound) {
+ let urlName;
+ let urlTemplate = this._getMapURLPref(index);
+ if (!urlTemplate) {
+ itemFound = false;
+ } else {
+ // Name is not mandatory, generate one if not found.
+ try {
+ urlName = Services.prefs.getComplexValue("mail.addr_book.mapit_url." + index + ".name",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ } catch (e) {
+ urlName = generateName(urlTemplate);
+ }
+ }
+ if (itemFound) {
+ addMapService(urlTemplate, urlName);
+ index++;
+ if (urlTemplate == defaultUrl)
+ defaultFound = true;
+ } else if (index < kUserIndex) {
+ // After iterating the base region provided urls, check for user defined ones.
+ index = kUserIndex;
+ itemFound = true;
+ }
+ }
+ if (!defaultFound) {
+ // If user had put a customized map URL into mail.addr_book.mapit_url.format
+ // preserve it as a new map service named with the URL.
+ // 'index' now points to the first unused entry in prefs.
+ let defaultName = generateName(defaultUrl);
+ addMapService(defaultUrl, defaultName);
+ Services.prefs.setCharPref("mail.addr_book.mapit_url." + index + ".format",
+ defaultUrl);
+ Services.prefs.setCharPref("mail.addr_book.mapit_url." + index + ".name",
+ defaultName);
+ }
+ ]]></body>
+ </method>
+
+ <!--
+ Save user selected mapping service.
+ @param aItem The chosen menuitem with map service.
+ -->
+ <method name="_chooseMapService">
+ <parameter name="aItem"/>
+ <body><![CDATA[
+ // Save selected URL as the default.
+ let defaultUrl = Components.classes["@mozilla.org/pref-localizedstring;1"]
+ .createInstance(Components.interfaces.nsIPrefLocalizedString);
+ defaultUrl.data = aItem.getAttribute("url");
+ Services.prefs.setComplexValue("mail.addr_book.mapit_url.format",
+ Components.interfaces.nsIPrefLocalizedString, defaultUrl);
+ ]]></body>
+ </method>
+
+ <!--
+ Generate map URL in the href attribute.
+ -->
+ <method name="_createMapItURL">
+ <body><![CDATA[
+ let urlFormat = this._getMapURLPref(0);
+ if (!urlFormat)
+ return null;
+
+ let address1 = this.getAttribute("map_address1");
+ let address2 = this.getAttribute("map_address2");
+ let city = this.getAttribute("map_city");
+ let state = this.getAttribute("map_state");
+ let zip = this.getAttribute("map_zip");
+ let country = this.getAttribute("map_country");
+
+ urlFormat = urlFormat.replace("@A1", encodeURIComponent(address1));
+ urlFormat = urlFormat.replace("@A2", encodeURIComponent(address2));
+ urlFormat = urlFormat.replace("@CI", encodeURIComponent(city));
+ urlFormat = urlFormat.replace("@ST", encodeURIComponent(state));
+ urlFormat = urlFormat.replace("@ZI", encodeURIComponent(zip));
+ urlFormat = urlFormat.replace("@CO", encodeURIComponent(country));
+
+ return urlFormat;
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="command">
+ <![CDATA[
+ this._chooseMapService(event.target);
+ event.stopPropagation();
+ ]]>
+ </handler>
+ <handler event="popupshowing">
+ <![CDATA[
+ this._listMapServices();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+</bindings>
diff --git a/mailnews/addrbook/content/print.css b/mailnews/addrbook/content/print.css
new file mode 100644
index 000000000..0f6965379
--- /dev/null
+++ b/mailnews/addrbook/content/print.css
@@ -0,0 +1,94 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+directory {
+ display: block;
+}
+
+section {
+ display:block;
+ margin: 15px 20px;
+}
+
+sectiontitle {
+ display:block;
+ font-weight:bold;
+ font-family:verdana;
+ font-size:small;
+}
+
+labelrow {
+ display:block;
+}
+
+label {
+ display:inline;
+ font-family:verdana;
+ font-size:small;
+}
+
+GeneratedName {
+ display:block;
+ font-weight:bold;
+ font-family:verdana;
+ margin-top: 20px;
+ margin-bottom: 3px;
+ margin-inline-end: 10px;
+ margin-inline-start: 10px;
+}
+
+FirstName, LastName,
+HomeAddress, HomeAddress2, HomeCountry,
+WorkAddress, WorkAddress2, WorkCountry,
+JobTitle, Department, Company, Notes {
+ display: block;
+ font-family: verdana;
+ font-size: small;
+}
+
+DisplayName, NickName,
+WorkPhone, HomePhone, FaxNumber, PagerNumber, CellularNumber,
+HomeCity, HomeState, HomeZipCode,
+WorkCity, WorkState, WorkZipCode,
+Custom1, Custom2, Custom3, Custom4 {
+ display: inline;
+ font-family: verdana;
+ font-size: small;
+}
+
+PrimaryEmail, SecondEmail,
+WebPage1, WebPage2 {
+ display: block;
+ font-family: verdana;
+ font-size: small;
+ text-decoration: underline;
+ color: #666666;
+}
+
+separator {
+ display: block;
+ border-bottom: 1px solid #000000;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-end: 0px;
+ margin-inline-start: 10px;
+}
+
+table {
+ display: table;
+}
+
+tr {
+ display: table-row;
+}
+
+td {
+ display: table-cell;
+ vertical-align: top;
+}
+
+PreferMailFormat {
+ display: none;
+}
+
diff --git a/mailnews/addrbook/moz.build b/mailnews/addrbook/moz.build
new file mode 100644
index 000000000..27dcb8746
--- /dev/null
+++ b/mailnews/addrbook/moz.build
@@ -0,0 +1,9 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ 'public',
+ 'src',
+]
diff --git a/mailnews/addrbook/prefs/content/pref-directory-add.js b/mailnews/addrbook/prefs/content/pref-directory-add.js
new file mode 100644
index 000000000..011b8aed9
--- /dev/null
+++ b/mailnews/addrbook/prefs/content/pref-directory-add.js
@@ -0,0 +1,394 @@
+/* -*- Mode: Java; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/mailServices.js");
+Components.utils.import("resource:///modules/hostnameUtils.jsm");
+
+var gCurrentDirectory = null;
+var gReplicationBundle = null;
+var gReplicationService =
+ Components.classes["@mozilla.org/addressbook/ldap-replication-service;1"].
+ getService(Components.interfaces.nsIAbLDAPReplicationService);
+var gReplicationCancelled = false;
+var gProgressText;
+var gProgressMeter;
+var gDownloadInProgress = false;
+
+var kDefaultMaxHits = 100;
+var kDefaultLDAPPort = 389;
+var kDefaultSecureLDAPPort = 636;
+var kLDAPDirectory = 0; // defined in nsDirPrefs.h
+
+var ldapOfflineObserver = {
+ observe: function(subject, topic, state)
+ {
+ // sanity checks
+ if (topic != "network:offline-status-changed") return;
+ setDownloadOfflineOnlineState(state == "offline");
+ }
+}
+
+function Startup()
+{
+ gReplicationBundle = document.getElementById("bundle_replication");
+
+ document.getElementById("download").label =
+ gReplicationBundle.getString("downloadButton");
+ document.getElementById("download").accessKey =
+ gReplicationBundle.getString("downloadButton.accesskey");
+
+ if ("arguments" in window && window.arguments[0]) {
+ gCurrentDirectory = window.arguments[0].selectedDirectory;
+ try {
+ fillSettings();
+ } catch (ex) {
+ dump("pref-directory-add.js:Startup(): fillSettings() exception: "
+ + ex + "\n");
+ }
+
+ let oldListName = gCurrentDirectory.dirName;
+ document.title = gReplicationBundle.getFormattedString("directoryTitleEdit", [oldListName]);
+
+ // Only set up the download button for online/offline status toggling
+ // if the pref isn't locked to disable the button.
+ if (!Services.prefs.prefIsLocked(gCurrentDirectory.dirPrefId +
+ ".disable_button_download")) {
+ // Now connect to the offline/online observer
+ Services.obs.addObserver(ldapOfflineObserver,
+ "network:offline-status-changed", false);
+
+ // Now set the initial offline/online state and update the state
+ setDownloadOfflineOnlineState(Services.io.offline);
+ }
+ } else {
+ document.title = gReplicationBundle.getString("directoryTitleNew");
+ fillDefaultSettings();
+ // Don't add observer here as it doesn't make any sense.
+ }
+}
+
+function onUnload()
+{
+ if ("arguments" in window &&
+ window.arguments[0] &&
+ !Services.prefs.prefIsLocked(gCurrentDirectory.dirPrefId +
+ ".disable_button_download")) {
+ // Remove the observer that we put in on dialog startup
+ Services.obs.removeObserver(ldapOfflineObserver,
+ "network:offline-status-changed");
+ }
+}
+
+var progressListener = {
+ onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus)
+ {
+ if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_START) {
+ // start the spinning
+ gProgressMeter.setAttribute("mode", "undetermined");
+ gProgressText.value = gReplicationBundle.getString(aStatus ?
+ "replicationStarted" :
+ "changesStarted");
+ gDownloadInProgress = true;
+ document.getElementById("download").label =
+ gReplicationBundle.getString("cancelDownloadButton");
+ document.getElementById("download").accessKey =
+ gReplicationBundle.getString("cancelDownloadButton.accesskey");
+ }
+
+ if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) {
+ EndDownload(aStatus);
+ }
+ },
+ onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress)
+ {
+ gProgressText.value = gReplicationBundle.getFormattedString("currentCount",
+ [aCurSelfProgress]);
+ },
+ onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags)
+ {
+ },
+ onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage)
+ {
+ },
+ onSecurityChange: function(aWebProgress, aRequest, state)
+ {
+ },
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Components.interfaces.nsIWebProgressListener) ||
+ iid.equals(Components.interfaces.nsISupportsWeakReference) ||
+ iid.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ }
+};
+
+function DownloadNow()
+{
+ if (!gDownloadInProgress) {
+ gProgressText = document.getElementById("replicationProgressText");
+ gProgressMeter = document.getElementById("replicationProgressMeter");
+
+ gProgressText.hidden = false;
+ gProgressMeter.hidden = false;
+ gReplicationCancelled = false;
+
+ try {
+ if (gCurrentDirectory instanceof Components.interfaces.nsIAbLDAPDirectory)
+ gReplicationService.startReplication(gCurrentDirectory,
+ progressListener);
+ else
+ EndDownload(false);
+ }
+ catch (ex) {
+ EndDownload(false);
+ }
+ } else {
+ gReplicationCancelled = true;
+ try {
+ gReplicationService.cancelReplication(gCurrentDirectory.dirPrefId);
+ }
+ catch (ex) {
+ // XXX todo
+ // perhaps replication hasn't started yet? This can happen if you hit cancel after attempting to replication when offline
+ dump("unexpected failure while cancelling. ex=" + ex + "\n");
+ }
+ }
+}
+
+function EndDownload(aStatus)
+{
+ document.getElementById("download").label =
+ gReplicationBundle.getString("downloadButton");
+ document.getElementById("download").accessKey =
+ gReplicationBundle.getString("downloadButton.accesskey");
+
+ // stop the spinning
+ gProgressMeter.setAttribute("mode", "normal");
+ gProgressMeter.setAttribute("value", "100");
+ gProgressMeter.hidden = true;
+
+ gDownloadInProgress = false;
+ gProgressText.value =
+ gReplicationBundle.getString(aStatus ? "replicationSucceeded" :
+ gReplicationCancelled ? "replicationCancelled" :
+ "replicationFailed");
+}
+
+// fill the settings panel with the data from the preferences.
+//
+function fillSettings()
+{
+ document.getElementById("description").value = gCurrentDirectory.dirName;
+
+ if (gCurrentDirectory instanceof Components.interfaces.nsIAbLDAPDirectory) {
+ var ldapUrl = gCurrentDirectory.lDAPURL;
+
+ document.getElementById("results").value = gCurrentDirectory.maxHits;
+ document.getElementById("login").value = gCurrentDirectory.authDn;
+ document.getElementById("hostname").value = ldapUrl.host;
+ document.getElementById("basedn").value = ldapUrl.dn;
+ document.getElementById("search").value = ldapUrl.filter;
+
+ var sub = document.getElementById("sub");
+ switch(ldapUrl.scope) {
+ case Components.interfaces.nsILDAPURL.SCOPE_ONELEVEL:
+ sub.radioGroup.selectedItem = document.getElementById("one");
+ break;
+ default:
+ sub.radioGroup.selectedItem = sub;
+ break;
+ }
+
+ var sasl = document.getElementById("saslMechanism");
+ switch (gCurrentDirectory.saslMechanism) {
+ case "GSSAPI":
+ sasl.selectedItem = document.getElementById("GSSAPI");
+ break;
+ default:
+ sasl.selectedItem = document.getElementById("Simple");
+ break;
+ }
+
+ var secure = ldapUrl.options & ldapUrl.OPT_SECURE
+ if (secure)
+ document.getElementById("secure").setAttribute("checked", "true");
+
+ if (ldapUrl.port == -1)
+ document.getElementById("port").value =
+ (secure ? kDefaultSecureLDAPPort : kDefaultLDAPPort);
+ else
+ document.getElementById("port").value = ldapUrl.port;
+ }
+
+ // check if any of the preferences for this server are locked.
+ //If they are locked disable them
+ DisableUriFields(gCurrentDirectory.dirPrefId + ".uri");
+ DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".description", "description");
+ DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".disable_button_download", "download");
+ DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".maxHits", "results");
+ DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".auth.dn", "login");
+}
+
+function DisableElementIfPrefIsLocked(aPrefName, aElementId)
+{
+ if (Services.prefs.prefIsLocked(aPrefName))
+ document.getElementById(aElementId).setAttribute('disabled', true);
+}
+
+// disables all the text fields corresponding to the .uri pref.
+function DisableUriFields(aPrefName)
+{
+ if (Services.prefs.prefIsLocked(aPrefName)) {
+ let lockedElements = document.querySelectorAll('[disableiflocked="true"]');
+ for (let i = 0; i < lockedElements.length; i++)
+ lockedElements[i].setAttribute('disabled', 'true');
+ }
+}
+
+function onSecure()
+{
+ document.getElementById("port").value =
+ document.getElementById("secure").checked ? kDefaultSecureLDAPPort :
+ kDefaultLDAPPort;
+}
+
+function fillDefaultSettings()
+{
+ document.getElementById("port").value = kDefaultLDAPPort;
+ document.getElementById("results").value = kDefaultMaxHits;
+ var sub = document.getElementById("sub");
+ sub.radioGroup.selectedItem = sub;
+
+ // Disable the download button and add some text indicating why.
+ document.getElementById("download").disabled = true;
+ document.getElementById("downloadWarningMsg").hidden = false;
+ document.getElementById("downloadWarningMsg").textContent = document.
+ getElementById("bundle_addressBook").
+ getString("abReplicationSaveSettings");
+}
+
+function hasCharacters(number)
+{
+ var re = /[0-9]/g;
+ var num = number.match(re);
+ if(num && (num.length == number.length))
+ return false;
+ else
+ return true;
+}
+
+function onAccept()
+{
+ try {
+ var pref_string_content = "";
+ var pref_string_title = "";
+
+ var description = document.getElementById("description").value;
+ var hostname = cleanUpHostName(document.getElementById("hostname").value);
+ var port = document.getElementById("port").value;
+ var secure = document.getElementById("secure");
+ var results = document.getElementById("results").value;
+ var errorValue = null;
+ var saslMechanism = "";
+ if ((!description) || (description.trim() == ""))
+ errorValue = "invalidName";
+ else if (!isLegalHostNameOrIP(hostname))
+ errorValue = "invalidHostname";
+ // XXX write isValidDn and call it on the dn string here?
+ else if (port && hasCharacters(port))
+ errorValue = "invalidPortNumber";
+ else if (results && hasCharacters(results))
+ errorValue = "invalidResults";
+ if (!errorValue) {
+ // XXX Due to the LDAP c-sdk pass a dummy url to the IO service, then
+ // update the parts (bug 473351).
+ let ldapUrl = Services.io.newURI(
+ (secure.checked ? "ldaps://" : "ldap://") + "localhost/dc=???", null, null)
+ .QueryInterface(Components.interfaces.nsILDAPURL);
+
+ ldapUrl.host = hostname;
+ ldapUrl.port = port ? port :
+ (secure.checked ? kDefaultSecureLDAPPort :
+ kDefaultLDAPPort);
+ ldapUrl.dn = document.getElementById("basedn").value;
+ ldapUrl.scope = document.getElementById("one").selected ?
+ Components.interfaces.nsILDAPURL.SCOPE_ONELEVEL :
+ Components.interfaces.nsILDAPURL.SCOPE_SUBTREE;
+
+ ldapUrl.filter = document.getElementById("search").value;
+ if (document.getElementById("GSSAPI").selected) {
+ saslMechanism = "GSSAPI";
+ }
+
+ // check if we are modifying an existing directory or adding a new directory
+ if (gCurrentDirectory) {
+ gCurrentDirectory.dirName = description;
+ gCurrentDirectory.lDAPURL = ldapUrl.QueryInterface(Components.interfaces.nsILDAPURL);
+ window.opener.gNewServerString = gCurrentDirectory.dirPrefId;
+ }
+ else { // adding a new directory
+ window.opener.gNewServerString =
+ MailServices.ab.newAddressBook(description, ldapUrl.spec, kLDAPDirectory);
+ }
+
+ // XXX This is really annoying - both new/modify Address Book don't
+ // give us back the new directory we just created - so go find it from
+ // rdf so we can set a few final things up on it.
+ var targetURI = "moz-abldapdirectory://" + window.opener.gNewServerString;
+ var theDirectory =
+ MailServices.ab.getDirectory(targetURI)
+ .QueryInterface(Components.interfaces.nsIAbLDAPDirectory);
+
+ theDirectory.maxHits = results;
+ theDirectory.authDn = document.getElementById("login").value;
+ theDirectory.saslMechanism = saslMechanism;
+
+ window.opener.gNewServer = description;
+ // set window.opener.gUpdate to true so that LDAP Directory Servers
+ // dialog gets updated
+ window.opener.gUpdate = true;
+ } else {
+ var addressBookBundle = document.getElementById("bundle_addressBook");
+
+ Services.prompt.alert(window,
+ document.title,
+ addressBookBundle.getString(errorValue));
+ return false;
+ }
+ } catch (outer) {
+ dump("Internal error in pref-directory-add.js:onAccept() " + outer + "\n");
+ }
+ return true;
+}
+
+function onCancel()
+{
+ window.opener.gUpdate = false;
+}
+
+
+// called by Help button in platform overlay
+function doHelpButton()
+{
+ openHelp("mail-ldap-properties");
+}
+
+// Sets the download button state for offline or online.
+// This function should only be called for ldap edit dialogs.
+function setDownloadOfflineOnlineState(isOffline)
+{
+ if (isOffline)
+ {
+ // Disable the download button and add some text indicating why.
+ document.getElementById("downloadWarningMsg").textContent = document.
+ getElementById("bundle_addressBook").
+ getString("abReplicationOfflineWarning");
+ }
+ document.getElementById("downloadWarningMsg").hidden = !isOffline;
+ document.getElementById("download").disabled = isOffline;
+}
diff --git a/mailnews/addrbook/prefs/content/pref-directory-add.xul b/mailnews/addrbook/prefs/content/pref-directory-add.xul
new file mode 100644
index 000000000..33fe0cd31
--- /dev/null
+++ b/mailnews/addrbook/prefs/content/pref-directory-add.xul
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/pref-directory-add.dtd">
+
+<dialog id="addDirectory"
+ style="width: &newDirectoryWidth;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="Startup();"
+ onunload="onUnload();"
+ buttons="accept,cancel"
+ ondialogaccept="return onAccept();"
+ ondialogcancel="return onCancel();">
+
+ <script type="application/javascript" src="chrome://messenger/content/addressbook/pref-directory-add.js"/>
+ <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+ <stringbundle id="bundle_replication" src="chrome://messenger/locale/addressbook/replicationProgress.properties"/>
+
+ <keyset id="keyset"/>
+ <vbox id="editDirectory">
+
+ <tabbox style="margin:5px">
+ <tabs id="directoryTabBox">
+ <tab label="&General.tab;"/>
+ <tab label="&Offline.tab;"/>
+ <tab label="&Advanced.tab;"/>
+ </tabs>
+
+ <tabpanels id="directoryTabPanels" flex="1">
+ <vbox>
+ <grid flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ <column/>
+ </columns>
+
+ <rows>
+ <row align="center">
+ <label value="&directoryName.label;" accesskey="&directoryName.accesskey;"
+ control="description"/>
+ <textbox id="description" flex="1"/>
+ <spacer flex="1"/>
+ </row>
+ <row align="center">
+ <label value="&directoryHostname.label;" accesskey="&directoryHostname.accesskey;"
+ control="hostname"/>
+ <textbox id="hostname" flex="1" disableiflocked="true" class="uri-element"/>
+ <spacer flex="1"/>
+ </row>
+ <row align="center">
+ <label value="&directoryBaseDN.label;"
+ accesskey="&directoryBaseDN.accesskey;"
+ control="basedn"/>
+ <vbox>
+ <textbox id="basedn" disableiflocked="true" class="uri-element"/>
+ </vbox>
+ <button label="&findButton.label;"
+ accesskey="&findButton.accesskey;" disabled="true"/>
+ </row>
+ <row align="center">
+ <label value="&portNumber.label;"
+ accesskey="&portNumber.accesskey;"
+ control="port"/>
+ <hbox>
+ <textbox id="port" type="number" size="5" min="1"
+ max="65535" hidespinbuttons="true"
+ disableiflocked="true"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label value="&directoryLogin.label;"
+ accesskey="&directoryLogin.accesskey;"
+ control="login"/>
+ <textbox id="login" flex="1" class="uri-element"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ <checkbox id="secure" label="&directorySecure.label;"
+ accesskey="&directorySecure.accesskey;"
+ oncommand="onSecure();" disableiflocked="true"/>
+ </vbox>
+ <vbox>
+ <description>&offlineText.label;</description>
+ <separator/>
+ <hbox>
+ <button id="download" oncommand="DownloadNow();"/>
+ <spacer flex="1"/>
+ </hbox>
+ <description id="downloadWarningMsg" hidden="true" class="error"/>
+ <description id="replicationProgressText" hidden="true"/>
+
+ <progressmeter id="replicationProgressMeter" mode="normal" value="0" hidden="true"/>
+ </vbox>
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+
+ <rows>
+ <row align="center">
+ <label value="&return.label;"
+ accesskey="&return.accesskey;"
+ control="results"/>
+ <hbox align="center">
+ <textbox id="results" type="number" size="10" min="1"
+ max="2147483647" increment="10"/>
+ <label value="&results.label;"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label value="&scope.label;" control="scope" accesskey="&scope.accesskey;"/>
+ <radiogroup id="scope" orient="horizontal">
+ <radio id="one" value="1" label="&scopeOneLevel.label;"
+ disableiflocked="true" accesskey="&scopeOneLevel.accesskey;"/>
+ <radio id="sub" value="2" label="&scopeSubtree.label;"
+ disableiflocked="true" accesskey="&scopeSubtree.accesskey;"/>
+ </radiogroup>
+ </row>
+ <row>
+ <label value="&searchFilter.label;"
+ accesskey="&searchFilter.accesskey;"
+ control="search"/>
+ <textbox id="search" multiline="true" flex="1" disableiflocked="true"/>
+ </row>
+ <row align="center">
+ <label value="&saslMechanism.label;" control="saslMechanism" accesskey="&saslMechanism.accesskey;"/>
+ <menulist id="saslMechanism">
+ <menupopup>
+ <menuitem id="Simple" value=""
+ label="&saslOff.label;"
+ accesskey="&saslOff.accesskey;"/>
+ <menuitem id="GSSAPI" value="GSSAPI"
+ label="&saslGSSAPI.label;"
+ accesskey="&saslGSSAPI.accesskey;"/>
+ </menupopup>
+ </menulist>
+ </row>
+ </rows>
+ </grid>
+ </tabpanels>
+ </tabbox>
+ </vbox>
+
+</dialog>
+
diff --git a/mailnews/addrbook/prefs/content/pref-editdirectories.js b/mailnews/addrbook/prefs/content/pref-editdirectories.js
new file mode 100644
index 000000000..dfb3e0559
--- /dev/null
+++ b/mailnews/addrbook/prefs/content/pref-editdirectories.js
@@ -0,0 +1,142 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/mailServices.js");
+
+// Listener to refresh the list items if something changes. In all these
+// cases we just rebuild the list as it is easier than searching/adding in the
+// correct places an would be an infrequent operation.
+var gAddressBookAbListener = {
+ onItemAdded: function(parentDir, item) {
+ if (item instanceof Components.interfaces.nsIAbDirectory) {
+ fillDirectoryList();
+ }
+ },
+ onItemRemoved: function(parentDir, item) {
+ if (item instanceof Components.interfaces.nsIAbDirectory) {
+ fillDirectoryList();
+ }
+ },
+ onItemPropertyChanged: function(item, property, oldValue, newValue) {
+ if (item instanceof Components.interfaces.nsIAbDirectory) {
+ fillDirectoryList();
+ }
+ }
+};
+
+function onInitEditDirectories()
+{
+ // For AbDeleteDirectory in abCommon.js
+ gAddressBookBundle = document.getElementById("bundle_addressBook");
+
+ // If the pref is locked disable the "Add" button
+ if (Services.prefs.prefIsLocked("ldap_2.disable_button_add"))
+ document.getElementById("addButton").setAttribute('disabled', true);
+
+ // Fill out the directory list
+ fillDirectoryList();
+
+ const nsIAbListener = Components.interfaces.nsIAbListener;
+ // Add a listener so we can update correctly if the list should change
+ MailServices.ab.addAddressBookListener(gAddressBookAbListener,
+ nsIAbListener.itemAdded |
+ nsIAbListener.directoryRemoved |
+ nsIAbListener.itemChanged);
+}
+
+function onUninitEditDirectories()
+{
+ MailServices.ab.removeAddressBookListener(gAddressBookAbListener);
+}
+
+function fillDirectoryList()
+{
+ var abList = document.getElementById("directoriesList");
+
+ // Empty out anything in the list
+ while (abList.hasChildNodes())
+ abList.lastChild.remove();
+
+ // Init the address book list
+ let directories = MailServices.ab.directories;
+ let holdingArray = [];
+ while (directories && directories.hasMoreElements()) {
+ let ab = directories.getNext();
+ if (ab instanceof Components.interfaces.nsIAbDirectory && ab.isRemote)
+ holdingArray.push(ab);
+ }
+
+ holdingArray.sort(function (a, b) { return a.dirName.localeCompare(b.dirName); });
+
+ holdingArray.forEach(function (ab) {
+ var item = document.createElement('listitem');
+ item.setAttribute("label", ab.dirName);
+ item.setAttribute("value", ab.URI);
+
+ abList.appendChild(item);
+ });
+}
+
+function selectDirectory()
+{
+ var abList = document.getElementById("directoriesList");
+ var editButton = document.getElementById("editButton");
+ var removeButton = document.getElementById("removeButton");
+
+ if (abList && abList.selectedItem) {
+ editButton.removeAttribute("disabled");
+
+ // If the disable delete button pref for the selected directory is set,
+ // disable the delete button for that directory.
+ let disable = false;
+ let ab = MailServices.ab.getDirectory(abList.value);
+ try {
+ disable = Services.prefs.getBoolPref(ab.dirPrefId + ".disable_delete");
+ }
+ catch(ex){
+ // If this preference is not set, it's ok.
+ }
+ if (disable)
+ removeButton.setAttribute("disabled", true);
+ else
+ removeButton.removeAttribute("disabled");
+ }
+ else {
+ editButton.setAttribute("disabled", true);
+ removeButton.setAttribute("disabled", true);
+ }
+}
+
+function dblClickDirectory(event)
+{
+ // We only care about left click events.
+ if (event.button != 0)
+ return;
+
+ editDirectory();
+}
+
+function editDirectory()
+{
+ var abList = document.getElementById("directoriesList");
+
+ if (abList && abList.selectedItem) {
+ let abURI = abList.value;
+ let ab = MailServices.ab.getDirectory(abURI);
+
+ window.openDialog(ab.propertiesChromeURI, "editDirectory",
+ "chrome,modal=yes,resizable=no",
+ { selectedDirectory: ab });
+ }
+}
+
+function removeDirectory()
+{
+ var abList = document.getElementById("directoriesList");
+
+ if (abList && abList.selectedItem)
+ AbDeleteDirectory(abList.value);
+}
diff --git a/mailnews/addrbook/prefs/content/pref-editdirectories.xul b/mailnews/addrbook/prefs/content/pref-editdirectories.xul
new file mode 100644
index 000000000..43ca17a95
--- /dev/null
+++ b/mailnews/addrbook/prefs/content/pref-editdirectories.xul
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/pref-directory.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ id="editDirectories"
+ title="&pref.ldap.window.title;"
+ buttons="accept"
+ onload="onInitEditDirectories();"
+ onunload="onUninitEditDirectories();">
+
+ <script type="application/javascript"
+ src="chrome://messenger/content/addressbook/abCommon.js"/>
+ <script type="application/javascript"
+ src="chrome://messenger/content/addressbook/pref-editdirectories.js"/>
+
+ <stringbundle id="bundle_addressBook"
+ src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+
+ <label value="&directoriesText.label;"
+ accesskey="&directoriesText.accesskey;" control="directoriesList"/>
+ <hbox flex="1">
+ <listbox id="directoriesList" flex="1" onselect="selectDirectory();"
+ ondblclick="dblClickDirectory(event);"/>
+ <vbox>
+ <button id="addButton" label="&addDirectory.label;"
+ accesskey="&addDirectory.accesskey;"
+ oncommand="AbNewLDAPDirectory();"/>
+ <button id="editButton" label="&editDirectory.label;"
+ accesskey="&editDirectory.accesskey;" disabled="true"
+ oncommand="editDirectory();"/>
+ <button id="removeButton" label="&deleteDirectory.label;"
+ accesskey="&deleteDirectory.accesskey;" disabled="true"
+ oncommand="removeDirectory();"/>
+ </vbox>
+ </hbox>
+</dialog>
diff --git a/mailnews/addrbook/public/moz.build b/mailnews/addrbook/public/moz.build
new file mode 100644
index 000000000..5925ccc8a
--- /dev/null
+++ b/mailnews/addrbook/public/moz.build
@@ -0,0 +1,47 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIAbAddressCollector.idl',
+ 'nsIAbAutoCompleteResult.idl',
+ 'nsIAbBooleanExpression.idl',
+ 'nsIAbCard.idl',
+ 'nsIAbCollection.idl',
+ 'nsIAbDirectory.idl',
+ 'nsIAbDirectoryQuery.idl',
+ 'nsIAbDirectoryQueryProxy.idl',
+ 'nsIAbDirectorySearch.idl',
+ 'nsIAbDirFactory.idl',
+ 'nsIAbDirFactoryService.idl',
+ 'nsIAbDirSearchListener.idl',
+ 'nsIAbItem.idl',
+ 'nsIAbLDAPAttributeMap.idl',
+ 'nsIAbLDIFService.idl',
+ 'nsIAbListener.idl',
+ 'nsIAbManager.idl',
+ 'nsIAbMDBDirectory.idl',
+ 'nsIAbView.idl',
+ 'nsIAddbookUrl.idl',
+ 'nsIAddrDatabase.idl',
+ 'nsIAddrDBAnnouncer.idl',
+ 'nsIAddrDBListener.idl',
+ 'nsIMsgVCardService.idl',
+]
+
+if CONFIG['MOZ_LDAP_XPCOM']:
+ XPIDL_SOURCES += [
+ 'nsIAbLDAPCard.idl',
+ 'nsIAbLDAPDirectory.idl',
+ 'nsIAbLDAPReplicationData.idl',
+ 'nsIAbLDAPReplicationQuery.idl',
+ 'nsIAbLDAPReplicationService.idl',
+ ]
+
+XPIDL_MODULE = 'addrbook'
+
+EXPORTS += [
+ 'nsAbBaseCID.h',
+]
+
diff --git a/mailnews/addrbook/public/nsAbBaseCID.h b/mailnews/addrbook/public/nsAbBaseCID.h
new file mode 100644
index 000000000..0dcc630c5
--- /dev/null
+++ b/mailnews/addrbook/public/nsAbBaseCID.h
@@ -0,0 +1,445 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbBaseCID_h__
+#define nsAbBaseCID_h__
+
+#include "nsISupports.h"
+#include "nsIFactory.h"
+#include "nsIComponentManager.h"
+
+//
+// The start of the contract ID for address book directory factories.
+//
+#define NS_AB_DIRECTORY_FACTORY_CONTRACTID_PREFIX \
+ "@mozilla.org/addressbook/directory-factory;1?name="
+
+//
+// The start of the contract ID for address book directory types
+//
+#define NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX \
+ "@mozilla.org/addressbook/directory;1?type="
+
+//
+// nsAbManager
+//
+#define NS_ABMANAGER_CONTRACTID \
+ "@mozilla.org/abmanager;1"
+
+#define NS_ABMANAGERSTARTUPHANDLER_CONTRACTID \
+ "@mozilla.org/commandlinehandler/general-startup;1?type=addressbook"
+
+#define NS_ABMANAGER_CID \
+{ /* {ad81b321-8a8a-42ca-a508-fe659de84586} */ \
+ 0xad81b321, 0x8a8a, 0x42ca, \
+ {0xa5, 0x08, 0xfe, 0x65, 0x9d, 0x8e, 0x45, 0x86} \
+}
+
+//
+// nsAbContentHandler
+//
+#define NS_ABCONTENTHANDLER_CID \
+{ /* {a72ad552-0484-4b5f-8d45-2d79158d22e3} */ \
+ 0xa72ad552, 0x0484, 0x4b5f, \
+ {0x8d, 0x45, 0x2d, 0x79, 0x15, 0x8d, 0x22, 0xe3} \
+}
+
+
+//
+// nsAbBSDirectory - the root address book
+//
+#define NS_ABDIRECTORY_CONTRACTID \
+ NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX "moz-abdirectory"
+
+#define NS_ABDIRECTORY_CID \
+{ /* {012D3C24-1DD2-11B2-BA79-B4AD359FC461}*/ \
+ 0x012D3C24, 0x1DD2, 0x11B2, \
+ {0xBA, 0x79, 0xB4, 0xAD, 0x35, 0x9F, 0xC4, 0x61} \
+}
+
+
+//
+// nsAddressBookDB
+//
+#define NS_ADDRDATABASE_CONTRACTID \
+ "@mozilla.org/addressbook/carddatabase;1"
+
+#define NS_ADDRDATABASE_CID \
+{ /* 63187917-1D19-11d3-A302-001083003D0C */ \
+ 0x63187917, 0x1d19, 0x11d3, \
+ {0xa3, 0x2, 0x0, 0x10, 0x83, 0x0, 0x3d, 0xc} \
+}
+
+//
+// nsAbCardProperty
+//
+#define NS_ABCARDPROPERTY_CONTRACTID \
+ "@mozilla.org/addressbook/cardproperty;1"
+#define NS_ABCARDPROPERTY_CID \
+{ /* 2B722171-2CEA-11d3-9E0B-00A0C92B5F0D */ \
+ 0x2b722171, 0x2cea, 0x11d3, \
+ {0x9e, 0xb, 0x0, 0xa0, 0xc9, 0x2b, 0x5f, 0xd} \
+}
+
+//
+// nsAbDirProperty
+//
+#define NS_ABDIRPROPERTY_CONTRACTID \
+ "@mozilla.org/addressbook/directoryproperty;1"
+#define NS_ABDIRPROPERTY_CID \
+{ /* 6FD8EC67-3965-11d3-A316-001083003D0C */ \
+ 0x6fd8ec67, 0x3965, 0x11d3, \
+ {0xa3, 0x16, 0x0, 0x10, 0x83, 0x0, 0x3d, 0xc} \
+}
+
+//
+// nsAbDirectoryProperties
+//
+#define NS_ABDIRECTORYPROPERTIES_CONTRACTID \
+ "@mozilla.org/addressbook/properties;1"
+#define NS_ABDIRECTORYPROPERTIES_CID \
+{ /* 8b00a972-1dd2-11b2-9d9c-9c377a9c3dba */ \
+ 0x8b00a972, 0x1dd2, 0x11b2, \
+ {0x9d, 0x9c, 0x9c, 0x37, 0x7a, 0x9c, 0x3d, 0xba} \
+}
+
+//
+// nsAbAddressCollector
+//
+#define NS_ABADDRESSCOLLECTOR_CONTRACTID \
+ "@mozilla.org/addressbook/services/addressCollector;1"
+#define NS_ABADDRESSCOLLECTOR_CID \
+{ /* e7702d5a-99d8-4648-bab7-919ea29f30b6 */ \
+ 0xe7702d5a, 0x99d8, 0x4648, \
+ {0xba, 0xb7, 0x91, 0x9e, 0xa2, 0x9f, 0x30, 0xb6} \
+}
+
+//
+// addbook URL
+//
+#define NS_ADDBOOKURL_CONTRACTID \
+ "@mozilla.org/addressbook/services/url;1?type=addbook"
+
+#define NS_ADDBOOKURL_CID \
+{ /* ff04c8e6-501e-11d3-a527-0060b0fc0444 */ \
+ 0xff04c8e6, 0x501e, 0x11d3, \
+ {0xa5, 0x27, 0x0, 0x60, 0xb0, 0xfc, 0x4, 0x44} \
+}
+
+//
+// addbook Protocol Handler
+//
+#define NS_ADDBOOK_HANDLER_CONTRACTID \
+ "@mozilla.org/addressbook/services/addbook;1"
+#define NS_ADDBOOK_HANDLER_CID \
+{ /* ff04c8e6-501e-11d3-ffcc-0060b0fc0444 */ \
+ 0xff04c8e6, 0x501e, 0x11d3, \
+ {0xff, 0xcc, 0x0, 0x60, 0xb0, 0xfc, 0x4, 0x44} \
+}
+
+//
+// directory factory service
+//
+#define NS_ABDIRFACTORYSERVICE_CONTRACTID \
+ "@mozilla.org/addressbook/directory-factory-service;1"
+
+#define NS_ABDIRFACTORYSERVICE_CID \
+{ /* {F8B212F2-742B-4A48-B7A0-4C44D4DDB121}*/ \
+ 0xF8B212F2, 0x742B, 0x4A48, \
+ {0xB7, 0xA0, 0x4C, 0x44, 0xD4, 0xDD, 0xB1, 0x21} \
+}
+
+//
+// mdb directory factory
+//
+#define NS_ABMDBDIRECTORY "moz-abmdbdirectory"
+
+#define NS_ABMDBDIRFACTORY_CONTRACTID \
+ NS_AB_DIRECTORY_FACTORY_CONTRACTID_PREFIX NS_ABMDBDIRECTORY
+
+#define NS_ABMDBDIRFACTORY_CID \
+{ /* {E1CB9C8A-722D-43E4-9D7B-7CCAE4B0338A}*/ \
+ 0xE1CB9C8A, 0x722D, 0x43E4, \
+ {0x9D, 0x7B, 0x7C, 0xCA, 0xE4, 0xB0, 0x33, 0x8A} \
+}
+
+//
+// nsAbMDBDirectory
+//
+#define NS_ABMDBDIRECTORY_CONTRACTID \
+ NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX NS_ABMDBDIRECTORY
+
+#define NS_ABMDBDIRECTORY_CID \
+{ /* {e618f894-1dd1-11b2-889c-9aaefaa90dde}*/ \
+ 0xe618f894, 0x1dd1, 0x11b2, \
+ {0x88, 0x9c, 0x9a, 0xae, 0xfa, 0xa9, 0x0d, 0xde} \
+}
+
+//
+// nsAbMDBCard
+//
+#define NS_ABMDBCARD_CONTRACTID \
+ "@mozilla.org/addressbook/moz-abmdbcard;1"
+
+#define NS_ABMDBCARD_CID \
+{ /* {f578a5d2-1dd1-11b2-8841-f45cc5e765f8} */ \
+ 0xf578a5d2, 0x1dd1, 0x11b2, \
+ {0x88, 0x41, 0xf4, 0x5c, 0xc5, 0xe7, 0x65, 0xf8} \
+}
+
+#ifdef XP_WIN
+//
+// nsAbOutlookDirectory
+//
+#define NS_ABOUTLOOKDIRECTORY_CONTRACTID \
+ NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX "moz-aboutlookdirectory"
+
+#define NS_ABOUTLOOKDIRECTORY_CID \
+{ /* {9cc57822-0599-4c47-a399-1c6fa185a05c}*/ \
+ 0x9cc57822, 0x0599, 0x4c47, \
+ {0xa3, 0x99, 0x1c, 0x6f, 0xa1, 0x85, 0xa0, 0x5c} \
+}
+
+//
+// Outlook directory factory
+//
+#define NS_ABOUTLOOKDIRFACTORY_CONTRACTID \
+ NS_AB_DIRECTORY_FACTORY_CONTRACTID_PREFIX "moz-aboutlookdirectory"
+
+#define NS_ABOUTLOOKDIRFACTORY_CID \
+{ /* {558ccc0f-2681-4dac-a066-debd8d26faf6}*/ \
+ 0x558ccc0f, 0x2681, 0x4dac, \
+ {0xa0, 0x66, 0xde, 0xbd, 0x8d, 0x26, 0xfa, 0xf6} \
+}
+#endif
+
+//
+// Addressbook Query support
+//
+
+#define NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID \
+ "@mozilla.org/addressbook/directory/query-arguments;1"
+
+#define NS_ABDIRECTORYQUERYARGUMENTS_CID \
+{ /* {f7dc2aeb-8e62-4750-965c-24b9e09ed8d2} */ \
+ 0xf7dc2aeb, 0x8e62, 0x4750, \
+ { 0x96, 0x5c, 0x24, 0xb9, 0xe0, 0x9e, 0xd8, 0xd2 } \
+}
+
+
+#define NS_BOOLEANCONDITIONSTRING_CONTRACTID \
+ "@mozilla.org/boolean-expression/condition-string;1"
+
+#define NS_BOOLEANCONDITIONSTRING_CID \
+{ /* {ca1944a9-527e-4c77-895d-d0466dd41cf5} */ \
+ 0xca1944a9, 0x527e, 0x4c77, \
+ { 0x89, 0x5d, 0xd0, 0x46, 0x6d, 0xd4, 0x1c, 0xf5 } \
+}
+
+
+#define NS_BOOLEANEXPRESSION_CONTRACTID \
+ "@mozilla.org/boolean-expression/n-peer;1"
+
+#define NS_BOOLEANEXPRESSION_CID \
+{ /* {2c2e75c8-6f56-4a50-af1c-72af5d0e8d41} */ \
+ 0x2c2e75c8, 0x6f56, 0x4a50, \
+ { 0xaf, 0x1c, 0x72, 0xaf, 0x5d, 0x0e, 0x8d, 0x41 } \
+}
+
+#define NS_ABDIRECTORYQUERYPROXY_CONTRACTID \
+ "@mozilla.org/addressbook/directory-query/proxy;1"
+
+#define NS_ABDIRECTORYQUERYPROXY_CID \
+{ /* {E162E335-541B-43B4-AAEA-FE591E240CAF}*/ \
+ 0xE162E335, 0x541B, 0x43B4, \
+ {0xAA, 0xEA, 0xFE, 0x59, 0x1E, 0x24, 0x0C, 0xAF} \
+}
+
+// nsAbLDAPDirectory
+//
+#define NS_ABLDAPDIRECTORY_CONTRACTID \
+ NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX "moz-abldapdirectory"
+
+#define NS_ABLDAPDIRECTORY_CID \
+{ /* {783E2777-66D7-4826-9E4B-8AB58C228A52}*/ \
+ 0x783E2777, 0x66D7, 0x4826, \
+ {0x9E, 0x4B, 0x8A, 0xB5, 0x8C, 0x22, 0x8A, 0x52} \
+}
+
+// nsAbLDAPDirectoryQuery
+//
+#define NS_ABLDAPDIRECTORYQUERY_CONTRACTID \
+ "@mozilla.org/addressbook/ldap-directory-query;1"
+
+#define NS_ABLDAPDIRECTORYQUERY_CID \
+{ /* {783E2777-66D7-4826-9E4B-8AB58C228A53}*/ \
+ 0x783E2777, 0x66D7, 0x4826, \
+ {0x9E, 0x4B, 0x8A, 0xB5, 0x8C, 0x22, 0x8A, 0x53} \
+}
+
+//
+// nsAbLDAPCard
+//
+#define NS_ABLDAPCARD_CONTRACTID \
+ "@mozilla.org/addressbook/moz-abldapcard"
+
+#define NS_ABLDAPCARD_CID \
+{ /* {10307B01-EBD6-465F-B972-1630410F70E6}*/ \
+ 0x10307B01, 0xEBD6, 0x465F, \
+ {0xB9, 0x72, 0x16, 0x30, 0x41, 0x0F, 0x70, 0xE6} \
+}
+
+//
+// LDAP directory factory
+//
+#define NS_ABLDAPDIRFACTORY_CONTRACTID \
+ NS_AB_DIRECTORY_FACTORY_CONTRACTID_PREFIX "moz-abldapdirectory"
+
+#define NS_ABLDAPDIRFACTORY_CID \
+{ /* {8e3701af-8828-426c-84ac-124825c778f8} */ \
+ 0x8e3701af, 0x8828, 0x426c, \
+ {0x84, 0xac, 0x12, 0x48, 0x25, 0xc7, 0x78, 0xf8} \
+}
+
+//
+// LDAP autocomplete directory factory
+//
+#define NS_ABLDAPACDIRFACTORY_CONTRACTID \
+ NS_AB_DIRECTORY_FACTORY_CONTRACTID_PREFIX "ldap"
+#define NS_ABLDAPSACDIRFACTORY_CONTRACTID \
+ NS_AB_DIRECTORY_FACTORY_CONTRACTID_PREFIX "ldaps"
+
+// nsAbLDAPAutoCompFormatter
+
+// 4e276d6d-9981-46b4-9070-92f344ac5f5a
+//
+#define NS_ABLDAPAUTOCOMPFORMATTER_CID \
+{ 0x4e276d6d, 0x9981, 0x46b4, \
+ { 0x90, 0x70, 0x92, 0xf3, 0x44, 0xac, 0x5f, 0x5a }}
+
+#define NS_ABLDAPAUTOCOMPFORMATTER_CONTRACTID \
+ "@mozilla.org/ldap-autocomplete-formatter;1?type=addrbook"
+
+
+// nsAbLDAPReplicationService
+//
+// {ece81280-2639-11d6-b791-00b0d06e5f27}
+//
+#define NS_ABLDAP_REPLICATIONSERVICE_CID \
+ {0xece81280, 0x2639, 0x11d6, \
+ { 0xb7, 0x91, 0x00, 0xb0, 0xd0, 0x6e, 0x5f, 0x27 }}
+
+#define NS_ABLDAP_REPLICATIONSERVICE_CONTRACTID \
+ "@mozilla.org/addressbook/ldap-replication-service;1"
+
+// nsAbLDAPReplicationQuery
+//
+// {5414fff0-263b-11d6-b791-00b0d06e5f27}
+//
+#define NS_ABLDAP_REPLICATIONQUERY_CID \
+ {0x5414fff0, 0x263b, 0x11d6, \
+ { 0xb7, 0x91, 0x00, 0xb0, 0xd0, 0x6e, 0x5f, 0x27 }}
+
+#define NS_ABLDAP_REPLICATIONQUERY_CONTRACTID \
+ "@mozilla.org/addressbook/ldap-replication-query;1"
+
+
+// nsAbLDAPChangeLogQuery
+//
+// {63E11D51-3C9B-11d6-B7B9-00B0D06E5F27}
+#define NS_ABLDAP_CHANGELOGQUERY_CID \
+ {0x63e11d51, 0x3c9b, 0x11d6, \
+ { 0xb7, 0xb9, 0x0, 0xb0, 0xd0, 0x6e, 0x5f, 0x27 }}
+
+#define NS_ABLDAP_CHANGELOGQUERY_CONTRACTID \
+ "@mozilla.org/addressbook/ldap-changelog-query;1"
+
+// nsAbLDAPProcessReplicationData
+//
+// {5414fff1-263b-11d6-b791-00b0d06e5f27}
+//
+#define NS_ABLDAP_PROCESSREPLICATIONDATA_CID \
+ {0x5414fff1, 0x263b, 0x11d6, \
+ { 0xb7, 0x91, 0x00, 0xb0, 0xd0, 0x6e, 0x5f, 0x27 }}
+
+#define NS_ABLDAP_PROCESSREPLICATIONDATA_CONTRACTID \
+ "@mozilla.org/addressbook/ldap-process-replication-data;1"
+
+
+// nsAbLDAPProcessChangeLogData
+//
+// {63E11D52-3C9B-11d6-B7B9-00B0D06E5F27}
+#define NS_ABLDAP_PROCESSCHANGELOGDATA_CID \
+ {0x63e11d52, 0x3c9b, 0x11d6, \
+ {0xb7, 0xb9, 0x0, 0xb0, 0xd0, 0x6e, 0x5f, 0x27 }}
+
+#define NS_ABLDAP_PROCESSCHANGELOGDATA_CONTRACTID \
+ "@mozilla.org/addressbook/ldap-process-changelog-data;1"
+
+// nsABView
+
+#define NS_ABVIEW_CID \
+{ 0xc5eb5d6a, 0x1dd1, 0x11b2, \
+ { 0xa0, 0x25, 0x94, 0xd1, 0x18, 0x1f, 0xc5, 0x9c }}
+
+#define NS_ABVIEW_CONTRACTID \
+ "@mozilla.org/addressbook/abview;1"
+
+#ifdef XP_MACOSX
+//
+// nsAbOSXDirectory
+//
+#define NS_ABOSXDIRECTORY_PREFIX "moz-abosxdirectory"
+#define NS_ABOSXCARD_PREFIX "moz-abosxcard"
+
+#define NS_ABOSXDIRECTORY_CONTRACTID \
+ NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX NS_ABOSXDIRECTORY_PREFIX
+
+#define NS_ABOSXDIRECTORY_CID \
+{ /* {83781cc6-c682-11d6-bdeb-0005024967b8}*/ \
+ 0x83781cc6, 0xc682, 0x11d6, \
+ {0xbd, 0xeb, 0x00, 0x05, 0x02, 0x49, 0x67, 0xb8} \
+}
+
+//
+// nsAbOSXCard
+//
+#define NS_ABOSXCARD_CONTRACTID \
+ NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX NS_ABOSXCARD_PREFIX
+
+#define NS_ABOSXCARD_CID \
+{ /* {89bbf582-c682-11d6-bc9d-0005024967b8}*/ \
+ 0x89bbf582, 0xc682, 0x11d6, \
+ {0xbc, 0x9d, 0x00, 0x05, 0x02, 0x49, 0x67, 0xb8} \
+}
+
+//
+// OS X directory factory
+//
+#define NS_ABOSXDIRFACTORY_CONTRACTID \
+ NS_AB_DIRECTORY_FACTORY_CONTRACTID_PREFIX NS_ABOSXDIRECTORY_PREFIX
+
+#define NS_ABOSXDIRFACTORY_CID \
+{ /* {90efe2fe-c682-11d6-9c83-0005024967b8}*/ \
+ 0x90efe2fe, 0xc682, 0x11d6, \
+ {0x9c, 0x83, 0x00, 0x05, 0x02, 0x49, 0x67, 0xb8} \
+}
+#endif
+
+#define NS_MSGVCARDSERVICE_CID \
+{ 0x3c4ac0da, 0x2cda, 0x4018, \
+ { 0x95, 0x51, 0xe1, 0x58, 0xb2, 0xe1, 0x22, 0xd3 }}
+
+#define NS_MSGVCARDSERVICE_CONTRACTID \
+ "@mozilla.org/addressbook/msgvcardservice;1"
+
+#define NS_ABLDIFSERVICE_CID \
+{ 0xdb6f46da, 0x8de3, 0x478d, \
+ { 0xb5, 0x39, 0x80, 0x13, 0x98, 0x65, 0x6c, 0xf6 }}
+
+#define NS_ABLDIFSERVICE_CONTRACTID \
+ "@mozilla.org/addressbook/abldifservice;1"
+
+#endif // nsAbBaseCID_h__
diff --git a/mailnews/addrbook/public/nsIAbAddressCollector.idl b/mailnews/addrbook/public/nsIAbAddressCollector.idl
new file mode 100644
index 000000000..3d5071a6b
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbAddressCollector.idl
@@ -0,0 +1,58 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIAbCard;
+
+/**
+ * nsIAbAddressCollector is the interface to the address collecter service.
+ * It will save and update the supplied addresses into the address book
+ * specified by the "mail.collect_addressbook" pref.
+ */
+[scriptable, uuid(069d3fba-37d4-4158-b401-a8efaeea0b66)]
+interface nsIAbAddressCollector : nsISupports {
+ /**
+ * Collects email addresses into the address book.
+ * If a card already exists for the email, the first/last/display names
+ * will be updated if they are supplied alongside the address.
+ * If a card does not exist for the email it will be created if aCreateCard
+ * is true.
+ *
+ * @param aAddresses The list of emails (in standard header format)
+ * to collect into the address book.
+ * @param aCreateCard Set to true if a card should be created if the
+ * email address doesn't exist.
+ * @param aSendFormat The send format to save for the card. See
+ * nsIAbPreferMailFormat for values. If updating a card
+ * this value will only be changed if the current value
+ * for the card is "unknown".
+ */
+ void collectAddress(in AUTF8String aAddresses, in boolean aCreateCard,
+ in unsigned long aSendFormat);
+
+ /**
+ * Collects a single name and email address into the address book.
+ * By default, it saves the address without checking for an existing one.
+ * See collectAddress for the general implementation.
+ *
+ * @param aEmail The email address to collect.
+ * @param aDisplayName The display name associated with the email address.
+ * @param aCreateCard Set to true if a card should be created if the
+ * email address doesn't exist (ignored if
+ * aSkipCheckExisting is true).
+ * @param aSendFormat The send format to save for the card. See
+ * nsIAbPreferMailFormat for values. If updating a card
+ * this value will only be changed if the current value
+ * for the card is "unknown".
+ * @param aSkipCheckExisting Optional parameter, if this is set then the
+ * implementation will skip checking for an
+ * existing card, and just create a new card.
+ */
+ void collectSingleAddress(in AUTF8String aEmail, in AUTF8String aDisplayName,
+ in boolean aCreateCard,
+ in unsigned long aSendFormat,
+ [optional] in boolean aSkipCheckExisting);
+};
diff --git a/mailnews/addrbook/public/nsIAbAutoCompleteResult.idl b/mailnews/addrbook/public/nsIAbAutoCompleteResult.idl
new file mode 100644
index 000000000..f54290acc
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbAutoCompleteResult.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIAutoCompleteResult.idl"
+
+interface nsIAbCard;
+
+/**
+ * This interface is used to extend the nsIAutoCompleteResult interface to
+ * provide extra facilities for obtaining more details of the results of
+ * an address book search.
+ */
+[scriptable, uuid(c0d35623-f719-4e43-ae24-573e393f87f9)]
+interface nsIAbAutoCompleteResult : nsIAutoCompleteResult {
+ /**
+ * Get the card from the result at the given index
+ */
+ nsIAbCard getCardAt(in long index);
+
+ /**
+ * Gets the email to use for the card within the result at the given index.
+ * This is the email that was matched against for the card where there are
+ * multiple email addresses on a card.
+ *
+ * @param index Index of the autocomplete result to return the value for.
+ * @result The email address to use from the card.
+ */
+ AString getEmailToUse(in long index);
+
+ /**
+ * The template used to build the query for this search. Optional.
+ */
+ attribute AString modelQuery;
+};
diff --git a/mailnews/addrbook/public/nsIAbBooleanExpression.idl b/mailnews/addrbook/public/nsIAbBooleanExpression.idl
new file mode 100644
index 000000000..aeef67b46
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbBooleanExpression.idl
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIArray;
+typedef long nsAbBooleanConditionType;
+
+/**
+ * Condition types
+ *
+ * Constants defining the types of condition
+ * to obtain a boolean result of TRUE or FALSE
+ *
+ */
+[scriptable, uuid(F51387B1-5AEF-4A1C-830E-7CD3B02366CE)]
+interface nsIAbBooleanConditionTypes
+{
+ const long Exists = 0;
+ const long DoesNotExist = 1;
+ const long Contains = 2;
+ const long DoesNotContain = 3;
+ const long Is = 4;
+ const long IsNot = 5;
+ const long BeginsWith = 6;
+ const long EndsWith = 7;
+ const long LessThan = 8;
+ const long GreaterThan = 9;
+ const long SoundsLike = 10;
+ const long RegExp = 11;
+};
+
+
+typedef long nsAbBooleanOperationType;
+
+/*
+ * Operation types
+ *
+ * Constants defining the boolean operation that
+ * should be performed between two boolean expressions
+ *
+ */
+[uuid(9bdd2e51-2be4-49a4-a558-36d1a812231a)]
+interface nsIAbBooleanOperationTypes
+{
+ const long AND = 0;
+ const long OR = 1;
+ const long NOT = 2;
+};
+
+
+/**
+ * String condition
+ *
+ * A string condition represents a leaf node in a
+ * boolean expression tree and represents
+ * test which will return TRUE or FALSE
+ *
+ * Condition is an expression which is a
+ * leaf node in a boolean expression tree
+ *
+ */
+[scriptable, uuid(C3869D72-CFD0-45F0-A0EC-3F67D83C7110)]
+interface nsIAbBooleanConditionString : nsISupports
+{
+ /**
+ * The condition for how the a value
+ * should be compared
+ *
+ */
+ attribute nsAbBooleanConditionType condition;
+
+ /**
+ * The lhs of the condition
+ *
+ * Represents a property name which
+ * should be evaluated to obtain the
+ * lhs.
+ *
+ */
+ attribute string name;
+
+ /**
+ * The rhs of the condition
+ *
+ * <name> [condition] value
+ *
+ */
+ attribute wstring value;
+};
+
+/**
+ * N Boolean expression type
+ *
+ * Supports Unary Binary and N boolean expressions
+ *
+ * An operation represents a node in a boolean
+ * expression tree which may contain one or more
+ * child conditions or expressions
+ *
+ */
+[scriptable, uuid(223a9462-1aeb-4c1f-b069-5fc6278989b2)]
+interface nsIAbBooleanExpression: nsISupports
+{
+ /**
+ * The boolean operation to be applied to
+ * results of all evaluated expressions
+ *
+ */
+ attribute nsAbBooleanOperationType operation;
+
+ /**
+ * List of peer expressions
+ *
+ * e1 [op] e2 [op] .... en
+ *
+ */
+ attribute nsIArray expressions;
+};
+
diff --git a/mailnews/addrbook/public/nsIAbCard.idl b/mailnews/addrbook/public/nsIAbCard.idl
new file mode 100644
index 000000000..ccd1c4c43
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbCard.idl
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIAbItem.idl"
+
+interface nsISimpleEnumerator;
+interface nsIVariant;
+
+[scriptable, uuid(97448252-F189-11d4-A422-001083003D0C)]
+interface nsIAbPreferMailFormat {
+ const unsigned long unknown = 0;
+ const unsigned long plaintext = 1;
+ const unsigned long html = 2;
+};
+
+/**
+ * An interface representing an address book card.
+ *
+ * The UUID of a card is a composition of a directory ID and a per-directory ID.
+ * The per-directory ID is reflected in the localId property. If either of these
+ * properties change, the UUID will change correspondingly.
+ *
+ * None of these IDs will be reflected in the property collection. Neither
+ * nsIAbCard::properties, nsIAbCard::deleteProperty, nor any of the property
+ * getters and setters are able to interact with these properties.
+ *
+ * Fundamentally, a card is a collection of properties. Modifying a property in
+ * some way on a card does not change the backend used to store the card; the
+ * directory is required to do make the changes here.
+ *
+ * The following are the core properties that are used:
+ * - Names:
+ * - FirstName, LastName
+ * - PhoneticFirstName, PhoneticLastName
+ * - DisplayName, NickName
+ * - SpouseName, FamilyName
+ * - PrimaryEmail, SecondEmail
+ * - Home Contact:
+ * - HomeAddress, HomeAddress2, HomeCity, HomeState, HomeZipCode, HomeCountry
+ * - HomePhone, HomePhoneType
+ * - Work contact. Same as home, but with `Work' instead of `Home'
+ * - Other Contact:
+ * - FaxNumber, FaxNumberType
+ * - PagerNumber, PagerNumberType
+ * - CellularNumber, CellularNumberType
+ * - JobTitle, Department, Company
+ * - _AimScreenName
+ * - Dates:
+ * - AnniversaryYear, AnniversaryMonth, AnniversaryDay
+ * - BirthYear, BirthMonth, BirthDay
+ * - WebPage1 (work), WebPage2 (home)
+ * - Custom1, Custom2, Custom3, Custom4
+ * - Notes
+ * - Integral properties:
+ * - LastModifiedDate
+ * - PopularityIndex
+ * - PreferMailFormat (see nsIAbPreferMailFormat)
+ * - Photo properties:
+ * - PhotoName
+ * - PhotoType
+ * - PhotoURI
+ *
+ * The contract id for the standard implementation is
+ * <tt>\@mozilla.org/addressbook/cardproperty;1</tt>.
+ */
+[scriptable, uuid(9bddf024-5178-4097-894e-d84b4ddde101)]
+interface nsIAbCard : nsIAbItem {
+ /**
+ * The UUID for the nsIAbDirectory containing this card.
+ *
+ * The directory considered to contain this card is the directory which
+ * produced this card (e.g., through nsIAbDirectory::getCardForProperty) or
+ * the last directory to modify this card, if another directory did so. If the
+ * last directory to modify this card deleted it, then this card is considered
+ * unassociated.
+ *
+ * If this card is not associated with a directory, this string will be empty.
+ *
+ * There is no standardized way to associate a card with multiple directories.
+ *
+ * Consumers of this interface outside of directory implementations SHOULD
+ * NOT, in general, modify this property.
+ */
+ attribute AUTF8String directoryId;
+
+ /**
+ * The per-directory ID of this card.
+ *
+ * This property is the second part of the tuple logically representing a card
+ * UUID. It shares many requirements with that of nsIAbItem::uuid. In
+ * particular:
+ * - It MUST be unique (within the scope of its directory).
+ * - The empty string MUST only be used to indicate that it has not yet been
+ * assigned a localId.
+ * - It is STRONGLY RECOMMENDED that this id is consistent across sessions and
+ * that, should the card be deleted, its ids will not be reused.
+ * - The format of localId is left undefined.
+ *
+ * As long as directoryId is not changed, this property SHOULD NOT be changed.
+ * If directoryId is changed, the new directory MAY choose to reuse the same
+ * localId if reasonable. However, consumers MUST NOT assume that two cards
+ * with different directoryIds but the same localId are logically the same
+ * card.
+ *
+ * Similar to directoryId, consumers of cards outside of directory
+ * implementations SHOULD NOT, in general, modify this property.
+ */
+ attribute AUTF8String localId;
+
+ /**
+ * A list of all the properties that this card has as an enumerator, whose
+ * members are all nsIProperty objects.
+ */
+ readonly attribute nsISimpleEnumerator properties;
+
+ /**
+ * Returns a property for the given name.
+ *
+ * @param name The case-sensitive name of the property to get.
+ * @param defaultValue The value to return if the property does not exist.
+ * @exception NS_ERROR_NOT_AVAILABLE if the named property does not exist.
+ * @exception NS_ERROR_CANNOT_CONVERT_DATA if the property cannot be converted
+ * to the desired type.
+ */
+ nsIVariant getProperty(in AUTF8String name, in nsIVariant defaultValue);
+ /**
+ * @{
+ * Returns a property for the given name. Javascript callers should NOT use these,
+ * but use getProperty instead. XPConnect will do the type conversion automagically.
+ *
+ * These functions convert values in the same manner as the default
+ * implementation of nsIVariant. Of particular note is that boolean variables
+ * are converted to integers as in C/C++ (true is a non-zero value), so that
+ * false will be converted to a string of "0" and not "false."
+ *
+ *
+ * @param name The case-sensitive name of the property to get.
+ * @exception NS_ERROR_NOT_AVAILABLE if the named property does not exist.
+ * @exception NS_ERROR_CANNOT_CONVERT_DATA if the property cannot be converted
+ * to the desired type.
+ */
+ AString getPropertyAsAString(in string name);
+ AUTF8String getPropertyAsAUTF8String(in string name);
+ unsigned long getPropertyAsUint32(in string name);
+ boolean getPropertyAsBool(in string name);
+
+ /** @} */
+
+ /**
+ * Assigns the given to value to the property of the given name.
+ *
+ * Should the property exist, its value will be overwritten. An
+ * implementation may impose additional semantic constraints for certain
+ * properties. However, such constraints might not be checked by this method.
+ *
+ * @warning A value MUST be convertible to a string; if this convention is not
+ * followed, consumers of cards may fail unpredictably or return incorrect
+ * results.
+ *
+ * @param name The case-sensitive name of the property to set.
+ * @param value The new value of the property.
+ */
+ void setProperty(in AUTF8String name, in nsIVariant value);
+
+ /**
+ * @{
+ * Sets a property for the given name. Javascript callers should NOT use these,
+ * but use setProperty instead. XPConnect will do the type conversion automagically.
+ *
+ * These functions convert values in the same manner as the default
+ * implementation of nsIVariant.
+ */
+ void setPropertyAsAString(in string name, in AString value);
+ void setPropertyAsAUTF8String(in string name, in AUTF8String value);
+ void setPropertyAsUint32(in string name, in unsigned long value);
+ void setPropertyAsBool(in string name, in boolean value);
+
+ /** @} */
+
+ /**
+ * Deletes the property with the given name.
+ *
+ * Some properties may not be deleted. However, the implementation will not
+ * check this constraint at this method. If such a property is deleted, an
+ * error may be thrown when the card is modified at the database level.
+ *
+ * @param name The case-sensitive name of the property to set.
+ */
+ void deleteProperty(in AUTF8String name);
+
+ /**
+ * @{
+ * These properties are shorthand for getProperty and setProperty.
+ */
+ attribute AString firstName;
+ attribute AString lastName;
+ attribute AString displayName;
+ attribute AString primaryEmail;
+ /** @} */
+
+ /**
+ * Determines whether or not a card has the supplied email address in either
+ * of its PrimaryEmail or SecondEmail attributes.
+ *
+ * Note: This function is likely to be temporary whilst we work out proper
+ * APIs for multi-valued attributes in bug 118665.
+ *
+ * @param aEmailAddress The email address to attempt to match against.
+ * @return True if aEmailAddress matches any of the email
+ * addresses stored in the card.
+ */
+ boolean hasEmailAddress(in AUTF8String aEmailAddress);
+
+ /**
+ * Translates a card into a specific format.
+ * The following types are supported:
+ * - base64xml
+ * - xml
+ * - vcard
+ *
+ * @param aType The type of item to translate the card into.
+ * @return A string containing the translated card.
+ * @exception NS_ERROR_ILLEGAL_VALUE if we do not recognize the type.
+ */
+ AUTF8String translateTo(in AUTF8String aType);
+
+ /**
+ * Translates a card from the specified format
+ */
+ //void translateFrom(in AUTF8String aType, in AUTF8String aData);
+
+ /**
+ * Generate a phonetic name from the card, using the firstName and lastName
+ * values.
+ *
+ * @param aLastNameFirst Set to True to put the last name before the first.
+ * @return A string containing the generated phonetic name.
+ */
+ AString generatePhoneticName(in boolean aLastNameFirst);
+
+ /**
+ * Generate a chat name from the card, containing the value of the
+ * first non-empty chat field.
+ *
+ * @return A string containing the generated chat name.
+ */
+ AString generateChatName();
+
+ /**
+ * This function will copy all values from one card to another.
+ *
+ * @param srcCard The source card to copy values from.
+ */
+ void copy(in nsIAbCard aSrcCard);
+
+ /**
+ * Returns true if this card is equal to the other card.
+ *
+ * The default implementation defines equal as this card pointing to the
+ * same object as @arg aCard; another implementation defines it as equality of
+ * properties and values.
+ *
+ * @warning The exact nature of equality is still undefined, and actual
+ * results may not match theoretical results. Most notably, the code
+ * <tt>a.equals(b) == b.equals(a)</tt> might not return true. In
+ * particular, calling equals on cards from different address books
+ * may return inaccurate results.
+ *
+ *
+ * @return Equality, as defined above.
+ * @param aCard The card to compare against.
+ */
+ boolean equals(in nsIAbCard aCard);
+
+ // PROPERTIES TO BE DELETED AS PART OF REWRITE
+
+ attribute boolean isMailList;
+ /**
+ * If isMailList is true then mailListURI
+ * will contain the URI of the associated
+ * mail list
+ */
+ attribute string mailListURI;
+};
+
+%{C++
+// A nice list of properties for the benefit of C++ clients
+#define kFirstNameProperty "FirstName"
+#define kLastNameProperty "LastName"
+#define kDisplayNameProperty "DisplayName"
+#define kNicknameProperty "NickName"
+#define kPriEmailProperty "PrimaryEmail"
+#define kPreferMailFormatProperty "PreferMailFormat"
+#define kLastModifiedDateProperty "LastModifiedDate"
+#define kPopularityIndexProperty "PopularityIndex"
+
+#define kPhoneticFirstNameProperty "PhoneticFirstName"
+#define kPhoneticLastNameProperty "PhoneticLastName"
+#define kSpouseNameProperty "SpouseName"
+#define kFamilyNameProperty "FamilyName"
+#define k2ndEmailProperty "SecondEmail"
+
+#define kHomeAddressProperty "HomeAddress"
+#define kHomeAddress2Property "HomeAddress2"
+#define kHomeCityProperty "HomeCity"
+#define kHomeStateProperty "HomeState"
+#define kHomeZipCodeProperty "HomeZipCode"
+#define kHomeCountryProperty "HomeCountry"
+#define kHomeWebPageProperty "WebPage2"
+
+#define kWorkAddressProperty "WorkAddress"
+#define kWorkAddress2Property "WorkAddress2"
+#define kWorkCityProperty "WorkCity"
+#define kWorkStateProperty "WorkState"
+#define kWorkZipCodeProperty "WorkZipCode"
+#define kWorkCountryProperty "WorkCountry"
+#define kWorkWebPageProperty "WebPage1"
+
+#define kHomePhoneProperty "HomePhone"
+#define kHomePhoneTypeProperty "HomePhoneType"
+#define kWorkPhoneProperty "WorkPhone"
+#define kWorkPhoneTypeProperty "WorkPhoneType"
+#define kFaxProperty "FaxNumber"
+#define kFaxTypeProperty "FaxNumberType"
+#define kPagerTypeProperty "PagerNumberType"
+#define kPagerProperty "PagerNumber"
+#define kCellularProperty "CellularNumber"
+#define kCellularTypeProperty "CellularNumberType"
+
+#define kJobTitleProperty "JobTitle"
+#define kDepartmentProperty "Department"
+#define kCompanyProperty "Company"
+#define kScreenNameProperty "_AimScreenName"
+#define kCustom1Property "Custom1"
+#define kCustom2Property "Custom2"
+#define kCustom3Property "Custom3"
+#define kCustom4Property "Custom4"
+#define kNotesProperty "Notes"
+
+#define kGtalkProperty "_GoogleTalk"
+#define kAIMProperty "_AimScreenName"
+#define kYahooProperty "_Yahoo"
+#define kSkypeProperty "_Skype"
+#define kQQProperty "_QQ"
+#define kMSNProperty "_MSN"
+#define kICQProperty "_ICQ"
+#define kXMPPProperty "_JabberId"
+#define kIRCProperty "_IRC"
+
+#define kAnniversaryYearProperty "AnniversaryYear"
+#define kAnniversaryMonthProperty "AnniversaryMonth"
+#define kAnniversaryDayProperty "AnniversaryDay"
+#define kBirthYearProperty "BirthYear"
+#define kBirthMonthProperty "BirthMonth"
+#define kBirthDayProperty "BirthDay"
+%}
diff --git a/mailnews/addrbook/public/nsIAbCollection.idl b/mailnews/addrbook/public/nsIAbCollection.idl
new file mode 100644
index 000000000..1efbcce16
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbCollection.idl
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIAbItem.idl"
+
+interface nsIAbCard;
+interface nsISimpleEnumerator;
+
+/**
+ * A collection of address book items.
+ */
+[scriptable, uuid(70f6123f-e06b-4101-9750-4ce73b38134b)]
+interface nsIAbCollection : nsIAbItem {
+
+ /**
+ * Returns true if this collection is read-only.
+ */
+ readonly attribute boolean readOnly;
+
+ /**
+ * Returns true if this collection is accessed over a network connection.
+ */
+ readonly attribute boolean isRemote;
+
+ /**
+ * Returns true if this collection is accessed over a secure connection.
+ *
+ * If isRemote returns false, then this value MUST be false as well.
+ */
+ readonly attribute boolean isSecure;
+
+ /**
+ * Returns an address book card for the specified email address if found.
+ *
+ * If there are multiple cards with the given email address, this method will
+ * return one of these cards in an implementation-defined manner.
+ *
+ * Matching is performed in a case-insensitive manner.
+ *
+ * This method performs a synchronous operation. If the collection cannot do
+ * the search in such a manner, then it should throw NS_ERROR_NOT_IMPLEMENTED.
+ *
+ * @param emailAddress The email address to find in any of the email address
+ * fields. If emailAddress is empty, the database won't
+ * be searched and the function will return as if no card
+ * was found.
+ * @return An nsIAbCard if one was found, else returns NULL.
+ * @exception NS_ERROR_NOT_IMPLEMENTED If the collection cannot do this.
+ */
+ nsIAbCard cardForEmailAddress(in AUTF8String emailAddress);
+
+ /**
+ * Returns an address book card for the specified property if found.
+ *
+ * If there are multiple cards with the given value for the property, this
+ * method will return one of these cards in an implementation-defined manner.
+ *
+ * This method performs a synchronous operation. If the collection cannot do
+ * the search in such a manner, then it should throw NS_ERROR_NOT_IMPLEMENTED.
+ *
+ * If the property is not natively a string, it can still be searched for
+ * using the string-encoded value of the property, e.g. "0". See
+ * nsIAbCard::getPropertyAsAUTF8String for more information. Empty values will
+ * return no match, to prevent spurious results.
+ *
+ * @param aProperty The property to look for.
+ * @param aValue The value to search for.
+ * @param aCaseSensitive True if matching should be done case-sensitively.
+ * @result An nsIAbCard if one was found, else returns NULL.
+ * @exception NS_ERROR_NOT_IMPLEMENTED If the collection cannot do this.
+ */
+ nsIAbCard getCardFromProperty(in string aProperty, in AUTF8String aValue,
+ in boolean aCaseSensitive);
+
+ /**
+ * Returns all address book cards with a specific property matching value
+ *
+ * This function is almost identical to getCardFromProperty, with the
+ * exception of returning all cards rather than just the first.
+ *
+ * @param aProperty The property to look for.
+ * @param aValue The value to search for.
+ * @param aCaseSensitive True if matching should be done case-sensitively.
+ * @result A nsISimpleEnumerator that holds nsIAbCard
+ * instances.
+ */
+ nsISimpleEnumerator getCardsFromProperty(in string aProperty,
+ in AUTF8String aValue,
+ in boolean aCaseSensitive);
+};
diff --git a/mailnews/addrbook/public/nsIAbDirFactory.idl b/mailnews/addrbook/public/nsIAbDirFactory.idl
new file mode 100644
index 000000000..3be1f6394
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbDirFactory.idl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsISimpleEnumerator.idl"
+
+interface nsIAbDirectory;
+
+[scriptable, uuid(ad61b4fc-d8d8-40b2-b924-4c10f28a8a17)]
+interface nsIAbDirFactory : nsISupports
+{
+ /**
+ * Get a top level address book directory and sub directories, given some
+ * properties.
+ *
+ * @param aDirName Name of the address book
+ *
+ * @param aURI URI of the address book
+ *
+ * @param aPrefName Pref name for the preferences of the address book
+ *
+ * @return Enumeration of nsIAbDirectory interfaces
+ */
+ nsISimpleEnumerator getDirectories(in AString aDirName, in ACString aURI,
+ in ACString aPrefName);
+
+ /**
+ * Delete a top level address book directory
+ *
+ */
+ void deleteDirectory (in nsIAbDirectory directory);
+};
+
diff --git a/mailnews/addrbook/public/nsIAbDirFactoryService.idl b/mailnews/addrbook/public/nsIAbDirFactoryService.idl
new file mode 100644
index 000000000..8276d17ba
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbDirFactoryService.idl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsISupports.idl"
+
+interface nsIAbDirFactory;
+
+[scriptable, uuid(154a951b-a310-400c-b98f-d769cc5d575f)]
+interface nsIAbDirFactoryService : nsISupports
+{
+ /**
+ * Obtain a directory factory component given a uri representing an address
+ * book. The scheme is extracted from the uri and contract id is generated
+ * of the form:
+ * @mozilla.org/addressbook/directory-factory;1?name=<scheme>
+ *
+ * This id is used to instantiate a registered component which implemented
+ * the nsIAbDirFactory interface.
+ *
+ * @param aURI The uri which contains the scheme that defines what directory
+ * factory instance is returned
+ */
+ nsIAbDirFactory getDirFactory(in ACString aURI);
+};
+
diff --git a/mailnews/addrbook/public/nsIAbDirSearchListener.idl b/mailnews/addrbook/public/nsIAbDirSearchListener.idl
new file mode 100644
index 000000000..2e8455c79
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbDirSearchListener.idl
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIAbCard;
+
+[scriptable, uuid(eafe2488-4efb-4ac8-a6b4-7756eb1650a3)]
+interface nsIAbDirSearchListener : nsISupports {
+ void onSearchFinished(in long aResult, in AString aErrorMsg);
+
+ void onSearchFoundCard(in nsIAbCard aCard);
+};
diff --git a/mailnews/addrbook/public/nsIAbDirectory.idl b/mailnews/addrbook/public/nsIAbDirectory.idl
new file mode 100644
index 000000000..e3e52beec
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbDirectory.idl
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIAbCollection.idl"
+#include "nsIAbCard.idl"
+
+interface nsISimpleEnumerator;
+interface nsIArray;
+interface nsIMutableArray;
+
+/* moz-abdirectory:// is the URI to access nsAbBSDirectory,
+ * which is the root directory for all types of address books
+ * this is used to get all address book directories. */
+
+%{C++
+#define kAllDirectoryRoot "moz-abdirectory://"
+
+#define kPersonalAddressbook "abook.mab"
+#define kPersonalAddressbookUri "moz-abmdbdirectory://abook.mab"
+#define kCollectedAddressbook "history.mab"
+#define kCollectedAddressbookUri "moz-abmdbdirectory://history.mab"
+
+#define kABFileName_PreviousSuffix ".na2" /* final v2 address book format */
+#define kABFileName_PreviousSuffixLen 4
+#define kABFileName_CurrentSuffix ".mab" /* v3 address book extension */
+%}
+
+/**
+ * A top-level address book directory.
+ *
+ * Please note that in order to be properly instantiated by nsIAbManager, every
+ * type of nsIAbDirectory must have a contract ID of the form:
+ *
+ * @mozilla.org/addressbook/directory;1?type=<AB URI Scheme>
+ *
+ * Where AB URI Scheme does not include the ://. For example, for the Mork-based
+ * address book, the scheme is "moz-abmdbdirectory", so the contract ID for
+ * the Mork-based address book type is:
+ *
+ * @mozilla.org/addressbook/directory;1?type=moz-abmdbdirectory
+ *
+ * The UUID of an nsIAbDirectory is its preference ID and its name, concatenated
+ * together.
+ */
+[scriptable, uuid(72dc868b-db5b-4daa-b6c6-071be4a05d02)]
+interface nsIAbDirectory : nsIAbCollection {
+
+ /**
+ * The chrome URI to use for bringing up a dialog to edit this directory.
+ * When opening the dialog, use a JS argument of
+ * {selectedDirectory: thisdir} where thisdir is this directory that you just
+ * got the chrome URI from.
+ */
+ readonly attribute ACString propertiesChromeURI;
+
+ /**
+ * The description of the directory. If this directory is not a mailing list,
+ * then setting this attribute will send round a "DirName" update via
+ * nsIAddrBookSession.
+ */
+ attribute AString dirName;
+
+ // XXX This should really be replaced by a QI or something better
+ readonly attribute long dirType;
+
+ // eliminated a bit more.
+
+ // The filename for address books within this directory.
+ readonly attribute ACString fileName;
+
+ // The URI of the address book
+ readonly attribute ACString URI;
+
+ // The position of the directory on the display.
+ readonly attribute long position;
+
+ // will be used for LDAP replication
+ attribute unsigned long lastModifiedDate;
+
+ // Defines whether this directory is a mail
+ // list or not
+ attribute boolean isMailList;
+
+ // Get the children directories
+ readonly attribute nsISimpleEnumerator childNodes;
+
+ /**
+ * Get the cards associated with the directory. This will return the cards
+ * associated with the mailing lists too.
+ */
+ readonly attribute nsISimpleEnumerator childCards;
+
+ /**
+ * Returns true if this directory represents a query - i.e. the rdf resource
+ * was something like moz-abmdbdirectory://abook.mab?....
+ */
+ readonly attribute boolean isQuery;
+
+ /**
+ * Initializes a directory, pointing to a particular
+ * URI
+ */
+ void init(in string aURI);
+
+ // Deletes either a mailing list or a top
+ // level directory, which also updates the
+ // preferences
+ void deleteDirectory(in nsIAbDirectory directory);
+
+ // Check if directory contains card
+ // If the implementation is asynchronous the card
+ // may not yet have arrived. If it is in the process
+ // of obtaining cards the method will throw an
+ // NS_ERROR_NOT_AVAILABLE exception if the card
+ // cannot be found.
+ boolean hasCard(in nsIAbCard cards);
+
+ // Check if directory contains directory
+ boolean hasDirectory(in nsIAbDirectory dir);
+
+ // Check if directory contains a mailinglist by name
+ boolean hasMailListWithName(in wstring aName);
+
+ /**
+ * Adds a card to the database.
+ *
+ * This card does not need to be of the same type as the database, e.g., one
+ * can add an nsIAbLDAPCard to an nsIAbMDBDirectory.
+ *
+ * @return "Real" card (eg nsIAbLDAPCard) that can be used for some
+ * extra functions.
+ */
+ nsIAbCard addCard(in nsIAbCard card);
+
+ /**
+ * Modifies a card in the database to match that supplied.
+ */
+ void modifyCard(in nsIAbCard modifiedCard);
+
+ /**
+ * Deletes the array of cards from the database.
+ *
+ * @param aCards The cards to delete from the database.
+ */
+ void deleteCards(in nsIArray aCards);
+
+ void dropCard(in nsIAbCard card, in boolean needToCopyCard);
+
+ /**
+ * Whether or not the directory should be searched when doing autocomplete,
+ * (currently by using GetChildCards); LDAP does not support this in online
+ * mode, so that should return false; additionally any other directory types
+ * that also do not support GetChildCards should return false.
+ *
+ * @param aIdentity An optional parameter detailing the identity key (see
+ * nsIMsgAccountManager) that this autocomplete is being
+ * run against.
+ * @return True if this directory should/can be used during
+ * local autocomplete.
+ */
+ boolean useForAutocomplete(in ACString aIdentityKey);
+
+ /**
+ * Does this directory support mailing lists? Note that in the case
+ * this directory is a mailing list and nested mailing lists are not
+ * supported, this will return false rather than true which the parent
+ * directory might.
+ */
+ readonly attribute boolean supportsMailingLists;
+
+ /**
+ * This attribute serves two purposes
+ * 1. If this directory is not a mail list, directories are stored here
+ * 2. If it is a mail list card entries are stored here
+ *
+ * @note This is a *live* array and not a static copy
+ */
+ attribute nsIMutableArray addressLists;
+
+ // Specific to a directory which stores mail lists
+
+ /**
+ * Creates a new mailing list in the directory. Currently only supported
+ * for top-level directories.
+ *
+ * @param list The new mailing list to add.
+ * @return The mailing list directory added, which may have been modified.
+ */
+ nsIAbDirectory addMailList(in nsIAbDirectory list);
+
+ /**
+ * Nick Name of the mailing list. This attribute is only really used when
+ * the nsIAbDirectory represents a mailing list.
+ */
+ attribute AString listNickName;
+
+ /**
+ * Description of the mailing list. This attribute is only really used when
+ * the nsIAbDirectory represents a mailing list.
+ */
+ attribute AString description;
+
+ /**
+ * Edits an existing mailing list (specified as listCard) into its parent
+ * directory. You should call this function on the resource with the same
+ * uri as the listCard.
+ *
+ * @param listCard A nsIAbCard version of the mailing list with the new
+ * values.
+ */
+ void editMailListToDatabase(in nsIAbCard listCard);
+
+ // Copies mail list properties from the srcList
+ void copyMailList(in nsIAbDirectory srcList);
+
+ /**
+ * Only creates a top level address book
+ * which is stored in the preferences
+ *
+ * Need to change to factory based approach
+ * to create new address books
+ *
+ * This method should become redundant or
+ * be only associated with card folders
+ *
+ * The parameters are the same as for
+ * nsIAbManager::newAddressBook
+ */
+ ACString createNewDirectory(in AString aDirName, in ACString aURI,
+ in unsigned long aType, in ACString aPrefName);
+
+ /* create a directory by passing the display name and address book uri */
+ void createDirectoryByURI(in AString displayName, in ACString aURI);
+
+ /**
+ * The id of the directory used in prefs e.g. "ldap_2.servers.pab"
+ * Setting this will cause directoryPrefs to be updated.
+ */
+ attribute ACString dirPrefId;
+
+ /**
+ * @name getXXXValue
+ *
+ * Helper functions to get different types of pref, but return a default
+ * value if a pref value was not obtained.
+ *
+ * @param aName The name of the pref within the branch dirPrefId to
+ * get a value from.
+ *
+ * @param aDefaultValue The default value to return if getting the pref fails
+ * or the pref is not present.
+ *
+ * @return The value of the pref or the default value.
+ *
+ * @exception NS_ERROR_NOT_INITIALIZED if the pref branch couldn't
+ * be obtained (e.g. dirPrefId isn't set).
+ */
+ //@{
+ long getIntValue(in string aName, in long aDefaultValue);
+ boolean getBoolValue(in string aName, in boolean aDefaultValue);
+ ACString getStringValue(in string aName, in ACString aDefaultValue);
+ AUTF8String getLocalizedStringValue(in string aName, in AUTF8String aDefaultValue);
+ //@}
+
+ /**
+ * The following attributes are read from an nsIAbDirectory via the above methods:
+ *
+ * HidesRecipients (Boolean)
+ * If true, and this nsIAbDirectory is a mailing list, then when sending mail to
+ * this list, recipients addresses will be hidden from one another by sending
+ * via BCC.
+ */
+
+ /**
+ * @name setXXXValue
+ *
+ * Helper functions to set different types of pref values.
+ *
+ * @param aName The name of the pref within the branch dirPrefId to
+ * get a value from.
+ *
+ * @param aValue The value to set the pref to.
+ *
+ * @exception NS_ERROR_NOT_INITIALIZED if the pref branch couldn't
+ * be obtained (e.g. dirPrefId isn't set).
+ */
+ //@{
+ void setIntValue(in string aName, in long aValue);
+ void setBoolValue(in string aName, in boolean aValue);
+ void setStringValue(in string aName, in ACString aValue);
+ void setLocalizedStringValue(in string aName, in AUTF8String aValue);
+ //@}
+
+};
diff --git a/mailnews/addrbook/public/nsIAbDirectoryQuery.idl b/mailnews/addrbook/public/nsIAbDirectoryQuery.idl
new file mode 100644
index 000000000..11a82d926
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbDirectoryQuery.idl
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsISupports.idl"
+
+interface nsIAbDirSearchListener;
+interface nsIAbCard;
+interface nsIAbDirectory;
+
+/**
+ * The arguments for a query.
+ *
+ * Contains an expression for perform matches
+ * and an array of properties which should be
+ * returned if a match is found from the expression
+ *
+ */
+[scriptable, uuid(03af3018-2590-4f4c-a88c-1fff6595ef05)]
+interface nsIAbDirectoryQueryArguments : nsISupports
+{
+ /**
+ * Defines the boolean expression for
+ * the matching of cards
+ *
+ */
+ attribute nsISupports expression;
+
+ /**
+ * Defines if sub directories should be
+ * queried
+ *
+ */
+ attribute boolean querySubDirectories;
+
+ /**
+ * A parameter which can be used to pass in data specific to a particular
+ * type of addressbook.
+ */
+ attribute nsISupports typeSpecificArg;
+
+ /**
+ * A custom search filter which user wants to use in LDAP query.
+ */
+ attribute AUTF8String filter;
+};
+
+
+[scriptable, uuid(3A6E0C0C-1DD2-11B2-B23D-EA3A8CCB333C)]
+interface nsIAbDirectoryQueryPropertyValue : nsISupports
+{
+ /**
+ * The property which should be matched
+ *
+ * For example 'primaryEmail' or 'homePhone'
+ * for card properties.
+ *
+ * Two further properties are defined that
+ * do not exist as properties on a card.
+ *
+ * 'card:nsIAbCard' which represents the interface
+ * of a card component
+ *
+ */
+ readonly attribute string name;
+
+ /**
+ * The value of the property
+ *
+ */
+ readonly attribute wstring value;
+
+ /**
+ * The value of the property
+ * as an interface
+ *
+ * Only valid if the corresponding
+ * property name is related to an
+ * interface instead of a wstring
+ *
+ */
+ readonly attribute nsISupports valueISupports;
+};
+
+[scriptable, uuid(516e7ffa-69bc-41db-a493-dfb4895832f3)]
+interface nsIAbDirectoryQueryResultListener : nsISupports
+{
+ /**
+ * Called when a match is found. May be called from a different thread to
+ * the one that initiates the query.
+ *
+ * @param aCard An individual result associated returned from a query
+ */
+ void onQueryFoundCard(in nsIAbCard aCard);
+
+ /**
+ * List of defined query results
+ *
+ */
+ const long queryResultMatch = 0;
+ const long queryResultComplete = 1;
+ const long queryResultStopped = 2;
+ const long queryResultError = 3;
+
+ /**
+ * Called when a query has finished. May be called from a different thread
+ * to the one that initiates the query.
+ *
+ * @param aResult A result code from the list above.
+ *
+ * @param aErrorCode An error code specific to the type of query.
+ */
+ void onQueryResult(in long aResult, in long aErrorCode);
+};
+
+[scriptable, uuid(60b5961c-ce61-47b3-aa99-6d865f734dee)]
+interface nsIAbDirectoryQuery : nsISupports
+{
+ /**
+ * Initiates a query on a directory and sub-directories for properties
+ * on cards
+ *
+ * @param aDirectory A directory that the query may get extra details
+ * from.
+ *
+ * @param aArguments The properties and values to match value could of
+ * type nsIAbDirectoryQueryMatchItem for matches other
+ * than ?contains?
+ *
+ * @param aListener The listener which will obtain individual query
+ * results.
+ *
+ * @param aResultLimit Limits the number of results returned to a maximum
+ * value.
+ *
+ * @param aTimeOut The maximum length of time for the query
+ *
+ * @return A context id for the query
+ */
+ long doQuery(in nsIAbDirectory aDirectory,
+ in nsIAbDirectoryQueryArguments aArguments,
+ in nsIAbDirSearchListener aListener,
+ in long aResultLimit,
+ in long aTimeOut);
+
+ /**
+ * Stops an existing query operation if
+ * query operation is asynchronous
+ *
+ * The nsIAbDirectoryQueryResultListener will
+ * be notified when query has stopped
+ *
+ * It is implementation specific if notification
+ * synchronous or asynchronous
+ *
+ * @param contextID
+ * The unique number returned from
+ * the doQuery methods
+ *
+ */
+ void stopQuery (in long contextID);
+};
diff --git a/mailnews/addrbook/public/nsIAbDirectoryQueryProxy.idl b/mailnews/addrbook/public/nsIAbDirectoryQueryProxy.idl
new file mode 100644
index 000000000..68e2923cd
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbDirectoryQueryProxy.idl
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsIAbDirectoryQuery.idl"
+
+[scriptable, uuid(b8034849-1e98-4d39-819c-15ba61a7434f)]
+interface nsIAbDirectoryQueryProxy : nsIAbDirectoryQuery
+{
+ void initiate();
+};
+
diff --git a/mailnews/addrbook/public/nsIAbDirectorySearch.idl b/mailnews/addrbook/public/nsIAbDirectorySearch.idl
new file mode 100644
index 000000000..818684499
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbDirectorySearch.idl
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsISupports.idl"
+
+/**
+ * Searching of cards on a directory.
+ *
+ * The search data is defined in the query
+ * section of the directory URI, for example
+ *
+ * moz-abldapdirectory://ldap1.holland/dc=sun,dc=com?<query>
+ *
+ * If no search data is defined then the methods
+ * will return immediately with no error.
+ */
+[scriptable, uuid(ABF26047-37E3-44FD-A28A-6D37A1B9CCB3)]
+interface nsIAbDirectorySearch : nsISupports
+{
+ /**
+ * Starts a search on the directory.
+ *
+ * If a search is already being performed
+ * it is stopped.
+ *
+ * The results from a search, cards, will
+ * returned by informing the address book
+ * session that a new card has been added
+ * to the directory.
+ *
+ * The nsIAbDirectoryQuery implementation
+ * of the directory component (or a proxy)
+ * may be used as an implementation for
+ * this specialization of query.
+ *
+ * This method is semantically equivalent
+ * to the nsIAbDirectory.getChildCards
+ * method when there is search criteria
+ * defined in the directory uri.
+ *
+ */
+ void startSearch ();
+
+ /**
+ * Stops a search on the directory.
+ *
+ */
+ void stopSearch ();
+};
+
diff --git a/mailnews/addrbook/public/nsIAbItem.idl b/mailnews/addrbook/public/nsIAbItem.idl
new file mode 100644
index 000000000..adda32d96
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbItem.idl
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIMsgHeaderParser;
+interface nsIStringBundle;
+
+/**
+ * A containable item for address books.
+ */
+[scriptable, uuid(bb691a55-cbfe-4cf8-974a-e18cfa845a73)]
+interface nsIAbItem : nsISupports {
+ /**
+ * A universally-unique identifier for this item.
+ *
+ * If this item cannot be associated with a UUID for some reason, it MUST
+ * return the empty string. The empty string MUST NOT be a valid UUID for any
+ * item. Under no circumstances may this function throw an error.
+ *
+ * It is STRONGLY RECOMMENDED that implementations guarantee that this UUID
+ * will not change between two different sessions of the application and that,
+ * if this item is deleted, the UUID will not be reused.
+ *
+ * The format of the UUID for a generic nsIAbItem is purposefully left
+ * undefined, although any item contained by an nsIAbDirectory SHOULD use
+ * nsIAbManager::generateUUID to generate the UUID.
+ */
+ readonly attribute AUTF8String uuid;
+
+ /**
+ * @{
+ * These constants reflect the possible values of the
+ * mail.addr_book.lastnamefirst preferences. They are intended to be used in
+ * generateName, defined below.
+ */
+ const unsigned long GENERATE_DISPLAY_NAME = 0;
+ const unsigned long GENERATE_LAST_FIRST_ORDER = 1;
+ const unsigned long GENERATE_FIRST_LAST_ORDER = 2;
+ /** @} */
+
+ /**
+ * Generate a name from the item for display purposes.
+ *
+ * If this item is an nsIAbCard, then it will use the aGenerateFormat option
+ * to determine the string to return.
+ * If this item is not an nsIAbCard, then the aGenerateFormat option may be
+ * ignored, and the displayName of the item returned.
+ *
+ * @param aGenerateFormat The format to generate as per the GENERATE_*
+ * constants above.
+ * @param aBundle An optional parameter that is a pointer to a string
+ * bundle that holds:
+ * chrome://messenger/locale/addressbook/addressBook.properties
+ * If this bundle is not supplied, then the function
+ * will obtain the bundle itself. If cached by the
+ * caller and supplied to this function, then
+ * performance will be improved over many calls.
+ * @return A string containing the generated name.
+ */
+ AString generateName(in long aGenerateFormat,
+ [optional] in nsIStringBundle aBundle);
+
+ /**
+ * Generate a formatted email address from the card, that can be used for
+ * sending emails.
+ *
+ * @param aExpandList If this card is a list, and this parameter is set
+ * to true, then the list will be expanded to include
+ * the emails of the cards within the list.
+ * @param aGroupMailLists If this card (or the items within this card) is a
+ * list, and this is set to true, then the list will
+ * be expanded in the RFC 2822 group format
+ * "displayname : email1 ; email2 ; etc".
+ * @param aHeaderParser An optional parameter pointing to the
+ * nsIMsgHeaderParser service. If this is not supplied
+ * the function will obtain the service itself. If
+ * cached by the called and supplied to this function,
+ * then performance will be improved over many calls.
+ * @return A string containing a comma-separated list of
+ * formatted addresses.
+ */
+ //AString generateFormattedEmail(in boolean aExpandList,
+ // in boolean aAsGroupMailLists,
+ // [optional] in nsIMsgHeaderParser aHeaderParser);
+
+};
+
diff --git a/mailnews/addrbook/public/nsIAbLDAPAttributeMap.idl b/mailnews/addrbook/public/nsIAbLDAPAttributeMap.idl
new file mode 100644
index 000000000..8b07f68af
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbLDAPAttributeMap.idl
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsILDAPMessage;
+interface nsIAbCard;
+
+/**
+ * A mapping between addressbook properties and ldap attributes.
+ *
+ * Each addressbook property can map to one or more attributes. If
+ * there is no entry in preferences for a field, the getters generally
+ * return null; empty strings are passed through as usual. The intent is
+ * that properties with a non-zero number of attributes can be overridden for
+ * a specific server by supplying a zero-length string. For this to work,
+ * most callers are likely to want to check for both success and a
+ * non-empty string.
+ *
+ * Note that the one exception to this pattern is getAttributes, which
+ * throws NS_ERROR_FAILURE for non-existent property entries, since
+ * XPConnect doesn't like returning null arrays.
+ *
+ * Note that each LDAP attribute can map to at most one addressbook
+ * property. The checkState method is a useful tool in enforcing
+ * this. Failure to enforce it may make it impossible to guarantee
+ * that getProperty will do something consistent and reasonable.
+ *
+ * Maybe someday once we support ldap autoconfig stuff (ie
+ * draft-joslin-config-schema-11.txt), we can simplify this and other
+ * code and only allow a property to map to a single attribute.
+ */
+[scriptable, uuid(fa019fd1-7f3d-417a-8957-154cca0240be)]
+interface nsIAbLDAPAttributeMap : nsISupports
+{
+ /**
+ * Get all the LDAP attributes associated with a given property
+ * name, in order of precedence (highest to lowest).
+ *
+ * @param aProperty the address book property to return attrs for
+ *
+ * @return a comma-separated list of attributes, null if no entry is
+ * present
+ */
+ ACString getAttributeList(in ACString aProperty);
+
+ /**
+ * Get all the LDAP attributes associated with a given property name, in
+ * order of precedence (highest to lowest).
+ *
+ * @param aProperty the address book property to return attrs for
+ *
+ * @return an array of attributes
+ *
+ * @exception NS_ERROR_FAILURE if there is no entry for this property
+ */
+ void getAttributes(in ACString aProperty, out unsigned long aCount,
+ [retval, array, size_is(aCount)] out string aAttrs);
+
+ /**
+ * Get the first (canonical) LDAP attribute associated with a given property
+ * name
+ *
+ * @param aProperty the address book property to return attrs for
+ *
+ * @return the first attribute associated with a given property,
+ * null if there is no entry for this property
+ */
+ ACString getFirstAttribute(in ACString aProperty);
+
+ /**
+ * Set an existing mapping to the comma-separated list of attributes.
+ *
+ * @param aProperty the mozilla addressbook property name
+ *
+ * @param aAttributeList a comma-separated list of attributes in
+ * order of precedence from high to low
+ *
+ * @param aAllowInconsistencies allow changes that would result in
+ * a map with an LDAP attribute associated
+ * with more than one property. Useful for
+ * doing a bunch of sets at once, and
+ * calling checkState at the end.
+ *
+ * @exception NS_ERROR_FAILURE making this change would result in a map
+ * with an LDAP attribute pointing to more
+ * than one property
+ */
+ void setAttributeList(in ACString aProperty, in ACString aAttributeList,
+ in boolean allowInconsistencies);
+
+ /**
+ * Find the Mozilla addressbook property name that this attribute should
+ * map to.
+ *
+ * @return the addressbook property name, null if it's not used in the map
+ */
+ ACString getProperty(in ACString aAttribute);
+
+ /**
+ * Get all attributes that may be used in an addressbook card via this
+ * property map (used for passing to to an LDAP search when you want
+ * everything that could be in a card returned).
+ *
+ * @return a comma-separated list of attribute names
+ *
+ * @exception NS_ERROR_FAILURE there are no attributes in this property map
+ */
+ ACString getAllCardAttributes();
+
+ /**
+ * Get all properties that may be used in an addressbook card via this
+ * property map.
+ *
+ * @return an array of properties
+ *
+ * @exception NS_ERROR_FAILURE there are no attributes in this property map
+ */
+ void getAllCardProperties(out unsigned long aCount,
+ [retval, array, size_is(aCount)] out string aProps);
+
+ /**
+ * Check that no LDAP attributes are listed in more than one property.
+ *
+ * @exception NS_ERROR_FAILURE one or more LDAP attributes are listed
+ * multiple times. The object is now in an
+ * inconsistent state, and should be either
+ * manually repaired or discarded.
+ */
+ void checkState();
+
+ /* These last two methods are really just for the convenience of the caller
+ * and to avoid tons of unnecessary crossing of the XPConnect boundary.
+ */
+
+ /**
+ * Set any attributes specified in the given prefbranch on this object.
+ *
+ * @param aPrefBranchName the pref branch containing all the
+ * property names
+ *
+ * @exception NS_ERROR_FAILURE one or more LDAP attributes are listed
+ * multiple times. The object is now in an
+ * inconsistent state, and should be either
+ * manually repaired or discarded.
+ */
+ void setFromPrefs(in ACString aPrefBranchName);
+
+ /**
+ * Set the properties on an addressbook card from the given LDAP message
+ * using the map in this object.
+ *
+ * @param aCard is the card object whose values are to be set
+ * @param aMessage is the LDAP message to get the values from
+ *
+ * @exception NS_ERROR_FAILURE is thrown if no addressbook properties
+ * are found in the message
+ */
+ void setCardPropertiesFromLDAPMessage(in nsILDAPMessage aMessage,
+ in nsIAbCard aCard);
+};
+
+/**
+ * The nsIAbLDAPAttributeMapService is used to build and hold a cache
+ * of maps.
+ */
+[scriptable, uuid(12e2d589-3c2a-48e4-8c82-b1e6464a0dfd)]
+interface nsIAbLDAPAttributeMapService : nsISupports
+{
+ /**
+ * Accessor to construct or return a cached copy of the attribute
+ * map for a given preference branch. The map is constructed by
+ * first taking the default map (as specified by the
+ * "ldap_2.servers.default.attrmap" prefbranch), and then having any
+ * preferences specified by aPrefBranchName override the defaults.
+ * LDIF import and export code should use the default map.
+ *
+ * @return the requested map
+ *
+ * @exception NS_ERROR_FAILURE error constructing the map;
+ * possibly because of a failure
+ * from checkState()
+ */
+ nsIAbLDAPAttributeMap getMapForPrefBranch(in ACString aPrefBranchName);
+};
+
+
+%{C++
+// test whether one of the getters has actually found an attribute
+#define ATTRMAP_FOUND_ATTR(rv, str) (NS_SUCCEEDED(rv) && !(str).IsEmpty())
+%}
diff --git a/mailnews/addrbook/public/nsIAbLDAPCard.idl b/mailnews/addrbook/public/nsIAbLDAPCard.idl
new file mode 100644
index 000000000..6761623f2
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbLDAPCard.idl
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIAbCard.idl"
+
+interface nsIAbLDAPAttributeMap;
+interface nsILDAPModification;
+interface nsILDAPMessage;
+interface nsIArray;
+
+[scriptable, uuid(2831b3b0-30ef-4070-8ad3-90ae04980e11)]
+interface nsIAbLDAPCard : nsISupports
+{
+ /**
+ * Returns the required information for an LDAP update message.
+ *
+ * @param aAttrMap The map between LDAP attributes and card properties
+ * @param aClassCount The number of objectClass values
+ * @param aClasses The objectClass values that the card needs to have
+ * @param updateType This should be one of:
+ * nsILDAPModification::MOD_ADD
+ * nsILDAPModification::MOD_REPLACE
+ *
+ * @return Returns an array of modifications required to
+ * add or replace the card in the ldap directory.
+ */
+ nsIArray getLDAPMessageInfo(in nsIAbLDAPAttributeMap aAttrMap,
+ in unsigned long aClassCount,
+ [array, size_is(aClassCount)] in string aClasses,
+ in long updateType);
+
+ /**
+ * Builds a relative distinguished name (RDN) with the given set of
+ * attributes.
+ *
+ * @param aAttrMap The map between LDAP attributes and card properties
+ * @param aAttrCount The number of attributes to use for the RDN
+ * @param aAttributes The name of the attributes to use for the RDN
+ *
+ */
+ ACString buildRdn(in nsIAbLDAPAttributeMap aAttrMap,
+ in unsigned long aAttrCount,
+ [array, size_is(aAttrCount)] in string aAttributes);
+
+ /**
+ * Stores meta-properties from a raw LDAP search result.
+ *
+ * @param aMessage The LDAP search result message.
+ *
+ */
+ void setMetaProperties(in nsILDAPMessage aMessage);
+
+ attribute ACString dn;
+};
diff --git a/mailnews/addrbook/public/nsIAbLDAPDirectory.idl b/mailnews/addrbook/public/nsIAbLDAPDirectory.idl
new file mode 100644
index 000000000..1c95ff623
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbLDAPDirectory.idl
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIMutableArray;
+interface nsIFile;
+interface nsIAddrDatabase;
+interface nsIAbLDAPAttributeMap;
+interface nsILDAPURL;
+
+%{C++
+#define kLDAPDirectoryRoot "moz-abldapdirectory://"
+#define kLDAPDirectoryRootLen 22
+%}
+
+/**
+ * XXX This should really inherit from nsIAbDirectory, and some day it will.
+ * But for now, doing that complicates implementation.
+ */
+[scriptable, uuid(90dde295-e354-4d58-Add8-f9b29a95942d)]
+interface nsIAbLDAPDirectory : nsISupports
+{
+ /**
+ * If set, these arrays of nsILDAPControls are passed through to the
+ * nsILDAPOperation that searchExt is called on.
+ */
+ attribute nsIMutableArray searchServerControls;
+ attribute nsIMutableArray searchClientControls;
+
+ /**
+ * The Replication File Name to use.
+ */
+ attribute ACString replicationFileName;
+
+ /**
+ * The version of LDAP protocol in use.
+ */
+ attribute unsigned long protocolVersion;
+
+ /**
+ * The SASL mechanism to use to authenticate to the LDAP server
+ * If this is an empty string, then a simple bind will be performed
+ * A non-zero string is assumed to be the name of the SASL mechanism.
+ * Currently the only supported mechanism is GSSAPI
+ */
+ attribute ACString saslMechanism;
+
+ /**
+ * The AuthDN to use to access the server.
+ */
+ attribute AUTF8String authDn;
+
+ /**
+ * The maximum number of matches that the server will return per a search.
+ */
+ attribute long maxHits;
+
+ /**
+ * The Last Change Number used for replication.
+ */
+ attribute long lastChangeNumber;
+
+ /**
+ * The LDAP server's scoping of the lastChangeNumber.
+ */
+ attribute ACString dataVersion;
+
+ /**
+ * The attribute map that is associated with this directory's server.
+ */
+ readonly attribute nsIAbLDAPAttributeMap attributeMap;
+
+ /**
+ * The LDAP URL for this directory. Note that this differs from
+ * nsIAbDirectory::URI. This attribute will give you a true ldap
+ * url, e.g. ldap://localhost:389/ whereas the uri will give you the
+ * directories rdf uri, e.g. moz-abldapdirectory://<pref base name>/.
+ */
+ attribute nsILDAPURL lDAPURL;
+
+ /**
+ * The replication (offline) file that this database uses.
+ */
+ readonly attribute nsIFile replicationFile;
+
+ /**
+ * A database that is set up for the replication file.
+ */
+ readonly attribute nsIAddrDatabase replicationDatabase;
+
+ /**
+ * The LDAP attributes used to build the Relative Distinguished Name
+ * of new cards, in the form of a comma separated list.
+ *
+ * The default is to use the common name (cn) attribute.
+ */
+ attribute ACString rdnAttributes;
+
+ /**
+ * The LDAP objectClass values added to cards when they are created/added,
+ * in the form of a comma separated list.
+ *
+ * The default is to use the following classes:
+ * top,person,organizationalPerson,inetOrgPerson,mozillaAbPersonAlpha
+ */
+ attribute ACString objectClasses;
+
+};
+
diff --git a/mailnews/addrbook/public/nsIAbLDAPReplicationData.idl b/mailnews/addrbook/public/nsIAbLDAPReplicationData.idl
new file mode 100644
index 000000000..4b811cf2a
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbLDAPReplicationData.idl
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIAbLDAPDirectory;
+interface nsILDAPConnection;
+interface nsILDAPURL;
+interface nsIAbLDAPReplicationQuery;
+interface nsIWebProgressListener;
+
+/**
+ * this service does replication of an LDAP directory to a local Mork AB Database.
+ */
+[scriptable, uuid(e628bbc9-8793-4f0b-bce4-990d399b1fca)]
+interface nsIAbLDAPProcessReplicationData : nsISupports
+{
+ /**
+ * readonly attribute giving the current replication state
+ */
+ readonly attribute int32_t replicationState;
+
+ /**
+ * replication states
+ */
+ const long kIdle = 0;
+ const long kAnonymousBinding = 1;
+ const long kAuthenticatedBinding = 2;
+ const long kSyncServerBinding = 3;
+ const long kSearchingAuthDN = 4;
+ const long kDecidingProtocol = 5;
+ const long kAuthenticating = 6;
+ const long kReplicatingAll = 7;
+ const long kSearchingRootDSE = 8;
+ const long kFindingChanges = 9;
+ const long kReplicatingChanges = 10;
+ const long kReplicationDone = 11;
+
+ /**
+ * readonly attribute giving the current protocol used
+ */
+ readonly attribute int32_t protocolUsed ;
+
+ /**
+ * replication protocols
+ */
+ const long kDefaultDownloadAll = 0;
+ const long kChangeLogProtocol = 1;
+ const long kLCUPProtocol = 2;
+ const long kLastUpdatedTimeStampMethod = 3;
+
+ /**
+ * this method initializes the implementation
+ */
+ void init(in nsIAbLDAPDirectory directory,
+ in nsILDAPConnection connection,
+ in nsILDAPURL url,
+ in nsIAbLDAPReplicationQuery query,
+ in nsIWebProgressListener progressListener);
+
+ /**
+ * this method a aborts the ongoing processing
+ */
+ void abort();
+};
+
diff --git a/mailnews/addrbook/public/nsIAbLDAPReplicationQuery.idl b/mailnews/addrbook/public/nsIAbLDAPReplicationQuery.idl
new file mode 100644
index 000000000..087e81bdd
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbLDAPReplicationQuery.idl
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIWebProgressListener;
+interface nsILDAPURL;
+interface nsILDAPConnection;
+interface nsILDAPOperation;
+interface nsIAbLDAPDirectory;
+
+/**
+ * this interface provides methods to perform LDAP Replication Queries
+ */
+[scriptable, uuid(460a739c-a8c1-4f24-b705-c89d136ab9f5)]
+interface nsIAbLDAPReplicationQuery : nsISupports
+{
+ /**
+ * initialize for the query
+ */
+ void init(in nsIAbLDAPDirectory aDirectory,
+ in nsIWebProgressListener aProgressListener);
+
+ /**
+ * Starts an LDAP query to do replication as needed
+ */
+ void doReplicationQuery();
+
+ /**
+ * Cancels the currently executing query
+ */
+ void cancelQuery();
+
+ /**
+ * this method is the callback when query is done, failed or successful
+ */
+ void done(in boolean aSuccess);
+};
+
+// XXX This interface currently isn't implemented as it didn't work.
+// Bug 311632 should fix it
+[scriptable, uuid(126202D1-4460-11d6-B7C2-00B0D06E5F27)]
+interface nsIAbLDAPChangeLogQuery : nsISupports
+{
+ /**
+ * Starts an LDAP query to find auth DN
+ */
+ void queryAuthDN(in AUTF8String aValueUsedToFindDn);
+
+ /**
+ * Starts an LDAP query to search server's Root DSE
+ */
+ void queryRootDSE();
+
+ /**
+ * Starts an LDAP ChangeLog query to find changelog entries
+ */
+ void queryChangeLog(in AUTF8String aChangeLogDN, in int32_t aLastChangeNo);
+
+ /**
+ * Starts an LDAP query to find changed entries
+ */
+ void queryChangedEntries(in AUTF8String aChangedEntryDN);
+};
+
diff --git a/mailnews/addrbook/public/nsIAbLDAPReplicationService.idl b/mailnews/addrbook/public/nsIAbLDAPReplicationService.idl
new file mode 100644
index 000000000..b58001538
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbLDAPReplicationService.idl
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIWebProgressListener;
+interface nsIAbLDAPDirectory;
+
+/**
+ * this service does replication of an LDAP directory to a local Mork AB Database.
+ */
+[scriptable, uuid(3f499c70-5ceb-4b91-8b7f-62c366859383)]
+interface nsIAbLDAPReplicationService: nsISupports {
+
+ /**
+ * Start Replication of given LDAP directory represented by the URI
+ */
+ void startReplication(in nsIAbLDAPDirectory aDirectory,
+ in nsIWebProgressListener progressListener);
+
+ /**
+ * Cancel Replication of given LDAP directory represented by the URI
+ */
+ void cancelReplication(in nsIAbLDAPDirectory aDirectory);
+
+ /**
+ * callback when replication is done, failure or success
+ */
+ void done(in boolean aSuccess);
+};
+
diff --git a/mailnews/addrbook/public/nsIAbLDIFService.idl b/mailnews/addrbook/public/nsIAbLDIFService.idl
new file mode 100644
index 000000000..74643ccbd
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbLDIFService.idl
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsISupports.idl"
+
+interface nsIFile;
+interface nsIAddrDatabase;
+
+[scriptable, uuid(7afaa95f-0b1c-4d8a-a65f-bb5073ed6d39)]
+interface nsIAbLDIFService : nsISupports {
+
+ /**
+ * Determine if a file is likely to be an LDIF file based on field
+ * names that commonly appear in LDIF files.
+ *
+ * @param aSrc The file to examine
+ *
+ * @return true if the file appears to be of LDIF type,
+ * false otherwise
+ */
+ boolean isLDIFFile(in nsIFile aSrc);
+
+ /**
+ * Imports a file into the specified address book.
+ *
+ * @param aDb The address book to import addresses into.
+ *
+ * @param aSrc The file to import addresses from.
+ *
+ * @param aStoreLocAsHome Stores the address as a home rather than work
+ * address.
+ *
+ * @param aProgress May be null, but if a pointer is supplied,
+ * then it will be updated regularly with the
+ * current position of reading from the file.
+ *
+ */
+ void importLDIFFile(in nsIAddrDatabase aDb, in nsIFile aSrc, in boolean aStoreLocAsHome, inout unsigned long aProgress);
+};
diff --git a/mailnews/addrbook/public/nsIAbListener.idl b/mailnews/addrbook/public/nsIAbListener.idl
new file mode 100644
index 000000000..cc9761abf
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbListener.idl
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+typedef unsigned long abListenerNotifyFlagValue;
+
+/**
+ * nsIAbListener
+ *
+ * Implement this interface to receive notifications of address book
+ * items being added, removed or changed with loaded address books.
+ *
+ * Subscribe to events by using nsIAbManager.
+ */
+[scriptable, uuid(b3ca8745-2dad-4032-ae2f-0b8622f32697)]
+interface nsIAbListener : nsISupports {
+ /**
+ * These flags are used when registering the listener with nsIAbManager to
+ * specify when to receive notifications of address book updates.
+ */
+
+ /**
+ * An address book, mailing list or card is added.
+ */
+ const abListenerNotifyFlagValue itemAdded = 0x1;
+ /**
+ * A mailing list or card is removed from an address book.
+ */
+ const abListenerNotifyFlagValue directoryItemRemoved = 0x2;
+ /**
+ * An address book is removed
+ */
+ const abListenerNotifyFlagValue directoryRemoved = 0x4;
+ /**
+ * An address book, mailing list or card is changed.
+ */
+ const abListenerNotifyFlagValue itemChanged = 0x8;
+ /**
+ * All of the above notifications are to be received.
+ */
+ const abListenerNotifyFlagValue all = 0xFFFFFFFF;
+
+ /**
+ * Called when an address book item (book, card or list) is added
+ *
+ * @param parentDir The parent of the item being added.
+ *
+ * @param item The item being added to the database (a
+ * directory or card).
+ *
+ */
+ void onItemAdded(in nsISupports parentDir, in nsISupports item);
+
+ /**
+ * Called when an address book, mailing list or card is removed. This
+ * is partially configurable when setting up the listener via
+ * nsIAddrBookSession
+ *
+ * @param parentDir The parent of the item being removed, this
+ * may be an empty directory in the case of a
+ * top level address book.
+ *
+ * @param item The item being removed from the database.
+ *
+ */
+ void onItemRemoved(in nsISupports parentDir, in nsISupports item);
+
+ /**
+ * Called when an address book item is changed. Note the current
+ * implementation means that property is either the literal string "DirName"
+ * or null, with oldValue and newValue being specified if the property is
+ * "DirName" otherwise they are null.
+ *
+ * @param item The item being updated (a directory or a
+ * card).
+ *
+ * @param property The property of the item being changed.
+ *
+ * @param oldValue The old value of the item property being
+ * changed if it is known, null otherwise.
+ *
+ * @param newValue The new value of the item property being
+ * changed.
+ *
+ */
+ void onItemPropertyChanged(in nsISupports item, in string property, in wstring oldValue, in wstring newValue);
+};
diff --git a/mailnews/addrbook/public/nsIAbMDBDirectory.idl b/mailnews/addrbook/public/nsIAbMDBDirectory.idl
new file mode 100644
index 000000000..200fcd8ee
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbMDBDirectory.idl
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+interface nsIAbDirectory;
+interface nsIAbCard;
+interface nsIAddrDatabase;
+
+%{C++
+#define kMDBDirectoryRoot "moz-abmdbdirectory://"
+#define kMDBDirectoryRootLen 21
+%}
+
+[scriptable, uuid(744072be-1ba0-46bc-af24-46e22567a2ea)]
+interface nsIAbMDBDirectory : nsISupports {
+
+ // Creates a directory component from the
+ // uriName, adds it to its children and returns
+ // the component
+ nsIAbDirectory addDirectory(in string uriName);
+
+ /**
+ * Supplies a nsIFile point to the database file for this directory
+ *
+ * @exception NS_ERROR_NOT_INITIALIZED If there is no filename preference
+ * present or it is empty
+ */
+ readonly attribute nsIFile databaseFile;
+
+ /**
+ * Supplies a nsIAddrDatabase that uses the databaseFile. See also
+ * databaseFile for possible exceptions.
+ */
+ readonly attribute nsIAddrDatabase database;
+
+ // Mail list specific
+ //
+
+ // Removes all elements from the addressLists
+ // property
+ [noscript] void removeElementsFromAddressList();
+
+ // Specific to a directory which stores mail lists
+ //
+
+ // Adds a directory to the addressLists attribute
+ void addMailListToDirectory(in nsIAbDirectory mailList);
+
+ // Specific to a directory which is a mail list
+ //
+
+ // Copies mail list properties from the srcList
+ void copyDBMailList(in nsIAbMDBDirectory srcListDB);
+
+ // Adds a card to the addressList attribute
+ void addAddressToList(in nsIAbCard card);
+
+ // Removes items from the addressLists member
+ void removeEmailAddressAt(in unsigned long aIndex);
+
+ attribute unsigned long dbRowID;
+
+ // Empty implementation, called by the data base
+ [noscript] void notifyDirItemAdded(in nsISupports item);
+
+ [noscript] void clearDatabase();
+};
diff --git a/mailnews/addrbook/public/nsIAbManager.idl b/mailnews/addrbook/public/nsIAbManager.idl
new file mode 100644
index 000000000..49a585544
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbManager.idl
@@ -0,0 +1,190 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIAbListener.idl"
+
+interface mozIDOMWindowProxy;
+interface nsIAbDirectory;
+interface nsIAbCard;
+interface nsIAbDirectoryProperties;
+interface nsIFile;
+interface nsISimpleEnumerator;
+interface nsIAbBooleanExpression;
+
+/**
+ * nsIAbManager is an interface to the main address book mananger
+ * via the contract id "@mozilla.org/abmanager;1"
+ *
+ * It contains the main functions to create and delete address books as well
+ * as some helper functions.
+ */
+[scriptable, uuid(ea0d8b3d-a549-4874-82d8-3a82cee2a3f1)]
+interface nsIAbManager : nsISupports
+{
+ /**
+ * Returns an enumerator containing all the top-level directories
+ * (non-recursive)
+ */
+ readonly attribute nsISimpleEnumerator directories;
+
+ /**
+ * Returns the directory that represents the supplied URI.
+ *
+ * @param aURI The URI of the address book to find.
+ * @return The found address book.
+ */
+ nsIAbDirectory getDirectory(in ACString aURI);
+
+ /**
+ * Returns the directory that has the supplied dirPrefId.
+ *
+ * @param aDirPrefId The dirPrefId of the directory.
+ * @return The found AB directory.
+ */
+ nsIAbDirectory getDirectoryFromId(in ACString aDirPrefId);
+
+ /**
+ * Creates a new address book.
+ *
+ * @param aDirName The description of the address book.
+ * @param aURI The URI for the address book. This is specific to each
+ * type of address book.
+ * @param aType The type of the address book (see nsDirPrefs.h)
+ * @param aPrefName Overrides the default of ldap_2.servers.<aDirName>
+ * (note that the caller must ensure its uniqueness).
+ */
+ ACString newAddressBook(in AString aDirName, in ACString aURI,
+ in unsigned long aType,
+ [optional] in ACString aPrefName);
+
+ /**
+ * Deletes an address book.
+ *
+ * @param aURI The URI for the address book. This is specific to each
+ * type of address book.
+ */
+ void deleteAddressBook(in ACString aURI);
+
+ /**
+ * Exports an address book, it will provide a dialog to the user for the
+ * location to save the file to and will then save the address book to media.
+ *
+ * @param aParentWin Parent Window for the file save dialog to use.
+ * @param aDirectory The directory to export.
+ */
+ void exportAddressBook(in mozIDOMWindowProxy aParentWin, in nsIAbDirectory aDirectory);
+
+ /**
+ * Adds a nsIAbListener to receive notifications of address book updates
+ * according to the specified notifyFlags.
+ *
+ * @param aListener The listener that is to receive updates.
+ * @param aNotifyFlags A bitwise-or of abListenerNotifyFlagValue items
+ * specifying which notifications to receive. See
+ * nsIAbListener for possible values.
+ */
+ void addAddressBookListener(in nsIAbListener aListener,
+ in abListenerNotifyFlagValue aNotifyFlags);
+
+ /**
+ * Removes a nsIAbListener from receive notifications of address book
+ * updates.
+ *
+ * @param aListener The listener that is to no longer receive updates.
+ */
+ void removeAddressBookListener(in nsIAbListener aListener);
+
+ /**
+ * Call to notify the registered listeners when a property on an item has
+ * changed.
+ *
+ * @param aItem The items that has changed (e.g. an nsIAbDirectory)
+ * @param aProperty The property that has changed (e.g. DirName)
+ * @param aOldValue The old value of the property.
+ * @param aNewValue The new value of the property.
+ */
+ void notifyItemPropertyChanged(in nsISupports aItem,
+ in string aProperty,
+ in wstring aOldValue,
+ in wstring aNewValue);
+
+ /**
+ * Call to notify the registered listeners when a directory item is added.
+ *
+ * @param aParentDirectory The parent directory of the item that has been
+ * added.
+ * @param aItem The item that has been added.
+ */
+ void notifyDirectoryItemAdded(in nsIAbDirectory aParentDirectory,
+ in nsISupports aItem);
+
+ /**
+ * Call to notify the registered listeners when a directory item is removed.
+ *
+ * @param aParentDirectory The parent directory of the item that has been
+ * removed.
+ * @param aItem The item that has been removed.
+ */
+ void notifyDirectoryItemDeleted(in nsIAbDirectory aParentDirectory,
+ in nsISupports aItem);
+
+ /**
+ * Call to notify the registered listeners when a directory is removed.
+ *
+ * @param aParentDirectory The parent directory of the directory that has
+ * been removed.
+ * @param aDirectory The directory that has been removed.
+ */
+ void notifyDirectoryDeleted(in nsIAbDirectory aParentDirectory,
+ in nsISupports aDirectory);
+
+ /**
+ * Returns the user profile directory. NOTE: this should not be used
+ * as it may go away soon.
+ */
+ readonly attribute nsIFile userProfileDirectory;
+
+ /**
+ * Finds out if the mailing list name exists in any *mork/MDB* based
+ * address book
+ *
+ * @param aName The name of the list to try and find.
+ *
+ * @return True if the name exists.
+ */
+ boolean mailListNameExists(in wstring name);
+
+ /**
+ * Translates an escaped vcard string into a nsIAbCard.
+ *
+ * @param escapedVCardStr The string containing the vcard.
+ *
+ * @return A card containing the translated vcard data.
+ */
+ nsIAbCard escapedVCardToAbCard(in string escapedVCardStr);
+
+ /**
+ * Generates a UUID from a (directory ID, local ID) tuple.
+ *
+ * Use of this method is preferred in such cases, since it is designed to work
+ * with other methods of this interface.
+ *
+ * @param directoryId The directory ID.
+ * @param localId The per-directory ID.
+ * @return A string to use for the UUID.
+ */
+ AUTF8String generateUUID(in AUTF8String directoryId, in AUTF8String localId);
+
+
+ /**
+ * A utility function that converts an nsIAbDirectory query string to an
+ * nsIAbBooleanExpression.
+ *
+ * @param aQueryString The nsIAbDirectory query string
+ * @return an nsIAbBooleanExpression for the query string
+ */
+ nsIAbBooleanExpression convertQueryStringToExpression(in AUTF8String aQueryString);
+};
diff --git a/mailnews/addrbook/public/nsIAbView.idl b/mailnews/addrbook/public/nsIAbView.idl
new file mode 100644
index 000000000..cd591a1e5
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAbView.idl
@@ -0,0 +1,109 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIAbCard;
+interface nsIAbDirectory;
+interface nsIArray;
+
+/// Define a class using this interface to listen to updates from nsIAbView.
+[scriptable, uuid(79ad5d6e-1dd2-11b2-addd-f547dab50d75)]
+interface nsIAbViewListener : nsISupports
+{
+ /// Called when the selection is changed in the tree
+ void onSelectionChanged();
+
+ /// Called when the total count of cards is changed.
+ void onCountChanged(in long total);
+};
+
+/**
+ * This interface and its associated nsAbView object provides an interface
+ * to allow a tree to be associated with an address book, and the results
+ * to be displayed in that tree.
+ *
+ * If you wish for the tree to display the results of a different address
+ * book, then call setView again. There is no need to delete and recreate the
+ * nsAbView object. If you wish to clear the view, then just call clearView.
+ */
+[scriptable, uuid(45e2fa9f-0b59-4090-a2fa-fb7042cf64a2)]
+interface nsIAbView : nsISupports
+{
+ /**
+ * Sets up the nsIAbView to look at the specified directory. This may be
+ * called multiple times.
+ *
+ * @param aDirectory The directory to search, this may be a directory
+ * with a query string.
+ * @param aViewListener An optional listener.
+ * @param aSortColumn The column to sort by. See the xul element with
+ * id abResultsTreeCols for possible values.
+ * @param aSortDirection The sort direction to use ("ascending"/"descending")
+ * @return The actual sortColumn (various switching of apps
+ * could cause the persisted sortColumn to be bogus).
+ */
+ AString setView(in nsIAbDirectory aAddressBook,
+ in nsIAbViewListener aAbViewListener,
+ in AString aSortColumn,
+ in AString aSortDirection);
+
+ /**
+ * Clears the view and releases any locally held copies of the address book
+ * directory. This should be called when the view is no longer required, e.g.
+ * on unload.
+ */
+ void clearView();
+
+ /**
+ * Sorts the tree by the specified parameters.
+ *
+ * @param aSortColumn The column to sort by. See the xul element with
+ * id abResultsTreeCols for possible values.
+ * @param aSortDirection The sort direction to use ("ascending"/"descending")
+ * @param aResort The function DOES optimize for the case when sortColumn
+ * and sortDirection is identical since the last call.
+ * If an unconditional resort is needed, set this to true.
+ */
+ void sortBy(in wstring aSortColumn, in wstring aSortDirection,
+ [optional] in boolean aResort);
+
+ /// Returns the current sort column
+ readonly attribute AString sortColumn;
+
+ /// Returns the current sort direction
+ readonly attribute AString sortDirection;
+
+ /**
+ * Returns the current directory that this view is hooked up to. May be
+ * null if no directory has been set.
+ */
+ readonly attribute nsIAbDirectory directory;
+
+ /**
+ * Returns the card associated with the given row.
+ *
+ * @param aRow The row from which to return the card.
+ * @return A card associated with the row, or null if row is not valid.
+ */
+ nsIAbCard getCardFromRow(in long aRow);
+
+ /// Selects all rows in the view.
+ void selectAll();
+
+ /// Deletes all the selected cards (no prompts are given).
+ void deleteSelectedCards();
+
+ /**
+ * Swaps the first and last name order, and updates the appropriate
+ * preference.
+ */
+ void swapFirstNameLastName();
+
+ /**
+ * Returns an array of the currently selected addresses.
+ */
+ readonly attribute nsIArray selectedAddresses;
+};
diff --git a/mailnews/addrbook/public/nsIAddbookUrl.idl b/mailnews/addrbook/public/nsIAddbookUrl.idl
new file mode 100644
index 000000000..5f2676dde
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAddbookUrl.idl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIURI.idl"
+
+[scriptable, uuid(6EB9D874-01AA-11d4-8FBE-000064657374)]
+interface nsIAddbookUrlOperation
+{
+ const long InvalidUrl = 0;
+ const long PrintAddressBook = 1;
+ const long AddVCard = 2;
+};
+
+[uuid(5f965083-e866-4bfb-ba40-13c344395798)]
+interface nsIAddbookUrl : nsIURI {
+ readonly attribute long addbookOperation;
+};
diff --git a/mailnews/addrbook/public/nsIAddrDBAnnouncer.idl b/mailnews/addrbook/public/nsIAddrDBAnnouncer.idl
new file mode 100644
index 000000000..735ca64cb
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAddrDBAnnouncer.idl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIAbCard.idl"
+#include "nsIAbDirectory.idl"
+
+interface nsIAddrDBListener;
+
+[scriptable, uuid(166b19a1-1235-4613-9601-816dedc48c9e)]
+interface nsIAddrDBAnnouncer : nsISupports {
+
+ void addListener(in nsIAddrDBListener listener);
+ void removeListener(in nsIAddrDBListener listener);
+
+ void notifyCardAttribChange(in unsigned long abCode);
+
+ /**
+ * Notify all the listeners of the database about an event performed
+ * on a card entry.
+ *
+ * @param aAbCode The code to indicate the type of event
+ * (see nsAddrDatabase.h AB_NOTIFY_CODE for values).
+ * @param aCard The card entry on which the event occurred.
+ * @param aParent The parent of card entry. This is set by
+ * object which performs the operation.
+ */
+ void notifyCardEntryChange(in unsigned long aAbCode,
+ in nsIAbCard aCard,
+ in nsIAbDirectory aParent);
+
+ void notifyAnnouncerGoingAway();
+};
diff --git a/mailnews/addrbook/public/nsIAddrDBListener.idl b/mailnews/addrbook/public/nsIAddrDBListener.idl
new file mode 100644
index 000000000..f407313e2
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAddrDBListener.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsISupports.idl"
+#include "nsIAbCard.idl"
+#include "nsIAbDirectory.idl"
+
+interface nsIAddrDBAnnouncer;
+
+[scriptable, uuid(5d7e5a7a-1ac9-46dc-abfd-758c98be26e9)]
+interface nsIAddrDBListener : nsISupports {
+
+ void onCardAttribChange(in unsigned long abCode);
+
+ /**
+ * Handle the card entry change event.
+ *
+ * @param aAbCode The code to indicate the type of event
+ * (see nsAddrDatabase.h AB_NOTIFY_CODE for values).
+ * @param aCard The card entry on which the event occurred.
+ * @param aParent The parent of card entry.
+ * If set to null, the event can be ignored.
+ * This happens during import & sync operations when
+ * listeners of a database need not be notified about
+ * card entry changes.
+ */
+ void onCardEntryChange (in unsigned long aAbCode,
+ in nsIAbCard aCard,
+ in nsIAbDirectory aParent);
+
+ void onListEntryChange (in unsigned long abCode,
+ in nsIAbDirectory list);
+ void onAnnouncerGoingAway();
+
+};
diff --git a/mailnews/addrbook/public/nsIAddrDatabase.idl b/mailnews/addrbook/public/nsIAddrDatabase.idl
new file mode 100644
index 000000000..e387b260f
--- /dev/null
+++ b/mailnews/addrbook/public/nsIAddrDatabase.idl
@@ -0,0 +1,311 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIAddrDBAnnouncer.idl"
+#include "nsIAbCard.idl"
+#include "nsIAbDirectory.idl"
+
+interface nsIFile;
+interface nsIMdbTableRowCursor;
+interface nsIMdbEnv;
+interface nsIMdbRow;
+interface nsIArray;
+interface nsISimpleEnumerator;
+
+%{C++
+// this is the prefix we for attributes that are specific
+// to the mozilla addressbook, and weren't in 4.x and aren't specified in
+// RFC 2789. used when exporting and import LDIF
+// see nsTextAddress.cpp, nsAddressBook.cpp
+#define MOZ_AB_LDIF_PREFIX "mozilla"
+
+// note, GeneratedName is not a real column
+// if you change any of this, make sure to change
+// Get / Set CardValue in nsAbCardProperty.cpp
+#define kPhoneticNameColumn "_PhoneticName"
+#define kAddressCharSetColumn "AddrCharSet"
+#define kMailListName "ListName"
+#define kMailListNickName "ListNickName"
+#define kMailListDescription "ListDescription"
+#define kMailListTotalAddresses "ListTotalAddresses"
+// not shown in the UI
+#define kLowerPriEmailColumn "LowercasePrimaryEmail"
+#define kLower2ndEmailColumn "LowercaseSecondEmail"
+
+// Palm Integration
+#define CARD_ATTRIB_PALMID "PalmRecId"
+#define CARD_ATTRIB_DISPLAY "DisplayName"
+
+%}
+
+[scriptable, uuid(20d4c6c3-0460-403e-aa9c-813654641566)]
+interface nsAddrDBCommitType
+{
+ const long kLargeCommit = 1;
+ const long kSessionCommit = 2;
+ const long kCompressCommit = 3;
+};
+
+[scriptable, uuid(c54973e4-d251-4b93-a0d0-81a616225061)]
+interface nsIAddrDatabase : nsIAddrDBAnnouncer {
+
+ /**
+ * Path to the address book database that this instance represents.
+ */
+ attribute nsIFile dbPath;
+ nsIAddrDatabase open(in nsIFile dbFile, in boolean create, in boolean upgrading);
+
+ void close(in boolean forceCommit);
+
+ /**
+ * Open the MDB database synchronously creating it if required. If
+ * successful, this routine will set up the m_mdbStore and m_mdbEnv of the
+ * database object so other database calls can work.
+ *
+ * @param dbName The location of the database file
+ * to open.
+ * @param create If set to true, will create the
+ * database file if it does not
+ * already exist.
+ * @exception NS_ERROR_FILE_NOT_FOUND The file was not found at the
+ * specified location (and create was
+ * false).
+ * @exception NS_ERROR_FILE_ACCESS_DENIED The file could not be opened as
+ * access was denied.
+ */
+ void openMDB(in nsIFile dbName, in boolean create);
+ void closeMDB(in boolean commit);
+
+ void commit(in unsigned long commitType);
+ void forceClosed();
+
+ /**
+ * Create a new card and add to the database
+ *
+ * @param aNewCard the card to be added
+ * @param aNotify if set to true, all the listeners of the
+ * database will be notified.
+ * @param aParent parent directory or mailing list to which the
+ * card is added. If set to null, listeners of the
+ * database will not be notified of card creation.
+ */
+ void createNewCardAndAddToDB(in nsIAbCard aNewCard, in boolean aNotify, in nsIAbDirectory aParent);
+
+ void createNewListCardAndAddToDB(in nsIAbDirectory list, in unsigned long listRowID, in nsIAbCard newCard, in boolean aNotify);
+
+ /**
+ * Create a new mailing list and add to the database
+ *
+ * @param aNewList the mailing list to be added.
+ * @param aNotify if set to true, all the listeners of the
+ * database will be notified.
+ * @param aParent parent directory to which the mailing list
+ * is added. If set to null, listeners of the database
+ * will not be notified of mailing list creation.
+ */
+ void createMailListAndAddToDB(in nsIAbDirectory aNewList, in boolean aNotify, in nsIAbDirectory aParent);
+
+ /**
+ * Enumerate the cards in the directory. The enumerator will return the
+ * cards associated with mailing lists too.
+ *
+ * @param directory the directory of which to enumerate the cards.
+ * @return an enumerator.
+ */
+ nsISimpleEnumerator enumerateCards(in nsIAbDirectory directory);
+
+ /**
+ * Enumerate the cards associated with the mailing lists in the directory.
+ *
+ * @param directory the directory of which to enumerate the cards.
+ * @return an enumerator.
+ */
+ nsISimpleEnumerator enumerateListAddresses(in nsIAbDirectory directory);
+
+ void getMailingListsFromDB(in nsIAbDirectory parentDir);
+
+ /**
+ * Delete a card from the database.
+ *
+ * @param aCard the card to be deleted.
+ * @param aNotify if set to true, all the listeners of the
+ * database will be notified.
+ * @param aParent parent directory from which the card
+ * is to be deleted. If set to null, listeners of
+ * the database will not be notified of card deletion.
+ */
+ void deleteCard(in nsIAbCard aCard, in boolean aNotify, in nsIAbDirectory aParent);
+
+ /**
+ * Edit a card in the database.
+ *
+ * @param aCard the card to be edited.
+ * @param aNotify if set to true, all the listeners of the
+ * database will be notified.
+ * @param aParent parent directory in which the card
+ * is to be edited. If set to null, listeners of
+ * the database will not be notified of card entry
+ * change.
+ */
+ void editCard(in nsIAbCard aCard, in boolean aNotify, in nsIAbDirectory aParent);
+ boolean containsCard(in nsIAbCard card);
+ /**
+ * Deletes a mailing list from the directory
+ *
+ * @param aMailList The nsIAbDirectory implementation of the mailing
+ * list that is to be deleted.
+ * @param aParent The parent of the mailing list that is being
+ * deleted. If this is supplied, then a notification
+ * of card entry change in the database will be made.
+ */
+ void deleteMailList(in nsIAbDirectory aMailList,
+ [optional] in nsIAbDirectory aParent);
+ void editMailList(in nsIAbDirectory mailList, in nsIAbCard listCard, in boolean aNotify);
+ boolean containsMailList(in nsIAbDirectory mailList);
+ void deleteCardFromMailList(in nsIAbDirectory mailList, in nsIAbCard card, in boolean aNotify);
+
+ /**
+ * Gets the first card which matches the attribute/value pair supplied.
+ *
+ * @param aDirectory The current nsIAbDirectory associated with this
+ * instance of the database.
+ * @param aName The attribute to look up the value in.
+ * @param aUTF8Value The value to look up in UTF8 format.
+ * @param aCaseInsensitive Set to true for case-insenstive matching.
+ * @result Returns an nsIAbCard if one is found, otherwise
+ * NULL.
+ */
+ nsIAbCard getCardFromAttribute(in nsIAbDirectory aDirectory, in string aName,
+ in AUTF8String aUTF8Value,
+ in boolean aCaseInsensitive);
+
+ /**
+ * Gets all cards which matches the attribute/value pair supplied.
+ *
+ * @param aDirectory The current nsIAbDirectory associated with this
+ * instance of the database.
+ * @param aName The attribute to look up the value in.
+ * @param aUTF8Value The value to look up in UTF8 format.
+ * @param aCaseInsensitive Set to true for case-insenstive matching.
+ * @result Returns an nsISimpleEnumerator of nsIAbCard
+ * instances.
+ */
+ nsISimpleEnumerator getCardsFromAttribute(in nsIAbDirectory aDirectory,
+ in string aName,
+ in AUTF8String uUTF8Value,
+ in boolean aCaseInsensitive);
+
+ boolean findMailListbyUnicodeName(in wstring listName);
+
+ void getCardCount(out uint32_t count);
+
+ [noscript] readonly attribute nsIMdbRow newRow;
+ [noscript] readonly attribute nsIMdbRow newListRow;
+ [noscript] void addCardRowToDB(in nsIMdbRow newRow);
+ [noscript] void addLdifListMember(in nsIMdbRow row, in string value);
+ [noscript] void addFirstName(in nsIMdbRow row, in string value);
+ [noscript] void addLastName(in nsIMdbRow row, in string value);
+ [noscript] void addPhoneticFirstName(in nsIMdbRow row, in string value);
+ [noscript] void addPhoneticLastName(in nsIMdbRow row, in string value);
+ [noscript] void addDisplayName(in nsIMdbRow row, in string value);
+ [noscript] void addNickName(in nsIMdbRow row, in string value);
+ [noscript] void addPrimaryEmail(in nsIMdbRow row, in string value);
+ [noscript] void add2ndEmail(in nsIMdbRow row, in string value);
+ [noscript] void addWorkPhone(in nsIMdbRow row, in string value);
+ [noscript] void addHomePhone(in nsIMdbRow row, in string value);
+ [noscript] void addFaxNumber(in nsIMdbRow row, in string value);
+ [noscript] void addPagerNumber(in nsIMdbRow row, in string value);
+ [noscript] void addCellularNumber(in nsIMdbRow row, in string value);
+ [noscript] void addWorkPhoneType(in nsIMdbRow row, in string value);
+ [noscript] void addHomePhoneType(in nsIMdbRow row, in string value);
+ [noscript] void addFaxNumberType(in nsIMdbRow row, in string value);
+ [noscript] void addPagerNumberType(in nsIMdbRow row, in string value);
+ [noscript] void addCellularNumberType(in nsIMdbRow row, in string value);
+ [noscript] void addHomeAddress(in nsIMdbRow row, in string value);
+ [noscript] void addHomeAddress2(in nsIMdbRow row, in string value);
+ [noscript] void addHomeCity(in nsIMdbRow row, in string value);
+ [noscript] void addHomeState(in nsIMdbRow row, in string value);
+ [noscript] void addHomeZipCode(in nsIMdbRow row, in string value);
+ [noscript] void addHomeCountry(in nsIMdbRow row, in string value);
+ [noscript] void addWorkAddress(in nsIMdbRow row, in string value);
+ [noscript] void addWorkAddress2(in nsIMdbRow row, in string value);
+ [noscript] void addWorkCity(in nsIMdbRow row, in string value);
+ [noscript] void addWorkState(in nsIMdbRow row, in string value);
+ [noscript] void addWorkZipCode(in nsIMdbRow row, in string value);
+ [noscript] void addWorkCountry(in nsIMdbRow row, in string value);
+ [noscript] void addJobTitle(in nsIMdbRow row, in string value);
+ [noscript] void addDepartment(in nsIMdbRow row, in string value);
+ [noscript] void addCompany(in nsIMdbRow row, in string value);
+ [noscript] void addAimScreenName(in nsIMdbRow row, in string value);
+ [noscript] void addAnniversaryYear(in nsIMdbRow row, in string value);
+ [noscript] void addAnniversaryMonth(in nsIMdbRow row, in string value);
+ [noscript] void addAnniversaryDay(in nsIMdbRow row, in string value);
+ [noscript] void addSpouseName(in nsIMdbRow row, in string value);
+ [noscript] void addFamilyName(in nsIMdbRow row, in string value);
+ [noscript] void addDefaultAddress(in nsIMdbRow row, in string value);
+ [noscript] void addCategory(in nsIMdbRow row, in string value);
+ [noscript] void addWebPage1(in nsIMdbRow row, in string value);
+ [noscript] void addWebPage2(in nsIMdbRow row, in string value);
+ [noscript] void addBirthYear(in nsIMdbRow row, in string value);
+ [noscript] void addBirthMonth(in nsIMdbRow row, in string value);
+ [noscript] void addBirthDay(in nsIMdbRow row, in string value);
+ [noscript] void addCustom1(in nsIMdbRow row, in string value);
+ [noscript] void addCustom2(in nsIMdbRow row, in string value);
+ [noscript] void addCustom3(in nsIMdbRow row, in string value);
+ [noscript] void addCustom4(in nsIMdbRow row, in string value);
+ [noscript] void addNotes(in nsIMdbRow row, in string value);
+ [noscript] void addPreferMailFormat(in nsIMdbRow row, in unsigned long value);
+ [noscript] void addPopularityIndex(in nsIMdbRow row, in unsigned long value);
+
+ [noscript] void addListName(in nsIMdbRow row, in string value);
+ [noscript] void addListNickName(in nsIMdbRow row, in string value);
+ [noscript] void addListDescription(in nsIMdbRow row, in string value);
+ [noscript] void addListDirNode(in nsIMdbRow listRow);
+
+ /**
+ * use for getting and setting generic string attributes
+ * like _AimScreenName
+ */
+ void setCardValue(in nsIAbCard card, in string name, in wstring value, in boolean notify);
+ wstring getCardValue(in nsIAbCard card, in string name);
+
+ /**
+ * Returns an array of the deleted cards currently stored in the mork file.
+ */
+ readonly attribute nsIArray deletedCardList;
+
+ /**
+ * Returns the count of the deleted card currently stored in the mork file.
+ */
+ readonly attribute unsigned long deletedCardCount;
+
+ /**
+ * Add the column representing the card to the mailing list row
+ * in the database.
+ *
+ * @param aPCard the card to be added.
+ * @param aPListRow the row to which the column will be added.
+ * @param aPos the position of the card in the mailing list.
+ * @param aPNewCard a pointer to hold the new card added to the row.
+ * @param aInMailingList If set to true, the card is already present
+ * in the mailing list
+ * @param aParent parent mailing list to which the card
+ * is added. If set to null, listeners of the
+ * database will not be notified of card creation.
+ * @param aRoot If the card is created while creating a new mailing
+ * list, its set to the parent addressbook.
+ * Set to null in other case.
+ */
+ void AddListCardColumnsToRow(in nsIAbCard aPCard,
+ in nsIMdbRow aPListRow,
+ in unsigned long aPos,
+ out nsIAbCard aPNewCard,
+ in boolean aInMailingList,
+ in nsIAbDirectory aParent,
+ in nsIAbDirectory aRoot);
+ void InitCardFromRow(in nsIAbCard aNewCard,in nsIMdbRow aCardRow);
+ void SetListAddressTotal(in nsIMdbRow aListRow, in uint32_t aTotal);
+ nsIMdbRow FindRowByCard(in nsIAbCard aCard);
+};
diff --git a/mailnews/addrbook/public/nsIMsgVCardService.idl b/mailnews/addrbook/public/nsIMsgVCardService.idl
new file mode 100644
index 000000000..e3e3411ca
--- /dev/null
+++ b/mailnews/addrbook/public/nsIMsgVCardService.idl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+%{C++
+#include "nsVCardObj.h"
+%}
+
+[ptr] native VObject_ptr(VObject);
+[ptr] native VObjectIterator_ptr(VObjectIterator);
+[ptr] native const_char_ptr(const char);
+
+[uuid(8b6ae917-676d-4f1f-bbad-2ecc9be0d9b1)]
+interface nsIMsgVCardService : nsISupports {
+ [noscript, notxpcom] void cleanVObject(in VObject_ptr o);
+ [noscript, notxpcom] VObject_ptr nextVObjectInList(in VObject_ptr o);
+ [noscript, notxpcom] VObject_ptr parse_MIME(in string input, in unsigned long len);
+ [noscript, notxpcom] charPtr fakeCString(in VObject_ptr o);
+ [noscript, notxpcom] VObject_ptr isAPropertyOf(in VObject_ptr o, in string id);
+ [noscript, notxpcom] charPtr writeMemoryVObjects(in string s, out long len, in VObject_ptr list, in boolean expandSpaces);
+ [noscript, notxpcom] VObject_ptr nextVObject(in VObjectIterator_ptr i);
+ [noscript, notxpcom] void initPropIterator(in VObjectIterator_ptr i, in VObject_ptr o);
+ [noscript, notxpcom] long moreIteration(in VObjectIterator_ptr i);
+ [noscript, notxpcom] const_char_ptr vObjectName(in VObject_ptr o);
+ [noscript, notxpcom] charPtr vObjectAnyValue(in VObject_ptr o);
+};
diff --git a/mailnews/addrbook/src/moz.build b/mailnews/addrbook/src/moz.build
new file mode 100644
index 000000000..648958cf2
--- /dev/null
+++ b/mailnews/addrbook/src/moz.build
@@ -0,0 +1,93 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'nsAbDirProperty.h',
+ 'nsDirPrefs.h',
+ 'nsVCardObj.h',
+]
+
+SOURCES += [
+ 'nsAbAddressCollector.cpp',
+ 'nsAbBooleanExpression.cpp',
+ 'nsAbBSDirectory.cpp',
+ 'nsAbCardProperty.cpp',
+ 'nsAbContentHandler.cpp',
+ 'nsAbDirectoryQuery.cpp',
+ 'nsAbDirectoryQueryProxy.cpp',
+ 'nsAbDirFactoryService.cpp',
+ 'nsAbDirProperty.cpp',
+ 'nsAbLDIFService.cpp',
+ 'nsAbManager.cpp',
+ 'nsAbMDBCard.cpp',
+ 'nsAbMDBDirectory.cpp',
+ 'nsAbMDBDirFactory.cpp',
+ 'nsAbMDBDirProperty.cpp',
+ 'nsAbQueryStringToExpression.cpp',
+ 'nsAbView.cpp',
+ 'nsAddbookProtocolHandler.cpp',
+ 'nsAddbookUrl.cpp',
+ 'nsAddrDatabase.cpp',
+ 'nsDirPrefs.cpp',
+ 'nsMsgVCardService.cpp',
+ 'nsVCard.cpp',
+ 'nsVCardObj.cpp',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT' and CONFIG['MOZ_MAPI_SUPPORT']:
+ SOURCES += [
+ 'nsAbOutlookDirectory.cpp',
+ 'nsAbOutlookDirFactory.cpp',
+ 'nsAbWinHelper.cpp',
+ 'nsMapiAddressBook.cpp',
+ 'nsWabAddressBook.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'Darwin':
+ SOURCES += [
+ 'nsAbOSXDirFactory.cpp',
+ ]
+
+ SOURCES += [
+ 'nsAbOSXCard.mm',
+ 'nsAbOSXDirectory.mm',
+ 'nsAbOSXUtils.mm',
+ ]
+
+if CONFIG['MOZ_LDAP_XPCOM']:
+ SOURCES += [
+ 'nsAbBoolExprToLDAPFilter.cpp',
+ 'nsAbLDAPCard.cpp',
+ 'nsAbLDAPDirectory.cpp',
+ 'nsAbLDAPDirectoryModify.cpp',
+ 'nsAbLDAPDirectoryQuery.cpp',
+ 'nsAbLDAPDirFactory.cpp',
+ 'nsAbLDAPListenerBase.cpp',
+ 'nsAbLDAPReplicationData.cpp',
+ 'nsAbLDAPReplicationQuery.cpp',
+ 'nsAbLDAPReplicationService.cpp',
+ ]
+ # XXX These files are not being built as they don't work. Bug 311632 should
+ # fix them.
+ # nsAbLDAPChangeLogQuery.cpp
+ # nsAbLDAPChangeLogData.cpp
+
+ EXTRA_COMPONENTS += [
+ 'nsAbLDAPAutoCompleteSearch.js',
+ ]
+
+ DEFINES['MOZ_LDAP_XPCOM'] = True
+
+EXTRA_COMPONENTS += [
+ 'nsAbAutoCompleteMyDomain.js',
+ 'nsAbAutoCompleteSearch.js',
+ 'nsAbLDAPAttributeMap.js',
+]
+
+EXTRA_PP_COMPONENTS += [
+ 'nsAddrbook.manifest',
+]
+
+FINAL_LIBRARY = 'mail'
diff --git a/mailnews/addrbook/src/nsAbAddressCollector.cpp b/mailnews/addrbook/src/nsAbAddressCollector.cpp
new file mode 100644
index 000000000..60f359601
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbAddressCollector.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "msgCore.h" // for pre-compiled headers
+#include "nsISimpleEnumerator.h"
+
+#include "nsIAbCard.h"
+#include "nsAbBaseCID.h"
+#include "nsAbAddressCollector.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsStringGlue.h"
+#include "prmem.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIAbManager.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+
+using namespace mozilla::mailnews;
+
+NS_IMPL_ISUPPORTS(nsAbAddressCollector, nsIAbAddressCollector, nsIObserver)
+
+#define PREF_MAIL_COLLECT_ADDRESSBOOK "mail.collect_addressbook"
+
+nsAbAddressCollector::nsAbAddressCollector()
+{
+}
+
+nsAbAddressCollector::~nsAbAddressCollector()
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPrefBranchInt(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv))
+ pPrefBranchInt->RemoveObserver(PREF_MAIL_COLLECT_ADDRESSBOOK, this);
+}
+
+/**
+ * Returns the first card found with the specified email address. This
+ * returns an already addrefed pointer to the card if the card is found.
+ */
+already_AddRefed<nsIAbCard>
+nsAbAddressCollector::GetCardForAddress(const nsACString &aEmailAddress,
+ nsIAbDirectory **aDirectory)
+{
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = abManager->GetDirectories(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ bool hasMore;
+ nsCOMPtr<nsISupports> supports;
+ nsCOMPtr<nsIAbDirectory> directory;
+ nsCOMPtr<nsIAbCard> result;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ rv = enumerator->GetNext(getter_AddRefs(supports));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ directory = do_QueryInterface(supports, &rv);
+ if (NS_FAILED(rv))
+ continue;
+
+ // Some implementations may return NS_ERROR_NOT_IMPLEMENTED here,
+ // so just catch the value and continue.
+ if (NS_FAILED(directory->CardForEmailAddress(aEmailAddress,
+ getter_AddRefs(result))))
+ {
+ continue;
+ }
+
+ if (result)
+ {
+ if (aDirectory)
+ directory.forget(aDirectory);
+ return result.forget();
+ }
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsAbAddressCollector::CollectAddress(const nsACString &aAddresses,
+ bool aCreateCard,
+ uint32_t aSendFormat)
+{
+ // If we've not got a valid directory, no point in going any further
+ if (!mDirectory)
+ return NS_OK;
+
+ // note that we're now setting the whole recipient list,
+ // not just the pretty name of the first recipient.
+ nsTArray<nsCString> names;
+ nsTArray<nsCString> addresses;
+ ExtractAllAddresses(EncodedHeader(aAddresses),
+ UTF16ArrayAdapter<>(names), UTF16ArrayAdapter<>(addresses));
+ uint32_t numAddresses = names.Length();
+
+ for (uint32_t i = 0; i < numAddresses; i++)
+ {
+ // Don't allow collection of addresses with no email address, it makes
+ // no sense. Whilst we should never get here in most normal cases, we
+ // should still be careful.
+ if (addresses[i].IsEmpty())
+ continue;
+
+ CollectSingleAddress(addresses[i], names[i], aCreateCard, aSendFormat,
+ false);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbAddressCollector::CollectSingleAddress(const nsACString &aEmail,
+ const nsACString &aDisplayName,
+ bool aCreateCard,
+ uint32_t aSendFormat,
+ bool aSkipCheckExisting)
+{
+ if (!mDirectory)
+ return NS_OK;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIAbDirectory> originDirectory;
+ nsCOMPtr<nsIAbCard> card = (!aSkipCheckExisting) ?
+ GetCardForAddress(aEmail, getter_AddRefs(originDirectory)) : nullptr;
+
+ if (!card && (aCreateCard || aSkipCheckExisting))
+ {
+ card = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && card)
+ {
+ // Set up the fields for the new card.
+ SetNamesForCard(card, aDisplayName);
+ AutoCollectScreenName(card, aEmail);
+
+ if (NS_SUCCEEDED(card->SetPrimaryEmail(NS_ConvertUTF8toUTF16(aEmail))))
+ {
+ card->SetPropertyAsUint32(kPreferMailFormatProperty, aSendFormat);
+
+ nsCOMPtr<nsIAbCard> addedCard;
+ rv = mDirectory->AddCard(card, getter_AddRefs(addedCard));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to add card");
+ }
+ }
+ }
+ else if (card && originDirectory)
+ {
+ // It could be that the origin directory is read-only, so don't try and
+ // write to it if it is.
+ bool readOnly;
+ rv = originDirectory->GetReadOnly(&readOnly);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (readOnly)
+ return NS_OK;
+
+ // address is already in the AB, so update the names
+ bool modifiedCard = false;
+
+ nsString displayName;
+ card->GetDisplayName(displayName);
+ // If we already have a display name, don't set the names on the card.
+ if (displayName.IsEmpty() && !aDisplayName.IsEmpty())
+ modifiedCard = SetNamesForCard(card, aDisplayName);
+
+ if (aSendFormat != nsIAbPreferMailFormat::unknown)
+ {
+ uint32_t currentFormat;
+ rv = card->GetPropertyAsUint32(kPreferMailFormatProperty,
+ &currentFormat);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get preferred mail format");
+
+ // we only want to update the AB if the current format is unknown
+ if (currentFormat == nsIAbPreferMailFormat::unknown &&
+ NS_SUCCEEDED(card->SetPropertyAsUint32(kPreferMailFormatProperty,
+ aSendFormat)))
+ modifiedCard = true;
+ }
+
+ if (modifiedCard)
+ originDirectory->ModifyCard(card);
+ }
+
+ return NS_OK;
+}
+
+// Works out the screen name to put on the card for some well-known addresses
+void
+nsAbAddressCollector::AutoCollectScreenName(nsIAbCard *aCard,
+ const nsACString &aEmail)
+{
+ if (!aCard)
+ return;
+
+ int32_t atPos = aEmail.FindChar('@');
+ if (atPos == -1)
+ return;
+
+ const nsACString& domain = Substring(aEmail, atPos + 1);
+
+ if (domain.IsEmpty())
+ return;
+ // username in
+ // username@aol.com (America Online)
+ // username@cs.com (Compuserve)
+ // username@netscape.net (Netscape webmail)
+ // are all AIM screennames. autocollect that info.
+ if (domain.Equals("aol.com") || domain.Equals("cs.com") ||
+ domain.Equals("netscape.net"))
+ aCard->SetPropertyAsAUTF8String(kScreenNameProperty, Substring(aEmail, 0, atPos));
+ else if (domain.Equals("gmail.com") || domain.Equals("googlemail.com"))
+ aCard->SetPropertyAsAUTF8String(kGtalkProperty, Substring(aEmail, 0, atPos));
+}
+
+// Returns true if the card was modified successfully.
+bool
+nsAbAddressCollector::SetNamesForCard(nsIAbCard *aSenderCard,
+ const nsACString &aFullName)
+{
+ nsCString firstName;
+ nsCString lastName;
+ bool modifiedCard = false;
+
+ if (NS_SUCCEEDED(aSenderCard->SetDisplayName(NS_ConvertUTF8toUTF16(aFullName))))
+ modifiedCard = true;
+
+ // Now split up the full name.
+ SplitFullName(nsCString(aFullName), firstName, lastName);
+
+ if (!firstName.IsEmpty() &&
+ NS_SUCCEEDED(aSenderCard->SetFirstName(NS_ConvertUTF8toUTF16(firstName))))
+ modifiedCard = true;
+
+ if (!lastName.IsEmpty() &&
+ NS_SUCCEEDED(aSenderCard->SetLastName(NS_ConvertUTF8toUTF16(lastName))))
+ modifiedCard = true;
+
+ if (modifiedCard)
+ aSenderCard->SetPropertyAsBool("PreferDisplayName", false);
+
+ return modifiedCard;
+}
+
+// Splits the first and last name based on the space between them.
+void
+nsAbAddressCollector::SplitFullName(const nsCString &aFullName, nsCString &aFirstName,
+ nsCString &aLastName)
+{
+ int index = aFullName.RFindChar(' ');
+ if (index != -1)
+ {
+ aLastName = Substring(aFullName, index + 1);
+ aFirstName = Substring(aFullName, 0, index);
+ }
+}
+
+// Observes the collected address book pref in case it changes.
+NS_IMETHODIMP
+nsAbAddressCollector::Observe(nsISupports *aSubject, const char *aTopic,
+ const char16_t *aData)
+{
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
+ if (!prefBranch) {
+ NS_ASSERTION(prefBranch, "failed to get prefs");
+ return NS_OK;
+ }
+
+ SetUpAbFromPrefs(prefBranch);
+ return NS_OK;
+}
+
+// Initialises the collector with the required items.
+nsresult
+nsAbAddressCollector::Init(void)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = prefBranch->AddObserver(PREF_MAIL_COLLECT_ADDRESSBOOK, this, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetUpAbFromPrefs(prefBranch);
+ return NS_OK;
+}
+
+// Performs the necessary changes to set up the collector for the specified
+// collected address book.
+void
+nsAbAddressCollector::SetUpAbFromPrefs(nsIPrefBranch *aPrefBranch)
+{
+ nsCString abURI;
+ aPrefBranch->GetCharPref(PREF_MAIL_COLLECT_ADDRESSBOOK,
+ getter_Copies(abURI));
+
+ if (abURI.IsEmpty())
+ abURI.AssignLiteral(kPersonalAddressbookUri);
+
+ if (abURI == mABURI)
+ return;
+
+ mDirectory = nullptr;
+ mABURI = abURI;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ rv = abManager->GetDirectory(mABURI, getter_AddRefs(mDirectory));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ bool readOnly;
+ rv = mDirectory->GetReadOnly(&readOnly);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ // If the directory is read-only, we can't write to it, so just blank it out
+ // here, and warn because we shouldn't hit this (UI is wrong).
+ if (readOnly)
+ {
+ NS_ERROR("Address Collection book preferences is set to a read-only book. "
+ "Address collection will not take place.");
+ mDirectory = nullptr;
+ }
+}
diff --git a/mailnews/addrbook/src/nsAbAddressCollector.h b/mailnews/addrbook/src/nsAbAddressCollector.h
new file mode 100644
index 000000000..7ef2236b8
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbAddressCollector.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsAbAddressCollector_H_
+#define _nsAbAddressCollector_H_
+
+#include "nsIAbAddressCollector.h"
+#include "nsCOMPtr.h"
+#include "nsIAbDirectory.h"
+#include "nsIAbCard.h"
+#include "nsIObserver.h"
+#include "nsStringGlue.h"
+
+class nsIPrefBranch;
+
+class nsAbAddressCollector : public nsIAbAddressCollector,
+ public nsIObserver
+{
+public:
+ nsAbAddressCollector();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABADDRESSCOLLECTOR
+ NS_DECL_NSIOBSERVER
+
+ nsresult Init();
+
+private:
+ virtual ~nsAbAddressCollector();
+ already_AddRefed<nsIAbCard> GetCardForAddress(const nsACString &aEmailAddress,
+ nsIAbDirectory **aDirectory);
+ void AutoCollectScreenName(nsIAbCard *aCard, const nsACString &aEmail);
+ bool SetNamesForCard(nsIAbCard *aSenderCard, const nsACString &aFullName);
+ void SplitFullName(const nsCString &aFullName, nsCString &aFirstName,
+ nsCString &aLastName);
+ void SetUpAbFromPrefs(nsIPrefBranch *aPrefBranch);
+ nsCOMPtr <nsIAbDirectory> mDirectory;
+ nsCString mABURI;
+};
+
+#endif // _nsAbAddressCollector_H_
+
diff --git a/mailnews/addrbook/src/nsAbAutoCompleteMyDomain.js b/mailnews/addrbook/src/nsAbAutoCompleteMyDomain.js
new file mode 100644
index 000000000..1ace4ca50
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbAutoCompleteMyDomain.js
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource:///modules/mailServices.js");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function nsAbAutoCompleteMyDomain() {}
+
+nsAbAutoCompleteMyDomain.prototype = {
+ classID: Components.ID("{5b259db2-e451-4de9-8a6f-cfba91402973}"),
+ QueryInterface: XPCOMUtils.generateQI([
+ Components.interfaces.nsIAutoCompleteSearch]),
+
+ cachedIdKey: "",
+ cachedIdentity: null,
+
+ applicableHeaders: new Set(["addr_to", "addr_cc", "addr_bcc", "addr_reply"]),
+
+ startSearch: function(aString, aSearchParam, aResult, aListener) {
+ let params = aSearchParam ? JSON.parse(aSearchParam) : {};
+ let applicable = ("type" in params) && this.applicableHeaders.has(params.type);
+ const ACR = Components.interfaces.nsIAutoCompleteResult;
+ var address = null;
+ if (applicable && aString && !aString.includes(",")) {
+ if (("idKey" in params) && (params.idKey != this.cachedIdKey)) {
+ this.cachedIdentity = MailServices.accounts.getIdentity(params.idKey);
+ this.cachedIdKey = params.idKey;
+ }
+ if (this.cachedIdentity.autocompleteToMyDomain)
+ address = aString.includes("@") ? aString :
+ this.cachedIdentity.email.replace(/[^@]*/, aString);
+ }
+
+ var result = {
+ searchString: aString,
+ searchResult: address ? ACR.RESULT_SUCCESS : ACR.RESULT_FAILURE,
+ defaultIndex: -1,
+ errorDescription: null,
+ matchCount: address ? 1 : 0,
+ getValueAt: function() { return address; },
+ getLabelAt: function() { return this.getValueAt(); },
+ getCommentAt: function() { return null; },
+ getStyleAt: function() { return "default-match"; },
+ getImageAt: function() { return null; },
+ getFinalCompleteValueAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+ removeValueAt: function() {}
+ };
+ aListener.onSearchResult(this, result);
+ },
+
+ stopSearch: function() {}
+};
+
+var components = [nsAbAutoCompleteMyDomain];
+var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
diff --git a/mailnews/addrbook/src/nsAbAutoCompleteSearch.js b/mailnews/addrbook/src/nsAbAutoCompleteSearch.js
new file mode 100644
index 000000000..c6c9b8db8
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbAutoCompleteSearch.js
@@ -0,0 +1,466 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/mailServices.js");
+Components.utils.import("resource:///modules/ABQueryUtils.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var ACR = Components.interfaces.nsIAutoCompleteResult;
+var nsIAbAutoCompleteResult = Components.interfaces.nsIAbAutoCompleteResult;
+
+function nsAbAutoCompleteResult(aSearchString) {
+ // Can't create this in the prototype as we'd get the same array for
+ // all instances
+ this._searchResults = []; // final results
+ this.searchString = aSearchString;
+ this._collectedValues = new Map(); // temporary unsorted results
+ // Get model query from pref; this will return mail.addr_book.autocompletequery.format.phonetic
+ // if mail.addr_book.show_phonetic_fields == true
+ this.modelQuery = getModelQuery("mail.addr_book.autocompletequery.format");
+ // check if the currently active model query has been modified by user
+ this._modelQueryHasUserValue = modelQueryHasUserValue("mail.addr_book.autocompletequery.format");
+}
+
+nsAbAutoCompleteResult.prototype = {
+ _searchResults: null,
+
+ // nsIAutoCompleteResult
+
+ modelQuery: null,
+ searchString: null,
+ searchResult: ACR.RESULT_NOMATCH,
+ defaultIndex: -1,
+ errorDescription: null,
+
+ get matchCount() {
+ return this._searchResults.length;
+ },
+
+ getValueAt: function getValueAt(aIndex) {
+ return this._searchResults[aIndex].value;
+ },
+
+ getLabelAt: function getLabelAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt: function getCommentAt(aIndex) {
+ return this._searchResults[aIndex].comment;
+ },
+
+ getStyleAt: function getStyleAt(aIndex) {
+ return "local-abook";
+ },
+
+ getImageAt: function getImageAt(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ removeValueAt: function removeValueAt(aRowIndex, aRemoveFromDB) {
+ },
+
+ // nsIAbAutoCompleteResult
+
+ getCardAt: function getCardAt(aIndex) {
+ return this._searchResults[aIndex].card;
+ },
+
+ getEmailToUse: function getEmailToUse(aIndex) {
+ return this._searchResults[aIndex].emailToUse;
+ },
+
+ // nsISupports
+
+ QueryInterface: XPCOMUtils.generateQI([ACR, nsIAbAutoCompleteResult])
+}
+
+function nsAbAutoCompleteSearch() {}
+
+nsAbAutoCompleteSearch.prototype = {
+ // For component registration
+ classID: Components.ID("2f946df9-114c-41fe-8899-81f10daf4f0c"),
+
+ // This is set from a preference,
+ // 0 = no comment column, 1 = name of address book this card came from
+ // Other numbers currently unused (hence default to zero)
+ _commentColumn: 0,
+ _parser: MailServices.headerParser,
+ _abManager: MailServices.ab,
+ applicableHeaders: new Set(["addr_to", "addr_cc", "addr_bcc", "addr_reply"]),
+
+ // Private methods
+
+ /**
+ * Returns the popularity index for a given card. This takes account of a
+ * translation bug whereby Thunderbird 2 stores its values in mork as
+ * hexadecimal, and Thunderbird 3 stores as decimal.
+ *
+ * @param aDirectory The directory that the card is in.
+ * @param aCard The card to return the popularity index for.
+ */
+ _getPopularityIndex: function _getPopularityIndex(aDirectory, aCard) {
+ let popularityValue = aCard.getProperty("PopularityIndex", "0");
+ let popularityIndex = parseInt(popularityValue);
+
+ // If we haven't parsed it the first time round, parse it as hexadecimal
+ // and repair so that we don't have to keep repairing.
+ if (isNaN(popularityIndex)) {
+ popularityIndex = parseInt(popularityValue, 16);
+
+ // If its still NaN, just give up, we shouldn't ever get here.
+ if (isNaN(popularityIndex))
+ popularityIndex = 0;
+
+ // Now store this change so that we're not changing it each time around.
+ if (!aDirectory.readOnly) {
+ aCard.setProperty("PopularityIndex", popularityIndex);
+ try {
+ aDirectory.modifyCard(aCard);
+ }
+ catch (ex) {
+ Components.utils.reportError(ex);
+ }
+ }
+ }
+ return popularityIndex;
+ },
+
+ /**
+ * Gets the score of the (full) address, given the search input. We want
+ * results that match the beginning of a "word" in the result to score better
+ * than a result that matches only in the middle of the word.
+ *
+ * @param aCard - the card whose score is being decided
+ * @param aAddress - full lower-cased address, including display name and address
+ * @param aSearchString - search string provided by user
+ * @return a score; a higher score is better than a lower one
+ */
+ _getScore: function(aCard, aAddress, aSearchString) {
+ const BEST = 100;
+
+ // We will firstly check if the search term provided by the user
+ // is the nick name for the card or at least in the beginning of it.
+ let nick = aCard.getProperty("NickName", "").toLocaleLowerCase();
+ aSearchString = aSearchString.toLocaleLowerCase();
+ if (nick == aSearchString)
+ return BEST + 1;
+ if (nick.indexOf(aSearchString) == 0)
+ return BEST;
+
+ // We'll do this case-insensitively and ignore the domain.
+ let atIdx = aAddress.lastIndexOf("@");
+ if (atIdx != -1) // mail lists don't have an @
+ aAddress = aAddress.substr(0, atIdx);
+ let idx = aAddress.indexOf(aSearchString);
+ if (idx == 0)
+ return BEST;
+ if (idx == -1)
+ return 0;
+
+ // We want to treat firstname, lastname and word boundary(ish) parts of
+ // the email address the same. E.g. for "John Doe (:xx) <jd.who@example.com>"
+ // all of these should score the same: "John", "Doe", "xx",
+ // ":xx", "jd", "who".
+ let prevCh = aAddress.charAt(idx - 1);
+ if (/[ :."'(\-_<&]/.test(prevCh))
+ return BEST;
+
+ // The match was inside a word -> we don't care about the position.
+ return 0;
+ },
+
+ /**
+ * Searches cards in the given directory. If a card is matched (and isn't
+ * a mailing list) then the function will add a result for each email address
+ * that exists.
+ *
+ * @param searchQuery The boolean search query to use.
+ * @param directory An nsIAbDirectory to search.
+ * @param result The result element to append results to.
+ */
+ _searchCards: function(searchQuery, directory, result) {
+ let childCards;
+ try {
+ childCards = this._abManager.getDirectory(directory.URI + searchQuery).childCards;
+ } catch (e) {
+ Components.utils.reportError("Error running addressbook query '" + searchQuery + "': " + e);
+ return;
+ }
+
+ // Cache this values to save going through xpconnect each time
+ var commentColumn = this._commentColumn == 1 ? directory.dirName : "";
+
+ // Now iterate through all the cards.
+ while (childCards.hasMoreElements()) {
+ var card = childCards.getNext();
+
+ if (card instanceof Components.interfaces.nsIAbCard) {
+ if (card.isMailList)
+ this._addToResult(commentColumn, directory, card, "", true, result);
+ else {
+ let email = card.primaryEmail;
+ if (email)
+ this._addToResult(commentColumn, directory, card, email, true, result);
+
+ email = card.getProperty("SecondEmail", "");
+ if (email)
+ this._addToResult(commentColumn, directory, card, email, false, result);
+ }
+ }
+ }
+ },
+
+ /**
+ * Checks the parent card and email address of an autocomplete results entry
+ * from a previous result against the search parameters to see if that entry
+ * should still be included in the narrowed-down result.
+ *
+ * @param aCard The card to check.
+ * @param aEmailToUse The email address to check against.
+ * @param aSearchWords Array of words in the multi word search string.
+ * @return True if the card matches the search parameters, false
+ * otherwise.
+ */
+ _checkEntry: function _checkEntry(aCard, aEmailToUse, aSearchWords) {
+ // Joining values of many fields in a single string so that a single
+ // search query can be fired on all of them at once. Separating them
+ // using spaces so that field1=> "abc" and field2=> "def" on joining
+ // shouldn't return true on search for "bcd".
+ // Note: This should be constructed from model query pref using
+ // getModelQuery("mail.addr_book.autocompletequery.format")
+ // but for now we hard-code the default value equivalent of the pref here
+ // or else bail out before and reconstruct the full c++ query if the pref
+ // has been customized (modelQueryHasUserValue), so that we won't get here.
+ let cumulativeFieldText = aCard.displayName + " " +
+ aCard.firstName + " " +
+ aCard.lastName + " " +
+ aEmailToUse + " " +
+ aCard.getProperty("NickName", "");
+ if (aCard.isMailList)
+ cumulativeFieldText += " " + aCard.getProperty("Notes", "");
+ cumulativeFieldText = cumulativeFieldText.toLocaleLowerCase();
+
+ return aSearchWords.every(String.prototype.includes,
+ cumulativeFieldText);
+ },
+
+ /**
+ * Checks to see if an emailAddress (name/address) is a duplicate of an
+ * existing entry already in the results. If the emailAddress is found, it
+ * will remove the existing element if the popularity of the new card is
+ * higher than the previous card.
+ *
+ * @param directory The directory that the card is in.
+ * @param card The card that could be a duplicate.
+ * @param lcEmailAddress The emailAddress (name/address combination) to check
+ * for duplicates against. Lowercased.
+ * @param currentResults The current results list.
+ */
+ _checkDuplicate: function (directory, card, lcEmailAddress, currentResults) {
+ let existingResult = currentResults._collectedValues.get(lcEmailAddress);
+ if (!existingResult)
+ return false;
+
+ let popIndex = this._getPopularityIndex(directory, card);
+ // It's a duplicate, is the new one more popular?
+ if (popIndex > existingResult.popularity) {
+ // Yes it is, so delete this element, return false and allow
+ // _addToResult to sort the new element into the correct place.
+ currentResults._collectedValues.delete(lcEmailAddress);
+ return false;
+ }
+ // Not more popular, but still a duplicate. Return true and _addToResult
+ // will just forget about it.
+ return true;
+ },
+
+ /**
+ * Adds a card to the results list if it isn't a duplicate. The function will
+ * order the results by popularity.
+ *
+ * @param commentColumn The text to be displayed in the comment column
+ * (if any).
+ * @param directory The directory that the card is in.
+ * @param card The card being added to the results.
+ * @param emailToUse The email address from the card that should be used
+ * for this result.
+ * @param isPrimaryEmail Is the emailToUse the primary email? Set to true if
+ * it is the case. For mailing lists set it to true.
+ * @param result The result to add the new entry to.
+ */
+ _addToResult: function(commentColumn, directory, card,
+ emailToUse, isPrimaryEmail, result) {
+ let mbox = this._parser.makeMailboxObject(card.displayName,
+ card.isMailList ? card.getProperty("Notes", "") || card.displayName :
+ emailToUse);
+ if (!mbox.email)
+ return;
+
+ let emailAddress = mbox.toString();
+ let lcEmailAddress = emailAddress.toLocaleLowerCase();
+
+ // If it is a duplicate, then just return and don't add it. The
+ // _checkDuplicate function deals with it all for us.
+ if (this._checkDuplicate(directory, card, lcEmailAddress, result))
+ return;
+
+ result._collectedValues.set(lcEmailAddress, {
+ value: emailAddress,
+ comment: commentColumn,
+ card: card,
+ isPrimaryEmail: isPrimaryEmail,
+ emailToUse: emailToUse,
+ popularity: this._getPopularityIndex(directory, card),
+ score: this._getScore(card, lcEmailAddress, result.searchString)
+ });
+ },
+
+ // nsIAutoCompleteSearch
+
+ /**
+ * Starts a search based on the given parameters.
+ *
+ * @see nsIAutoCompleteSearch for parameter details.
+ *
+ * It is expected that aSearchParam contains the identity (if any) to use
+ * for determining if an address book should be autocompleted against.
+ */
+ startSearch: function startSearch(aSearchString, aSearchParam,
+ aPreviousResult, aListener) {
+ let params = aSearchParam ? JSON.parse(aSearchParam) : {};
+ var result = new nsAbAutoCompleteResult(aSearchString);
+ if (("type" in params) && !this.applicableHeaders.has(params.type)) {
+ result.searchResult = ACR.RESULT_IGNORED;
+ aListener.onSearchResult(this, result);
+ return;
+ }
+
+ let fullString = aSearchString && aSearchString.trim().toLocaleLowerCase();
+
+ // If the search string is empty, or contains a comma, or the user
+ // hasn't enabled autocomplete, then just return no matches or the
+ // result ignored.
+ // The comma check is so that we don't autocomplete against the user
+ // entering multiple addresses.
+ if (!fullString || aSearchString.includes(",")) {
+ result.searchResult = ACR.RESULT_IGNORED;
+ aListener.onSearchResult(this, result);
+ return;
+ }
+
+ // Array of all the terms from the fullString search query
+ // (separated on the basis of spaces or exact terms on the
+ // basis of quotes).
+ let searchWords = getSearchTokens(fullString);
+
+ // Find out about the comment column
+ try {
+ this._commentColumn = Services.prefs.getIntPref("mail.autoComplete.commentColumn");
+ } catch(e) { }
+
+ if (aPreviousResult instanceof nsIAbAutoCompleteResult &&
+ aSearchString.startsWith(aPreviousResult.searchString) &&
+ aPreviousResult.searchResult == ACR.RESULT_SUCCESS &&
+ !result._modelQueryHasUserValue &&
+ result.modelQuery == aPreviousResult.modelQuery) {
+ // We have successful previous matches, and model query has not changed since
+ // previous search, therefore just iterate through the list of previous result
+ // entries and reduce as appropriate (via _checkEntry function).
+ // Test for model query change is required: when reverting back from custom to
+ // default query, result._modelQueryHasUserValue==false, but we must bail out.
+ // Todo: However, if autocomplete model query has been customized, we fall
+ // back to using the full query again instead of reducing result list in js;
+ // The full query might be less performant as it's fired against entire AB,
+ // so we should try morphing the query for js. We can't use the _checkEntry
+ // js query yet because it is hardcoded (mimic default model query).
+ // At least we now allow users to customize their autocomplete model query...
+ for (let i = 0; i < aPreviousResult.matchCount; ++i) {
+ let card = aPreviousResult.getCardAt(i);
+ let email = aPreviousResult.getEmailToUse(i);
+ if (this._checkEntry(card, email, searchWords)) {
+ // Add matches into the results array. We re-sort as needed later.
+ result._searchResults.push({
+ value: aPreviousResult.getValueAt(i),
+ comment: aPreviousResult.getCommentAt(i),
+ card: card,
+ isPrimaryEmail: (card.primaryEmail == email),
+ emailToUse: email,
+ popularity: parseInt(card.getProperty("PopularityIndex", "0")),
+ score: this._getScore(card,
+ aPreviousResult.getValueAt(i).toLocaleLowerCase(),
+ fullString)
+ });
+ }
+ }
+ }
+ else
+ {
+ // Construct the search query from pref; using a query means we can
+ // optimise on running the search through c++ which is better for string
+ // comparisons (_checkEntry is relatively slow).
+ // When user's fullstring search expression is a multiword query, search
+ // for each word separately so that each result contains all the words
+ // from the fullstring in the fields of the addressbook card
+ // (see bug 558931 for explanations).
+ // Use helper method to split up search query to multi-word search
+ // query against multiple fields.
+ let searchWords = getSearchTokens(fullString);
+ let searchQuery = generateQueryURI(result.modelQuery, searchWords);
+
+ // Now do the searching
+ let allABs = this._abManager.directories;
+
+ // We're not going to bother searching sub-directories, currently the
+ // architecture forces all cards that are in mailing lists to be in ABs as
+ // well, therefore by searching sub-directories (aka mailing lists) we're
+ // just going to find duplicates.
+ while (allABs.hasMoreElements()) {
+ let dir = allABs.getNext();
+ if (dir instanceof Components.interfaces.nsIAbDirectory &&
+ dir.useForAutocomplete(("idKey" in params) ? params.idKey : null)) {
+ this._searchCards(searchQuery, dir, result);
+ }
+ }
+
+ result._searchResults = [...result._collectedValues.values()];
+ }
+
+ // Sort the results. Scoring may have changed so do it even if this is
+ // just filtered previous results.
+ result._searchResults.sort(function(a, b) {
+ // Order by 1) descending score, then 2) descending popularity,
+ // then 3) primary email before secondary for the same card, then
+ // 4) by emails sorted alphabetically.
+ return (b.score - a.score) ||
+ (b.popularity - a.popularity) ||
+ ((a.card == b.card && a.isPrimaryEmail) ? -1 : 0) ||
+ a.value.localeCompare(b.value);
+ });
+
+ if (result.matchCount) {
+ result.searchResult = ACR.RESULT_SUCCESS;
+ result.defaultIndex = 0;
+ }
+
+ aListener.onSearchResult(this, result);
+ },
+
+ stopSearch: function stopSearch() {
+ },
+
+ // nsISupports
+
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces
+ .nsIAutoCompleteSearch])
+};
+
+// Module
+
+var components = [nsAbAutoCompleteSearch];
+var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
diff --git a/mailnews/addrbook/src/nsAbBSDirectory.cpp b/mailnews/addrbook/src/nsAbBSDirectory.cpp
new file mode 100644
index 000000000..0d018bbda
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbBSDirectory.cpp
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIPrefService.h"
+#include "nsAbBSDirectory.h"
+
+#include "nsDirPrefs.h"
+#include "nsAbBaseCID.h"
+#include "nsAddrDatabase.h"
+#include "nsIAbManager.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsServiceManagerUtils.h"
+#include "nsAbDirFactoryService.h"
+#include "nsAbMDBDirFactory.h"
+#include "nsArrayEnumerator.h"
+
+#include "nsCRTGlue.h"
+
+nsAbBSDirectory::nsAbBSDirectory()
+: mInitialized(false)
+, mServers(13)
+{
+}
+
+nsAbBSDirectory::~nsAbBSDirectory()
+{
+}
+
+NS_IMETHODIMP nsAbBSDirectory::Init(const char *aURI)
+{
+ mURI = aURI;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsAbBSDirectory, nsAbDirProperty)
+
+nsresult nsAbBSDirectory::CreateDirectoriesFromFactory(const nsACString &aURI,
+ DIR_Server *aServer,
+ bool aNotify)
+{
+ nsresult rv;
+
+ // Get the directory factory service
+ nsCOMPtr<nsIAbDirFactoryService> dirFactoryService =
+ do_GetService(NS_ABDIRFACTORYSERVICE_CONTRACTID,&rv);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ // Get the directory factory from the URI
+ nsCOMPtr<nsIAbDirFactory> dirFactory;
+ rv = dirFactoryService->GetDirFactory(aURI, getter_AddRefs(dirFactory));
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ // Create the directories
+ nsCOMPtr<nsISimpleEnumerator> newDirEnumerator;
+ rv = dirFactory->GetDirectories(NS_ConvertUTF8toUTF16(aServer->description),
+ aURI,
+ nsDependentCString(aServer->prefName),
+ getter_AddRefs(newDirEnumerator));
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ // Enumerate through the directories adding them
+ // to the sub directories array
+ bool hasMore;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+
+ while (NS_SUCCEEDED(newDirEnumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> newDirSupports;
+ rv = newDirEnumerator->GetNext(getter_AddRefs(newDirSupports));
+ if(NS_FAILED(rv))
+ continue;
+
+ nsCOMPtr<nsIAbDirectory> childDir = do_QueryInterface(newDirSupports, &rv);
+ if(NS_FAILED(rv))
+ continue;
+
+ // Define a relationship between the preference
+ // entry and the directory
+ mServers.Put(childDir, aServer);
+
+ mSubDirectories.AppendObject(childDir);
+
+ if (aNotify && abManager)
+ abManager->NotifyDirectoryItemAdded(this, childDir);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbBSDirectory::GetChildNodes(nsISimpleEnumerator* *aResult)
+{
+ nsresult rv = EnsureInitialized();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_NewArrayEnumerator(aResult, mSubDirectories);
+}
+
+nsresult nsAbBSDirectory::EnsureInitialized()
+{
+ if (mInitialized)
+ return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbDirFactoryService> dirFactoryService =
+ do_GetService(NS_ABDIRFACTORYSERVICE_CONTRACTID,&rv);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ nsTArray<DIR_Server*> *directories = DIR_GetDirectories();
+ if (!directories)
+ return NS_ERROR_FAILURE;
+
+ int32_t count = directories->Length();
+ for (int32_t i = 0; i < count; i++)
+ {
+ DIR_Server *server = directories->ElementAt(i);
+
+ // if this is a 4.x, local .na2 addressbook (PABDirectory)
+ // we must skip it.
+ // mozilla can't handle 4.x .na2 addressbooks
+ // note, the filename might be na2 for 4.x LDAP directories
+ // (we used the .na2 file for replication), and we don't want to skip
+ // those. see bug #127007
+ uint32_t fileNameLen = strlen(server->fileName);
+ if (((fileNameLen > kABFileName_PreviousSuffixLen) &&
+ strcmp(server->fileName + fileNameLen - kABFileName_PreviousSuffixLen,
+ kABFileName_PreviousSuffix) == 0) &&
+ (server->dirType == PABDirectory))
+ continue;
+
+ // Set the uri property
+ nsAutoCString URI (server->uri);
+ // This is in case the uri is never set
+ // in the nsDirPref.cpp code.
+ if (!server->uri)
+ {
+ URI = NS_LITERAL_CSTRING(kMDBDirectoryRoot);
+ URI += nsDependentCString(server->fileName);
+ }
+
+ /*
+ * Check that we are not converting from a
+ * a 4.x address book file e.g. pab.na2
+ * check if the URI ends with ".na2"
+ */
+ if (StringEndsWith(URI, NS_LITERAL_CSTRING(kABFileName_PreviousSuffix)))
+ URI.Replace(kMDBDirectoryRootLen, URI.Length() - kMDBDirectoryRootLen, server->fileName);
+
+ // Create the directories
+ rv = CreateDirectoriesFromFactory(URI, server, false /* notify */);
+
+ // If we failed, this could be because something has set a pref for us
+ // which is now broke (e.g. no factory present). So just ignore this one
+ // and move on.
+ if (NS_FAILED(rv))
+ NS_WARNING("CreateDirectoriesFromFactory failed - Invalid factory?");
+ }
+
+ mInitialized = true;
+ // sort directories by position...
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbBSDirectory::CreateNewDirectory(const nsAString &aDirName,
+ const nsACString &aURI,
+ uint32_t aType,
+ const nsACString &aPrefName,
+ nsACString &aResult)
+{
+ nsresult rv = EnsureInitialized();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /*
+ * TODO
+ * This procedure is still MDB specific
+ * due to the dependence on the current
+ * nsDirPref.cpp code
+ */
+
+ nsCString URI(aURI);
+
+ /*
+ * The creation of the address book in the preferences
+ * is very MDB implementation specific.
+ * If the fileName attribute is null then it will
+ * create an appropriate file name.
+ * Somehow have to resolve this issue so that it
+ * is more general.
+ *
+ */
+ DIR_Server* server = nullptr;
+ rv = DIR_AddNewAddressBook(aDirName, EmptyCString(), URI,
+ (DirectoryType)aType, aPrefName, &server);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ if (aType == PABDirectory) {
+ // Add the URI property
+ URI.AssignLiteral(kMDBDirectoryRoot);
+ URI.Append(nsDependentCString(server->fileName));
+ }
+
+ aResult.Assign(server->prefName);
+
+ rv = CreateDirectoriesFromFactory(URI, server, true /* notify */);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+NS_IMETHODIMP nsAbBSDirectory::CreateDirectoryByURI(const nsAString &aDisplayName,
+ const nsACString &aURI)
+{
+ nsresult rv = EnsureInitialized();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString fileName;
+ if (StringBeginsWith(aURI, NS_LITERAL_CSTRING(kMDBDirectoryRoot)))
+ fileName = Substring(aURI, kMDBDirectoryRootLen);
+
+ DIR_Server * server = nullptr;
+ rv = DIR_AddNewAddressBook(aDisplayName, fileName, aURI,
+ PABDirectory, EmptyCString(), &server);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = CreateDirectoriesFromFactory(aURI, server, true /* notify */);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+NS_IMETHODIMP nsAbBSDirectory::DeleteDirectory(nsIAbDirectory *directory)
+{
+ NS_ENSURE_ARG_POINTER(directory);
+
+ nsresult rv = EnsureInitialized();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ DIR_Server *server = nullptr;
+ mServers.Get(directory, &server);
+
+ if (!server)
+ return NS_ERROR_FAILURE;
+
+ struct GetDirectories
+ {
+ GetDirectories(DIR_Server* aServer) : mServer(aServer) { }
+
+ nsCOMArray<nsIAbDirectory> directories;
+ DIR_Server* mServer;
+ };
+ GetDirectories getDirectories(server);
+ for (auto iter = mServers.Iter(); !iter.Done(); iter.Next()) {
+ if (iter.UserData() == getDirectories.mServer) {
+ nsCOMPtr<nsIAbDirectory> abDir = do_QueryInterface(iter.Key());
+ getDirectories.directories.AppendObject(abDir);
+ }
+ }
+
+ DIR_DeleteServerFromList(server);
+
+ nsCOMPtr<nsIAbDirFactoryService> dirFactoryService =
+ do_GetService(NS_ABDIRFACTORYSERVICE_CONTRACTID,&rv);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ uint32_t count = getDirectories.directories.Count();
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID);
+
+ for (uint32_t i = 0; i < count; i++) {
+ nsCOMPtr<nsIAbDirectory> d = getDirectories.directories[i];
+
+ mServers.Remove(d);
+ mSubDirectories.RemoveObject(d);
+
+ if (abManager)
+ abManager->NotifyDirectoryDeleted(this, d);
+
+ nsCString uri;
+ rv = d->GetURI(uri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirFactory> dirFactory;
+ rv = dirFactoryService->GetDirFactory(uri, getter_AddRefs(dirFactory));
+ if (NS_FAILED(rv))
+ continue;
+
+ rv = dirFactory->DeleteDirectory(d);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbBSDirectory::HasDirectory(nsIAbDirectory *dir, bool *hasDir)
+{
+ if (!hasDir)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = EnsureInitialized();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ DIR_Server *dirServer = nullptr;
+ mServers.Get(dir, &dirServer);
+ return DIR_ContainsServer(dirServer, hasDir);
+}
+
+NS_IMETHODIMP nsAbBSDirectory::UseForAutocomplete(const nsACString &aIdentityKey,
+ bool *aResult)
+{
+ // For the "root" directory (kAllDirectoryRoot) always return true so that
+ // we can search sub directories that may or may not be local.
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbBSDirectory::GetURI(nsACString &aURI)
+{
+ if (mURI.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ aURI = mURI;
+ return NS_OK;
+}
+
diff --git a/mailnews/addrbook/src/nsAbBSDirectory.h b/mailnews/addrbook/src/nsAbBSDirectory.h
new file mode 100644
index 000000000..bc550dbf5
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbBSDirectory.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef nsAbBSDirectory_h__
+#define nsAbBSDirectory_h__
+
+#include "mozilla/Attributes.h"
+#include "nsAbDirProperty.h"
+
+#include "nsDataHashtable.h"
+#include "nsCOMArray.h"
+
+class nsAbBSDirectory : public nsAbDirProperty
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsAbBSDirectory();
+
+ // nsIAbDirectory methods
+ NS_IMETHOD Init(const char *aURI) override;
+ NS_IMETHOD GetChildNodes(nsISimpleEnumerator* *result) override;
+ NS_IMETHOD CreateNewDirectory(const nsAString &aDirName,
+ const nsACString &aURI,
+ uint32_t aType,
+ const nsACString &aPrefName,
+ nsACString &aResult) override;
+ NS_IMETHOD CreateDirectoryByURI(const nsAString &aDisplayName,
+ const nsACString &aURI) override;
+ NS_IMETHOD DeleteDirectory(nsIAbDirectory *directory) override;
+ NS_IMETHOD HasDirectory(nsIAbDirectory *dir, bool *hasDir) override;
+ NS_IMETHOD UseForAutocomplete(const nsACString &aIdentityKey, bool *aResult) override;
+ NS_IMETHOD GetURI(nsACString &aURI) override;
+
+protected:
+ virtual ~nsAbBSDirectory();
+ nsresult EnsureInitialized();
+ nsresult CreateDirectoriesFromFactory(const nsACString &aURI,
+ DIR_Server* aServer, bool aNotify);
+
+protected:
+ bool mInitialized;
+ nsCOMArray<nsIAbDirectory> mSubDirectories;
+ nsDataHashtable<nsISupportsHashKey, DIR_Server*> mServers;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbBoolExprToLDAPFilter.cpp b/mailnews/addrbook/src/nsAbBoolExprToLDAPFilter.cpp
new file mode 100644
index 000000000..679ee792d
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbBoolExprToLDAPFilter.cpp
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIAbLDAPAttributeMap.h"
+#include "nsAbBoolExprToLDAPFilter.h"
+#include "nsStringGlue.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+
+const int nsAbBoolExprToLDAPFilter::TRANSLATE_CARD_PROPERTY = 1 << 0 ;
+const int nsAbBoolExprToLDAPFilter::ALLOW_NON_CONVERTABLE_CARD_PROPERTY = 1 << 1 ;
+
+nsresult nsAbBoolExprToLDAPFilter::Convert (
+ nsIAbLDAPAttributeMap* map,
+ nsIAbBooleanExpression* expression,
+ nsCString& filter,
+ int flags)
+{
+ nsCString f;
+ nsresult rv = FilterExpression (map, expression, f, flags);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ filter = f;
+ return rv;
+}
+
+nsresult nsAbBoolExprToLDAPFilter::FilterExpression (
+ nsIAbLDAPAttributeMap* map,
+ nsIAbBooleanExpression* expression,
+ nsCString& filter,
+ int flags)
+{
+ nsCOMPtr<nsIArray> childExpressions;
+ nsresult rv = expression->GetExpressions(getter_AddRefs(childExpressions));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t count;
+ rv = childExpressions->GetLength(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (count == 0)
+ return NS_OK;
+
+ nsAbBooleanOperationType operation;
+ rv = expression->GetOperation(&operation);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /*
+ * 3rd party query integration with Mozilla is achieved
+ * by calling nsAbLDAPDirectoryQuery::DoQuery(). Thus
+ * we can arrive here with a query asking for all the
+ * ldap attributes using the card:nsIAbCard interface.
+ *
+ * So we need to check that we are not creating a condition
+ * filter against this expression otherwise we will end up with an invalid
+ * filter equal to "(|)".
+ */
+
+ if (count == 1 )
+ {
+ nsCOMPtr<nsIAbBooleanConditionString>
+ childCondition(do_QueryElementAt(childExpressions, 1, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCString name;
+ rv = childCondition->GetName (getter_Copies (name));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if(name.Equals("card:nsIAbCard"))
+ return NS_OK;
+ }
+ }
+
+ filter.AppendLiteral("(");
+ switch (operation)
+ {
+ case nsIAbBooleanOperationTypes::AND:
+ filter.AppendLiteral("&");
+ rv = FilterExpressions (map, childExpressions, filter, flags);
+ break;
+ case nsIAbBooleanOperationTypes::OR:
+ filter.AppendLiteral("|");
+ rv = FilterExpressions (map, childExpressions, filter, flags);
+ break;
+ case nsIAbBooleanOperationTypes::NOT:
+ if (count > 1)
+ return NS_ERROR_FAILURE;
+ filter.AppendLiteral("!");
+ rv = FilterExpressions (map, childExpressions, filter, flags);
+ break;
+ default:
+ break;
+ }
+ filter.AppendLiteral(")");
+
+ return rv;
+}
+
+nsresult nsAbBoolExprToLDAPFilter::FilterExpressions (
+ nsIAbLDAPAttributeMap *map,
+ nsIArray* expressions,
+ nsCString& filter,
+ int flags)
+{
+ uint32_t count;
+ nsresult rv = expressions->GetLength(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbBooleanConditionString> childCondition;
+ nsCOMPtr<nsIAbBooleanExpression> childExpression;
+ for (uint32_t i = 0; i < count; i++)
+ {
+ childCondition = do_QueryElementAt(expressions, i, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = FilterCondition (map, childCondition, filter, flags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ continue;
+ }
+
+ childExpression = do_QueryElementAt(expressions, i, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = FilterExpression (map, childExpression, filter, flags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ continue;
+ }
+ }
+
+ return rv;
+}
+
+nsresult nsAbBoolExprToLDAPFilter::FilterCondition (
+ nsIAbLDAPAttributeMap* map,
+ nsIAbBooleanConditionString* condition,
+ nsCString& filter,
+ int flags)
+{
+ nsCString name;
+ nsresult rv = condition->GetName(getter_Copies (name));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString ldapAttr(name);
+ if (flags & TRANSLATE_CARD_PROPERTY)
+ {
+ rv = map->GetFirstAttribute (name, ldapAttr);
+ if (!(flags & ALLOW_NON_CONVERTABLE_CARD_PROPERTY) &&
+ !ATTRMAP_FOUND_ATTR(rv, ldapAttr))
+ return NS_OK;
+ }
+
+ nsAbBooleanConditionType conditionType;
+ rv = condition->GetCondition(&conditionType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString value;
+ rv = condition->GetValue (getter_Copies (value));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ConvertUTF16toUTF8 vUTF8 (value);
+
+ switch (conditionType)
+ {
+ case nsIAbBooleanConditionTypes::DoesNotExist:
+ filter.AppendLiteral("(!(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("=*))");
+ break;
+ case nsIAbBooleanConditionTypes::Exists:
+ filter.AppendLiteral("(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("=*)");
+ break;
+ case nsIAbBooleanConditionTypes::Contains:
+ filter.AppendLiteral("(");
+ filter.Append(ldapAttr);
+ filter.Append("=*");
+ filter.Append(vUTF8);
+ filter.AppendLiteral("*)");
+ break;
+ case nsIAbBooleanConditionTypes::DoesNotContain:
+ filter.AppendLiteral("(!(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("=*");
+ filter.Append(vUTF8);
+ filter.AppendLiteral("*))");
+ break;
+ case nsIAbBooleanConditionTypes::Is:
+ filter.AppendLiteral("(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("=");
+ filter.Append(vUTF8);
+ filter.AppendLiteral(")");
+ break;
+ case nsIAbBooleanConditionTypes::IsNot:
+ filter.AppendLiteral("(!(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("=");
+ filter.Append(vUTF8);
+ filter.AppendLiteral("))");
+ break;
+ case nsIAbBooleanConditionTypes::BeginsWith:
+ filter.AppendLiteral("(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("=");
+ filter.Append(vUTF8);
+ filter.AppendLiteral("*)");
+ break;
+ case nsIAbBooleanConditionTypes::EndsWith:
+ filter.AppendLiteral("(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("=*");
+ filter.Append(vUTF8);
+ filter.AppendLiteral(")");
+ break;
+ case nsIAbBooleanConditionTypes::LessThan:
+ filter.AppendLiteral("(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("<=");
+ filter.Append(vUTF8);
+ filter.AppendLiteral(")");
+ break;
+ case nsIAbBooleanConditionTypes::GreaterThan:
+ filter.AppendLiteral("(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral(">=");
+ filter.Append(vUTF8);
+ filter.AppendLiteral(")");
+ break;
+ case nsIAbBooleanConditionTypes::SoundsLike:
+ filter.AppendLiteral("(");
+ filter.Append(ldapAttr);
+ filter.AppendLiteral("~=");
+ filter.Append(vUTF8);
+ filter.AppendLiteral(")");
+ break;
+ case nsIAbBooleanConditionTypes::RegExp:
+ break;
+ default:
+ break;
+ }
+
+ return rv;
+}
+
diff --git a/mailnews/addrbook/src/nsAbBoolExprToLDAPFilter.h b/mailnews/addrbook/src/nsAbBoolExprToLDAPFilter.h
new file mode 100644
index 000000000..5ea892595
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbBoolExprToLDAPFilter.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBooleanExpressionToLDAPFilter_h__
+#define nsBooleanExpressionToLDAPFilter_h__
+
+#include "nsIAbBooleanExpression.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+
+class nsIAbLDAPAttributeMap;
+
+class nsAbBoolExprToLDAPFilter
+{
+public:
+ static const int TRANSLATE_CARD_PROPERTY ;
+ static const int ALLOW_NON_CONVERTABLE_CARD_PROPERTY ;
+
+ static nsresult Convert (
+ nsIAbLDAPAttributeMap* map,
+ nsIAbBooleanExpression* expression,
+ nsCString& filter,
+ int flags = TRANSLATE_CARD_PROPERTY);
+
+protected:
+ static nsresult FilterExpression (
+ nsIAbLDAPAttributeMap* map,
+ nsIAbBooleanExpression* expression,
+ nsCString& filter,
+ int flags);
+ static nsresult FilterExpressions (
+ nsIAbLDAPAttributeMap* map,
+ nsIArray* expressions,
+ nsCString& filter,
+ int flags);
+ static nsresult FilterCondition (
+ nsIAbLDAPAttributeMap* map,
+ nsIAbBooleanConditionString* condition,
+ nsCString& filter,
+ int flags);
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbBooleanExpression.cpp b/mailnews/addrbook/src/nsAbBooleanExpression.cpp
new file mode 100644
index 000000000..a1a39c1fa
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbBooleanExpression.cpp
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbBooleanExpression.h"
+#include "nsComponentManagerUtils.h"
+
+NS_IMPL_ISUPPORTS(nsAbBooleanConditionString, nsIAbBooleanConditionString)
+
+nsAbBooleanConditionString::nsAbBooleanConditionString() :
+ mCondition (nsIAbBooleanConditionTypes::Exists)
+{
+}
+
+nsAbBooleanConditionString::~nsAbBooleanConditionString()
+{
+}
+
+/* attribute nsAbBooleanConditionType condition; */
+NS_IMETHODIMP nsAbBooleanConditionString::GetCondition(nsAbBooleanConditionType *aCondition)
+{
+ if (!aCondition)
+ return NS_ERROR_NULL_POINTER;
+
+ *aCondition = mCondition;
+
+ return NS_OK;
+}
+NS_IMETHODIMP nsAbBooleanConditionString::SetCondition(nsAbBooleanConditionType aCondition)
+{
+ mCondition = aCondition;
+
+ return NS_OK;
+}
+
+/* attribute string name; */
+NS_IMETHODIMP nsAbBooleanConditionString::GetName(char** aName)
+{
+ if (!aName)
+ return NS_ERROR_NULL_POINTER;
+
+ *aName = mName.IsEmpty() ? 0 : ToNewCString(mName);
+
+ return NS_OK;
+
+}
+NS_IMETHODIMP nsAbBooleanConditionString::SetName(const char* aName)
+{
+ if (!aName)
+ return NS_ERROR_NULL_POINTER;
+
+ mName = aName;
+
+ return NS_OK;
+}
+
+/* attribute wstring value; */
+NS_IMETHODIMP nsAbBooleanConditionString::GetValue(char16_t** aValue)
+{
+ if (!aValue)
+ return NS_ERROR_NULL_POINTER;
+
+ *aValue = ToNewUnicode(mValue);
+
+ return NS_OK;
+}
+NS_IMETHODIMP nsAbBooleanConditionString::SetValue(const char16_t * aValue)
+{
+ if (!aValue)
+ return NS_ERROR_NULL_POINTER;
+
+ mValue = aValue;
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsAbBooleanExpression, nsIAbBooleanExpression)
+
+nsAbBooleanExpression::nsAbBooleanExpression() :
+ mOperation (nsIAbBooleanOperationTypes::AND)
+{
+}
+
+nsAbBooleanExpression::~nsAbBooleanExpression()
+{
+}
+
+/* attribute nsAbBooleanOperationType operation; */
+NS_IMETHODIMP nsAbBooleanExpression::GetOperation(nsAbBooleanOperationType *aOperation)
+{
+ if (!aOperation)
+ return NS_ERROR_NULL_POINTER;
+
+ *aOperation = mOperation;
+
+ return NS_OK;
+}
+NS_IMETHODIMP nsAbBooleanExpression::SetOperation(nsAbBooleanOperationType aOperation)
+{
+ mOperation = aOperation;
+
+ return NS_OK;
+}
+
+/* attribute nsIArray expressions; */
+NS_IMETHODIMP nsAbBooleanExpression::GetExpressions(nsIArray **aExpressions)
+{
+ if (!aExpressions)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!mExpressions)
+ {
+ mExpressions = do_CreateInstance(NS_ARRAY_CONTRACTID);
+
+ if (!mExpressions)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(*aExpressions = mExpressions);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbBooleanExpression::SetExpressions(nsIArray *aExpressions)
+{
+ if (!aExpressions)
+ return NS_ERROR_NULL_POINTER;
+
+ mExpressions = aExpressions;
+
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbBooleanExpression.h b/mailnews/addrbook/src/nsAbBooleanExpression.h
new file mode 100644
index 000000000..697caf5f5
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbBooleanExpression.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbBooleanExpression_h__
+#define nsAbBooleanExpression_h__
+
+#include "nsIAbBooleanExpression.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsIArray.h"
+
+class nsAbBooleanConditionString : public nsIAbBooleanConditionString
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABBOOLEANCONDITIONSTRING
+
+ nsAbBooleanConditionString();
+
+protected:
+ virtual ~nsAbBooleanConditionString();
+ nsAbBooleanConditionType mCondition;
+ nsCString mName;
+ nsString mValue;
+};
+
+class nsAbBooleanExpression: public nsIAbBooleanExpression
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABBOOLEANEXPRESSION
+
+ nsAbBooleanExpression();
+
+protected:
+ virtual ~nsAbBooleanExpression();
+ nsAbBooleanOperationType mOperation;
+ nsCOMPtr<nsIArray> mExpressions;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbCardProperty.cpp b/mailnews/addrbook/src/nsAbCardProperty.cpp
new file mode 100644
index 000000000..2c40a4034
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbCardProperty.cpp
@@ -0,0 +1,1193 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbCardProperty.h"
+#include "nsAbBaseCID.h"
+#include "nsIPrefService.h"
+#include "nsIAddrDatabase.h"
+#include "plbase64.h"
+#include "nsIStringBundle.h"
+#include "plstr.h"
+#include "nsMsgUtils.h"
+#include "nsINetUtil.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMemory.h"
+#include "nsVCardObj.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "mozITXTToHTMLConv.h"
+#include "nsIAbManager.h"
+
+#include "nsVariant.h"
+#include "nsIProperty.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
+#include "prmem.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Services.h"
+using namespace mozilla;
+
+#define PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST "mail.addr_book.lastnamefirst"
+
+const char sAddrbookProperties[] = "chrome://messenger/locale/addressbook/addressBook.properties";
+
+enum EAppendType {
+ eAppendLine,
+ eAppendLabel,
+ eAppendCityStateZip
+};
+
+struct AppendItem {
+ const char *mColumn;
+ const char* mLabel;
+ EAppendType mAppendType;
+};
+
+static const AppendItem NAME_ATTRS_ARRAY[] = {
+ {kDisplayNameProperty, "propertyDisplayName", eAppendLabel},
+ {kNicknameProperty, "propertyNickname", eAppendLabel},
+ {kPriEmailProperty, "", eAppendLine},
+#ifndef MOZ_THUNDERBIRD
+ {k2ndEmailProperty, "", eAppendLine},
+ {kScreenNameProperty, "propertyScreenName", eAppendLabel}
+#else
+ {k2ndEmailProperty, "", eAppendLine}
+#endif
+};
+
+static const AppendItem PHONE_ATTRS_ARRAY[] = {
+ {kWorkPhoneProperty, "propertyWork", eAppendLabel},
+ {kHomePhoneProperty, "propertyHome", eAppendLabel},
+ {kFaxProperty, "propertyFax", eAppendLabel},
+ {kPagerProperty, "propertyPager", eAppendLabel},
+ {kCellularProperty, "propertyCellular", eAppendLabel}
+};
+
+static const AppendItem HOME_ATTRS_ARRAY[] = {
+ {kHomeAddressProperty, "", eAppendLine},
+ {kHomeAddress2Property, "", eAppendLine},
+ {kHomeCityProperty, "", eAppendCityStateZip},
+ {kHomeCountryProperty, "", eAppendLine},
+ {kHomeWebPageProperty, "", eAppendLine}
+};
+
+static const AppendItem WORK_ATTRS_ARRAY[] = {
+ {kJobTitleProperty, "", eAppendLine},
+ {kDepartmentProperty, "", eAppendLine},
+ {kCompanyProperty, "", eAppendLine},
+ {kWorkAddressProperty, "", eAppendLine},
+ {kWorkAddress2Property, "", eAppendLine},
+ {kWorkCityProperty, "", eAppendCityStateZip},
+ {kWorkCountryProperty, "", eAppendLine},
+ {kWorkWebPageProperty, "", eAppendLine}
+};
+
+static const AppendItem CUSTOM_ATTRS_ARRAY[] = {
+ {kCustom1Property, "propertyCustom1", eAppendLabel},
+ {kCustom2Property, "propertyCustom2", eAppendLabel},
+ {kCustom3Property, "propertyCustom3", eAppendLabel},
+ {kCustom4Property, "propertyCustom4", eAppendLabel},
+ {kNotesProperty, "", eAppendLine}
+};
+
+#ifdef MOZ_THUNDERBIRD
+
+static const AppendItem CHAT_ATTRS_ARRAY[] = {
+ {kGtalkProperty, "propertyGtalk", eAppendLabel},
+ {kAIMProperty, "propertyAIM", eAppendLabel},
+ {kYahooProperty, "propertyYahoo", eAppendLabel},
+ {kSkypeProperty, "propertySkype", eAppendLabel},
+ {kQQProperty, "propertyQQ", eAppendLabel},
+ {kMSNProperty, "propertyMSN", eAppendLabel},
+ {kICQProperty, "propertyICQ", eAppendLabel},
+ {kXMPPProperty, "propertyXMPP", eAppendLabel},
+ {kIRCProperty, "propertyIRC", eAppendLabel}
+};
+#endif
+
+nsAbCardProperty::nsAbCardProperty()
+ : m_IsMailList(false)
+{
+ // Initialize some default properties
+ SetPropertyAsUint32(kPreferMailFormatProperty, nsIAbPreferMailFormat::unknown);
+ SetPropertyAsUint32(kPopularityIndexProperty, 0);
+ // Uninitialized...
+ SetPropertyAsUint32(kLastModifiedDateProperty, 0);
+}
+
+nsAbCardProperty::~nsAbCardProperty(void)
+{
+}
+
+NS_IMPL_ISUPPORTS(nsAbCardProperty, nsIAbCard, nsIAbItem)
+
+NS_IMETHODIMP nsAbCardProperty::GetUuid(nsACString &uuid)
+{
+ // If we have indeterminate sub-ids, return an empty uuid.
+ if (m_directoryId.Equals("") || m_localId.Equals(""))
+ {
+ uuid.Truncate();
+ return NS_OK;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> manager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return manager->GenerateUUID(m_directoryId, m_localId, uuid);
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetDirectoryId(nsACString &dirId)
+{
+ dirId = m_directoryId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetDirectoryId(const nsACString &aDirId)
+{
+ m_directoryId = aDirId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetLocalId(nsACString &localId)
+{
+ localId = m_localId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetLocalId(const nsACString &aLocalId)
+{
+ m_localId = aLocalId;
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP nsAbCardProperty::GetIsMailList(bool *aIsMailList)
+{
+ *aIsMailList = m_IsMailList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetIsMailList(bool aIsMailList)
+{
+ m_IsMailList = aIsMailList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetMailListURI(char **aMailListURI)
+{
+ if (aMailListURI)
+ {
+ *aMailListURI = ToNewCString(m_MailListURI);
+ return (*aMailListURI) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+ else
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetMailListURI(const char *aMailListURI)
+{
+ if (aMailListURI)
+ {
+ m_MailListURI = aMailListURI;
+ return NS_OK;
+ }
+ else
+ return NS_ERROR_NULL_POINTER;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Property bag portion of nsAbCardProperty
+///////////////////////////////////////////////////////////////////////////////
+
+class nsAbSimpleProperty final : public nsIProperty {
+public:
+ nsAbSimpleProperty(const nsACString& aName, nsIVariant* aValue)
+ : mName(aName), mValue(aValue)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROPERTY
+protected:
+ ~nsAbSimpleProperty() {}
+ nsCString mName;
+ nsCOMPtr<nsIVariant> mValue;
+};
+
+NS_IMPL_ISUPPORTS(nsAbSimpleProperty, nsIProperty)
+
+NS_IMETHODIMP
+nsAbSimpleProperty::GetName(nsAString& aName)
+{
+ aName.Assign(NS_ConvertUTF8toUTF16(mName));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbSimpleProperty::GetValue(nsIVariant* *aValue)
+{
+ NS_IF_ADDREF(*aValue = mValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetProperties(nsISimpleEnumerator **props)
+{
+ nsCOMArray<nsIProperty> propertyArray(m_properties.Count());
+ for (auto iter = m_properties.Iter(); !iter.Done(); iter.Next()) {
+ propertyArray.AppendObject(new nsAbSimpleProperty(iter.Key(),
+ iter.UserData()));
+ }
+ return NS_NewArrayEnumerator(props, propertyArray);
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetProperty(const nsACString &name,
+ nsIVariant *defaultValue,
+ nsIVariant **value)
+{
+ if (!m_properties.Get(name, value))
+ NS_ADDREF(*value = defaultValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetPropertyAsAString(const char *name, nsAString &value)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ nsCOMPtr<nsIVariant> variant;
+ return m_properties.Get(nsDependentCString(name), getter_AddRefs(variant)) ?
+ variant->GetAsAString(value) : NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetPropertyAsAUTF8String(const char *name, nsACString &value)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ nsCOMPtr<nsIVariant> variant;
+ return m_properties.Get(nsDependentCString(name), getter_AddRefs(variant)) ?
+ variant->GetAsAUTF8String(value) : NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetPropertyAsUint32(const char *name, uint32_t *value)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ nsCOMPtr<nsIVariant> variant;
+ return m_properties.Get(nsDependentCString(name), getter_AddRefs(variant)) ?
+ variant->GetAsUint32(value) : NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetPropertyAsBool(const char *name, bool *value)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ nsCOMPtr<nsIVariant> variant;
+ return m_properties.Get(nsDependentCString(name), getter_AddRefs(variant)) ?
+ variant->GetAsBool(value) : NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetProperty(const nsACString &name, nsIVariant *value)
+{
+ m_properties.Put(name, value);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetPropertyAsAString(const char *name, const nsAString &value)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
+ variant->SetAsAString(value);
+ m_properties.Put(nsDependentCString(name), variant);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetPropertyAsAUTF8String(const char *name, const nsACString &value)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
+ variant->SetAsAUTF8String(value);
+ m_properties.Put(nsDependentCString(name), variant);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetPropertyAsUint32(const char *name, uint32_t value)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
+ variant->SetAsUint32(value);
+ m_properties.Put(nsDependentCString(name), variant);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetPropertyAsBool(const char *name, bool value)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
+ variant->SetAsBool(value);
+ m_properties.Put(nsDependentCString(name), variant);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::DeleteProperty(const nsACString &name)
+{
+ m_properties.Remove(name);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetFirstName(nsAString &aString)
+{
+ nsresult rv = GetPropertyAsAString(kFirstNameProperty, aString);
+ if (rv == NS_ERROR_NOT_AVAILABLE)
+ {
+ aString.Truncate();
+ return NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetFirstName(const nsAString &aString)
+{
+ return SetPropertyAsAString(kFirstNameProperty, aString);
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetLastName(nsAString &aString)
+{
+ nsresult rv = GetPropertyAsAString(kLastNameProperty, aString);
+ if (rv == NS_ERROR_NOT_AVAILABLE)
+ {
+ aString.Truncate();
+ return NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetLastName(const nsAString &aString)
+{
+ return SetPropertyAsAString(kLastNameProperty, aString);
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetDisplayName(nsAString &aString)
+{
+ nsresult rv = GetPropertyAsAString(kDisplayNameProperty, aString);
+ if (rv == NS_ERROR_NOT_AVAILABLE)
+ {
+ aString.Truncate();
+ return NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetDisplayName(const nsAString &aString)
+{
+ return SetPropertyAsAString(kDisplayNameProperty, aString);
+}
+
+NS_IMETHODIMP nsAbCardProperty::GetPrimaryEmail(nsAString &aString)
+{
+ nsresult rv = GetPropertyAsAString(kPriEmailProperty, aString);
+ if (rv == NS_ERROR_NOT_AVAILABLE)
+ {
+ aString.Truncate();
+ return NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbCardProperty::SetPrimaryEmail(const nsAString &aString)
+{
+ return SetPropertyAsAString(kPriEmailProperty, aString);
+}
+
+NS_IMETHODIMP nsAbCardProperty::HasEmailAddress(const nsACString &aEmailAddress,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = false;
+
+ nsCString emailAddress;
+ nsresult rv = GetPropertyAsAUTF8String(kPriEmailProperty, emailAddress);
+ if (rv != NS_ERROR_NOT_AVAILABLE &&
+ emailAddress.Equals(aEmailAddress, nsCaseInsensitiveCStringComparator()))
+ {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ rv = GetPropertyAsAUTF8String(k2ndEmailProperty, emailAddress);
+ if (rv != NS_ERROR_NOT_AVAILABLE &&
+ emailAddress.Equals(aEmailAddress, nsCaseInsensitiveCStringComparator()))
+ *aResult = true;
+
+ return NS_OK;
+}
+
+// This function may be overridden by derived classes for
+// nsAb*Card specific implementations.
+NS_IMETHODIMP nsAbCardProperty::Copy(nsIAbCard* srcCard)
+{
+ NS_ENSURE_ARG_POINTER(srcCard);
+
+ nsCOMPtr<nsISimpleEnumerator> properties;
+ nsresult rv = srcCard->GetProperties(getter_AddRefs(properties));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ nsCOMPtr<nsISupports> result;
+ while (NS_SUCCEEDED(rv = properties->HasMoreElements(&hasMore)) && hasMore)
+ {
+ rv = properties->GetNext(getter_AddRefs(result));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIProperty> property = do_QueryInterface(result, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString name;
+ property->GetName(name);
+ nsCOMPtr<nsIVariant> value;
+ property->GetValue(getter_AddRefs(value));
+
+ SetProperty(NS_ConvertUTF16toUTF8(name), value);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isMailList;
+ srcCard->GetIsMailList(&isMailList);
+ SetIsMailList(isMailList);
+
+ nsCString mailListURI;
+ srcCard->GetMailListURI(getter_Copies(mailListURI));
+ SetMailListURI(mailListURI.get());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::Equals(nsIAbCard *card, bool *result)
+{
+ *result = (card == this);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// The following methods are other views of a card
+////////////////////////////////////////////////////////////////////////////////
+
+// XXX: Use the category manager instead of this file to implement these
+NS_IMETHODIMP nsAbCardProperty::TranslateTo(const nsACString &type, nsACString &result)
+{
+ if (type.EqualsLiteral("base64xml"))
+ return ConvertToBase64EncodedXML(result);
+ else if (type.EqualsLiteral("xml"))
+ {
+ nsString utf16String;
+ nsresult rv = ConvertToXMLPrintData(utf16String);
+ NS_ENSURE_SUCCESS(rv, rv);
+ result = NS_ConvertUTF16toUTF8(utf16String);
+ return NS_OK;
+ }
+ else if (type.EqualsLiteral("vcard"))
+ return ConvertToEscapedVCard(result);
+
+ return NS_ERROR_ILLEGAL_VALUE;
+}
+//
+static VObject* myAddPropValue(VObject *o, const char *propName, const char16_t *propValue, bool *aCardHasData)
+{
+ if (aCardHasData)
+ *aCardHasData = true;
+ return addPropValue(o, propName, NS_ConvertUTF16toUTF8(propValue).get());
+}
+
+nsresult nsAbCardProperty::ConvertToEscapedVCard(nsACString &aResult)
+{
+ nsString str;
+ nsresult rv;
+ bool vCardHasData = false;
+ VObject* vObj = newVObject(VCCardProp);
+ VObject* t;
+
+ // [comment from 4.x]
+ // Big flame coming....so Vobject is not designed at all to work with an array of
+ // attribute values. It wants you to have all of the attributes easily available. You
+ // cannot add one attribute at a time as you find them to the vobject. Why? Because
+ // it creates a property for a particular type like phone number and then that property
+ // has multiple values. This implementation is not pretty. I can hear my algos prof
+ // yelling from here.....I have to do a linear search through my attributes array for
+ // EACH vcard property we want to set. *sigh* One day I will have time to come back
+ // to this function and remedy this O(m*n) function where n = # attribute values and
+ // m = # of vcard properties....
+
+ (void)GetDisplayName(str);
+ if (!str.IsEmpty()) {
+ myAddPropValue(vObj, VCFullNameProp, str.get(), &vCardHasData);
+ }
+
+ (void)GetLastName(str);
+ if (!str.IsEmpty()) {
+ t = isAPropertyOf(vObj, VCNameProp);
+ if (!t)
+ t = addProp(vObj, VCNameProp);
+ myAddPropValue(t, VCFamilyNameProp, str.get(), &vCardHasData);
+ }
+
+ (void)GetFirstName(str);
+ if (!str.IsEmpty()) {
+ t = isAPropertyOf(vObj, VCNameProp);
+ if (!t)
+ t = addProp(vObj, VCNameProp);
+ myAddPropValue(t, VCGivenNameProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kCompanyProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = isAPropertyOf(vObj, VCOrgProp);
+ if (!t)
+ t = addProp(vObj, VCOrgProp);
+ myAddPropValue(t, VCOrgNameProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kDepartmentProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = isAPropertyOf(vObj, VCOrgProp);
+ if (!t)
+ t = addProp(vObj, VCOrgProp);
+ myAddPropValue(t, VCOrgUnitProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kWorkAddress2Property, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = isAPropertyOf(vObj, VCAdrProp);
+ if (!t)
+ t = addProp(vObj, VCAdrProp);
+ myAddPropValue(t, VCPostalBoxProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kWorkAddressProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = isAPropertyOf(vObj, VCAdrProp);
+ if (!t)
+ t = addProp(vObj, VCAdrProp);
+ myAddPropValue(t, VCStreetAddressProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kWorkCityProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = isAPropertyOf(vObj, VCAdrProp);
+ if (!t)
+ t = addProp(vObj, VCAdrProp);
+ myAddPropValue(t, VCCityProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kWorkStateProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = isAPropertyOf(vObj, VCAdrProp);
+ if (!t)
+ t = addProp(vObj, VCAdrProp);
+ myAddPropValue(t, VCRegionProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kWorkZipCodeProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = isAPropertyOf(vObj, VCAdrProp);
+ if (!t)
+ t = addProp(vObj, VCAdrProp);
+ myAddPropValue(t, VCPostalCodeProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kWorkCountryProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = isAPropertyOf(vObj, VCAdrProp);
+ if (!t)
+ t = addProp(vObj, VCAdrProp);
+ myAddPropValue(t, VCCountryNameProp, str.get(), &vCardHasData);
+ }
+ else
+ {
+ // only add this if VCAdrProp already exists
+ t = isAPropertyOf(vObj, VCAdrProp);
+ if (t)
+ {
+ addProp(t, VCDomesticProp);
+ }
+ }
+
+ (void)GetPrimaryEmail(str);
+ if (!str.IsEmpty())
+ {
+ t = myAddPropValue(vObj, VCEmailAddressProp, str.get(), &vCardHasData);
+ addProp(t, VCInternetProp);
+ }
+
+ rv = GetPropertyAsAString(kJobTitleProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ myAddPropValue(vObj, VCTitleProp, str.get(), &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kWorkPhoneProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = myAddPropValue(vObj, VCTelephoneProp, str.get(), &vCardHasData);
+ addProp(t, VCWorkProp);
+ }
+
+ rv = GetPropertyAsAString(kFaxProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = myAddPropValue(vObj, VCTelephoneProp, str.get(), &vCardHasData);
+ addProp(t, VCFaxProp);
+ }
+
+ rv = GetPropertyAsAString(kPagerProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = myAddPropValue(vObj, VCTelephoneProp, str.get(), &vCardHasData);
+ addProp(t, VCPagerProp);
+ }
+
+ rv = GetPropertyAsAString(kHomePhoneProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = myAddPropValue(vObj, VCTelephoneProp, str.get(), &vCardHasData);
+ addProp(t, VCHomeProp);
+ }
+
+ rv = GetPropertyAsAString(kCellularProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ t = myAddPropValue(vObj, VCTelephoneProp, str.get(), &vCardHasData);
+ addProp(t, VCCellularProp);
+ }
+
+ rv = GetPropertyAsAString(kNotesProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ myAddPropValue(vObj, VCNoteProp, str.get(), &vCardHasData);
+ }
+
+ uint32_t format;
+ rv = GetPropertyAsUint32(kPreferMailFormatProperty, &format);
+ if (NS_SUCCEEDED(rv) && format == nsIAbPreferMailFormat::html) {
+ myAddPropValue(vObj, VCUseHTML, u"TRUE", &vCardHasData);
+ }
+ else if (NS_SUCCEEDED(rv) && format == nsIAbPreferMailFormat::plaintext) {
+ myAddPropValue(vObj, VCUseHTML, u"FALSE", &vCardHasData);
+ }
+
+ rv = GetPropertyAsAString(kWorkWebPageProperty, str);
+ if (NS_SUCCEEDED(rv) && !str.IsEmpty())
+ {
+ myAddPropValue(vObj, VCURLProp, str.get(), &vCardHasData);
+ }
+
+ myAddPropValue(vObj, VCVersionProp, u"2.1", nullptr);
+
+ if (!vCardHasData) {
+ aResult.Truncate();
+ cleanVObject(vObj);
+ return NS_OK;
+ }
+
+ int len = 0;
+ char *vCard = writeMemVObject(0, &len, vObj);
+ if (vObj)
+ cleanVObject(vObj);
+
+ nsCString escResult;
+ MsgEscapeString(nsDependentCString(vCard), nsINetUtil::ESCAPE_URL_PATH, escResult);
+ aResult = escResult;
+ return NS_OK;
+}
+
+nsresult nsAbCardProperty::ConvertToBase64EncodedXML(nsACString &result)
+{
+ nsresult rv;
+ nsString xmlStr;
+
+ xmlStr.AppendLiteral("<?xml version=\"1.0\"?>\n"
+ "<?xml-stylesheet type=\"text/css\" href=\"chrome://messagebody/content/addressbook/print.css\"?>\n"
+ "<directory>\n");
+
+ // Get Address Book string and set it as title of XML document
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ if (stringBundleService) {
+ rv = stringBundleService->CreateBundle(sAddrbookProperties, getter_AddRefs(bundle));
+ if (NS_SUCCEEDED(rv)) {
+ nsString addrBook;
+ rv = bundle->GetStringFromName(u"addressBook", getter_Copies(addrBook));
+ if (NS_SUCCEEDED(rv)) {
+ xmlStr.AppendLiteral("<title xmlns=\"http://www.w3.org/1999/xhtml\">");
+ xmlStr.Append(addrBook);
+ xmlStr.AppendLiteral("</title>\n");
+ }
+ }
+ }
+
+ nsString xmlSubstr;
+ rv = ConvertToXMLPrintData(xmlSubstr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ xmlStr.Append(xmlSubstr);
+ xmlStr.AppendLiteral("</directory>\n");
+
+ char *tmpRes = PL_Base64Encode(NS_ConvertUTF16toUTF8(xmlStr).get(), 0, nullptr);
+ result.Assign(tmpRes);
+ PR_Free(tmpRes);
+ return NS_OK;
+}
+
+nsresult nsAbCardProperty::ConvertToXMLPrintData(nsAString &aXMLSubstr)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ int32_t generatedNameFormat;
+ rv = prefBranch->GetIntPref(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, &generatedNameFormat);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(stringBundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = stringBundleService->CreateBundle(sAddrbookProperties, getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsString generatedName;
+ rv = GenerateName(generatedNameFormat, bundle, generatedName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<mozITXTToHTMLConv> conv = do_CreateInstance(MOZ_TXTTOHTMLCONV_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsString xmlStr;
+ xmlStr.SetLength(4096); // to reduce allocations. should be enough for most cards
+ xmlStr.AssignLiteral("<GeneratedName>\n");
+
+ // use ScanTXT to convert < > & to safe values.
+ nsString safeText;
+ if (!generatedName.IsEmpty()) {
+ rv = conv->ScanTXT(generatedName.get(), mozITXTToHTMLConv::kEntities,
+ getter_Copies(safeText));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ if (safeText.IsEmpty()) {
+ nsAutoString primaryEmail;
+ GetPrimaryEmail(primaryEmail);
+
+ // use ScanTXT to convert < > & to safe values.
+ rv = conv->ScanTXT(primaryEmail.get(), mozITXTToHTMLConv::kEntities,
+ getter_Copies(safeText));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ xmlStr.Append(safeText);
+
+ xmlStr.AppendLiteral("</GeneratedName>\n"
+ "<table><tr><td>");
+
+ rv = AppendSection(NAME_ATTRS_ARRAY, sizeof(NAME_ATTRS_ARRAY)/sizeof(AppendItem), EmptyString(), bundle, conv, xmlStr);
+
+ xmlStr.AppendLiteral("</td></tr><tr><td>");
+
+ rv = AppendSection(PHONE_ATTRS_ARRAY, sizeof(PHONE_ATTRS_ARRAY)/sizeof(AppendItem), NS_LITERAL_STRING("headingPhone"), bundle, conv, xmlStr);
+
+ if (!m_IsMailList) {
+ rv = AppendSection(CUSTOM_ATTRS_ARRAY, sizeof(CUSTOM_ATTRS_ARRAY)/sizeof(AppendItem), NS_LITERAL_STRING("headingOther"), bundle, conv, xmlStr);
+#ifdef MOZ_THUNDERBIRD
+ rv = AppendSection(CHAT_ATTRS_ARRAY, sizeof(CHAT_ATTRS_ARRAY)/sizeof(AppendItem), NS_LITERAL_STRING("headingChat"), bundle, conv, xmlStr);
+#endif
+ }
+ else {
+ rv = AppendSection(CUSTOM_ATTRS_ARRAY, sizeof(CUSTOM_ATTRS_ARRAY)/sizeof(AppendItem), NS_LITERAL_STRING("headingDescription"),
+ bundle, conv, xmlStr);
+
+ xmlStr.AppendLiteral("<section><sectiontitle>");
+
+ nsString headingAddresses;
+ rv = bundle->GetStringFromName(u"headingAddresses", getter_Copies(headingAddresses));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ xmlStr.Append(headingAddresses);
+ xmlStr.AppendLiteral("</sectiontitle>");
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIAbDirectory> mailList = nullptr;
+ rv = abManager->GetDirectory(m_MailListURI, getter_AddRefs(mailList));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> addresses;
+ rv = mailList->GetAddressLists(getter_AddRefs(addresses));
+ if (addresses) {
+ uint32_t total = 0;
+ addresses->GetLength(&total);
+ if (total) {
+ uint32_t i;
+ nsAutoString displayName;
+ nsAutoString primaryEmail;
+ for (i = 0; i < total; i++) {
+ nsCOMPtr <nsIAbCard> listCard = do_QueryElementAt(addresses, i, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ xmlStr.AppendLiteral("<PrimaryEmail>\n");
+
+ rv = listCard->GetDisplayName(displayName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // use ScanTXT to convert < > & to safe values.
+ nsString safeText;
+ rv = conv->ScanTXT(displayName.get(), mozITXTToHTMLConv::kEntities,
+ getter_Copies(safeText));
+ NS_ENSURE_SUCCESS(rv,rv);
+ xmlStr.Append(safeText);
+
+ xmlStr.AppendLiteral(" &lt;");
+
+ listCard->GetPrimaryEmail(primaryEmail);
+
+ // use ScanTXT to convert < > & to safe values.
+ rv = conv->ScanTXT(primaryEmail.get(), mozITXTToHTMLConv::kEntities,
+ getter_Copies(safeText));
+ NS_ENSURE_SUCCESS(rv,rv);
+ xmlStr.Append(safeText);
+
+ xmlStr.AppendLiteral("&gt;</PrimaryEmail>\n");
+ }
+ }
+ }
+ xmlStr.AppendLiteral("</section>");
+ }
+
+ xmlStr.AppendLiteral("</td><td>");
+
+ rv = AppendSection(HOME_ATTRS_ARRAY, sizeof(HOME_ATTRS_ARRAY)/sizeof(AppendItem), NS_LITERAL_STRING("headingHome"), bundle, conv, xmlStr);
+ rv = AppendSection(WORK_ATTRS_ARRAY, sizeof(WORK_ATTRS_ARRAY)/sizeof(AppendItem), NS_LITERAL_STRING("headingWork"), bundle, conv, xmlStr);
+
+ xmlStr.AppendLiteral("</td></tr></table>");
+
+ aXMLSubstr = xmlStr;
+
+ return NS_OK;
+}
+
+nsresult nsAbCardProperty::AppendSection(const AppendItem *aArray, int16_t aCount, const nsString& aHeading,
+ nsIStringBundle *aBundle,
+ mozITXTToHTMLConv *aConv,
+ nsString &aResult)
+{
+ nsresult rv = NS_OK;
+
+ aResult.AppendLiteral("<section>");
+
+ nsString attrValue;
+ bool sectionIsEmpty = true;
+
+ int16_t i = 0;
+ for (i=0;i<aCount;i++) {
+ rv = GetPropertyAsAString(aArray[i].mColumn, attrValue);
+ if (NS_SUCCEEDED(rv) && !attrValue.IsEmpty())
+ sectionIsEmpty = false;
+ }
+
+ if (!sectionIsEmpty && !aHeading.IsEmpty()) {
+ nsString heading;
+ rv = aBundle->GetStringFromName(aHeading.get(), getter_Copies(heading));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aResult.AppendLiteral("<sectiontitle>");
+ aResult.Append(heading);
+ aResult.AppendLiteral("</sectiontitle>");
+ }
+
+ for (i=0;i<aCount;i++) {
+ switch (aArray[i].mAppendType) {
+ case eAppendLine:
+ rv = AppendLine(aArray[i], aConv, aResult);
+ break;
+ case eAppendLabel:
+ rv = AppendLabel(aArray[i], aBundle, aConv, aResult);
+ break;
+ case eAppendCityStateZip:
+ rv = AppendCityStateZip(aArray[i], aBundle, aConv, aResult);
+ break;
+ default:
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+
+ if (NS_FAILED(rv)) {
+ NS_WARNING("append item failed");
+ break;
+ }
+ }
+ aResult.AppendLiteral("</section>");
+
+ return rv;
+}
+
+nsresult nsAbCardProperty::AppendLine(const AppendItem &aItem,
+ mozITXTToHTMLConv *aConv,
+ nsString &aResult)
+{
+ NS_ENSURE_ARG_POINTER(aConv);
+
+ nsString attrValue;
+ nsresult rv = GetPropertyAsAString(aItem.mColumn, attrValue);
+
+ if (NS_FAILED(rv) || attrValue.IsEmpty())
+ return NS_OK;
+
+ aResult.Append(char16_t('<'));
+ aResult.Append(NS_ConvertUTF8toUTF16(aItem.mColumn));
+ aResult.Append(char16_t('>'));
+
+ // use ScanTXT to convert < > & to safe values.
+ nsString safeText;
+ rv = aConv->ScanTXT(attrValue.get(), mozITXTToHTMLConv::kEntities, getter_Copies(safeText));
+ NS_ENSURE_SUCCESS(rv,rv);
+ aResult.Append(safeText);
+
+ aResult.AppendLiteral("</");
+ aResult.Append(NS_ConvertUTF8toUTF16(aItem.mColumn));
+ aResult.Append(char16_t('>'));
+
+ return NS_OK;
+}
+
+nsresult nsAbCardProperty::AppendLabel(const AppendItem &aItem,
+ nsIStringBundle *aBundle,
+ mozITXTToHTMLConv *aConv,
+ nsString &aResult)
+{
+ NS_ENSURE_ARG_POINTER(aBundle);
+
+ nsresult rv;
+ nsString label, attrValue;
+
+ rv = GetPropertyAsAString(aItem.mColumn, attrValue);
+
+ if (NS_FAILED(rv) || attrValue.IsEmpty())
+ return NS_OK;
+
+ rv = aBundle->GetStringFromName(NS_ConvertUTF8toUTF16(aItem.mLabel).get(), getter_Copies(label));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aResult.AppendLiteral("<labelrow><label>");
+
+ aResult.Append(label);
+ aResult.AppendLiteral(": </label>");
+
+ rv = AppendLine(aItem, aConv, aResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ aResult.AppendLiteral("</labelrow>");
+
+ return NS_OK;
+}
+
+nsresult nsAbCardProperty::AppendCityStateZip(const AppendItem &aItem,
+ nsIStringBundle *aBundle,
+ mozITXTToHTMLConv *aConv,
+ nsString &aResult)
+{
+ NS_ENSURE_ARG_POINTER(aBundle);
+
+ nsresult rv;
+ AppendItem item;
+ const char *statePropName, *zipPropName;
+
+ if (strcmp(aItem.mColumn, kHomeCityProperty) == 0) {
+ statePropName = kHomeStateProperty;
+ zipPropName = kHomeZipCodeProperty;
+ }
+ else {
+ statePropName = kWorkStateProperty;
+ zipPropName = kWorkZipCodeProperty;
+ }
+
+ nsAutoString cityResult, stateResult, zipResult;
+
+ rv = AppendLine(aItem, aConv, cityResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ item.mColumn = statePropName;
+ item.mLabel = "";
+
+ rv = AppendLine(item, aConv, stateResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ item.mColumn = zipPropName;
+
+ rv = AppendLine(item, aConv, zipResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsString formattedString;
+
+ if (!cityResult.IsEmpty() && !stateResult.IsEmpty() && !zipResult.IsEmpty()) {
+ const char16_t *formatStrings[] = { cityResult.get(), stateResult.get(), zipResult.get() };
+ rv = aBundle->FormatStringFromName(u"cityAndStateAndZip", formatStrings, ArrayLength(formatStrings), getter_Copies(formattedString));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ else if (!cityResult.IsEmpty() && !stateResult.IsEmpty() && zipResult.IsEmpty()) {
+ const char16_t *formatStrings[] = { cityResult.get(), stateResult.get() };
+ rv = aBundle->FormatStringFromName(u"cityAndStateNoZip", formatStrings, ArrayLength(formatStrings), getter_Copies(formattedString));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ else if ((!cityResult.IsEmpty() && stateResult.IsEmpty() && !zipResult.IsEmpty()) ||
+ (cityResult.IsEmpty() && !stateResult.IsEmpty() && !zipResult.IsEmpty())) {
+ const char16_t *formatStrings[] = { cityResult.IsEmpty() ? stateResult.get() : cityResult.get(), zipResult.get() };
+ rv = aBundle->FormatStringFromName(u"cityOrStateAndZip", formatStrings, ArrayLength(formatStrings), getter_Copies(formattedString));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ else {
+ if (!cityResult.IsEmpty())
+ formattedString = cityResult;
+ else if (!stateResult.IsEmpty())
+ formattedString = stateResult;
+ else
+ formattedString = zipResult;
+ }
+
+ aResult.Append(formattedString);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GenerateName(int32_t aGenerateFormat,
+ nsIStringBundle* aBundle,
+ nsAString &aResult)
+{
+ aResult.Truncate();
+
+ // Cache the first and last names
+ nsAutoString firstName, lastName;
+ GetFirstName(firstName);
+ GetLastName(lastName);
+
+ // No need to check for aBundle present straight away, only do that if we're
+ // actually going to use it.
+ if (aGenerateFormat == GENERATE_DISPLAY_NAME)
+ GetDisplayName(aResult);
+ else if (lastName.IsEmpty())
+ aResult = firstName;
+ else if (firstName.IsEmpty())
+ aResult = lastName;
+ else {
+ nsresult rv;
+ nsCOMPtr<nsIStringBundle> bundle(aBundle);
+ if (!bundle) {
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(stringBundleService, NS_ERROR_UNEXPECTED);
+
+ rv = stringBundleService->CreateBundle(sAddrbookProperties,
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsString result;
+
+ if (aGenerateFormat == GENERATE_LAST_FIRST_ORDER) {
+ const char16_t *stringParams[2] = {lastName.get(), firstName.get()};
+
+ rv = bundle->FormatStringFromName(u"lastFirstFormat",
+ stringParams, 2, getter_Copies(result));
+ }
+ else {
+ const char16_t *stringParams[2] = {firstName.get(), lastName.get()};
+
+ rv = bundle->FormatStringFromName(u"firstLastFormat",
+ stringParams, 2, getter_Copies(result));
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aResult.Assign(result);
+ }
+
+ if (aResult.IsEmpty())
+ {
+ // The normal names have failed, does this card have a company name? If so,
+ // use that instead, because that is likely to be more meaningful than an
+ // email address.
+ //
+ // If this errors, the string isn't found and we'll fall into the next
+ // check.
+ (void) GetPropertyAsAString(kCompanyProperty, aResult);
+ }
+
+ if (aResult.IsEmpty())
+ {
+ // see bug #211078
+ // if there is no generated name at this point
+ // use the userid from the email address
+ // it is better than nothing.
+ GetPrimaryEmail(aResult);
+ int32_t index = aResult.FindChar('@');
+ if (index != -1)
+ aResult.SetLength(index);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GeneratePhoneticName(bool aLastNameFirst,
+ nsAString &aResult)
+{
+ nsAutoString firstName, lastName;
+ GetPropertyAsAString(kPhoneticFirstNameProperty, firstName);
+ GetPropertyAsAString(kPhoneticLastNameProperty, lastName);
+
+ if (aLastNameFirst)
+ {
+ aResult = lastName;
+ aResult += firstName;
+ }
+ else
+ {
+ aResult = firstName;
+ aResult += lastName;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbCardProperty::GenerateChatName(nsAString &aResult)
+{
+ aResult.Truncate();
+
+#define CHECK_CHAT_PROPERTY(aProtocol) \
+ if (NS_SUCCEEDED(GetPropertyAsAString(k##aProtocol##Property, aResult)) && \
+ !aResult.IsEmpty()) \
+ return NS_OK
+ CHECK_CHAT_PROPERTY(Gtalk);
+ CHECK_CHAT_PROPERTY(AIM);
+ CHECK_CHAT_PROPERTY(Yahoo);
+ CHECK_CHAT_PROPERTY(Skype);
+ CHECK_CHAT_PROPERTY(QQ);
+ CHECK_CHAT_PROPERTY(MSN);
+ CHECK_CHAT_PROPERTY(ICQ);
+ CHECK_CHAT_PROPERTY(XMPP);
+ CHECK_CHAT_PROPERTY(IRC);
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbCardProperty.h b/mailnews/addrbook/src/nsAbCardProperty.h
new file mode 100644
index 000000000..46ba50079
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbCardProperty.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/********************************************************************************************************
+
+ Interface for representing Address Book Person Card Property
+
+*********************************************************************************************************/
+
+#ifndef nsAbCardProperty_h__
+#define nsAbCardProperty_h__
+
+#include "nsIAbCard.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+
+#include "nsInterfaceHashtable.h"
+#include "nsIVariant.h"
+
+class nsIStringBundle;
+class mozITXTToHTMLConv;
+struct AppendItem;
+
+ /*
+ * Address Book Card Property
+ */
+
+class nsAbCardProperty: public nsIAbCard
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABCARD
+ NS_DECL_NSIABITEM
+
+ nsAbCardProperty();
+
+protected:
+ virtual ~nsAbCardProperty();
+ bool m_IsMailList;
+ nsCString m_MailListURI;
+
+ // Store most of the properties here
+ nsInterfaceHashtable<nsCStringHashKey, nsIVariant> m_properties;
+
+ nsCString m_directoryId, m_localId;
+private:
+ nsresult AppendSection(const AppendItem *aArray, int16_t aCount, const nsString& aHeading, nsIStringBundle *aBundle, mozITXTToHTMLConv *aConv, nsString &aResult);
+ nsresult AppendLine(const AppendItem &aItem, mozITXTToHTMLConv *aConv, nsString &aResult);
+ nsresult AppendLabel(const AppendItem &aItem, nsIStringBundle *aBundle, mozITXTToHTMLConv *aConv, nsString &aResult);
+ nsresult AppendCityStateZip(const AppendItem &aItem, nsIStringBundle *aBundle, mozITXTToHTMLConv *aConv, nsString &aResult);
+
+ nsresult ConvertToBase64EncodedXML(nsACString &result);
+ nsresult ConvertToXMLPrintData(nsAString &result);
+ nsresult ConvertToEscapedVCard(nsACString &result);
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbContentHandler.cpp b/mailnews/addrbook/src/nsAbContentHandler.cpp
new file mode 100644
index 000000000..6e283d82b
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbContentHandler.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbContentHandler.h"
+#include "nsAbBaseCID.h"
+#include "nsNetUtil.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsNullPrincipal.h"
+#include "nsISupportsPrimitives.h"
+#include "plstr.h"
+#include "nsPIDOMWindow.h"
+#include "mozIDOMWindow.h"
+#include "nsMsgUtils.h"
+#include "nsIMsgVCardService.h"
+#include "nsIAbCard.h"
+#include "nsIAbManager.h"
+#include "nsVCard.h"
+#include "nsIChannel.h"
+//
+// nsAbContentHandler
+//
+nsAbContentHandler::nsAbContentHandler()
+{
+}
+
+nsAbContentHandler::~nsAbContentHandler()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsAbContentHandler, nsIContentHandler,
+ nsIStreamLoaderObserver)
+
+NS_IMETHODIMP
+nsAbContentHandler::HandleContent(const char *aContentType,
+ nsIInterfaceRequestor *aWindowContext,
+ nsIRequest *request)
+{
+ NS_ENSURE_ARG_POINTER(request);
+
+ nsresult rv = NS_OK;
+
+ // First of all, get the content type and make sure it is a content type we know how to handle!
+ if (PL_strcasecmp(aContentType, "application/x-addvcard") == 0) {
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
+ if (!aChannel) return NS_ERROR_FAILURE;
+
+ rv = aChannel->GetURI(getter_AddRefs(uri));
+ if (uri)
+ {
+ nsAutoCString path;
+ rv = uri->GetPath(path);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ const char *startOfVCard = strstr(path.get(), "add?vcard=");
+ if (startOfVCard)
+ {
+ nsCString unescapedData;
+
+ // XXX todo, explain why we is escaped twice
+ MsgUnescapeString(nsDependentCString(startOfVCard + strlen("add?vcard=")),
+ 0, unescapedData);
+
+ if (!aWindowContext)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<mozIDOMWindowProxy> domWindow = do_GetInterface(aWindowContext);
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+ nsCOMPtr<nsPIDOMWindowOuter> parentWindow = nsPIDOMWindowOuter::From(domWindow);
+ parentWindow = parentWindow->GetOuterWindow();
+ NS_ENSURE_ARG_POINTER(parentWindow);
+
+ nsCOMPtr<nsIAbManager> ab =
+ do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIAbCard> cardFromVCard;
+ rv = ab->EscapedVCardToAbCard(unescapedData.get(),
+ getter_AddRefs(cardFromVCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsInterfacePointer> ifptr =
+ do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ifptr->SetData(cardFromVCard);
+ ifptr->SetDataIID(&NS_GET_IID(nsIAbCard));
+
+ nsCOMPtr<nsPIDOMWindowOuter> dialogWindow;
+
+ rv = parentWindow->OpenDialog(
+ NS_LITERAL_STRING("chrome://messenger/content/addressbook/abNewCardDialog.xul"),
+ EmptyString(),
+ NS_LITERAL_STRING("chrome,resizable=no,titlebar,modal,centerscreen"),
+ ifptr, getter_AddRefs(dialogWindow));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ rv = NS_OK;
+ }
+ }
+ else if (PL_strcasecmp(aContentType, "text/x-vcard") == 0) {
+ // create a vcard stream listener that can parse the data stream
+ // and bring up the appropriate UI
+
+ // (1) cancel the current load operation. We'll restart it
+ request->Cancel(NS_ERROR_ABORT);
+ // get the url we were trying to open
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
+
+ rv = channel->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // create a stream loader to handle the v-card data
+ nsCOMPtr<nsIStreamLoader> streamLoader;
+ rv = NS_NewStreamLoader(getter_AddRefs(streamLoader),
+ uri,
+ this,
+ nullPrincipal,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ }
+ else // The content-type was not application/x-addvcard...
+ return NS_ERROR_WONT_HANDLE_CONTENT;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsAbContentHandler::OnStreamComplete(nsIStreamLoader *aLoader,
+ nsISupports *aContext, nsresult aStatus,
+ uint32_t datalen, const uint8_t *data)
+{
+ NS_ENSURE_ARG_POINTER(aContext);
+ NS_ENSURE_SUCCESS(aStatus, aStatus); // don't process the vcard if we got a status error
+ nsresult rv = NS_OK;
+
+ // take our vCard string and open up an address book window based on it
+ nsCOMPtr<nsIMsgVCardService> vCardService = do_GetService(NS_MSGVCARDSERVICE_CONTRACTID);
+ if (vCardService)
+ {
+ nsAutoPtr<VObject> vObj(vCardService->Parse_MIME((const char *)data, datalen));
+ if (vObj)
+ {
+ int32_t len = 0;
+ nsCString vCard;
+ vCard.Adopt(vCardService->WriteMemoryVObjects(0, &len, vObj, false));
+
+ nsCOMPtr<nsIAbManager> ab =
+ do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIAbCard> cardFromVCard;
+ rv = ab->EscapedVCardToAbCard(vCard.get(),
+ getter_AddRefs(cardFromVCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<mozIDOMWindowProxy> domWindow = do_GetInterface(aContext);
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+ nsCOMPtr<nsPIDOMWindowOuter> parentWindow = nsPIDOMWindowOuter::From(domWindow);
+ parentWindow = parentWindow->GetOuterWindow();
+ NS_ENSURE_ARG_POINTER(parentWindow);
+
+ nsCOMPtr<nsPIDOMWindowOuter> dialogWindow;
+ rv = parentWindow->OpenDialog(
+ NS_LITERAL_STRING("chrome://messenger/content/addressbook/abNewCardDialog.xul"),
+ EmptyString(),
+ NS_LITERAL_STRING("chrome,resizable=no,titlebar,modal,centerscreen"),
+ cardFromVCard, getter_AddRefs(dialogWindow));
+ }
+ }
+
+ return rv;
+}
diff --git a/mailnews/addrbook/src/nsAbContentHandler.h b/mailnews/addrbook/src/nsAbContentHandler.h
new file mode 100644
index 000000000..e5405ae0e
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbContentHandler.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsAbContentHandler_h
+#define __nsAbContentHandler_h
+
+#include "nsIStreamLoader.h"
+#include "nsIContentHandler.h"
+
+class nsAbContentHandler : public nsIContentHandler,
+ public nsIStreamLoaderObserver
+{
+public:
+ nsAbContentHandler();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSICONTENTHANDLER
+ NS_DECL_NSISTREAMLOADEROBSERVER
+
+private:
+ virtual ~nsAbContentHandler();
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbDirFactoryService.cpp b/mailnews/addrbook/src/nsAbDirFactoryService.cpp
new file mode 100644
index 000000000..4ddf8e635
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbDirFactoryService.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIIOService.h"
+#include "nsNetCID.h"
+#include "nsMemory.h"
+#include "nsStringGlue.h"
+#include "plstr.h"
+
+#include "nsAbBaseCID.h"
+#include "nsAbDirFactoryService.h"
+#include "nsIAbDirFactory.h"
+#include "mozilla/Services.h"
+
+NS_IMPL_ISUPPORTS(nsAbDirFactoryService, nsIAbDirFactoryService)
+
+nsAbDirFactoryService::nsAbDirFactoryService()
+{
+}
+
+nsAbDirFactoryService::~nsAbDirFactoryService()
+{
+}
+
+/* nsIAbDirFactory getDirFactory (in string uri); */
+NS_IMETHODIMP
+nsAbDirFactoryService::GetDirFactory(const nsACString &aURI,
+ nsIAbDirFactory** aDirFactory)
+{
+ NS_ENSURE_ARG_POINTER(aDirFactory);
+
+ nsresult rv;
+
+ // Obtain the network IO service
+ nsCOMPtr<nsIIOService> nsService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(nsService, NS_ERROR_UNEXPECTED);
+
+ // Extract the scheme
+ nsAutoCString scheme;
+ rv = nsService->ExtractScheme(aURI, scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Try to find a factory using the component manager.
+ nsAutoCString contractID;
+ contractID.AssignLiteral(NS_AB_DIRECTORY_FACTORY_CONTRACTID_PREFIX);
+ contractID.Append(scheme);
+
+ return CallCreateInstance(contractID.get(), aDirFactory);
+}
diff --git a/mailnews/addrbook/src/nsAbDirFactoryService.h b/mailnews/addrbook/src/nsAbDirFactoryService.h
new file mode 100644
index 000000000..77396fbaa
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbDirFactoryService.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbDirFactoryService_h__
+#define nsAbDirFactoryService_h__
+
+#include "nsIAbDirFactoryService.h"
+
+class nsAbDirFactoryService : public nsIAbDirFactoryService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABDIRFACTORYSERVICE
+
+ nsAbDirFactoryService();
+
+private:
+ virtual ~nsAbDirFactoryService();
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbDirProperty.cpp b/mailnews/addrbook/src/nsAbDirProperty.cpp
new file mode 100644
index 000000000..fbae06220
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbDirProperty.cpp
@@ -0,0 +1,593 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbDirProperty.h"
+#include "nsAbBaseCID.h"
+#include "nsIAbCard.h"
+#include "nsDirPrefs.h"
+#include "nsIPrefService.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "prmem.h"
+#include "nsIAbManager.h"
+#include "nsArrayUtils.h"
+
+// From nsDirPrefs
+#define kDefaultPosition 1
+
+nsAbDirProperty::nsAbDirProperty(void)
+ : m_LastModifiedDate(0),
+ mIsValidURI(false),
+ mIsQueryURI(false)
+{
+ m_IsMailList = false;
+}
+
+nsAbDirProperty::~nsAbDirProperty(void)
+{
+#if 0
+ // this code causes a regression #138647
+ // don't turn it on until you figure it out
+ if (m_AddressList) {
+ uint32_t count;
+ nsresult rv;
+ rv = m_AddressList->GetLength(&count);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
+ int32_t i;
+ for (i = count - 1; i >= 0; i--)
+ m_AddressList->RemoveElementAt(i);
+ }
+#endif
+}
+
+NS_IMPL_ISUPPORTS(nsAbDirProperty, nsIAbDirectory, nsISupportsWeakReference,
+ nsIAbCollection, nsIAbItem)
+
+NS_IMETHODIMP nsAbDirProperty::GetUuid(nsACString &uuid)
+{
+ // XXX: not all directories have a dirPrefId...
+ nsresult rv = GetDirPrefId(uuid);
+ NS_ENSURE_SUCCESS(rv, rv);
+ uuid.Append('&');
+ nsString dirName;
+ GetDirName(dirName);
+ uuid.Append(NS_ConvertUTF16toUTF8(dirName));
+ return rv;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GenerateName(int32_t aGenerateFormat,
+ nsIStringBundle *aBundle,
+ nsAString &name)
+{
+ return GetDirName(name);
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetPropertiesChromeURI(nsACString &aResult)
+{
+ aResult.AssignLiteral("chrome://messenger/content/addressbook/abAddressBookNameDialog.xul");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetDirName(nsAString &aDirName)
+{
+ if (m_DirPrefId.IsEmpty())
+ {
+ aDirName = m_ListDirName;
+ return NS_OK;
+ }
+
+ nsCString dirName;
+ nsresult rv = GetLocalizedStringValue("description", EmptyCString(), dirName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // In TB 2 only some prefs had chrome:// URIs. We had code in place that would
+ // only get the localized string pref for the particular address books that
+ // were built-in.
+ // Additionally, nsIPrefBranch::getComplexValue will only get a non-user-set,
+ // non-locked pref value if it is a chrome:// URI and will get the string
+ // value at that chrome URI. This breaks extensions/autoconfig that want to
+ // set default pref values and allow users to change directory names.
+ //
+ // Now we have to support this, and so if for whatever reason we fail to get
+ // the localized version, then we try and get the non-localized version
+ // instead. If the string value is empty, then we'll just get the empty value
+ // back here.
+ if (dirName.IsEmpty())
+ {
+ rv = GetStringValue("description", EmptyCString(), dirName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ CopyUTF8toUTF16(dirName, aDirName);
+ return NS_OK;
+}
+
+// XXX Although mailing lists could use the NotifyItemPropertyChanged
+// mechanism here, it requires some rework on how we write/save data
+// relating to mailing lists, so we're just using the old method of a
+// local variable to store the mailing list name.
+NS_IMETHODIMP nsAbDirProperty::SetDirName(const nsAString &aDirName)
+{
+ if (m_DirPrefId.IsEmpty())
+ {
+ m_ListDirName = aDirName;
+ return NS_OK;
+ }
+
+ // Store the old value.
+ nsString oldDirName;
+ nsresult rv = GetDirName(oldDirName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Save the new value
+ rv = SetLocalizedStringValue("description", NS_ConvertUTF16toUTF8(aDirName));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv))
+ // We inherit from nsIAbDirectory, so this static cast should be safe.
+ abManager->NotifyItemPropertyChanged(static_cast<nsIAbDirectory*>(this),
+ "DirName", oldDirName.get(),
+ nsString(aDirName).get());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetDirType(int32_t *aDirType)
+{
+ return GetIntValue("dirType", LDAPDirectory, aDirType);
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetFileName(nsACString &aFileName)
+{
+ return GetStringValue("filename", EmptyCString(), aFileName);
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetURI(nsACString &aURI)
+{
+ // XXX Should we complete this for Mailing Lists?
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetPosition(int32_t *aPosition)
+{
+ return GetIntValue("position", kDefaultPosition, aPosition);
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetLastModifiedDate(uint32_t *aLastModifiedDate)
+{
+ NS_ENSURE_ARG_POINTER(aLastModifiedDate);
+ *aLastModifiedDate = m_LastModifiedDate;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetLastModifiedDate(uint32_t aLastModifiedDate)
+{
+ if (aLastModifiedDate)
+ {
+ m_LastModifiedDate = aLastModifiedDate;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetListNickName(nsAString &aListNickName)
+{
+ aListNickName = m_ListNickName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetListNickName(const nsAString &aListNickName)
+{
+ m_ListNickName = aListNickName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetDescription(nsAString &aDescription)
+{
+ aDescription = m_Description;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetDescription(const nsAString &aDescription)
+{
+ m_Description = aDescription;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetIsMailList(bool *aIsMailList)
+{
+ *aIsMailList = m_IsMailList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetIsMailList(bool aIsMailList)
+{
+ m_IsMailList = aIsMailList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetAddressLists(nsIMutableArray * *aAddressLists)
+{
+ if (!m_AddressList)
+ {
+ nsresult rv;
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *aAddressLists = m_AddressList;
+ NS_ADDREF(*aAddressLists);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetAddressLists(nsIMutableArray * aAddressLists)
+{
+ m_AddressList = aAddressLists;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::CopyMailList(nsIAbDirectory* srcList)
+{
+ SetIsMailList(true);
+
+ nsString str;
+ srcList->GetDirName(str);
+ SetDirName(str);
+ srcList->GetListNickName(str);
+ SetListNickName(str);
+ srcList->GetDescription(str);
+ SetDescription(str);
+
+ nsCOMPtr<nsIMutableArray> pAddressLists;
+ srcList->GetAddressLists(getter_AddRefs(pAddressLists));
+ SetAddressLists(pAddressLists);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetIsQuery(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ // Mailing lists are not queries by default, individual directory types
+ // will override this.
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbDirProperty::Init(const char *aURI)
+{
+ mURINoQuery = aURI;
+ mURI = aURI;
+ mIsValidURI = true;
+
+ int32_t searchCharLocation = mURINoQuery.FindChar('?');
+ if (searchCharLocation >= 0)
+ {
+ mQueryString = Substring(mURINoQuery, searchCharLocation + 1);
+ mURINoQuery.SetLength(searchCharLocation);
+ mIsQueryURI = true;
+ }
+
+ return NS_OK;
+}
+
+// nsIAbDirectory NOT IMPLEMENTED methods
+NS_IMETHODIMP
+nsAbDirProperty::GetChildNodes(nsISimpleEnumerator **childList)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+nsAbDirProperty::GetChildCards(nsISimpleEnumerator **childCards)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+nsAbDirProperty::DeleteDirectory(nsIAbDirectory *directory)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+nsAbDirProperty::HasCard(nsIAbCard *cards, bool *hasCard)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+nsAbDirProperty::HasDirectory(nsIAbDirectory *dir, bool *hasDir)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+nsAbDirProperty::HasMailListWithName(const char16_t *aName, bool *aHasList)
+{
+ NS_ENSURE_ARG_POINTER(aName);
+ NS_ENSURE_ARG_POINTER(aHasList);
+
+ *aHasList = false;
+
+ bool supportsLists = false;
+ nsresult rv = GetSupportsMailingLists(&supportsLists);
+ if (NS_FAILED(rv) || !supportsLists)
+ return NS_OK;
+
+ if (m_IsMailList)
+ return NS_OK;
+
+ nsCOMPtr<nsIMutableArray> addressLists;
+ rv = GetAddressLists(getter_AddRefs(addressLists));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t listCount = 0;
+ rv = addressLists->GetLength(&listCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t i = 0; i < listCount; i++)
+ {
+ nsCOMPtr<nsIAbDirectory> listDir(do_QueryElementAt(addressLists, i, &rv));
+ if (NS_SUCCEEDED(rv) && listDir)
+ {
+ nsAutoString listName;
+ rv = listDir->GetDirName(listName);
+ if (NS_SUCCEEDED(rv) && listName.Equals(aName))
+ {
+ *aHasList = true;
+ return NS_OK;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbDirProperty::CreateNewDirectory(const nsAString &aDirName,
+ const nsACString &aURI,
+ uint32_t aType,
+ const nsACString &aPrefName,
+ nsACString &aResult)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+nsAbDirProperty::CreateDirectoryByURI(const nsAString &aDisplayName,
+ const nsACString &aURI)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::AddMailList(nsIAbDirectory *list, nsIAbDirectory **addedList)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::EditMailListToDatabase(nsIAbCard *listCard)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::AddCard(nsIAbCard *childCard, nsIAbCard **addedCard)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::ModifyCard(nsIAbCard *aModifiedCard)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::DeleteCards(nsIArray *cards)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::DropCard(nsIAbCard *childCard, bool needToCopyCard)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::CardForEmailAddress(const nsACString &aEmailAddress,
+ nsIAbCard ** aAbCard)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::GetCardFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool caseSensitive,
+ nsIAbCard **result)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP nsAbDirProperty::GetCardsFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool caseSensitive,
+ nsISimpleEnumerator **result)
+{ return NS_ERROR_NOT_IMPLEMENTED; }
+
+
+NS_IMETHODIMP nsAbDirProperty::GetSupportsMailingLists(bool *aSupportsMailingsLists)
+{
+ NS_ENSURE_ARG_POINTER(aSupportsMailingsLists);
+ // We don't currently support nested mailing lists, so only return true if
+ // we're not a mailing list.
+ *aSupportsMailingsLists = !m_IsMailList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetReadOnly(bool *aReadOnly)
+{
+ NS_ENSURE_ARG_POINTER(aReadOnly);
+ // Default is that we are writable. Any implementation that is read-only must
+ // override this method.
+ *aReadOnly = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetIsRemote(bool *aIsRemote)
+{
+ NS_ENSURE_ARG_POINTER(aIsRemote);
+ *aIsRemote = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetIsSecure(bool *aIsSecure)
+{
+ NS_ENSURE_ARG_POINTER(aIsSecure);
+ *aIsSecure = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::UseForAutocomplete(const nsACString &aIdentityKey,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // Is local autocomplete enabled?
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return prefBranch->GetBoolPref("mail.enable_autocomplete", aResult);
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetDirPrefId(nsACString &aDirPrefId)
+{
+ aDirPrefId = m_DirPrefId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetDirPrefId(const nsACString &aDirPrefId)
+{
+ if (!m_DirPrefId.Equals(aDirPrefId))
+ {
+ m_DirPrefId.Assign(aDirPrefId);
+ // Clear the directory pref branch so that it is re-initialized next
+ // time its required.
+ m_DirectoryPrefs = nullptr;
+ }
+ return NS_OK;
+}
+
+nsresult nsAbDirProperty::InitDirectoryPrefs()
+{
+ if (m_DirPrefId.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString realPrefId(m_DirPrefId);
+ realPrefId.Append('.');
+
+ return prefService->GetBranch(realPrefId.get(), getter_AddRefs(m_DirectoryPrefs));
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetIntValue(const char *aName,
+ int32_t aDefaultValue,
+ int32_t *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!m_DirectoryPrefs && NS_FAILED(InitDirectoryPrefs()))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (NS_FAILED(m_DirectoryPrefs->GetIntPref(aName, aResult)))
+ *aResult = aDefaultValue;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetBoolValue(const char *aName,
+ bool aDefaultValue,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!m_DirectoryPrefs && NS_FAILED(InitDirectoryPrefs()))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (NS_FAILED(m_DirectoryPrefs->GetBoolPref(aName, aResult)))
+ *aResult = aDefaultValue;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::GetStringValue(const char *aName,
+ const nsACString &aDefaultValue,
+ nsACString &aResult)
+{
+ if (!m_DirectoryPrefs && NS_FAILED(InitDirectoryPrefs()))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCString value;
+
+ /* unfortunately, there may be some prefs out there which look like (null) */
+ if (NS_SUCCEEDED(m_DirectoryPrefs->GetCharPref(aName, getter_Copies(value))) &&
+ !value.EqualsLiteral("(null"))
+ aResult = value;
+ else
+ aResult = aDefaultValue;
+
+ return NS_OK;
+}
+/*
+ * Get localized unicode string pref from properties file, convert into an
+ * UTF8 string since address book prefs store as UTF8 strings. So far there
+ * are 2 default prefs stored in addressbook.properties.
+ * "ldap_2.servers.pab.description"
+ * "ldap_2.servers.history.description"
+ */
+NS_IMETHODIMP nsAbDirProperty::GetLocalizedStringValue(const char *aName,
+ const nsACString &aDefaultValue,
+ nsACString &aResult)
+{
+ if (!m_DirectoryPrefs && NS_FAILED(InitDirectoryPrefs()))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsString wvalue;
+ nsCOMPtr<nsIPrefLocalizedString> locStr;
+
+ nsresult rv = m_DirectoryPrefs->GetComplexValue(aName,
+ NS_GET_IID(nsIPrefLocalizedString),
+ getter_AddRefs(locStr));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = locStr->ToString(getter_Copies(wvalue));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (wvalue.IsEmpty())
+ aResult = aDefaultValue;
+ else
+ CopyUTF16toUTF8(wvalue, aResult);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetIntValue(const char *aName,
+ int32_t aValue)
+{
+ if (!m_DirectoryPrefs && NS_FAILED(InitDirectoryPrefs()))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return m_DirectoryPrefs->SetIntPref(aName, aValue);
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetBoolValue(const char *aName,
+ bool aValue)
+{
+ if (!m_DirectoryPrefs && NS_FAILED(InitDirectoryPrefs()))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return m_DirectoryPrefs->SetBoolPref(aName, aValue);
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetStringValue(const char *aName,
+ const nsACString &aValue)
+{
+ if (!m_DirectoryPrefs && NS_FAILED(InitDirectoryPrefs()))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return m_DirectoryPrefs->SetCharPref(aName, nsCString(aValue).get());
+}
+
+NS_IMETHODIMP nsAbDirProperty::SetLocalizedStringValue(const char *aName,
+ const nsACString &aValue)
+{
+ if (!m_DirectoryPrefs && NS_FAILED(InitDirectoryPrefs()))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefLocalizedString> locStr(
+ do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = locStr->SetData(NS_ConvertUTF8toUTF16(aValue).get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return m_DirectoryPrefs->SetComplexValue(aName,
+ NS_GET_IID(nsIPrefLocalizedString),
+ locStr);
+}
diff --git a/mailnews/addrbook/src/nsAbDirProperty.h b/mailnews/addrbook/src/nsAbDirProperty.h
new file mode 100644
index 000000000..99d16a133
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbDirProperty.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/********************************************************************************************************
+
+ Interface for representing Address Book Directory
+
+*********************************************************************************************************/
+
+#ifndef nsAbDirProperty_h__
+#define nsAbDirProperty_h__
+
+#include "nsIAbDirectory.h" /* include the interface we are going to support */
+#include "nsIAbCard.h"
+#include "nsCOMPtr.h"
+#include "nsDirPrefs.h"
+#include "nsIAddrDatabase.h"
+#include "nsStringGlue.h"
+#include "nsIPrefBranch.h"
+#include "nsIMutableArray.h"
+#include "nsWeakReference.h"
+
+ /*
+ * Address Book Directory
+ */
+
+class nsAbDirProperty: public nsIAbDirectory,
+ public nsSupportsWeakReference
+{
+public:
+ nsAbDirProperty(void);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABITEM
+ NS_DECL_NSIABCOLLECTION
+ NS_DECL_NSIABDIRECTORY
+
+protected:
+ virtual ~nsAbDirProperty(void);
+
+ /**
+ * Initialise the directory prefs for this branch
+ */
+ nsresult InitDirectoryPrefs();
+
+ uint32_t m_LastModifiedDate;
+
+ nsString m_ListDirName;
+ nsString m_ListName;
+ nsString m_ListNickName;
+ nsString m_Description;
+ bool m_IsMailList;
+
+ nsCString mURI;
+ nsCString mQueryString;
+ nsCString mURINoQuery;
+ bool mIsValidURI;
+ bool mIsQueryURI;
+
+
+ /*
+ * Note that any derived implementations should ensure that this item
+ * (m_DirPrefId) is correctly initialised correctly
+ */
+ nsCString m_DirPrefId; // ie,"ldap_2.servers.pab"
+
+ nsCOMPtr<nsIPrefBranch> m_DirectoryPrefs;
+ nsCOMPtr<nsIMutableArray> m_AddressList;
+};
+#endif
diff --git a/mailnews/addrbook/src/nsAbDirectoryQuery.cpp b/mailnews/addrbook/src/nsAbDirectoryQuery.cpp
new file mode 100644
index 000000000..9c9826ac8
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbDirectoryQuery.cpp
@@ -0,0 +1,528 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbDirectoryQuery.h"
+#include "nsAbDirectoryQueryProxy.h"
+#include "nsAbUtils.h"
+#include "nsAbBooleanExpression.h"
+#include "nsArrayUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsStringGlue.h"
+#include "nsUnicharUtils.h"
+#include "nsIAbDirSearchListener.h"
+#include "nsISimpleEnumerator.h"
+#include "nsMsgUtils.h"
+
+NS_IMPL_ISUPPORTS(nsAbDirectoryQuerySimpleBooleanExpression, nsIAbBooleanExpression)
+
+nsAbDirectoryQuerySimpleBooleanExpression::nsAbDirectoryQuerySimpleBooleanExpression() :
+ mOperation (nsIAbBooleanOperationTypes::AND)
+{
+}
+
+nsAbDirectoryQuerySimpleBooleanExpression::~nsAbDirectoryQuerySimpleBooleanExpression()
+{
+}
+
+/* attribute nsAbBooleanOperationType operation; */
+NS_IMETHODIMP nsAbDirectoryQuerySimpleBooleanExpression::GetOperation(nsAbBooleanOperationType *aOperation)
+{
+ if (!aOperation)
+ return NS_ERROR_NULL_POINTER;
+
+ *aOperation = mOperation;
+
+ return NS_OK;
+}
+NS_IMETHODIMP nsAbDirectoryQuerySimpleBooleanExpression::SetOperation(nsAbBooleanOperationType aOperation)
+{
+ if (aOperation != nsIAbBooleanOperationTypes::AND &&
+ aOperation != nsIAbBooleanOperationTypes::OR)
+ return NS_ERROR_FAILURE;
+
+ mOperation = aOperation;
+
+ return NS_OK;
+}
+
+/* attribute nsIArray expressions; */
+NS_IMETHODIMP nsAbDirectoryQuerySimpleBooleanExpression::GetExpressions(nsIArray **aExpressions)
+{
+ if (!aExpressions)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!mExpressions)
+ {
+ mExpressions = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!mExpressions)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(*aExpressions = mExpressions);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirectoryQuerySimpleBooleanExpression::SetExpressions(nsIArray *aExpressions)
+{
+ if (!aExpressions)
+ return NS_ERROR_NULL_POINTER;
+
+ // Ensure all the items are of the right type.
+ nsresult rv;
+ uint32_t count;
+ rv = aExpressions->GetLength(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbBooleanConditionString> queryExpression;
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ queryExpression = do_QueryElementAt(aExpressions, i, &rv);
+ if (NS_FAILED(rv))
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ // Values ok, so we can just save and return.
+ mExpressions = aExpressions;
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsAbDirectoryQueryArguments, nsIAbDirectoryQueryArguments)
+
+nsAbDirectoryQueryArguments::nsAbDirectoryQueryArguments() :
+ mQuerySubDirectories(true)
+{
+}
+
+nsAbDirectoryQueryArguments::~nsAbDirectoryQueryArguments()
+{
+}
+
+/* attribute nsISupports matchItems; */
+NS_IMETHODIMP nsAbDirectoryQueryArguments::GetExpression(nsISupports** aExpression)
+{
+ if (!aExpression)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_IF_ADDREF(*aExpression = mExpression);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirectoryQueryArguments::SetExpression(nsISupports* aExpression)
+{
+ mExpression = aExpression;
+ return NS_OK;
+}
+
+/* attribute boolean querySubDirectories; */
+NS_IMETHODIMP nsAbDirectoryQueryArguments::GetQuerySubDirectories(bool* aQuerySubDirectories)
+{
+ NS_ENSURE_ARG_POINTER(aQuerySubDirectories);
+ *aQuerySubDirectories = mQuerySubDirectories;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirectoryQueryArguments::SetQuerySubDirectories(bool aQuerySubDirectories)
+{
+ mQuerySubDirectories = aQuerySubDirectories;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirectoryQueryArguments::GetTypeSpecificArg(nsISupports** aArg)
+{
+ NS_ENSURE_ARG_POINTER(aArg);
+
+ NS_IF_ADDREF(*aArg = mTypeSpecificArg);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirectoryQueryArguments::SetTypeSpecificArg(nsISupports* aArg)
+{
+ mTypeSpecificArg = aArg;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirectoryQueryArguments::GetFilter(nsACString & aFilter)
+{
+ aFilter.Assign(mFilter);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbDirectoryQueryArguments::SetFilter(const nsACString & aFilter)
+{
+ mFilter.Assign(aFilter);
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsAbDirectoryQueryPropertyValue, nsIAbDirectoryQueryPropertyValue)
+
+nsAbDirectoryQueryPropertyValue::nsAbDirectoryQueryPropertyValue()
+{
+}
+
+nsAbDirectoryQueryPropertyValue::nsAbDirectoryQueryPropertyValue(const char* aName,
+ const char16_t* aValue)
+{
+ mName = aName;
+ mValue = aValue;
+}
+
+nsAbDirectoryQueryPropertyValue::nsAbDirectoryQueryPropertyValue(const char* aName,
+ nsISupports* aValueISupports)
+{
+ mName = aName;
+ mValueISupports = aValueISupports;
+}
+
+nsAbDirectoryQueryPropertyValue::~nsAbDirectoryQueryPropertyValue()
+{
+}
+
+/* read only attribute string name; */
+NS_IMETHODIMP nsAbDirectoryQueryPropertyValue::GetName(char* *aName)
+{
+ *aName = mName.IsEmpty() ? 0 : ToNewCString(mName);
+
+ return NS_OK;
+}
+
+/* read only attribute wstring value; */
+NS_IMETHODIMP nsAbDirectoryQueryPropertyValue::GetValue(char16_t* *aValue)
+{
+ *aValue = ToNewUnicode(mValue);
+ if (!(*aValue))
+ return NS_ERROR_OUT_OF_MEMORY;
+ else
+ return NS_OK;
+}
+
+/* readonly attribute nsISupports valueISupports; */
+NS_IMETHODIMP nsAbDirectoryQueryPropertyValue::GetValueISupports(nsISupports* *aValueISupports)
+{
+ if (!mValueISupports)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_IF_ADDREF(*aValueISupports = mValueISupports);
+ return NS_OK;
+}
+
+/* Implementation file */
+NS_IMPL_ISUPPORTS(nsAbDirectoryQuery, nsIAbDirectoryQuery)
+
+nsAbDirectoryQuery::nsAbDirectoryQuery()
+{
+}
+
+nsAbDirectoryQuery::~nsAbDirectoryQuery()
+{
+}
+
+NS_IMETHODIMP nsAbDirectoryQuery::DoQuery(nsIAbDirectory *aDirectory,
+ nsIAbDirectoryQueryArguments* arguments,
+ nsIAbDirSearchListener* listener,
+ int32_t resultLimit, int32_t timeOut,
+ int32_t* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+
+ nsCOMPtr<nsISupports> supportsExpression;
+ nsresult rv = arguments->GetExpression(getter_AddRefs(supportsExpression));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbBooleanExpression> expression(do_QueryInterface(supportsExpression, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool doSubDirectories;
+ rv = arguments->GetQuerySubDirectories(&doSubDirectories);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = query(aDirectory, expression, listener, doSubDirectories, &resultLimit);
+
+ rv = NS_FAILED(rv) ? queryError(listener) : queryFinished(listener);
+
+ *_retval = 0;
+ return rv;
+}
+
+/* void stopQuery (in long contextID); */
+NS_IMETHODIMP nsAbDirectoryQuery::StopQuery(int32_t contextID)
+{
+ return NS_OK;
+}
+
+
+nsresult nsAbDirectoryQuery::query(nsIAbDirectory* directory,
+ nsIAbBooleanExpression* expression,
+ nsIAbDirSearchListener* listener,
+ bool doSubDirectories,
+ int32_t* resultLimit)
+{
+ if (*resultLimit == 0)
+ return NS_OK;
+
+ nsresult rv = queryCards(directory, expression, listener, resultLimit);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (doSubDirectories && resultLimit != 0)
+ {
+ rv = queryChildren(directory, expression, listener, doSubDirectories,
+ resultLimit);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return rv;
+}
+
+nsresult nsAbDirectoryQuery::queryChildren(nsIAbDirectory* directory,
+ nsIAbBooleanExpression* expression,
+ nsIAbDirSearchListener* listener,
+ bool doSubDirectories,
+ int32_t* resultLimit)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsISimpleEnumerator> subDirectories;
+ rv = directory->GetChildNodes(getter_AddRefs(subDirectories));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(rv = subDirectories->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> item;
+ rv = subDirectories->GetNext (getter_AddRefs (item));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> subDirectory(do_QueryInterface(item, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = query(subDirectory, expression, listener, doSubDirectories, resultLimit);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ }
+ return NS_OK;
+}
+
+nsresult nsAbDirectoryQuery::queryCards(nsIAbDirectory* directory,
+ nsIAbBooleanExpression* expression,
+ nsIAbDirSearchListener* listener,
+ int32_t* resultLimit)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsISimpleEnumerator> cards;
+ rv = directory->GetChildCards(getter_AddRefs(cards));
+ if (NS_FAILED(rv))
+ {
+ if (rv != NS_ERROR_NOT_IMPLEMENTED)
+ NS_ENSURE_SUCCESS(rv, rv);
+ else
+ return NS_OK;
+ }
+
+ if (!cards)
+ return NS_OK;
+
+ bool more;
+ while (NS_SUCCEEDED(cards->HasMoreElements(&more)) && more)
+ {
+ nsCOMPtr<nsISupports> item;
+ rv = cards->GetNext(getter_AddRefs(item));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> card(do_QueryInterface(item, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = matchCard (card, expression, listener, resultLimit);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (*resultLimit == 0)
+ return NS_OK;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsAbDirectoryQuery::matchCard(nsIAbCard* card,
+ nsIAbBooleanExpression* expression,
+ nsIAbDirSearchListener* listener,
+ int32_t* resultLimit)
+{
+ bool matchFound = false;
+ nsresult rv = matchCardExpression(card, expression, &matchFound);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (matchFound)
+ {
+ (*resultLimit)--;
+ rv = queryMatch(card, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return rv;
+}
+
+nsresult nsAbDirectoryQuery::matchCardExpression(nsIAbCard* card,
+ nsIAbBooleanExpression* expression,
+ bool* result)
+{
+ nsAbBooleanOperationType operation;
+ nsresult rv = expression->GetOperation (&operation);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIArray> childExpressions;
+ rv = expression->GetExpressions (getter_AddRefs (childExpressions));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t count;
+ rv = childExpressions->GetLength(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (operation == nsIAbBooleanOperationTypes::NOT &&
+ count > 1)
+ return NS_ERROR_FAILURE;
+
+ bool value = *result = false;
+ nsCOMPtr<nsIAbBooleanConditionString> childCondition;
+ nsCOMPtr<nsIAbBooleanExpression> childExpression;
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ childCondition = do_QueryElementAt(childExpressions, i, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = matchCardCondition (card, childCondition, &value);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else
+ {
+ childExpression = do_QueryElementAt(childExpressions, i, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = matchCardExpression (card, childExpression, &value);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else
+ return NS_ERROR_FAILURE;
+ }
+ if (operation == nsIAbBooleanOperationTypes::OR && value)
+ break;
+ else if (operation == nsIAbBooleanOperationTypes::AND && !value)
+ break;
+ else if (operation == nsIAbBooleanOperationTypes::NOT)
+ value = !value;
+ }
+ *result = value;
+
+ return NS_OK;
+}
+
+nsresult nsAbDirectoryQuery::matchCardCondition(nsIAbCard* card,
+ nsIAbBooleanConditionString* condition,
+ bool* matchFound)
+{
+ nsAbBooleanConditionType conditionType;
+ nsresult rv = condition->GetCondition (&conditionType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString name;
+ rv = condition->GetName (getter_Copies (name));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (name.Equals ("card:nsIAbCard"))
+ {
+ *matchFound = (conditionType == nsIAbBooleanConditionTypes::Exists);
+ return NS_OK;
+ }
+
+ nsString matchValue;
+ rv = condition->GetValue (getter_Copies (matchValue));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (name.EqualsLiteral("IsMailList"))
+ {
+ bool isMailList;
+ rv = card->GetIsMailList(&isMailList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Only equals is supported.
+ if (conditionType != nsIAbBooleanConditionTypes::Is)
+ return NS_ERROR_FAILURE;
+
+ *matchFound = isMailList ? matchValue.EqualsLiteral("TRUE") :
+ matchValue.EqualsLiteral("FALSE");
+ return NS_OK;
+ }
+
+ nsString value;
+ (void)card->GetPropertyAsAString(name.get(), value);
+
+ if (value.IsEmpty())
+ {
+ *matchFound = (conditionType == nsIAbBooleanConditionTypes::DoesNotExist) ?
+ true : false;
+ return NS_OK;
+ }
+
+ /* TODO
+ * What about allowing choice between case insensitive
+ * and case sensitive comparisons?
+ *
+ */
+ switch (conditionType)
+ {
+ case nsIAbBooleanConditionTypes::Exists:
+ *matchFound = true;
+ break;
+ case nsIAbBooleanConditionTypes::Contains:
+ *matchFound = CaseInsensitiveFindInReadable(matchValue, value);
+ break;
+ case nsIAbBooleanConditionTypes::DoesNotContain:
+ *matchFound = !CaseInsensitiveFindInReadable(matchValue, value);
+ break;
+ case nsIAbBooleanConditionTypes::Is:
+ *matchFound = value.Equals(matchValue, nsCaseInsensitiveStringComparator());
+ break;
+ case nsIAbBooleanConditionTypes::IsNot:
+ *matchFound = !value.Equals(matchValue, nsCaseInsensitiveStringComparator());
+ break;
+ case nsIAbBooleanConditionTypes::BeginsWith:
+ *matchFound = StringBeginsWith(value, matchValue, nsCaseInsensitiveStringComparator());
+ break;
+ case nsIAbBooleanConditionTypes::LessThan:
+ *matchFound = Compare(value, matchValue, nsCaseInsensitiveStringComparator()) < 0;
+ break;
+ case nsIAbBooleanConditionTypes::GreaterThan:
+ *matchFound = Compare(value, matchValue, nsCaseInsensitiveStringComparator()) > 0;
+ break;
+ case nsIAbBooleanConditionTypes::EndsWith:
+ *matchFound = StringEndsWith(value, matchValue, nsCaseInsensitiveStringComparator());
+ break;
+ case nsIAbBooleanConditionTypes::SoundsLike:
+ case nsIAbBooleanConditionTypes::RegExp:
+ *matchFound = false;
+ break;
+ default:
+ *matchFound = false;
+ }
+
+ return rv;
+}
+
+nsresult nsAbDirectoryQuery::queryMatch(nsIAbCard* card,
+ nsIAbDirSearchListener* listener)
+{
+ return listener->OnSearchFoundCard(card);
+}
+
+nsresult nsAbDirectoryQuery::queryFinished(nsIAbDirSearchListener* listener)
+{
+ return listener->OnSearchFinished(nsIAbDirectoryQueryResultListener::queryResultComplete, EmptyString());
+}
+
+nsresult nsAbDirectoryQuery::queryError(nsIAbDirSearchListener* listener)
+{
+ return listener->OnSearchFinished(nsIAbDirectoryQueryResultListener::queryResultError, EmptyString());
+}
diff --git a/mailnews/addrbook/src/nsAbDirectoryQuery.h b/mailnews/addrbook/src/nsAbDirectoryQuery.h
new file mode 100644
index 000000000..99aa943aa
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbDirectoryQuery.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbDirectoryQuery_h__
+#define nsAbDirectoryQuery_h__
+
+#include "nsIAbDirectoryQuery.h"
+#include "nsIAbDirectory.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsIArray.h"
+#include "nsIAbBooleanExpression.h"
+
+class nsAbDirectoryQuerySimpleBooleanExpression : public nsIAbBooleanExpression
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABBOOLEANEXPRESSION
+
+ nsAbDirectoryQuerySimpleBooleanExpression();
+
+private:
+ virtual ~nsAbDirectoryQuerySimpleBooleanExpression();
+
+public:
+ nsCOMPtr<nsIArray> mExpressions;
+ nsAbBooleanOperationType mOperation;
+};
+
+
+class nsAbDirectoryQueryArguments : public nsIAbDirectoryQueryArguments
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABDIRECTORYQUERYARGUMENTS
+
+ nsAbDirectoryQueryArguments();
+
+private:
+ virtual ~nsAbDirectoryQueryArguments();
+
+protected:
+ nsCOMPtr<nsISupports> mExpression;
+ nsCOMPtr<nsISupports> mTypeSpecificArg;
+ bool mQuerySubDirectories;
+ nsCString mFilter;
+};
+
+
+class nsAbDirectoryQueryPropertyValue : public nsIAbDirectoryQueryPropertyValue
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABDIRECTORYQUERYPROPERTYVALUE
+
+ nsAbDirectoryQueryPropertyValue();
+ nsAbDirectoryQueryPropertyValue(const char* aName,
+ const char16_t* aValue);
+ nsAbDirectoryQueryPropertyValue(const char* aName,
+ nsISupports* aValueISupports);
+
+protected:
+ virtual ~nsAbDirectoryQueryPropertyValue();
+ nsCString mName;
+ nsString mValue;
+ nsCOMPtr<nsISupports> mValueISupports;
+};
+
+
+class nsAbDirectoryQuery : public nsIAbDirectoryQuery
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABDIRECTORYQUERY
+
+ nsAbDirectoryQuery();
+
+protected:
+ virtual ~nsAbDirectoryQuery();
+ nsresult query(nsIAbDirectory* directory,
+ nsIAbBooleanExpression* expression,
+ nsIAbDirSearchListener* listener,
+ bool doSubDirectories,
+ int32_t* resultLimit);
+ nsresult queryChildren(nsIAbDirectory* directory,
+ nsIAbBooleanExpression* expression,
+ nsIAbDirSearchListener* listener,
+ bool doSubDirectories,
+ int32_t* resultLimit);
+ nsresult queryCards(nsIAbDirectory* directory,
+ nsIAbBooleanExpression* expression,
+ nsIAbDirSearchListener* listener,
+ int32_t* resultLimit);
+ nsresult matchCard(nsIAbCard* card,
+ nsIAbBooleanExpression* expression,
+ nsIAbDirSearchListener* listener,
+ int32_t* resultLimit);
+ nsresult matchCardExpression(nsIAbCard* card,
+ nsIAbBooleanExpression* expression,
+ bool* result);
+ nsresult matchCardCondition(nsIAbCard* card,
+ nsIAbBooleanConditionString* condition,
+ bool* matchFound);
+
+ nsresult queryMatch (nsIAbCard* card,
+ nsIAbDirSearchListener* listener);
+ nsresult queryFinished(nsIAbDirSearchListener* listener);
+ nsresult queryError(nsIAbDirSearchListener* listener);
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbDirectoryQueryProxy.cpp b/mailnews/addrbook/src/nsAbDirectoryQueryProxy.cpp
new file mode 100644
index 000000000..553f3b903
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbDirectoryQueryProxy.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbDirectoryQuery.h"
+#include "nsAbDirectoryQueryProxy.h"
+
+NS_IMPL_ISUPPORTS(nsAbDirectoryQueryProxy, nsIAbDirectoryQueryProxy, nsIAbDirectoryQuery)
+
+nsAbDirectoryQueryProxy::nsAbDirectoryQueryProxy() :
+ mInitiated (false)
+{
+}
+
+nsAbDirectoryQueryProxy::~nsAbDirectoryQueryProxy()
+{
+}
+
+/* void initiate (in nsIAbDirectory directory); */
+NS_IMETHODIMP nsAbDirectoryQueryProxy::Initiate()
+{
+ if (mInitiated)
+ return NS_OK;
+
+ mDirectoryQuery = new nsAbDirectoryQuery();
+
+ mInitiated = true;
+
+ return NS_OK;
+}
+
+
diff --git a/mailnews/addrbook/src/nsAbDirectoryQueryProxy.h b/mailnews/addrbook/src/nsAbDirectoryQueryProxy.h
new file mode 100644
index 000000000..89a323c75
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbDirectoryQueryProxy.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbDirectoryQueryProxy_h__
+#define nsAbDirectoryQueryProxy_h__
+
+#include "nsIAbDirectoryQueryProxy.h"
+#include "nsCOMPtr.h"
+
+class nsAbDirectoryQueryProxy : public nsIAbDirectoryQueryProxy
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSIABDIRECTORYQUERY(mDirectoryQuery->)
+ NS_DECL_NSIABDIRECTORYQUERYPROXY
+
+ nsAbDirectoryQueryProxy();
+
+protected:
+ virtual ~nsAbDirectoryQueryProxy();
+ bool mInitiated;
+ nsCOMPtr<nsIAbDirectoryQuery> mDirectoryQuery;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbLDAPAttributeMap.js b/mailnews/addrbook/src/nsAbLDAPAttributeMap.js
new file mode 100644
index 000000000..8ff2406d2
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPAttributeMap.js
@@ -0,0 +1,247 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var NS_ABLDAPATTRIBUTEMAP_CID = Components.ID(
+ "{127b341a-bdda-4270-85e1-edff569a9b85}");
+var NS_ABLDAPATTRIBUTEMAPSERVICE_CID = Components.ID(
+ "{4ed7d5e1-8800-40da-9e78-c4f509d7ac5e}");
+
+function nsAbLDAPAttributeMap() {
+ this.mPropertyMap = {};
+ this.mAttrMap = {};
+}
+
+nsAbLDAPAttributeMap.prototype = {
+ classID: NS_ABLDAPATTRIBUTEMAP_CID,
+
+ getAttributeList: function getAttributeList(aProperty) {
+
+ if (!(aProperty in this.mPropertyMap)) {
+ return null;
+ }
+
+ // return the joined list
+ return this.mPropertyMap[aProperty].join(",");
+ },
+
+ getAttributes: function getAttributes(aProperty, aCount, aAttrs) {
+
+ // fail if no entry for this
+ if (!(aProperty in this.mPropertyMap)) {
+ throw Components.results.NS_ERROR_FAILURE;
+ }
+
+ aAttrs = this.mPropertyMap[aProperty];
+ aCount = aAttrs.length;
+ return aAttrs;
+ },
+
+ getFirstAttribute: function getFirstAttribute(aProperty) {
+
+ // fail if no entry for this
+ if (!(aProperty in this.mPropertyMap)) {
+ return null;
+ }
+
+ return this.mPropertyMap[aProperty][0];
+ },
+
+ setAttributeList: function setAttributeList(aProperty, aAttributeList,
+ aAllowInconsistencies) {
+
+ var attrs = aAttributeList.split(",");
+
+ // check to make sure this call won't allow multiple mappings to be
+ // created, if requested
+ if (!aAllowInconsistencies) {
+ for (var attr of attrs) {
+ if (attr in this.mAttrMap && this.mAttrMap[attr] != aProperty) {
+ throw Components.results.NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ // delete any attr mappings created by the existing property map entry
+ if (aProperty in this.mPropertyMap) {
+ for (attr of this.mPropertyMap[aProperty]) {
+ delete this.mAttrMap[attr];
+ }
+ }
+
+ // add these attrs to the attrmap
+ for (attr of attrs) {
+ this.mAttrMap[attr] = aProperty;
+ }
+
+ // add them to the property map
+ this.mPropertyMap[aProperty] = attrs;
+ },
+
+ getProperty: function getProperty(aAttribute) {
+
+ if (!(aAttribute in this.mAttrMap)) {
+ return null;
+ }
+
+ return this.mAttrMap[aAttribute];
+ },
+
+ getAllCardAttributes: function getAllCardAttributes() {
+ var attrs = [];
+ for (var prop in this.mPropertyMap) {
+ let attrArray = this.mPropertyMap[prop];
+ attrs = attrs.concat(attrArray);
+ }
+
+ if (!attrs.length) {
+ throw Components.results.NS_ERROR_FAILURE;
+ }
+
+ return attrs.join(",");
+ },
+
+ getAllCardProperties: function getAllCardProperties(aCount) {
+
+ var props = [];
+ for (var prop in this.mPropertyMap) {
+ props.push(prop);
+ }
+
+ aCount.value = props.length;
+ return props;
+ },
+
+ setFromPrefs: function setFromPrefs(aPrefBranchName) {
+ // get the right pref branch
+ let branch = Services.prefs.getBranch(aPrefBranchName + ".");
+
+ // get the list of children
+ var childCount = {};
+ var children = branch.getChildList("", childCount);
+
+ // do the actual sets
+ for (var child of children) {
+ this.setAttributeList(child, branch.getCharPref(child), true);
+ }
+
+ // ensure that everything is kosher
+ this.checkState();
+ },
+
+ setCardPropertiesFromLDAPMessage: function
+ setCardPropertiesFromLDAPMessage(aMessage, aCard) {
+
+ var cardValueWasSet = false;
+
+ var msgAttrCount = {};
+ var msgAttrs = aMessage.getAttributes(msgAttrCount);
+
+ // downcase the array for comparison
+ function toLower(a) { return a.toLowerCase(); }
+ msgAttrs = msgAttrs.map(toLower);
+
+ // deal with each addressbook property
+ for (var prop in this.mPropertyMap) {
+
+ // go through the list of possible attrs in precedence order
+ for (var attr of this.mPropertyMap[prop]) {
+
+ attr = attr.toLowerCase();
+
+ // find the first attr that exists in this message
+ if (msgAttrs.indexOf(attr) != -1) {
+
+ try {
+ var values = aMessage.getValues(attr, {});
+ // strip out the optional label from the labeledURI
+ if (attr == "labeleduri" && values[0]) {
+ var index = values[0].indexOf(" ");
+ if (index != -1)
+ values[0] = values[0].substring(0, index);
+ }
+ aCard.setProperty(prop, values[0]);
+
+ cardValueWasSet = true;
+ break;
+ } catch (ex) {
+ // ignore any errors getting message values or setting card values
+ }
+ }
+ }
+ }
+
+ if (!cardValueWasSet) {
+ throw Components.results.NS_ERROR_FAILURE;
+ }
+
+ return;
+ },
+
+ checkState: function checkState() {
+
+ var attrsSeen = [];
+
+ for (var prop in this.mPropertyMap) {
+ let attrArray = this.mPropertyMap[prop];
+ for (var attr of attrArray) {
+
+ // multiple attributes that mapped to the empty string are permitted
+ if (!attr.length) {
+ continue;
+ }
+
+ // if we've seen this before, there's a problem
+ if (attrsSeen.indexOf(attr) != -1) {
+ throw Components.results.NS_ERROR_FAILURE;
+ }
+
+ // remember that we've seen it now
+ attrsSeen.push(attr);
+ }
+ }
+
+ return;
+ },
+
+ QueryInterface: XPCOMUtils
+ .generateQI([Components.interfaces.nsIAbLDAPAttributeMap])
+}
+
+function nsAbLDAPAttributeMapService() {
+}
+
+nsAbLDAPAttributeMapService.prototype = {
+
+ classID: NS_ABLDAPATTRIBUTEMAPSERVICE_CID,
+
+ mAttrMaps: {},
+
+ getMapForPrefBranch: function getMapForPrefBranch(aPrefBranchName) {
+
+ // if we've already got this map, return it
+ if (aPrefBranchName in this.mAttrMaps) {
+ return this.mAttrMaps[aPrefBranchName];
+ }
+
+ // otherwise, try and create it
+ var attrMap = new nsAbLDAPAttributeMap();
+ attrMap.setFromPrefs("ldap_2.servers.default.attrmap");
+ attrMap.setFromPrefs(aPrefBranchName + ".attrmap");
+
+ // cache
+ this.mAttrMaps[aPrefBranchName] = attrMap;
+
+ // and return
+ return attrMap;
+ },
+
+ QueryInterface: XPCOMUtils
+ .generateQI([Components.interfaces.nsIAbLDAPAttributeMapService])
+}
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([nsAbLDAPAttributeMap, nsAbLDAPAttributeMapService]);
+
diff --git a/mailnews/addrbook/src/nsAbLDAPAutoCompleteSearch.js b/mailnews/addrbook/src/nsAbLDAPAutoCompleteSearch.js
new file mode 100644
index 000000000..1c62d7e97
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPAutoCompleteSearch.js
@@ -0,0 +1,325 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource:///modules/mailServices.js");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var ACR = Components.interfaces.nsIAutoCompleteResult;
+var nsIAbAutoCompleteResult = Components.interfaces.nsIAbAutoCompleteResult;
+var nsIAbDirectoryQueryResultListener =
+ Components.interfaces.nsIAbDirectoryQueryResultListener;
+
+// nsAbLDAPAutoCompleteResult
+// Derived from nsIAbAutoCompleteResult, provides a LDAP specific result
+// implementation.
+
+function nsAbLDAPAutoCompleteResult(aSearchString) {
+ // Can't create this in the prototype as we'd get the same array for
+ // all instances
+ this._searchResults = [];
+ this.searchString = aSearchString;
+}
+
+nsAbLDAPAutoCompleteResult.prototype = {
+ _searchResults: null,
+ _commentColumn: "",
+
+ // nsIAutoCompleteResult
+
+ searchString: null,
+ searchResult: ACR.RESULT_NOMATCH,
+ defaultIndex: -1,
+ errorDescription: null,
+
+ get matchCount() {
+ return this._searchResults.length;
+ },
+
+ getLabelAt: function getLabelAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getValueAt: function getValueAt(aIndex) {
+ return this._searchResults[aIndex].value;
+ },
+
+ getCommentAt: function getCommentAt(aIndex) {
+ return this._commentColumn;
+ },
+
+ getStyleAt: function getStyleAt(aIndex) {
+ return this.searchResult == ACR.RESULT_FAILURE ? "remote-err" :
+ "remote-abook";
+ },
+
+ getImageAt: function getImageAt(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ removeValueAt: function removeValueAt(aRowIndex, aRemoveFromDB) {
+ },
+
+ // nsIAbAutoCompleteResult
+
+ getCardAt: function getCardAt(aIndex) {
+ return this._searchResults[aIndex].card;
+ },
+
+ // nsISupports
+
+ QueryInterface: XPCOMUtils.generateQI([ACR, nsIAbAutoCompleteResult])
+}
+
+function nsAbLDAPAutoCompleteSearch() {
+ Services.obs.addObserver(this, "quit-application", false);
+ this._timer = Components.classes["@mozilla.org/timer;1"]
+ .createInstance(Components.interfaces.nsITimer);
+}
+
+nsAbLDAPAutoCompleteSearch.prototype = {
+ // For component registration
+ classID: Components.ID("227e6482-fe9f-441f-9b7d-7b60375e7449"),
+
+ // A short-lived LDAP directory cache.
+ // To avoid recreating components as the user completes, we maintain the most
+ // recently used address book, nsAbLDAPDirectoryQuery and search context.
+ // However the cache is discarded if it has not been used for a minute.
+ // This is done to avoid problems with LDAP sessions timing out and hanging.
+ _query: null,
+ _book: null,
+ _attributes: null,
+ _context: -1,
+ _timer: null,
+
+ // The current search result.
+ _result: null,
+ // The listener to pass back results to.
+ _listener: null,
+
+ _parser: MailServices.headerParser,
+
+ applicableHeaders: new Set(["addr_to", "addr_cc", "addr_bcc", "addr_reply"]),
+
+ // Private methods
+
+ _checkDuplicate: function _checkDuplicate(card, emailAddress) {
+ var lcEmailAddress = emailAddress.toLocaleLowerCase();
+
+ return this._result._searchResults.some(function(result) {
+ return result.value.toLocaleLowerCase() == lcEmailAddress;
+ });
+ },
+
+ _addToResult: function(card) {
+ let mbox = this._parser.makeMailboxObject(card.displayName,
+ card.isMailList ? card.getProperty("Notes", "") || card.displayName :
+ card.primaryEmail);
+ if (!mbox.email)
+ return;
+
+ let emailAddress = mbox.toString();
+
+ // If it is a duplicate, then just return and don't add it. The
+ // _checkDuplicate function deals with it all for us.
+ if (this._checkDuplicate(card, emailAddress))
+ return;
+
+ // Find out where to insert the card.
+ var insertPosition = 0;
+
+ // Next sort on full address
+ while (insertPosition < this._result._searchResults.length &&
+ emailAddress > this._result._searchResults[insertPosition].value)
+ ++insertPosition;
+
+ this._result._searchResults.splice(insertPosition, 0, {
+ value: emailAddress,
+ card: card,
+ });
+ },
+
+ // nsIObserver
+
+ observe: function observer(subject, topic, data) {
+ if (topic == "quit-application") {
+ Services.obs.removeObserver(this, "quit-application");
+ } else if (topic != "timer-callback") {
+ return;
+ }
+
+ // Force the individual query items to null, so that the memory
+ // gets collected straight away.
+ this.stopSearch();
+ this._book = null;
+ this._context = -1;
+ this._query = null;
+ this._attributes = null;
+ },
+
+ // nsIAutoCompleteSearch
+
+ startSearch: function startSearch(aSearchString, aParam,
+ aPreviousResult, aListener) {
+ let params = JSON.parse(aParam) || {};
+ let applicable = !("type" in params) || this.applicableHeaders.has(params.type);
+
+ this._result = new nsAbLDAPAutoCompleteResult(aSearchString);
+ aSearchString = aSearchString.toLocaleLowerCase();
+
+ // If the search string isn't value, or contains a comma, or the user
+ // hasn't enabled autocomplete, then just return no matches / or the
+ // result ignored.
+ // The comma check is so that we don't autocomplete against the user
+ // entering multiple addresses.
+ if (!applicable || !aSearchString || aSearchString.includes(",")) {
+ this._result.searchResult = ACR.RESULT_IGNORED;
+ aListener.onSearchResult(this, this._result);
+ return;
+ }
+
+ // The rules here: If the current identity has a directoryServer set, then
+ // use that, otherwise, try the global preference instead.
+ var acDirURI = null;
+ var identity;
+
+ if ("idKey" in params) {
+ try {
+ identity = MailServices.accounts.getIdentity(params.idKey);
+ }
+ catch(ex) {
+ Components.utils.reportError("Couldn't get specified identity, " +
+ "falling back to global settings");
+ }
+ }
+
+ // Does the current identity override the global preference?
+ if (identity && identity.overrideGlobalPref)
+ acDirURI = identity.directoryServer;
+ else {
+ // Try the global one
+ if (Services.prefs.getBoolPref("ldap_2.autoComplete.useDirectory"))
+ acDirURI = Services.prefs.getCharPref("ldap_2.autoComplete.directoryServer");
+ }
+
+ if (!acDirURI) {
+ // No directory to search, send a no match and return.
+ aListener.onSearchResult(this, this._result);
+ return;
+ }
+
+ this.stopSearch();
+
+ // If we don't already have a cached query for this URI, build a new one.
+ acDirURI = "moz-abldapdirectory://" + acDirURI;
+ if (!this._book || this._book.URI != acDirURI) {
+ this._query =
+ Components.classes["@mozilla.org/addressbook/ldap-directory-query;1"]
+ .createInstance(Components.interfaces.nsIAbDirectoryQuery);
+ this._book = MailServices.ab.getDirectory(acDirURI)
+ .QueryInterface(Components.interfaces.nsIAbLDAPDirectory);
+
+ // Create a minimal map just for the display name and primary email.
+ this._attributes =
+ Components.classes["@mozilla.org/addressbook/ldap-attribute-map;1"]
+ .createInstance(Components.interfaces.nsIAbLDAPAttributeMap);
+ this._attributes.setAttributeList("DisplayName",
+ this._book.attributeMap.getAttributeList("DisplayName", {}), true);
+ this._attributes.setAttributeList("PrimaryEmail",
+ this._book.attributeMap.getAttributeList("PrimaryEmail", {}), true);
+ }
+
+ this._result._commentColumn = this._book.dirName;
+ this._listener = aListener;
+ this._timer.init(this, 60000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+
+ var args =
+ Components.classes["@mozilla.org/addressbook/directory/query-arguments;1"]
+ .createInstance(Components.interfaces.nsIAbDirectoryQueryArguments);
+
+ var filterTemplate = this._book.getStringValue("autoComplete.filterTemplate", "");
+
+ // Use default value when preference is not set or it contains empty string
+ if (!filterTemplate)
+ filterTemplate = "(|(cn=%v1*%v2-*)(mail=%v1*%v2-*)(sn=%v1*%v2-*))";
+
+ // Create filter from filter template and search string
+ var ldapSvc = Components.classes["@mozilla.org/network/ldap-service;1"]
+ .getService(Components.interfaces.nsILDAPService);
+ var filter = ldapSvc.createFilter(1024, filterTemplate, "", "", "", aSearchString);
+ if (!filter)
+ throw new Error("Filter string is empty, check if filterTemplate variable is valid in prefs.js.");
+ args.typeSpecificArg = this._attributes;
+ args.querySubDirectories = true;
+ args.filter = filter;
+
+ // Start the actual search
+ this._context =
+ this._query.doQuery(this._book, args, this, this._book.maxHits, 0);
+ },
+
+ stopSearch: function stopSearch() {
+ if (this._listener) {
+ this._query.stopQuery(this._context);
+ this._listener = null;
+ }
+ },
+
+ // nsIAbDirSearchListener
+
+ onSearchFinished: function onSearchFinished(aResult, aErrorMsg) {
+ if (!this._listener)
+ return;
+
+ if (aResult == nsIAbDirectoryQueryResultListener.queryResultComplete) {
+ if (this._result.matchCount) {
+ this._result.searchResult = ACR.RESULT_SUCCESS;
+ this._result.defaultIndex = 0;
+ }
+ else
+ this._result.searchResult = ACR.RESULT_NOMATCH;
+ }
+ else if (aResult == nsIAbDirectoryQueryResultListener.queryResultError) {
+ this._result.searchResult = ACR.RESULT_FAILURE;
+ this._result.defaultIndex = 0;
+ }
+ // const long queryResultStopped = 2;
+ // const long queryResultError = 3;
+ this._listener.onSearchResult(this, this._result);
+ this._listener = null;
+ },
+
+ onSearchFoundCard: function onSearchFoundCard(aCard) {
+ if (!this._listener)
+ return;
+
+ this._addToResult(aCard);
+
+ /* XXX autocomplete doesn't expect you to rearrange while searching
+ if (this._result.matchCount)
+ this._result.searchResult = ACR.RESULT_SUCCESS_ONGOING;
+ else
+ this._result.searchResult = ACR.RESULT_NOMATCH_ONGOING;
+
+ this._listener.onSearchResult(this, this._result);
+ */
+ },
+
+ // nsISupports
+
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIObserver,
+ Components.interfaces
+ .nsIAutoCompleteSearch,
+ Components.interfaces
+ .nsIAbDirSearchListener])
+};
+
+// Module
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([nsAbLDAPAutoCompleteSearch]);
diff --git a/mailnews/addrbook/src/nsAbLDAPCard.cpp b/mailnews/addrbook/src/nsAbLDAPCard.cpp
new file mode 100644
index 000000000..6dcfdedbb
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPCard.cpp
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbLDAPCard.h"
+#include "nsIMutableArray.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPModification.h"
+#include "nsILDAPBERValue.h"
+#include "nsILDAPMessage.h"
+#include "nsIAbLDAPAttributeMap.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsAbBaseCID.h"
+#include "nsAbUtils.h"
+#include "nsILDAPErrors.h"
+
+#include <stdio.h>
+
+#define kDNColumn "DN"
+
+nsAbLDAPCard::nsAbLDAPCard()
+{
+}
+
+nsAbLDAPCard::~nsAbLDAPCard()
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAbLDAPCard, nsAbCardProperty, nsIAbLDAPCard)
+
+/* Retrieves the changes to the LDAP card and stores them in an LDAP
+ * update message.
+ *
+ * Calling this method changes the LDAP card, it updates the
+ * meta-properties (m_*) to reflect what the LDAP contents will be once
+ * the update has been performed. This allows you to do multiple (successful)
+ * consecutive edits on a card in a search result. If the meta-properties
+ * were not updated, incorrect assuptions would be made about what object
+ * classes to add, or what attributes to clear.
+ *
+ * XXX: We need to take care when integrating this code with the asynchronous
+ * update dialogs, as the current code in nsAbLDAPDirectory has a problem
+ * when an update fails: the modified card still gets stored and shown to
+ * the user instead of being discarded. There is one especially tricky case:
+ * when you do an update on a card which changes its DN, you have two
+ * operations (rename, then update the other attributes). If the rename
+ * operation succeeds and not the update of the attributes, you are
+ * "somewhere in between" the original card and the updated card.
+*/
+NS_IMETHODIMP nsAbLDAPCard::GetLDAPMessageInfo(
+ nsIAbLDAPAttributeMap *aAttributeMap,
+ const uint32_t aClassCount,
+ const char **aClasses,
+ int32_t aType,
+ nsIArray **aLDAPAddMessageInfo)
+{
+ NS_ENSURE_ARG_POINTER(aAttributeMap);
+ NS_ENSURE_ARG_POINTER(aClasses);
+ NS_ENSURE_ARG_POINTER(aLDAPAddMessageInfo);
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> modArray =
+ do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Add any missing object classes. We never remove any object
+ // classes: if an entry has additional object classes, it's probably
+ // for a good reason.
+ nsAutoCString oclass;
+ for (uint32_t i = 0; i < aClassCount; ++i)
+ {
+ oclass.Assign(nsDependentCString(aClasses[i]));
+ ToLowerCase(oclass);
+
+ if (!m_objectClass.Contains(oclass))
+ {
+ m_objectClass.AppendElement(oclass);
+ printf("LDAP : adding objectClass %s\n", oclass.get());
+ }
+ }
+
+ nsCOMPtr<nsILDAPModification> mod =
+ do_CreateInstance("@mozilla.org/network/ldap-modification;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> values =
+ do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t i = 0; i < m_objectClass.Length(); ++i)
+ {
+ nsCOMPtr<nsILDAPBERValue> value =
+ do_CreateInstance("@mozilla.org/network/ldap-ber-value;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = value->SetFromUTF8(m_objectClass.ElementAt(i));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = values->AppendElement(value, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = mod->SetUpModification(aType, NS_LITERAL_CSTRING("objectClass"), values);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ modArray->AppendElement(mod, false);
+
+ // Add card properties
+ CharPtrArrayGuard props;
+ rv = aAttributeMap->GetAllCardProperties(props.GetSizeAddr(),
+ props.GetArrayAddr());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString attr;
+ nsCString propvalue;
+ for (uint32_t i = 0; i < props.GetSize(); ++i)
+ {
+ // Skip some attributes that don't map to LDAP.
+ //
+ // BirthYear : by default this is mapped to 'birthyear',
+ // which is not part of mozillaAbPersonAlpha
+ //
+ // LastModifiedDate : by default this is mapped to 'modifytimestamp',
+ // which cannot be modified
+ //
+ // PreferMailFormat : by default this is mapped to 'mozillaUseHtmlMail',
+ // which is a boolean, not plaintext/html/unknown
+ if (!strcmp(props[i], kBirthYearProperty) ||
+ !strcmp(props[i], kLastModifiedDateProperty) ||
+ !strcmp(props[i], kPreferMailFormatProperty))
+ continue;
+
+ rv = aAttributeMap->GetFirstAttribute(nsDependentCString(props[i]),
+ attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ ToLowerCase(attr);
+
+ // If the property is not mapped to an attribute, skip it.
+ if (attr.IsEmpty())
+ continue;
+
+ nsCOMPtr<nsILDAPModification> mod =
+ do_CreateInstance("@mozilla.org/network/ldap-modification;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ size_t index = m_attributes.IndexOf(attr);
+
+ rv = GetPropertyAsAUTF8String(props[i], propvalue);
+
+ if (NS_SUCCEEDED(rv) &&!propvalue.IsEmpty())
+ {
+ // If the new value is not empty, add/update it
+ nsCOMPtr<nsILDAPBERValue> value =
+ do_CreateInstance("@mozilla.org/network/ldap-ber-value;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = value->SetFromUTF8(propvalue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mod->SetUpModificationOneValue(aType, attr, value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printf("LDAP : setting attribute %s (%s) to '%s'\n", attr.get(),
+ props[i], propvalue.get());
+ modArray->AppendElement(mod, false);
+ if (index != m_attributes.NoIndex)
+ m_attributes.AppendElement(attr);
+
+ }
+ else if (aType == nsILDAPModification::MOD_REPLACE &&
+ index != m_attributes.NoIndex)
+ {
+ // If the new value is empty, we are performing an update
+ // and the attribute was previously set, clear it
+ nsCOMPtr<nsIMutableArray> novalues =
+ do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mod->SetUpModification(aType, attr, novalues);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printf("LDAP : removing attribute %s (%s)\n", attr.get(), props[i]);
+ modArray->AppendElement(mod, false);
+ m_attributes.RemoveElementAt(index);
+ }
+ }
+
+ NS_ADDREF(*aLDAPAddMessageInfo = modArray);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPCard::BuildRdn(nsIAbLDAPAttributeMap *aAttributeMap,
+ const uint32_t aAttrCount,
+ const char **aAttributes,
+ nsACString &aRdn)
+{
+ NS_ENSURE_ARG_POINTER(aAttributeMap);
+ NS_ENSURE_ARG_POINTER(aAttributes);
+
+ nsresult rv;
+ nsCString attr;
+ nsAutoCString prop;
+ nsCString propvalue;
+
+ aRdn.Truncate();
+ for (uint32_t i = 0; i < aAttrCount; ++i)
+ {
+ attr.Assign(nsDependentCString(aAttributes[i]));
+
+ // Lookup the property corresponding to the attribute
+ rv = aAttributeMap->GetProperty(attr, prop);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the property value
+ rv = GetPropertyAsAUTF8String(prop.get(), propvalue);
+
+ // XXX The case where an attribute needed to build the Relative
+ // Distinguished Name is not set needs to be handled by the caller,
+ // so as to let the user know what is missing.
+ if (NS_FAILED(rv) || propvalue.IsEmpty())
+ {
+ NS_ERROR("nsAbLDAPCard::BuildRdn: a required attribute is not set");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ aRdn.Append(attr);
+ aRdn.AppendLiteral("=");
+ aRdn.Append(propvalue);
+ if (i < aAttrCount - 1)
+ aRdn.AppendLiteral("+");
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPCard::GetDn(nsACString &aDN)
+{
+ return GetPropertyAsAUTF8String(kDNColumn, aDN);
+}
+
+NS_IMETHODIMP nsAbLDAPCard::SetDn(const nsACString &aDN)
+{
+ SetLocalId(aDN);
+ return SetPropertyAsAUTF8String(kDNColumn, aDN);
+}
+
+NS_IMETHODIMP nsAbLDAPCard::SetMetaProperties(nsILDAPMessage *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+
+ // Get DN
+ nsAutoCString dn;
+ nsresult rv = aMessage->GetDn(dn);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetDn(dn);
+
+ // Get the list of set attributes
+ CharPtrArrayGuard attrs;
+ rv = aMessage->GetAttributes(attrs.GetSizeAddr(), attrs.GetArrayAddr());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString attr;
+ m_attributes.Clear();
+ for (uint32_t i = 0; i < attrs.GetSize(); ++i)
+ {
+ attr.Assign(nsDependentCString(attrs[i]));
+ ToLowerCase(attr);
+ m_attributes.AppendElement(attr);
+ }
+
+ // Get the objectClass values
+ m_objectClass.Clear();
+ PRUnicharPtrArrayGuard vals;
+ rv = aMessage->GetValues("objectClass", vals.GetSizeAddr(),
+ vals.GetArrayAddr());
+
+ // objectClass is not always included in search result entries and
+ // nsILDAPMessage::GetValues returns NS_ERROR_LDAP_DECODING_ERROR if the
+ // requested attribute doesn't exist.
+ if (rv == NS_ERROR_LDAP_DECODING_ERROR)
+ return NS_OK;
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString oclass;
+ for (uint32_t i = 0; i < vals.GetSize(); ++i)
+ {
+ oclass.Assign(NS_LossyConvertUTF16toASCII(nsDependentString(vals[i])));
+ ToLowerCase(oclass);
+ m_objectClass.AppendElement(oclass);
+ }
+
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbLDAPCard.h b/mailnews/addrbook/src/nsAbLDAPCard.h
new file mode 100644
index 000000000..ef11b50e4
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPCard.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbLDAPCard_h__
+#define nsAbLDAPCard_h__
+
+#include "nsAbCardProperty.h"
+#include "nsIAbLDAPCard.h"
+#include "nsTArray.h"
+
+class nsIMutableArray;
+
+class nsAbLDAPCard : public nsAbCardProperty,
+ public nsIAbLDAPCard
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIABLDAPCARD
+
+ nsAbLDAPCard();
+
+protected:
+ virtual ~nsAbLDAPCard();
+ nsTArray<nsCString> m_attributes;
+ nsTArray<nsCString> m_objectClass;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbLDAPChangeLogData.cpp b/mailnews/addrbook/src/nsAbLDAPChangeLogData.cpp
new file mode 100644
index 000000000..cc4c04250
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPChangeLogData.cpp
@@ -0,0 +1,542 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbLDAPChangeLogData.h"
+#include "nsAbLDAPChangeLogQuery.h"
+#include "nsILDAPMessage.h"
+#include "nsIAbCard.h"
+#include "nsIAddrBookSession.h"
+#include "nsAbBaseCID.h"
+#include "nsAbUtils.h"
+#include "nsAbMDBCard.h"
+#include "nsAbLDAPCard.h"
+#include "nsIAuthPrompt.h"
+#include "nsIStringBundle.h"
+#include "nsIWindowWatcher.h"
+#include "nsUnicharUtils.h"
+#include "plstr.h"
+#include "nsILDAPErrors.h"
+#include "prmem.h"
+#include "mozilla/Services.h"
+
+// Defined here since to be used
+// only locally to this file.
+enum UpdateOp {
+ NO_OP,
+ ENTRY_ADD,
+ ENTRY_DELETE,
+ ENTRY_MODIFY
+};
+
+nsAbLDAPProcessChangeLogData::nsAbLDAPProcessChangeLogData()
+: mUseChangeLog(false),
+ mChangeLogEntriesCount(0),
+ mEntriesAddedQueryCount(0)
+{
+ mRootDSEEntry.firstChangeNumber = 0;
+ mRootDSEEntry.lastChangeNumber = 0;
+}
+
+nsAbLDAPProcessChangeLogData::~nsAbLDAPProcessChangeLogData()
+{
+
+}
+
+NS_IMETHODIMP nsAbLDAPProcessChangeLogData::Init(nsIAbLDAPReplicationQuery * query, nsIWebProgressListener *progressListener)
+{
+ NS_ENSURE_ARG_POINTER(query);
+
+ // Here we are assuming that the caller will pass a nsAbLDAPChangeLogQuery object,
+ // an implementation derived from the implementation of nsIAbLDAPReplicationQuery.
+ nsresult rv = NS_OK;
+ mChangeLogQuery = do_QueryInterface(query, &rv);
+ if(NS_FAILED(rv))
+ return rv;
+
+ // Call the parent's Init now.
+ return nsAbLDAPProcessReplicationData::Init(query, progressListener);
+}
+
+nsresult nsAbLDAPProcessChangeLogData::OnLDAPBind(nsILDAPMessage *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ int32_t errCode;
+
+ nsresult rv = aMessage->GetErrorCode(&errCode);
+ if(NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+
+ if(errCode != nsILDAPErrors::SUCCESS) {
+ Done(false);
+ return NS_ERROR_FAILURE;
+ }
+
+ switch(mState) {
+ case kAnonymousBinding :
+ rv = GetAuthData();
+ if(NS_SUCCEEDED(rv))
+ rv = mChangeLogQuery->QueryAuthDN(mAuthUserID);
+ if(NS_SUCCEEDED(rv))
+ mState = kSearchingAuthDN;
+ break;
+ case kAuthenticatedBinding :
+ rv = mChangeLogQuery->QueryRootDSE();
+ if(NS_SUCCEEDED(rv))
+ mState = kSearchingRootDSE;
+ break;
+ } //end of switch
+
+ if(NS_FAILED(rv))
+ Abort();
+
+ return rv;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::OnLDAPSearchEntry(nsILDAPMessage *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_OK;
+
+ switch(mState)
+ {
+ case kSearchingAuthDN :
+ {
+ nsAutoCString authDN;
+ rv = aMessage->GetDn(authDN);
+ if(NS_SUCCEEDED(rv) && !authDN.IsEmpty())
+ mAuthDN = authDN.get();
+ }
+ break;
+ case kSearchingRootDSE:
+ rv = ParseRootDSEEntry(aMessage);
+ break;
+ case kFindingChanges:
+ rv = ParseChangeLogEntries(aMessage);
+ break;
+ // Fall through since we only add (for updates we delete and add)
+ case kReplicatingChanges:
+ case kReplicatingAll :
+ return nsAbLDAPProcessReplicationData::OnLDAPSearchEntry(aMessage);
+ }
+
+ if(NS_FAILED(rv))
+ Abort();
+
+ return rv;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::OnLDAPSearchResult(nsILDAPMessage *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ int32_t errorCode;
+
+ nsresult rv = aMessage->GetErrorCode(&errorCode);
+
+ if(NS_SUCCEEDED(rv))
+ {
+ if(errorCode == nsILDAPErrors::SUCCESS || errorCode == nsILDAPErrors::SIZELIMIT_EXCEEDED) {
+ switch(mState) {
+ case kSearchingAuthDN :
+ rv = OnSearchAuthDNDone();
+ break;
+ case kSearchingRootDSE:
+ {
+ // Before starting the changeLog check the DB file, if its not there or bogus
+ // we need to create a new one and set to all.
+ nsCOMPtr<nsIAddrBookSession> abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ break;
+ nsCOMPtr<nsIFile> dbPath;
+ rv = abSession->GetUserProfileDirectory(getter_AddRefs(dbPath));
+ if (NS_FAILED(rv))
+ break;
+
+ nsAutoCString fileName;
+ rv = mDirectory->GetReplicationFileName(fileName);
+ if (NS_FAILED(rv))
+ break;
+
+ rv = dbPath->AppendNative(fileName);
+ if (NS_FAILED(rv))
+ break;
+
+ bool fileExists;
+ rv = dbPath->Exists(&fileExists);
+ if (NS_FAILED(rv))
+ break;
+
+ int64_t fileSize;
+ rv = dbPath->GetFileSize(&fileSize);
+ if(NS_FAILED(rv))
+ break;
+
+ if (!fileExists || !fileSize)
+ mUseChangeLog = false;
+
+ // Open / create the AB here since it calls Done,
+ // just return from here.
+ if (mUseChangeLog)
+ rv = OpenABForReplicatedDir(false);
+ else
+ rv = OpenABForReplicatedDir(true);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Now start the appropriate query
+ rv = OnSearchRootDSEDone();
+ break;
+ }
+ case kFindingChanges:
+ rv = OnFindingChangesDone();
+ // If success we return from here since
+ // this changes state to kReplicatingChanges
+ // and it falls thru into the if clause below.
+ if (NS_SUCCEEDED(rv))
+ return rv;
+ break;
+ case kReplicatingAll :
+ return nsAbLDAPProcessReplicationData::OnLDAPSearchResult(aMessage);
+ } // end of switch
+ }
+ else
+ rv = NS_ERROR_FAILURE;
+ // If one of the changed entry in changelog is not found,
+ // continue with replicating the next one.
+ if(mState == kReplicatingChanges)
+ rv = OnReplicatingChangeDone();
+ } // end of outer if
+
+ if(NS_FAILED(rv))
+ Abort();
+
+ return rv;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::GetAuthData()
+{
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ if (!wwatch)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIAuthPrompt> dialog;
+ nsresult rv = wwatch->GetNewAuthPrompter(0, getter_AddRefs(dialog));
+ if (NS_FAILED(rv))
+ return rv;
+ if (!dialog)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsILDAPURL> url;
+ rv = mQuery->GetReplicationURL(getter_AddRefs(url));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString serverUri;
+ rv = url->GetSpec(serverUri);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(bundle));
+ if (NS_FAILED (rv))
+ return rv ;
+
+ nsString title;
+ rv = bundle->GetStringFromName(u"AuthDlgTitle", getter_Copies(title));
+ if (NS_FAILED (rv))
+ return rv ;
+
+ nsString desc;
+ rv = bundle->GetStringFromName(u"AuthDlgDesc", getter_Copies(desc));
+ if (NS_FAILED (rv))
+ return rv ;
+
+ nsString username;
+ nsString password;
+ bool btnResult = false;
+ rv = dialog->PromptUsernameAndPassword(title, desc,
+ NS_ConvertUTF8toUTF16(serverUri).get(),
+ nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
+ getter_Copies(username), getter_Copies(password),
+ &btnResult);
+ if(NS_SUCCEEDED(rv) && btnResult) {
+ CopyUTF16toUTF8(username, mAuthUserID);
+ CopyUTF16toUTF8(password, mAuthPswd);
+ }
+ else
+ rv = NS_ERROR_FAILURE;
+
+ return rv;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::OnSearchAuthDNDone()
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCOMPtr<nsILDAPURL> url;
+ nsresult rv = mQuery->GetReplicationURL(getter_AddRefs(url));
+ if(NS_SUCCEEDED(rv))
+ rv = mQuery->ConnectToLDAPServer(url, mAuthDN);
+ if(NS_SUCCEEDED(rv)) {
+ mState = kAuthenticatedBinding;
+ rv = mDirectory->SetAuthDn(mAuthDN);
+ }
+
+ return rv;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::ParseRootDSEEntry(nsILDAPMessage *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // Populate the RootDSEChangeLogEntry
+ CharPtrArrayGuard attrs;
+ nsresult rv = aMessage->GetAttributes(attrs.GetSizeAddr(), attrs.GetArrayAddr());
+ // No attributes
+ if(NS_FAILED(rv))
+ return rv;
+
+ for(int32_t i=attrs.GetSize()-1; i >= 0; i--) {
+ PRUnicharPtrArrayGuard vals;
+ rv = aMessage->GetValues(attrs.GetArray()[i], vals.GetSizeAddr(), vals.GetArrayAddr());
+ if(NS_FAILED(rv))
+ continue;
+ if(vals.GetSize()) {
+ if (!PL_strcasecmp(attrs[i], "changelog"))
+ CopyUTF16toUTF8(vals[0], mRootDSEEntry.changeLogDN);
+ if (!PL_strcasecmp(attrs[i], "firstChangeNumber"))
+ mRootDSEEntry.firstChangeNumber = atol(NS_LossyConvertUTF16toASCII(vals[0]).get());
+ if (!PL_strcasecmp(attrs[i], "lastChangeNumber"))
+ mRootDSEEntry.lastChangeNumber = atol(NS_LossyConvertUTF16toASCII(vals[0]).get());
+ if (!PL_strcasecmp(attrs[i], "dataVersion"))
+ CopyUTF16toUTF8(vals[0], mRootDSEEntry.dataVersion);
+ }
+ }
+
+ int32_t lastChangeNumber;
+ mDirectory->GetLastChangeNumber(&lastChangeNumber);
+
+ if ((mRootDSEEntry.lastChangeNumber > 0) &&
+ (lastChangeNumber < mRootDSEEntry.lastChangeNumber) &&
+ (lastChangeNumber > mRootDSEEntry.firstChangeNumber))
+ mUseChangeLog = true;
+
+ if (mRootDSEEntry.lastChangeNumber &&
+ (lastChangeNumber == mRootDSEEntry.lastChangeNumber)) {
+ Done(true); // We are up to date no need to replicate, db not open yet so call Done
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::OnSearchRootDSEDone()
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_OK;
+
+ if(mUseChangeLog) {
+ rv = mChangeLogQuery->QueryChangeLog(mRootDSEEntry.changeLogDN, mRootDSEEntry.lastChangeNumber);
+ if (NS_FAILED(rv))
+ return rv;
+ mState = kFindingChanges;
+ if(mListener)
+ mListener->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_START, false);
+ }
+ else {
+ rv = mQuery->QueryAllEntries();
+ if (NS_FAILED(rv))
+ return rv;
+ mState = kReplicatingAll;
+ if(mListener)
+ mListener->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_START, true);
+ }
+
+ rv = mDirectory->SetLastChangeNumber(mRootDSEEntry.lastChangeNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mDirectory->SetDataVersion(mRootDSEEntry.dataVersion);
+
+ return rv;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::ParseChangeLogEntries(nsILDAPMessage *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // Populate the RootDSEChangeLogEntry
+ CharPtrArrayGuard attrs;
+ nsresult rv = aMessage->GetAttributes(attrs.GetSizeAddr(), attrs.GetArrayAddr());
+ // No attributes
+ if(NS_FAILED(rv))
+ return rv;
+
+ nsAutoString targetDN;
+ UpdateOp operation = NO_OP;
+ for(int32_t i = attrs.GetSize()-1; i >= 0; i--) {
+ PRUnicharPtrArrayGuard vals;
+ rv = aMessage->GetValues(attrs.GetArray()[i], vals.GetSizeAddr(), vals.GetArrayAddr());
+ if(NS_FAILED(rv))
+ continue;
+ if(vals.GetSize()) {
+ if (!PL_strcasecmp(attrs[i], "targetdn"))
+ targetDN = vals[0];
+ if (!PL_strcasecmp(attrs[i], "changetype")) {
+ if (!Compare(nsDependentString(vals[0]), NS_LITERAL_STRING("add"), nsCaseInsensitiveStringComparator()))
+ operation = ENTRY_ADD;
+ if (!Compare(nsDependentString(vals[0]), NS_LITERAL_STRING("modify"), nsCaseInsensitiveStringComparator()))
+ operation = ENTRY_MODIFY;
+ if (!Compare(nsDependentString(vals[0]), NS_LITERAL_STRING("delete"), nsCaseInsensitiveStringComparator()))
+ operation = ENTRY_DELETE;
+ }
+ }
+ }
+
+ mChangeLogEntriesCount++;
+ if(!(mChangeLogEntriesCount % 10)) { // Inform the listener every 10 entries
+ mListener->OnProgressChange(nullptr,nullptr,mChangeLogEntriesCount, -1, mChangeLogEntriesCount, -1);
+ // In case if the LDAP Connection thread is starved and causes problem
+ // uncomment this one and try.
+ // PR_Sleep(PR_INTERVAL_NO_WAIT); // give others a chance
+ }
+
+#ifdef DEBUG_rdayal
+ printf ("ChangeLog Replication : Updated Entry : %s for OpType : %u\n",
+ NS_ConvertUTF16toUTF8(targetDN).get(), operation);
+#endif
+
+ switch(operation) {
+ case ENTRY_ADD:
+ // Add the DN to the add list if not already in the list
+ if(!(mEntriesToAdd.IndexOf(targetDN) >= 0))
+ mEntriesToAdd.AppendString(targetDN);
+ break;
+ case ENTRY_DELETE:
+ // Do not check the return here since delete may fail if
+ // entry deleted in changelog does not exist in DB
+ // for e.g if the user specifies a filter, so go next entry
+ DeleteCard(targetDN);
+ break;
+ case ENTRY_MODIFY:
+ // For modify, delete the entry from DB and add updated entry
+ // we do this since we cannot access the changes attribs of changelog
+ rv = DeleteCard(targetDN);
+ if (NS_SUCCEEDED(rv))
+ if(!(mEntriesToAdd.IndexOf(targetDN) >= 0))
+ mEntriesToAdd.AppendString(targetDN);
+ break;
+ default:
+ // Should not come here, would come here only
+ // if the entry is not a changeLog entry
+ NS_WARNING("nsAbLDAPProcessChangeLogData::ParseChangeLogEntries"
+ "Not an changelog entry");
+ }
+
+ // Go ahead processing the next entry, a modify or delete DB operation
+ // can 'correctly' fail if the entry is not present in the DB,
+ // e.g. in case a filter is specified.
+ return NS_OK;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::OnFindingChangesDone()
+{
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+#ifdef DEBUG_rdayal
+ printf ("ChangeLog Replication : Finding Changes Done \n");
+#endif
+
+ nsresult rv = NS_OK;
+
+ // No entries to add/update (for updates too we delete and add) entries,
+ // we took care of deletes in ParseChangeLogEntries, all Done!
+ mEntriesAddedQueryCount = mEntriesToAdd.Count();
+ if(mEntriesAddedQueryCount <= 0) {
+ if(mReplicationDB && mDBOpen) {
+ // Close the DB, no need to commit since we have not made
+ // any changes yet to the DB.
+ rv = mReplicationDB->Close(false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication DB Close(no commit) on Success failed");
+ mDBOpen = false;
+ // Once are done with the replication file, delete the backup file
+ if(mBackupReplicationFile) {
+ rv = mBackupReplicationFile->Remove(false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication BackupFile Remove on Success failed");
+ }
+ }
+ Done(true);
+ return NS_OK;
+ }
+
+ // Decrement the count first to get the correct array element
+ mEntriesAddedQueryCount--;
+ rv = mChangeLogQuery->QueryChangedEntries(NS_ConvertUTF16toUTF8(*(mEntriesToAdd[mEntriesAddedQueryCount])));
+ if (NS_FAILED(rv))
+ return rv;
+
+ if(mListener && NS_SUCCEEDED(rv))
+ mListener->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_START, true);
+
+ mState = kReplicatingChanges;
+ return rv;
+}
+
+nsresult nsAbLDAPProcessChangeLogData::OnReplicatingChangeDone()
+{
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_OK;
+
+ if(!mEntriesAddedQueryCount)
+ {
+ if(mReplicationDB && mDBOpen) {
+ rv = mReplicationDB->Close(true); // Commit and close the DB
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication DB Close (commit) on Success failed");
+ mDBOpen = false;
+ }
+ // Once we done with the replication file, delete the backup file.
+ if(mBackupReplicationFile) {
+ rv = mBackupReplicationFile->Remove(false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication BackupFile Remove on Success failed");
+ }
+ Done(true); // All data is received
+ return NS_OK;
+ }
+
+ // Remove the entry already added from the list and query the next one.
+ if(mEntriesAddedQueryCount < mEntriesToAdd.Count() && mEntriesAddedQueryCount >= 0)
+ mEntriesToAdd.RemoveStringAt(mEntriesAddedQueryCount);
+ mEntriesAddedQueryCount--;
+ rv = mChangeLogQuery->QueryChangedEntries(NS_ConvertUTF16toUTF8(*(mEntriesToAdd[mEntriesAddedQueryCount])));
+
+ return rv;
+}
+
diff --git a/mailnews/addrbook/src/nsAbLDAPChangeLogData.h b/mailnews/addrbook/src/nsAbLDAPChangeLogData.h
new file mode 100644
index 000000000..9300e34ec
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPChangeLogData.h
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbLDAPChangeLogData_h__
+#define nsAbLDAPChangeLogData_h__
+
+#include "mozilla/Attributes.h"
+#include "nsAbLDAPReplicationData.h"
+#include "nsAbLDAPChangeLogQuery.h"
+
+typedef struct {
+ nsCString changeLogDN;
+ int32_t firstChangeNumber;
+ int32_t lastChangeNumber;
+ nsCString dataVersion;
+} RootDSEChangeLogEntry;
+
+class nsAbLDAPProcessChangeLogData : public nsAbLDAPProcessReplicationData
+{
+public :
+
+ nsAbLDAPProcessChangeLogData();
+
+ NS_IMETHOD Init(nsIAbLDAPReplicationQuery * query, nsIWebProgressListener *progressListener);
+
+protected :
+ ~nsAbLDAPProcessChangeLogData();
+
+ nsCOMPtr <nsIAbLDAPChangeLogQuery> mChangeLogQuery;
+
+ nsresult OnLDAPBind(nsILDAPMessage *aMessage);
+ nsresult OnLDAPSearchEntry(nsILDAPMessage *aMessage) override;
+ nsresult OnLDAPSearchResult(nsILDAPMessage *aMessage) override;
+
+ nsresult ParseChangeLogEntries(nsILDAPMessage *aMessage);
+ nsresult ParseRootDSEEntry(nsILDAPMessage *aMessage);
+
+ nsresult GetAuthData(); // displays username and password prompt
+ nsCString mAuthUserID; // user id of the user making the connection
+
+ nsresult OnSearchAuthDNDone();
+ nsresult OnSearchRootDSEDone();
+ nsresult OnFindingChangesDone();
+ nsresult OnReplicatingChangeDone();
+
+ RootDSEChangeLogEntry mRootDSEEntry;
+ bool mUseChangeLog;
+ int32_t mChangeLogEntriesCount;
+
+ int32_t mEntriesAddedQueryCount;
+ nsStringArray mEntriesToAdd;
+};
+
+
+#endif // nsAbLDAPChangeLogData_h__
+
diff --git a/mailnews/addrbook/src/nsAbLDAPChangeLogQuery.cpp b/mailnews/addrbook/src/nsAbLDAPChangeLogQuery.cpp
new file mode 100644
index 000000000..1bdf474d6
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPChangeLogQuery.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsCOMPtr.h"
+#include "nsAbLDAPChangeLogQuery.h"
+#include "nsAbLDAPReplicationService.h"
+#include "nsAbLDAPChangeLogData.h"
+#include "nsAbUtils.h"
+#include "prprf.h"
+#include "nsDirPrefs.h"
+#include "nsAbBaseCID.h"
+
+
+// The tables below were originally in nsAbLDAPProperties.cpp, which has since
+// gone away.
+static const char * sChangeLogRootDSEAttribs[] =
+{
+ "changelog",
+ "firstChangeNumber",
+ "lastChangeNumber",
+ "dataVersion"
+};
+static const char * sChangeLogEntryAttribs[] =
+{
+ "targetdn",
+ "changetype"
+};
+
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAbLDAPChangeLogQuery, nsAbLDAPReplicationQuery, nsIAbLDAPChangeLogQuery)
+
+nsAbLDAPChangeLogQuery::nsAbLDAPChangeLogQuery()
+{
+}
+
+nsAbLDAPChangeLogQuery::~nsAbLDAPChangeLogQuery()
+{
+
+}
+
+// this is to be defined only till this is not hooked to SSL to get authDN and authPswd
+#define USE_AUTHDLG
+
+NS_IMETHODIMP nsAbLDAPChangeLogQuery::Init(const nsACString & aPrefName, nsIWebProgressListener *aProgressListener)
+{
+ if(aPrefName.IsEmpty())
+ return NS_ERROR_UNEXPECTED;
+
+ mDirPrefName = aPrefName;
+
+ nsresult rv = InitLDAPData();
+ if(NS_FAILED(rv))
+ return rv;
+
+ // create the ChangeLog Data Processor
+ mDataProcessor = do_CreateInstance(NS_ABLDAP_PROCESSCHANGELOGDATA_CONTRACTID, &rv);
+ if(NS_FAILED(rv))
+ return rv;
+
+ // 'this' initialized
+ mInitialized = true;
+
+ return mDataProcessor->Init(this, aProgressListener);
+}
+
+NS_IMETHODIMP nsAbLDAPChangeLogQuery::DoReplicationQuery()
+{
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+#ifdef USE_AUTHDLG
+ return ConnectToLDAPServer(mURL, EmptyCString());
+#else
+ mDataProcessor->PopulateAuthData();
+ return ConnectToLDAPServer(mURL, mAuthDN);
+#endif
+}
+
+NS_IMETHODIMP nsAbLDAPChangeLogQuery::QueryAuthDN(const nsACString & aValueUsedToFindDn)
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCOMPtr<nsIAbLDAPAttributeMap> attrMap;
+ nsresult rv = mDirectory->GetAttributeMap(getter_AddRefs(attrMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString filter;
+ rv = attrMap->GetFirstAttribute(NS_LITERAL_CSTRING("PrimaryEmail"), filter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ filter += '=';
+ filter += aValueUsedToFindDn;
+
+ nsAutoCString dn;
+ rv = mURL->GetDn(dn);
+ if(NS_FAILED(rv))
+ return rv;
+
+ rv = CreateNewLDAPOperation();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX We really should be using LDAP_NO_ATTRS here once its exposed via
+ // the XPCOM layer of the directory code.
+ return mOperation->SearchExt(dn, nsILDAPURL::SCOPE_SUBTREE, filter,
+ 0, nullptr,
+ 0, 0);
+}
+
+NS_IMETHODIMP nsAbLDAPChangeLogQuery::QueryRootDSE()
+{
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = CreateNewLDAPOperation();
+ NS_ENSURE_SUCCESS(rv, rv);
+ return mOperation->SearchExt(EmptyCString(), nsILDAPURL::SCOPE_BASE,
+ NS_LITERAL_CSTRING("objectclass=*"),
+ sizeof(sChangeLogRootDSEAttribs),
+ sChangeLogRootDSEAttribs, 0, 0);
+}
+
+NS_IMETHODIMP nsAbLDAPChangeLogQuery::QueryChangeLog(const nsACString & aChangeLogDN, int32_t aLastChangeNo)
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+ if (aChangeLogDN.IsEmpty())
+ return NS_ERROR_UNEXPECTED;
+
+ int32_t lastChangeNumber;
+ nsresult rv = mDirectory->GetLastChangeNumber(&lastChangeNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // make sure that the filter here just have one condition
+ // and should not be enclosed in enclosing brackets.
+ // also condition '>' doesnot work, it should be '>='/
+ nsAutoCString filter (NS_LITERAL_CSTRING("changenumber>="));
+ filter.AppendInt(lastChangeNumber + 1);
+
+ rv = CreateNewLDAPOperation();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mOperation->SearchExt(aChangeLogDN, nsILDAPURL::SCOPE_ONELEVEL, filter,
+ sizeof(sChangeLogEntryAttribs),
+ sChangeLogEntryAttribs, 0, 0);
+}
+
+NS_IMETHODIMP nsAbLDAPChangeLogQuery::QueryChangedEntries(const nsACString & aChangedEntryDN)
+{
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+ if(aChangedEntryDN.IsEmpty())
+ return NS_ERROR_UNEXPECTED;
+
+ nsAutoCString urlFilter;
+ nsresult rv = mURL->GetFilter(urlFilter);
+ if(NS_FAILED(rv))
+ return rv;
+
+ int32_t scope;
+ rv = mURL->GetScope(&scope);
+ if(NS_FAILED(rv))
+ return rv;
+
+ CharPtrArrayGuard attributes;
+ rv = mURL->GetAttributes(attributes.GetSizeAddr(), attributes.GetArrayAddr());
+ if(NS_FAILED(rv))
+ return rv;
+
+ rv = CreateNewLDAPOperation();
+ NS_ENSURE_SUCCESS(rv, rv);
+ return mOperation->SearchExt(aChangedEntryDN, scope, urlFilter,
+ attributes.GetSize(), attributes.GetArray(),
+ 0, 0);
+}
+
diff --git a/mailnews/addrbook/src/nsAbLDAPChangeLogQuery.h b/mailnews/addrbook/src/nsAbLDAPChangeLogQuery.h
new file mode 100644
index 000000000..9652f470e
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPChangeLogQuery.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbLDAPChangeLogQuery_h__
+#define nsAbLDAPChangeLogQuery_h__
+
+#include "mozilla/Attributes.h"
+#include "nsAbLDAPReplicationQuery.h"
+#include "nsStringGlue.h"
+
+class nsAbLDAPChangeLogQuery : public nsIAbLDAPChangeLogQuery,
+ public nsAbLDAPReplicationQuery
+{
+public :
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABLDAPCHANGELOGQUERY
+
+ nsAbLDAPChangeLogQuery();
+ virtual ~nsAbLDAPChangeLogQuery();
+
+ NS_IMETHOD DoReplicationQuery() override;
+ NS_IMETHOD Init(const nsACString & aPrefName, nsIWebProgressListener *aProgressListener);
+};
+
+#endif // nsAbLDAPChangeLogQuery_h__
diff --git a/mailnews/addrbook/src/nsAbLDAPDirFactory.cpp b/mailnews/addrbook/src/nsAbLDAPDirFactory.cpp
new file mode 100644
index 000000000..299f2a953
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirFactory.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbLDAPDirFactory.h"
+#include "nsAbUtils.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsIAbManager.h"
+#include "nsIAbDirectory.h"
+#include "nsAbLDAPDirectory.h"
+
+#include "nsEnumeratorUtils.h"
+#include "nsAbBaseCID.h"
+
+NS_IMPL_ISUPPORTS(nsAbLDAPDirFactory, nsIAbDirFactory)
+
+nsAbLDAPDirFactory::nsAbLDAPDirFactory()
+{
+}
+
+nsAbLDAPDirFactory::~nsAbLDAPDirFactory()
+{
+}
+
+NS_IMETHODIMP
+nsAbLDAPDirFactory::GetDirectories(const nsAString &aDirName,
+ const nsACString &aURI,
+ const nsACString &aPrefName,
+ nsISimpleEnumerator **aDirectories)
+{
+ NS_ENSURE_ARG_POINTER(aDirectories);
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ if (Substring(aURI, 0, 5).EqualsLiteral("ldap:") ||
+ Substring(aURI, 0, 6).EqualsLiteral("ldaps:")) {
+ /*
+ * If the URI starts with ldap: or ldaps:
+ * then this directory is an LDAP directory.
+ *
+ * We don't want to use the ldap:// or ldaps:// URI
+ * as the URI because the ldap:// or ldaps:// URI
+ * will contain the hostname, basedn, port, etc.
+ * so if those attributes changed, we'll run into the
+ * the same problem that we hit with changing username / hostname
+ * for mail servers. To solve this problem, we add an extra
+ * level of indirection. The URI that we generate
+ * (the bridge URI) will be moz-abldapdirectory://<prefName>
+ * and when we need the hostname, basedn, port, etc,
+ * we'll use the <prefName> to get the necessary prefs.
+ * note, <prefName> does not change.
+ */
+ nsAutoCString bridgeURI;
+ bridgeURI = NS_LITERAL_CSTRING(kLDAPDirectoryRoot);
+ bridgeURI += aPrefName;
+ rv = abManager->GetDirectory(bridgeURI, getter_AddRefs(directory));
+ }
+ else {
+ rv = abManager->GetDirectory(aURI, getter_AddRefs(directory));
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_NewSingletonEnumerator(aDirectories, directory);
+}
+
+/* void deleteDirectory (in nsIAbDirectory directory); */
+NS_IMETHODIMP
+nsAbLDAPDirFactory::DeleteDirectory(nsIAbDirectory *directory)
+{
+ // No actual deletion - as the LDAP Address Book is not physically
+ // created in the corresponding CreateDirectory() unlike the Personal
+ // Address Books. But we still need to return NS_OK from here.
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbLDAPDirFactory.h b/mailnews/addrbook/src/nsAbLDAPDirFactory.h
new file mode 100644
index 000000000..9285fbc05
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirFactory.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbLDAPDirFactory_h__
+#define nsAbLDAPDirFactory_h__
+
+#include "nsIAbDirFactory.h"
+
+class nsAbLDAPDirFactory : public nsIAbDirFactory
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABDIRFACTORY
+
+ nsAbLDAPDirFactory();
+
+private:
+ virtual ~nsAbLDAPDirFactory();
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbLDAPDirectory.cpp b/mailnews/addrbook/src/nsAbLDAPDirectory.cpp
new file mode 100644
index 000000000..d1bb484c0
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirectory.cpp
@@ -0,0 +1,948 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbLDAPDirectory.h"
+
+#include "nsAbQueryStringToExpression.h"
+
+#include "nsAbBaseCID.h"
+#include "nsIAbManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNetCID.h"
+#include "nsIIOService.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsEnumeratorUtils.h"
+#include "nsIAbLDAPAttributeMap.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsILDAPURL.h"
+#include "nsILDAPConnection.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIFile.h"
+#include "nsILDAPModification.h"
+#include "nsILDAPService.h"
+#include "nsIAbLDAPCard.h"
+#include "nsAbUtils.h"
+#include "nsArrayUtils.h"
+#include "nsIPrefService.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgUtils.h"
+#include "mozilla/Services.h"
+
+#define kDefaultMaxHits 100
+
+using namespace mozilla;
+
+nsAbLDAPDirectory::nsAbLDAPDirectory() :
+ nsAbDirProperty(),
+ mPerformingQuery(false),
+ mContext(0),
+ mLock("nsAbLDAPDirectory.mLock")
+{
+}
+
+nsAbLDAPDirectory::~nsAbLDAPDirectory()
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAbLDAPDirectory, nsAbDirProperty,
+ nsISupportsWeakReference, nsIAbDirSearchListener,
+ nsIAbLDAPDirectory)
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetPropertiesChromeURI(nsACString &aResult)
+{
+ aResult.AssignLiteral("chrome://messenger/content/addressbook/pref-directory-add.xul");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::Init(const char* aURI)
+{
+ // We need to ensure that the m_DirPrefId is initialized properly
+ nsAutoCString uri(aURI);
+
+ // Find the first ? (of the search params) if there is one.
+ // We know we can start at the end of the moz-abldapdirectory:// because
+ // that's the URI we should have been passed.
+ int32_t searchCharLocation = uri.FindChar('?', kLDAPDirectoryRootLen);
+
+ if (searchCharLocation == -1)
+ m_DirPrefId = Substring(uri, kLDAPDirectoryRootLen);
+ else
+ m_DirPrefId = Substring(uri, kLDAPDirectoryRootLen, searchCharLocation - kLDAPDirectoryRootLen);
+
+ return nsAbDirProperty::Init(aURI);
+}
+
+nsresult nsAbLDAPDirectory::Initiate()
+{
+ return NS_OK;
+}
+
+/*
+ *
+ * nsIAbDirectory methods
+ *
+ */
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetURI(nsACString &aURI)
+{
+ if (mURI.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ aURI = mURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetChildNodes(nsISimpleEnumerator* *aResult)
+{
+ return NS_NewEmptyEnumerator(aResult);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetChildCards(nsISimpleEnumerator** result)
+{
+ nsresult rv;
+
+ // when offline, we need to get the child cards for the local, replicated mdb directory
+ bool offline;
+ nsCOMPtr <nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED);
+ rv = ioService->GetOffline(&offline);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (offline) {
+ nsCString fileName;
+ rv = GetReplicationFileName(fileName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // if there is no fileName, bail out now.
+ if (fileName.IsEmpty())
+ return NS_OK;
+
+ // perform the same query, but on the local directory
+ nsAutoCString localDirectoryURI(NS_LITERAL_CSTRING(kMDBDirectoryRoot));
+ localDirectoryURI.Append(fileName);
+ if (mIsQueryURI)
+ {
+ localDirectoryURI.AppendLiteral("?");
+ localDirectoryURI.Append(mQueryString);
+ }
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(localDirectoryURI,
+ getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = directory->GetChildCards(result);
+ }
+ else {
+ // Start the search
+ rv = StartSearch();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_NewEmptyEnumerator(result);
+ }
+
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetIsQuery(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mIsQueryURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::HasCard(nsIAbCard* card, bool* hasCard)
+{
+ nsresult rv = Initiate ();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Enter lock
+ MutexAutoLock lock (mLock);
+
+ *hasCard = mCache.Get(card, nullptr);
+ if (!*hasCard && mPerformingQuery)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetLDAPURL(nsILDAPURL** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // Rather than using GetURI here we call GetStringValue directly so
+ // we can handle the case where the URI isn't specified (see comments
+ // below)
+ nsAutoCString URI;
+ nsresult rv = GetStringValue("uri", EmptyCString(), URI);
+ if (NS_FAILED(rv) || URI.IsEmpty())
+ {
+ /*
+ * A recent change in Mozilla now means that the LDAP Address Book
+ * URI is based on the unique preference name value i.e.
+ * [moz-abldapdirectory://prefName]
+ * Prior to this valid change it was based on the actual uri i.e.
+ * [moz-abldapdirectory://host:port/basedn]
+ * Basing the resource on the prefName allows these attributes to
+ * change.
+ *
+ * But the uri value was also the means by which third-party
+ * products could integrate with Mozilla's LDAP Address Books
+ * without necessarily having an entry in the preferences file
+ * or more importantly needing to be able to change the
+ * preferences entries. Thus to set the URI Spec now, it is
+ * only necessary to read the uri pref entry, while in the
+ * case where it is not a preference, we need to replace the
+ * "moz-abldapdirectory".
+ */
+ URI = mURINoQuery;
+ if (StringBeginsWith(URI, NS_LITERAL_CSTRING(kLDAPDirectoryRoot)))
+ URI.Replace(0, kLDAPDirectoryRootLen, NS_LITERAL_CSTRING("ldap://"));
+ }
+
+ nsCOMPtr<nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIURI> result;
+ rv = ioService->NewURI(URI, nullptr, nullptr, getter_AddRefs(result));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(result, aResult);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetLDAPURL(nsILDAPURL *aUrl)
+{
+ NS_ENSURE_ARG_POINTER(aUrl);
+
+ nsAutoCString oldUrl;
+ // Note, it doesn't matter if GetStringValue fails - we'll just send an
+ // update if its blank (i.e. old value not set).
+ GetStringValue("uri", EmptyCString(), oldUrl);
+
+ // Actually set the new value.
+ nsCString tempLDAPURL;
+ nsresult rv = aUrl->GetSpec(tempLDAPURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = SetStringValue("uri", tempLDAPURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now we need to send an update which will ensure our indicators and
+ // listeners get updated correctly.
+
+ // See if they both start with ldaps: or ldap:
+ bool newIsNotSecure = StringHead(tempLDAPURL, 5).Equals("ldap:");
+
+ if (oldUrl.IsEmpty() ||
+ StringHead(oldUrl, 5).Equals("ldap:") != newIsNotSecure)
+ {
+ // They don't so its time to send round an update.
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We inherit from nsIAbDirectory, so this static cast should be safe.
+ abManager->NotifyItemPropertyChanged(static_cast<nsIAbDirectory*>(this),
+ "IsSecure",
+ (newIsNotSecure ? u"true" : u"false"),
+ (newIsNotSecure ? u"false" : u"true"));
+ }
+
+ return NS_OK;
+}
+
+/*
+ *
+ * nsIAbDirectorySearch methods
+ *
+ */
+
+NS_IMETHODIMP nsAbLDAPDirectory::StartSearch ()
+{
+ if (!mIsQueryURI || mQueryString.IsEmpty())
+ return NS_OK;
+
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = StopSearch();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectoryQueryArguments> arguments = do_CreateInstance(NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID,&rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbBooleanExpression> expression;
+ rv = nsAbQueryStringToExpression::Convert(mQueryString,
+ getter_AddRefs(expression));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = arguments->SetExpression(expression);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = arguments->SetQuerySubDirectories(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the max hits to return
+ int32_t maxHits;
+ rv = GetMaxHits(&maxHits);
+ if (NS_FAILED(rv))
+ maxHits = kDefaultMaxHits;
+
+ // get the appropriate ldap attribute map, and pass it in via the
+ // TypeSpecificArgument
+ nsCOMPtr<nsIAbLDAPAttributeMap> attrMap;
+ rv = GetAttributeMap(getter_AddRefs(attrMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> typeSpecificArg = do_QueryInterface(attrMap, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = arguments->SetTypeSpecificArg(attrMap);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mDirectoryQuery)
+ {
+ mDirectoryQuery = do_CreateInstance(NS_ABLDAPDIRECTORYQUERY_CONTRACTID,
+ &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Perform the query
+ rv = mDirectoryQuery->DoQuery(this, arguments, this, maxHits, 0, &mContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Enter lock
+ MutexAutoLock lock(mLock);
+ mPerformingQuery = true;
+ mCache.Clear();
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::StopSearch ()
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Enter lock
+ {
+ MutexAutoLock lockGuard(mLock);
+ if (!mPerformingQuery)
+ return NS_OK;
+ mPerformingQuery = false;
+ }
+ // Exit lock
+
+ if (!mDirectoryQuery)
+ return NS_ERROR_NULL_POINTER;
+
+ return mDirectoryQuery->StopQuery(mContext);
+}
+
+/*
+ *
+ * nsAbDirSearchListenerContext methods
+ *
+ */
+NS_IMETHODIMP nsAbLDAPDirectory::OnSearchFinished(int32_t aResult, const nsAString &aErrorMessage)
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MutexAutoLock lock(mLock);
+ mPerformingQuery = false;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::OnSearchFoundCard(nsIAbCard* card)
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Enter lock
+ {
+ MutexAutoLock lock(mLock);
+ mCache.Put(card, card);
+ }
+ // Exit lock
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ if(NS_SUCCEEDED(rv))
+ abManager->NotifyDirectoryItemAdded(this, card);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetSupportsMailingLists(bool *aSupportsMailingsLists)
+{
+ NS_ENSURE_ARG_POINTER(aSupportsMailingsLists);
+ *aSupportsMailingsLists = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetReadOnly(bool *aReadOnly)
+{
+ NS_ENSURE_ARG_POINTER(aReadOnly);
+
+ *aReadOnly = true;
+
+#ifdef MOZ_EXPERIMENTAL_WRITEABLE_LDAP
+ bool readOnly;
+ nsresult rv = GetBoolValue("readonly", false, &readOnly);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (readOnly)
+ return NS_OK;
+
+ // when online, we'll allow writing as well
+ bool offline;
+ nsCOMPtr <nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED);
+
+ rv = ioService->GetOffline(&offline);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!offline)
+ *aReadOnly = false;
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetIsRemote(bool *aIsRemote)
+{
+ NS_ENSURE_ARG_POINTER(aIsRemote);
+ *aIsRemote = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetIsSecure(bool *aIsSecure)
+{
+ NS_ENSURE_ARG_POINTER(aIsSecure);
+
+ nsAutoCString URI;
+ nsresult rv = GetStringValue("uri", EmptyCString(), URI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // to determine if this is a secure directory, check if the uri is ldaps:// or not
+ *aIsSecure = (strncmp(URI.get(), "ldaps:", 6) == 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::UseForAutocomplete(const nsACString &aIdentityKey,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // Set this to false by default to make the code easier below.
+ *aResult = false;
+
+ nsresult rv;
+ bool offline = false;
+ nsCOMPtr <nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED);
+
+ rv = ioService->GetOffline(&offline);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we're online, then don't allow search during local autocomplete - must
+ // use the separate LDAP autocomplete session due to the current interfaces
+ if (!offline)
+ return NS_OK;
+
+ // Is the use directory pref set for autocompletion?
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool useDirectory = false;
+ rv = prefs->GetBoolPref("ldap_2.autoComplete.useDirectory", &useDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // No need to search if not set up globally for LDAP autocompletion and we've
+ // not been given an identity.
+ if (!useDirectory && aIdentityKey.IsEmpty())
+ return NS_OK;
+
+ nsCString prefName;
+ if (!aIdentityKey.IsEmpty())
+ {
+ // If we have an identity string, try and find out the required directory
+ // server.
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+
+ // If we failed, just return, we can't do much about this.
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgIdentity> identity;
+ rv = accountManager->GetIdentity(aIdentityKey, getter_AddRefs(identity));
+ if (NS_SUCCEEDED(rv))
+ {
+ bool overrideGlobalPref = false;
+ identity->GetOverrideGlobalPref(&overrideGlobalPref);
+ if (overrideGlobalPref)
+ identity->GetDirectoryServer(prefName);
+ }
+ }
+
+ // If the preference name is still empty but useDirectory is false, then
+ // the global one is not available, nor is the overriden one.
+ if (prefName.IsEmpty() && !useDirectory)
+ return NS_OK;
+ }
+
+ // If we failed to get the identity preference, or the pref name is empty
+ // try the global preference.
+ if (prefName.IsEmpty())
+ {
+ nsresult rv = prefs->GetCharPref("ldap_2.autoComplete.directoryServer",
+ getter_Copies(prefName));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ // Now see if the pref name matches our pref id.
+ if (prefName.Equals(m_DirPrefId))
+ {
+ // Yes it does, one last check - does the replication file exist?
+ nsresult rv;
+ nsCOMPtr<nsIFile> databaseFile;
+ // If we can't get the file, then there is no database to use
+ if (NS_FAILED(GetReplicationFile(getter_AddRefs(databaseFile))))
+ return NS_OK;
+
+ bool exists;
+ rv = databaseFile->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = exists;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetSearchClientControls(nsIMutableArray **aControls)
+{
+ NS_IF_ADDREF(*aControls = mSearchClientControls);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetSearchClientControls(nsIMutableArray *aControls)
+{
+ mSearchClientControls = aControls;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetSearchServerControls(nsIMutableArray **aControls)
+{
+ NS_IF_ADDREF(*aControls = mSearchServerControls);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetSearchServerControls(nsIMutableArray *aControls)
+{
+ mSearchServerControls = aControls;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetProtocolVersion(uint32_t *aProtocolVersion)
+{
+ nsAutoCString versionString;
+
+ nsresult rv = GetStringValue("protocolVersion", NS_LITERAL_CSTRING("3"), versionString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aProtocolVersion = versionString.EqualsLiteral("3") ?
+ (uint32_t)nsILDAPConnection::VERSION3 :
+ (uint32_t)nsILDAPConnection::VERSION2;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetProtocolVersion(uint32_t aProtocolVersion)
+{
+ // XXX We should cancel any existing LDAP connections here and
+ // be ready to re-initialise them with the new auth details.
+ return SetStringValue("protocolVersion",
+ aProtocolVersion == nsILDAPConnection::VERSION3 ?
+ NS_LITERAL_CSTRING("3") : NS_LITERAL_CSTRING("2"));
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetMaxHits(int32_t *aMaxHits)
+{
+ return GetIntValue("maxHits", kDefaultMaxHits, aMaxHits);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetMaxHits(int32_t aMaxHits)
+{
+ return SetIntValue("maxHits", aMaxHits);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetReplicationFileName(nsACString &aReplicationFileName)
+{
+ return GetStringValue("filename", EmptyCString(), aReplicationFileName);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetReplicationFileName(const nsACString &aReplicationFileName)
+{
+ return SetStringValue("filename", aReplicationFileName);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetAuthDn(nsACString &aAuthDn)
+{
+ return GetStringValue("auth.dn", EmptyCString(), aAuthDn);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetAuthDn(const nsACString &aAuthDn)
+{
+ // XXX We should cancel any existing LDAP connections here and
+ // be ready to re-initialise them with the new auth details.
+ return SetStringValue("auth.dn", aAuthDn);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetSaslMechanism(nsACString &aSaslMechanism)
+{
+ return GetStringValue("auth.saslmech", EmptyCString(), aSaslMechanism);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetSaslMechanism(const nsACString &aSaslMechanism)
+{
+ return SetStringValue("auth.saslmech", aSaslMechanism);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetLastChangeNumber(int32_t *aLastChangeNumber)
+{
+ return GetIntValue("lastChangeNumber", -1, aLastChangeNumber);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetLastChangeNumber(int32_t aLastChangeNumber)
+{
+ return SetIntValue("lastChangeNumber", aLastChangeNumber);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetDataVersion(nsACString &aDataVersion)
+{
+ return GetStringValue("dataVersion", EmptyCString(), aDataVersion);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetDataVersion(const nsACString &aDataVersion)
+{
+ return SetStringValue("dataVersion", aDataVersion);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetAttributeMap(nsIAbLDAPAttributeMap **aAttributeMap)
+{
+ NS_ENSURE_ARG_POINTER(aAttributeMap);
+
+ nsresult rv;
+ nsCOMPtr<nsIAbLDAPAttributeMapService> mapSvc =
+ do_GetService("@mozilla.org/addressbook/ldap-attribute-map-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mapSvc->GetMapForPrefBranch(m_DirPrefId, aAttributeMap);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetReplicationFile(nsIFile **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsCString fileName;
+ nsresult rv = GetStringValue("filename", EmptyCString(), fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (fileName.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCOMPtr<nsIFile> profileDir;
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(profileDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = profileDir->AppendNative(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ADDREF(*aResult = profileDir);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetReplicationDatabase(nsIAddrDatabase **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> databaseFile;
+ rv = GetReplicationFile(getter_AddRefs(databaseFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAddrDatabase> addrDBFactory =
+ do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return addrDBFactory->Open(databaseFile, false /* no create */, true,
+ aResult);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::AddCard(nsIAbCard *aUpdatedCard,
+ nsIAbCard **aAddedCard)
+{
+ NS_ENSURE_ARG_POINTER(aUpdatedCard);
+ NS_ENSURE_ARG_POINTER(aAddedCard);
+
+ nsCOMPtr<nsIAbLDAPAttributeMap> attrMap;
+ nsresult rv = GetAttributeMap(getter_AddRefs(attrMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create a new LDAP card
+ nsCOMPtr<nsIAbLDAPCard> card =
+ do_CreateInstance(NS_ABLDAPCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Copy over the card data
+ nsCOMPtr<nsIAbCard> copyToCard = do_QueryInterface(card, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = copyToCard->Copy(aUpdatedCard);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Retrieve preferences
+ nsAutoCString prefString;
+ rv = GetRdnAttributes(prefString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CharPtrArrayGuard rdnAttrs;
+ rv = SplitStringList(prefString, rdnAttrs.GetSizeAddr(),
+ rdnAttrs.GetArrayAddr());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetObjectClasses(prefString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CharPtrArrayGuard objClass;
+ rv = SplitStringList(prefString, objClass.GetSizeAddr(),
+ objClass.GetArrayAddr());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Process updates
+ nsCOMPtr<nsIArray> modArray;
+ rv = card->GetLDAPMessageInfo(attrMap, objClass.GetSize(), objClass.GetArray(),
+ nsILDAPModification::MOD_ADD, getter_AddRefs(modArray));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // For new cards, the base DN is the search base DN
+ nsCOMPtr<nsILDAPURL> currentUrl;
+ rv = GetLDAPURL(getter_AddRefs(currentUrl));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString baseDN;
+ rv = currentUrl->GetDn(baseDN);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Calculate DN
+ nsAutoCString cardDN;
+ rv = card->BuildRdn(attrMap, rdnAttrs.GetSize(), rdnAttrs.GetArray(),
+ cardDN);
+ NS_ENSURE_SUCCESS(rv, rv);
+ cardDN.AppendLiteral(",");
+ cardDN.Append(baseDN);
+
+ rv = card->SetDn(cardDN);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString ourUuid;
+ GetUuid(ourUuid);
+ copyToCard->SetDirectoryId(ourUuid);
+
+ // Launch query
+ rv = DoModify(this, nsILDAPModification::MOD_ADD, cardDN, modArray,
+ EmptyCString(), EmptyCString());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ADDREF(*aAddedCard = copyToCard);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::DeleteCards(nsIArray *aCards)
+{
+ uint32_t cardCount;
+ uint32_t i;
+ nsAutoCString cardDN;
+
+ nsresult rv = aCards->GetLength(&cardCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (i = 0; i < cardCount; ++i)
+ {
+ nsCOMPtr<nsIAbLDAPCard> card(do_QueryElementAt(aCards, i, &rv));
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("Wrong type of card passed to nsAbLDAPDirectory::DeleteCards");
+ break;
+ }
+
+ // Set up the search ldap url - this is mURL
+ rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = card->GetDn(cardDN);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> realCard(do_QueryInterface(card));
+ realCard->SetDirectoryId(EmptyCString());
+
+ // Launch query
+ rv = DoModify(this, nsILDAPModification::MOD_DELETE, cardDN, nullptr,
+ EmptyCString(), EmptyCString());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::ModifyCard(nsIAbCard *aUpdatedCard)
+{
+ NS_ENSURE_ARG_POINTER(aUpdatedCard);
+
+ nsCOMPtr<nsIAbLDAPAttributeMap> attrMap;
+ nsresult rv = GetAttributeMap(getter_AddRefs(attrMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the LDAP card
+ nsCOMPtr<nsIAbLDAPCard> card = do_QueryInterface(aUpdatedCard, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Retrieve preferences
+ nsAutoCString prefString;
+ rv = GetObjectClasses(prefString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CharPtrArrayGuard objClass;
+ rv = SplitStringList(prefString, objClass.GetSizeAddr(),
+ objClass.GetArrayAddr());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Process updates
+ nsCOMPtr<nsIArray> modArray;
+ rv = card->GetLDAPMessageInfo(attrMap, objClass.GetSize(), objClass.GetArray(),
+ nsILDAPModification::MOD_REPLACE, getter_AddRefs(modArray));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get current DN
+ nsAutoCString oldDN;
+ rv = card->GetDn(oldDN);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILDAPService> ldapSvc = do_GetService(
+ "@mozilla.org/network/ldap-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Retrieve base DN and RDN attributes
+ nsAutoCString baseDN;
+ nsAutoCString oldRDN;
+ CharPtrArrayGuard rdnAttrs;
+ rv = ldapSvc->ParseDn(oldDN.get(), oldRDN, baseDN,
+ rdnAttrs.GetSizeAddr(), rdnAttrs.GetArrayAddr());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Calculate new RDN and check whether it has changed
+ nsAutoCString newRDN;
+ rv = card->BuildRdn(attrMap, rdnAttrs.GetSize(), rdnAttrs.GetArray(),
+ newRDN);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (newRDN.Equals(oldRDN))
+ {
+ // Launch query
+ rv = DoModify(this, nsILDAPModification::MOD_REPLACE, oldDN, modArray,
+ EmptyCString(), EmptyCString());
+ }
+ else
+ {
+ // Build and store the new DN
+ nsAutoCString newDN(newRDN);
+ newDN.AppendLiteral(",");
+ newDN.Append(baseDN);
+
+ rv = card->SetDn(newDN);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Launch query
+ rv = DoModify(this, nsILDAPModification::MOD_REPLACE, oldDN, modArray,
+ newRDN, baseDN);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetRdnAttributes(nsACString &aRdnAttributes)
+{
+ return GetStringValue("rdnAttributes", NS_LITERAL_CSTRING("cn"),
+ aRdnAttributes);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetRdnAttributes(const nsACString &aRdnAttributes)
+{
+ return SetStringValue("rdnAttributes", aRdnAttributes);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::GetObjectClasses(nsACString &aObjectClasses)
+{
+ return GetStringValue("objectClasses", NS_LITERAL_CSTRING(
+ "top,person,organizationalPerson,inetOrgPerson,mozillaAbPersonAlpha"),
+ aObjectClasses);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetObjectClasses(const nsACString &aObjectClasses)
+{
+ return SetStringValue("objectClasses", aObjectClasses);
+}
+
+nsresult nsAbLDAPDirectory::SplitStringList(
+ const nsACString& aString,
+ uint32_t *aCount,
+ char ***aValues)
+{
+ NS_ENSURE_ARG_POINTER(aCount);
+ NS_ENSURE_ARG_POINTER(aValues);
+
+ nsTArray<nsCString> strarr;
+ ParseString(aString, ',', strarr);
+
+ char **cArray = nullptr;
+ if (!(cArray = static_cast<char **>(moz_xmalloc(
+ strarr.Length() * sizeof(char *)))))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for (uint32_t i = 0; i < strarr.Length(); ++i)
+ {
+ if (!(cArray[i] = ToNewCString(strarr[i])))
+ {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(strarr.Length(), cArray);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ *aCount = strarr.Length();
+ *aValues = cArray;
+ return NS_OK;
+}
+
diff --git a/mailnews/addrbook/src/nsAbLDAPDirectory.h b/mailnews/addrbook/src/nsAbLDAPDirectory.h
new file mode 100644
index 000000000..6e5279a97
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirectory.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbLDAPDirectory_h__
+#define nsAbLDAPDirectory_h__
+
+#include "mozilla/Attributes.h"
+#include "nsAbDirProperty.h"
+#include "nsAbLDAPDirectoryModify.h"
+#include "nsIAbDirectoryQuery.h"
+#include "nsIAbDirectorySearch.h"
+#include "nsIAbDirSearchListener.h"
+#include "nsIAbLDAPDirectory.h"
+#include "nsIMutableArray.h"
+#include "nsInterfaceHashtable.h"
+#include "mozilla/Mutex.h"
+
+class nsAbLDAPDirectory :
+ public nsAbDirProperty, // nsIAbDirectory
+ public nsAbLDAPDirectoryModify,
+ public nsIAbDirectorySearch,
+ public nsIAbLDAPDirectory,
+ public nsIAbDirSearchListener
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsAbLDAPDirectory();
+
+ NS_IMETHOD Init(const char *aUri) override;
+
+ // nsIAbDirectory methods
+ NS_IMETHOD GetPropertiesChromeURI(nsACString &aResult) override;
+ NS_IMETHOD GetURI(nsACString &aURI) override;
+ NS_IMETHOD GetChildNodes(nsISimpleEnumerator* *result) override;
+ NS_IMETHOD GetChildCards(nsISimpleEnumerator* *result) override;
+ NS_IMETHOD GetIsQuery(bool *aResult) override;
+ NS_IMETHOD HasCard(nsIAbCard *cards, bool *hasCard) override;
+ NS_IMETHOD GetSupportsMailingLists(bool *aSupportsMailingsLists) override;
+ NS_IMETHOD GetReadOnly(bool *aReadOnly) override;
+ NS_IMETHOD GetIsRemote(bool *aIsRemote) override;
+ NS_IMETHOD GetIsSecure(bool *aIsRemote) override;
+ NS_IMETHOD UseForAutocomplete(const nsACString &aIdentityKey, bool *aResult) override;
+ NS_IMETHOD AddCard(nsIAbCard *aChildCard, nsIAbCard **aAddedCard) override;
+ NS_IMETHOD ModifyCard(nsIAbCard *aModifiedCard) override;
+ NS_IMETHOD DeleteCards(nsIArray *aCards) override;
+
+ // nsIAbDirectorySearch methods
+ NS_DECL_NSIABDIRECTORYSEARCH
+ NS_DECL_NSIABLDAPDIRECTORY
+ NS_DECL_NSIABDIRSEARCHLISTENER
+
+protected:
+ virtual ~nsAbLDAPDirectory();
+ nsresult Initiate();
+
+ nsresult SplitStringList(const nsACString& aString,
+ uint32_t *aCount,
+ char ***aValues);
+
+ bool mPerformingQuery;
+ int32_t mContext;
+ int32_t mMaxHits;
+
+ nsInterfaceHashtable<nsISupportsHashKey, nsIAbCard> mCache;
+
+ mozilla::Mutex mLock;
+ nsCOMPtr<nsIAbDirectoryQuery> mDirectoryQuery;
+ nsCOMPtr<nsIMutableArray> mSearchServerControls;
+ nsCOMPtr<nsIMutableArray> mSearchClientControls;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbLDAPDirectoryModify.cpp b/mailnews/addrbook/src/nsAbLDAPDirectoryModify.cpp
new file mode 100644
index 000000000..95af79c04
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirectoryModify.cpp
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbLDAPDirectoryModify.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPErrors.h"
+#include "nsILDAPModification.h"
+#include "nsIServiceManager.h"
+#include "nsIAbLDAPDirectory.h"
+#include "nsIMutableArray.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include <stdio.h>
+
+using namespace mozilla;
+
+class nsAbModifyLDAPMessageListener : public nsAbLDAPListenerBase
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ nsAbModifyLDAPMessageListener(const int32_t type,
+ const nsACString &cardDN,
+ nsIArray* modArray,
+ const nsACString &newRDN,
+ const nsACString &newBaseDN,
+ nsILDAPURL* directoryUrl,
+ nsILDAPConnection* connection,
+ nsIMutableArray* serverSearchControls,
+ nsIMutableArray* clientSearchControls,
+ const nsACString &login,
+ const int32_t timeOut = 0);
+ // nsILDAPMessageListener
+ NS_IMETHOD OnLDAPMessage(nsILDAPMessage *aMessage) override;
+
+protected:
+ virtual ~nsAbModifyLDAPMessageListener();
+
+ nsresult Cancel();
+ virtual void InitFailed(bool aCancelled = false) override;
+ virtual nsresult DoTask() override;
+ nsresult DoMainTask();
+ nsresult OnLDAPMessageModifyResult(nsILDAPMessage *aMessage);
+ nsresult OnLDAPMessageRenameResult(nsILDAPMessage *aMessage);
+
+ int32_t mType;
+ nsCString mCardDN;
+ nsCOMPtr<nsIArray> mModification;
+ nsCString mNewRDN;
+ nsCString mNewBaseDN;
+
+ bool mFinished;
+ bool mCanceled;
+ bool mFlagRename;
+
+ nsCOMPtr<nsILDAPOperation> mModifyOperation;
+ nsCOMPtr<nsIMutableArray> mServerSearchControls;
+ nsCOMPtr<nsIMutableArray> mClientSearchControls;
+};
+
+
+NS_IMPL_ISUPPORTS(nsAbModifyLDAPMessageListener, nsILDAPMessageListener)
+
+nsAbModifyLDAPMessageListener::nsAbModifyLDAPMessageListener(
+ const int32_t type,
+ const nsACString &cardDN,
+ nsIArray* modArray,
+ const nsACString &newRDN,
+ const nsACString &newBaseDN,
+ nsILDAPURL* directoryUrl,
+ nsILDAPConnection* connection,
+ nsIMutableArray* serverSearchControls,
+ nsIMutableArray* clientSearchControls,
+ const nsACString &login,
+ const int32_t timeOut) :
+ nsAbLDAPListenerBase(directoryUrl, connection, login, timeOut),
+ mType(type),
+ mCardDN(cardDN),
+ mModification(modArray),
+ mNewRDN(newRDN),
+ mNewBaseDN(newBaseDN),
+ mFinished(false),
+ mCanceled(false),
+ mFlagRename(false),
+ mServerSearchControls(serverSearchControls),
+ mClientSearchControls(clientSearchControls)
+{
+ if (mType == nsILDAPModification::MOD_REPLACE &&
+ !mNewRDN.IsEmpty() && !mNewBaseDN.IsEmpty())
+ mFlagRename = true;
+}
+
+nsAbModifyLDAPMessageListener::~nsAbModifyLDAPMessageListener ()
+{
+}
+
+nsresult nsAbModifyLDAPMessageListener::Cancel ()
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MutexAutoLock lock(mLock);
+
+ if (mFinished || mCanceled)
+ return NS_OK;
+
+ mCanceled = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbModifyLDAPMessageListener::OnLDAPMessage(nsILDAPMessage *aMessage)
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t messageType;
+ rv = aMessage->GetType(&messageType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool cancelOperation = false;
+
+ // Enter lock
+ {
+ MutexAutoLock lock (mLock);
+
+ if (mFinished)
+ return NS_OK;
+
+ // for these messages, no matter the outcome, we're done
+ if ((messageType == nsILDAPMessage::RES_ADD) ||
+ (messageType == nsILDAPMessage::RES_DELETE) ||
+ (messageType == nsILDAPMessage::RES_MODIFY))
+ mFinished = true;
+ else if (mCanceled)
+ {
+ mFinished = true;
+ cancelOperation = true;
+ }
+ }
+ // Leave lock
+
+ // nsCOMPtr<nsIAbDirectoryQueryResult> queryResult;
+ if (!cancelOperation)
+ {
+ switch (messageType)
+ {
+ case nsILDAPMessage::RES_BIND:
+ rv = OnLDAPMessageBind(aMessage);
+ if (NS_FAILED(rv))
+ // We know the bind failed and hence the message has an error, so we
+ // can just call ModifyResult with the message and that'll sort it out
+ // for us.
+ rv = OnLDAPMessageModifyResult(aMessage);
+ break;
+ case nsILDAPMessage::RES_ADD:
+ case nsILDAPMessage::RES_MODIFY:
+ case nsILDAPMessage::RES_DELETE:
+ rv = OnLDAPMessageModifyResult(aMessage);
+ break;
+ case nsILDAPMessage::RES_MODDN:
+ mFlagRename = false;
+ rv = OnLDAPMessageRenameResult(aMessage);
+ if (NS_FAILED(rv))
+ // Rename failed, so we stop here
+ mFinished = true;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if (mModifyOperation)
+ rv = mModifyOperation->AbandonExt();
+
+ // reset because we might re-use this listener...except don't do this
+ // until the search is done, so we'll ignore results from a previous
+ // search.
+ mCanceled = mFinished = false;
+ }
+
+ return rv;
+}
+
+void nsAbModifyLDAPMessageListener::InitFailed(bool aCancelled)
+{
+ // XXX Just cancel the operation for now
+ // we'll need to review this when we've got the proper listeners in place.
+ Cancel();
+}
+
+nsresult nsAbModifyLDAPMessageListener::DoTask()
+{
+ nsresult rv;
+ mCanceled = mFinished = false;
+
+ mModifyOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mModifyOperation->Init (mConnection, this, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX do we need the search controls?
+ rv = mModifyOperation->SetServerControls(mServerSearchControls);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mModifyOperation->SetClientControls(mClientSearchControls);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mFlagRename)
+ return mModifyOperation->Rename(mCardDN, mNewRDN, mNewBaseDN, true);
+
+ switch (mType)
+ {
+ case nsILDAPModification::MOD_ADD:
+ return mModifyOperation->AddExt(mCardDN, mModification);
+ case nsILDAPModification::MOD_DELETE:
+ return mModifyOperation->DeleteExt(mCardDN);
+ case nsILDAPModification::MOD_REPLACE:
+ return mModifyOperation->ModifyExt(mCardDN, mModification);
+ default:
+ NS_ERROR("Bad LDAP modification requested");
+ return NS_ERROR_UNEXPECTED;
+ }
+}
+
+nsresult nsAbModifyLDAPMessageListener::OnLDAPMessageModifyResult(nsILDAPMessage *aMessage)
+{
+ nsresult rv;
+ NS_ENSURE_ARG_POINTER(aMessage);
+
+ int32_t errCode;
+ rv = aMessage->GetErrorCode(&errCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (errCode != nsILDAPErrors::SUCCESS)
+ {
+ nsAutoCString errMessage;
+ rv = aMessage->GetErrorMessage(errMessage);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printf("LDAP modification failed (code: %i, message: %s)\n",
+ errCode, errMessage.get());
+ return NS_ERROR_FAILURE;
+ }
+
+ printf("LDAP modification succeeded\n");
+ return NS_OK;
+}
+
+nsresult nsAbModifyLDAPMessageListener::OnLDAPMessageRenameResult(nsILDAPMessage *aMessage)
+{
+ nsresult rv;
+ NS_ENSURE_ARG_POINTER(aMessage);
+
+ int32_t errCode;
+ rv = aMessage->GetErrorCode(&errCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (errCode != nsILDAPErrors::SUCCESS)
+ {
+ nsAutoCString errMessage;
+ rv = aMessage->GetErrorMessage(errMessage);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printf("LDAP rename failed (code: %i, message: %s)\n",
+ errCode, errMessage.get());
+ return NS_ERROR_FAILURE;
+ }
+
+ // Rename succeeded, now update the card DN and
+ // process the main task
+ mCardDN.Assign(mNewRDN);
+ mCardDN.AppendLiteral(",");
+ mCardDN.Append(mNewBaseDN);
+
+ printf("LDAP rename succeeded\n");
+ return DoTask();
+}
+
+nsAbLDAPDirectoryModify::nsAbLDAPDirectoryModify()
+{
+}
+
+nsAbLDAPDirectoryModify::~nsAbLDAPDirectoryModify()
+{
+}
+
+nsresult nsAbLDAPDirectoryModify::DoModify(nsIAbLDAPDirectory *directory,
+ const int32_t &updateType,
+ const nsACString &cardDN,
+ nsIArray* modArray,
+ const nsACString &newRDN,
+ const nsACString &newBaseDN)
+{
+ NS_ENSURE_ARG_POINTER(directory);
+ // modArray may be null in the delete operation case.
+ if (!modArray &&
+ (updateType == nsILDAPModification::MOD_ADD ||
+ updateType == nsILDAPModification::MOD_REPLACE))
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ // it's an error if we don't have a dn
+ if (cardDN.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ nsCOMPtr<nsILDAPURL> currentUrl;
+ rv = directory->GetLDAPURL(getter_AddRefs(currentUrl));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the ldap connection
+ nsCOMPtr<nsILDAPConnection> ldapConnection =
+ do_CreateInstance(NS_LDAPCONNECTION_CONTRACTID, &rv);
+
+ nsCOMPtr<nsIMutableArray> serverSearchControls;
+ rv = directory->GetSearchServerControls(getter_AddRefs(serverSearchControls));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> clientSearchControls;
+ rv = directory->GetSearchClientControls(getter_AddRefs(clientSearchControls));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /*
+ // XXX we need to fix how this all works - specifically, see the first patch
+ // on bug 124553 for how the query equivalent did this
+ // too soon? Do we need a new listener?
+ if (alreadyInitialized)
+ {
+ nsAbQueryLDAPMessageListener *msgListener =
+ NS_STATIC_CAST(nsAbQueryLDAPMessageListener *,
+ NS_STATIC_CAST(nsILDAPMessageListener *, mListener.get()));
+ if (msgListener)
+ {
+ msgListener->mUrl = url;
+ return msgListener->DoSearch();
+ }
+ }*/
+
+ nsCString login;
+ rv = directory->GetAuthDn(login);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t protocolVersion;
+ rv = directory->GetProtocolVersion(&protocolVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Initiate LDAP message listener
+ nsAbModifyLDAPMessageListener* _messageListener =
+ new nsAbModifyLDAPMessageListener(updateType, cardDN, modArray,
+ newRDN, newBaseDN,
+ currentUrl,
+ ldapConnection,
+ serverSearchControls,
+ clientSearchControls,
+ login,
+ 0);
+ if (_messageListener == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Now lets initialize the LDAP connection properly. We'll kick
+ // off the bind operation in the callback function, |OnLDAPInit()|.
+ return ldapConnection->Init(currentUrl, login,
+ _messageListener, nullptr, protocolVersion);
+}
+
diff --git a/mailnews/addrbook/src/nsAbLDAPDirectoryModify.h b/mailnews/addrbook/src/nsAbLDAPDirectoryModify.h
new file mode 100644
index 000000000..8e14b8368
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirectoryModify.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbLDAPDirectoryModify_h__
+#define nsAbLDAPDirectoryModify_h__
+
+#include "nsAbLDAPListenerBase.h"
+#include "nsIAbLDAPDirectory.h"
+#include "nsILDAPOperation.h"
+#include "nsIArray.h"
+
+class nsILDAPURL;
+
+class nsAbLDAPDirectoryModify
+{
+public:
+ nsAbLDAPDirectoryModify();
+ virtual ~nsAbLDAPDirectoryModify();
+
+protected:
+ nsresult DoModify(nsIAbLDAPDirectory *directory,
+ const int32_t &aUpdateType,
+ const nsACString &aCardDN,
+ nsIArray* modArray,
+ const nsACString &aNewRDN,
+ const nsACString &aNewBaseDN);
+};
+
+#endif // nsAbLDAPDirectoryModify_h__
diff --git a/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
new file mode 100644
index 000000000..9b22c796c
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
@@ -0,0 +1,610 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbLDAPDirectoryQuery.h"
+#include "nsAbBoolExprToLDAPFilter.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPErrors.h"
+#include "nsILDAPOperation.h"
+#include "nsIAbLDAPAttributeMap.h"
+#include "nsIAbLDAPCard.h"
+#include "nsAbUtils.h"
+#include "nsAbBaseCID.h"
+#include "nsStringGlue.h"
+#include "prprf.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsAbLDAPDirectory.h"
+#include "nsAbLDAPListenerBase.h"
+#include "nsXPCOMCIDInternal.h"
+
+using namespace mozilla;
+
+// nsAbLDAPListenerBase inherits nsILDAPMessageListener
+class nsAbQueryLDAPMessageListener : public nsAbLDAPListenerBase
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // Note that the directoryUrl is the details of the ldap directory
+ // without any search params or return attributes specified. The searchUrl
+ // therefore has the search params and return attributes specified.
+ // nsAbQueryLDAPMessageListener(nsIAbDirectoryQuery* directoryQuery,
+ nsAbQueryLDAPMessageListener(nsIAbDirectoryQueryResultListener* resultListener,
+ nsILDAPURL* directoryUrl,
+ nsILDAPURL* searchUrl,
+ nsILDAPConnection* connection,
+ nsIAbDirectoryQueryArguments* queryArguments,
+ nsIMutableArray* serverSearchControls,
+ nsIMutableArray* clientSearchControls,
+ const nsACString &login,
+ const nsACString &mechanism,
+ const int32_t resultLimit = -1,
+ const int32_t timeOut = 0);
+
+ // nsILDAPMessageListener
+ NS_IMETHOD OnLDAPMessage(nsILDAPMessage *aMessage) override;
+
+protected:
+ virtual ~nsAbQueryLDAPMessageListener ();
+ nsresult OnLDAPMessageSearchEntry(nsILDAPMessage *aMessage);
+ nsresult OnLDAPMessageSearchResult(nsILDAPMessage *aMessage);
+
+ friend class nsAbLDAPDirectoryQuery;
+
+ nsresult Cancel();
+ virtual nsresult DoTask() override;
+ virtual void InitFailed(bool aCancelled = false) override;
+
+ nsCOMPtr<nsILDAPURL> mSearchUrl;
+ nsIAbDirectoryQueryResultListener *mResultListener;
+ int32_t mContextID;
+ nsCOMPtr<nsIAbDirectoryQueryArguments> mQueryArguments;
+ int32_t mResultLimit;
+
+ bool mFinished;
+ bool mCanceled;
+ bool mWaitingForPrevQueryToFinish;
+
+ nsCOMPtr<nsIMutableArray> mServerSearchControls;
+ nsCOMPtr<nsIMutableArray> mClientSearchControls;
+};
+
+
+NS_IMPL_ISUPPORTS(nsAbQueryLDAPMessageListener, nsILDAPMessageListener)
+
+nsAbQueryLDAPMessageListener::nsAbQueryLDAPMessageListener(
+ nsIAbDirectoryQueryResultListener *resultListener,
+ nsILDAPURL* directoryUrl,
+ nsILDAPURL* searchUrl,
+ nsILDAPConnection* connection,
+ nsIAbDirectoryQueryArguments* queryArguments,
+ nsIMutableArray* serverSearchControls,
+ nsIMutableArray* clientSearchControls,
+ const nsACString &login,
+ const nsACString &mechanism,
+ const int32_t resultLimit,
+ const int32_t timeOut) :
+ nsAbLDAPListenerBase(directoryUrl, connection, login, timeOut),
+ mSearchUrl(searchUrl),
+ mResultListener(resultListener),
+ mQueryArguments(queryArguments),
+ mResultLimit(resultLimit),
+ mFinished(false),
+ mCanceled(false),
+ mWaitingForPrevQueryToFinish(false),
+ mServerSearchControls(serverSearchControls),
+ mClientSearchControls(clientSearchControls)
+{
+ mSaslMechanism.Assign(mechanism);
+}
+
+nsAbQueryLDAPMessageListener::~nsAbQueryLDAPMessageListener ()
+{
+}
+
+nsresult nsAbQueryLDAPMessageListener::Cancel ()
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MutexAutoLock lock(mLock);
+
+ if (mFinished || mCanceled)
+ return NS_OK;
+
+ mCanceled = true;
+ if (!mFinished)
+ mWaitingForPrevQueryToFinish = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbQueryLDAPMessageListener::OnLDAPMessage(nsILDAPMessage *aMessage)
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t messageType;
+ rv = aMessage->GetType(&messageType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool cancelOperation = false;
+
+ // Enter lock
+ {
+ MutexAutoLock lock (mLock);
+
+ if (mFinished)
+ return NS_OK;
+
+ if (messageType == nsILDAPMessage::RES_SEARCH_RESULT)
+ mFinished = true;
+ else if (mCanceled)
+ {
+ mFinished = true;
+ cancelOperation = true;
+ }
+ }
+ // Leave lock
+
+ if (!mResultListener)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!cancelOperation)
+ {
+ switch (messageType)
+ {
+ case nsILDAPMessage::RES_BIND:
+ rv = OnLDAPMessageBind(aMessage);
+ if (NS_FAILED(rv))
+ // We know the bind failed and hence the message has an error, so we
+ // can just call SearchResult with the message and that'll sort it out
+ // for us.
+ rv = OnLDAPMessageSearchResult(aMessage);
+ break;
+ case nsILDAPMessage::RES_SEARCH_ENTRY:
+ if (!mFinished && !mWaitingForPrevQueryToFinish)
+ rv = OnLDAPMessageSearchEntry(aMessage);
+ break;
+ case nsILDAPMessage::RES_SEARCH_RESULT:
+ mWaitingForPrevQueryToFinish = false;
+ rv = OnLDAPMessageSearchResult(aMessage);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if (mOperation)
+ rv = mOperation->AbandonExt();
+
+ rv = mResultListener->OnQueryResult(
+ nsIAbDirectoryQueryResultListener::queryResultStopped, 0);
+
+ // reset because we might re-use this listener...except don't do this
+ // until the search is done, so we'll ignore results from a previous
+ // search.
+ if (messageType == nsILDAPMessage::RES_SEARCH_RESULT)
+ mCanceled = mFinished = false;
+ }
+
+ return rv;
+}
+
+nsresult nsAbQueryLDAPMessageListener::DoTask()
+{
+ nsresult rv;
+ mCanceled = mFinished = false;
+
+ mOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mOperation->Init(mConnection, this, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString dn;
+ rv = mSearchUrl->GetDn(dn);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t scope;
+ rv = mSearchUrl->GetScope(&scope);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString filter;
+ rv = mSearchUrl->GetFilter(filter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString attributes;
+ rv = mSearchUrl->GetAttributes(attributes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mOperation->SetServerControls(mServerSearchControls);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mOperation->SetClientControls(mClientSearchControls);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mOperation->SearchExt(dn, scope, filter, attributes, mTimeOut,
+ mResultLimit);
+}
+
+void nsAbQueryLDAPMessageListener::InitFailed(bool aCancelled)
+{
+ if (!mResultListener)
+ return;
+
+ // In the !aCancelled case we know there was an error, but we won't be
+ // able to translate it, so just return an error code of zero.
+ mResultListener->OnQueryResult(
+ aCancelled ? nsIAbDirectoryQueryResultListener::queryResultStopped :
+ nsIAbDirectoryQueryResultListener::queryResultError, 0);
+}
+
+nsresult nsAbQueryLDAPMessageListener::OnLDAPMessageSearchEntry(nsILDAPMessage *aMessage)
+{
+ nsresult rv;
+
+ if (!mResultListener)
+ return NS_ERROR_NULL_POINTER;
+
+ // the map for translating between LDAP attrs <-> addrbook fields
+ nsCOMPtr<nsISupports> iSupportsMap;
+ rv = mQueryArguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> card = do_CreateInstance(NS_ABLDAPCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = map->SetCardPropertiesFromLDAPMessage(aMessage, card);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbLDAPCard> ldapCard = do_QueryInterface(card, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ldapCard->SetMetaProperties(aMessage);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mResultListener->OnQueryFoundCard(card);
+}
+
+nsresult nsAbQueryLDAPMessageListener::OnLDAPMessageSearchResult(nsILDAPMessage *aMessage)
+{
+ int32_t errorCode;
+ nsresult rv = aMessage->GetErrorCode(&errorCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (errorCode == nsILDAPErrors::SUCCESS || errorCode == nsILDAPErrors::SIZELIMIT_EXCEEDED)
+ return mResultListener->OnQueryResult(
+ nsIAbDirectoryQueryResultListener::queryResultComplete, 0);
+
+ return mResultListener->OnQueryResult(
+ nsIAbDirectoryQueryResultListener::queryResultError, errorCode);
+}
+
+// nsAbLDAPDirectoryQuery
+
+NS_IMPL_ISUPPORTS(nsAbLDAPDirectoryQuery, nsIAbDirectoryQuery,
+ nsIAbDirectoryQueryResultListener)
+
+nsAbLDAPDirectoryQuery::nsAbLDAPDirectoryQuery() :
+ mInitialized(false)
+{
+}
+
+nsAbLDAPDirectoryQuery::~nsAbLDAPDirectoryQuery()
+{
+}
+
+NS_IMETHODIMP nsAbLDAPDirectoryQuery::DoQuery(nsIAbDirectory *aDirectory,
+ nsIAbDirectoryQueryArguments* aArguments,
+ nsIAbDirSearchListener* aListener,
+ int32_t aResultLimit,
+ int32_t aTimeOut,
+ int32_t* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+ NS_ENSURE_ARG_POINTER(aArguments);
+
+ mListeners.AppendObject(aListener);
+
+ // Ensure existing query is stopped. Context id doesn't matter here
+ nsresult rv = StopQuery(0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInitialized = true;
+
+ // Get the current directory as LDAP specific
+ nsCOMPtr<nsIAbLDAPDirectory> directory(do_QueryInterface(aDirectory, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We also need the current URL to check as well...
+ nsCOMPtr<nsILDAPURL> currentUrl;
+ rv = directory->GetLDAPURL(getter_AddRefs(currentUrl));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString login;
+ rv = directory->GetAuthDn(login);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString saslMechanism;
+ rv = directory->GetSaslMechanism(saslMechanism);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t protocolVersion;
+ rv = directory->GetProtocolVersion(&protocolVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // To do:
+ // Ensure query is stopped
+ // If connection params have changed re-create connection
+ // else reuse existing connection
+
+ bool redoConnection = false;
+
+ if (!mConnection || !mDirectoryUrl)
+ {
+ mDirectoryUrl = currentUrl;
+ aDirectory->GetUuid(mDirectoryId);
+ mCurrentLogin = login;
+ mCurrentMechanism = saslMechanism;
+ mCurrentProtocolVersion = protocolVersion;
+ redoConnection = true;
+ }
+ else
+ {
+ bool equal;
+ rv = mDirectoryUrl->Equals(currentUrl, &equal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!equal)
+ {
+ mDirectoryUrl = currentUrl;
+ aDirectory->GetUuid(mDirectoryId);
+ mCurrentLogin = login;
+ mCurrentMechanism = saslMechanism;
+ mCurrentProtocolVersion = protocolVersion;
+ redoConnection = true;
+ }
+ else
+ {
+ // Has login or version changed?
+ if (login != mCurrentLogin ||
+ saslMechanism != mCurrentMechanism ||
+ protocolVersion != mCurrentProtocolVersion)
+ {
+ redoConnection = true;
+ mCurrentLogin = login;
+ mCurrentMechanism = saslMechanism;
+ mCurrentProtocolVersion = protocolVersion;
+ }
+ }
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ rv = mDirectoryUrl->Clone(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILDAPURL> url(do_QueryInterface(uri, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get/Set the return attributes
+ nsCOMPtr<nsISupports> iSupportsMap;
+ rv = aArguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Require all attributes that are mapped to card properties
+ nsAutoCString returnAttributes;
+ rv = map->GetAllCardAttributes(returnAttributes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = url->SetAttributes(returnAttributes);
+ // Now do the error check
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Also require the objectClass attribute, it is used by
+ // nsAbLDAPCard::SetMetaProperties
+ rv = url->AddAttribute(NS_LITERAL_CSTRING("objectClass"));
+
+ nsAutoCString filter;
+
+ // Get filter from arguments if set:
+ rv = aArguments->GetFilter(filter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (filter.IsEmpty()) {
+ // Get the filter
+ nsCOMPtr<nsISupports> supportsExpression;
+ rv = aArguments->GetExpression(getter_AddRefs(supportsExpression));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbBooleanExpression> expression(do_QueryInterface(supportsExpression, &rv));
+
+ // figure out how we map attribute names to addressbook fields for this
+ // query
+ rv = nsAbBoolExprToLDAPFilter::Convert(map, expression, filter);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ /*
+ * Mozilla itself cannot arrive here with a blank filter
+ * as the nsAbLDAPDirectory::StartSearch() disallows it.
+ * But 3rd party LDAP query integration with Mozilla begins
+ * in this method.
+ *
+ * Default the filter string if blank, otherwise it gets
+ * set to (objectclass=*) which returns everything. Set
+ * the default to (objectclass=inetorgperson) as this
+ * is the most appropriate default objectclass which is
+ * central to the makeup of the mozilla ldap address book
+ * entries.
+ */
+ if (filter.IsEmpty())
+ {
+ filter.AssignLiteral("(objectclass=inetorgperson)");
+ }
+
+ // get the directoryFilter from the directory url and merge it with the user's
+ // search filter
+ nsAutoCString urlFilter;
+ rv = mDirectoryUrl->GetFilter(urlFilter);
+
+ // if urlFilter is unset (or set to the default "objectclass=*"), there's
+ // no need to AND in an empty search term, so leave prefix and suffix empty
+
+ nsAutoCString searchFilter;
+ if (urlFilter.Length() && !urlFilter.EqualsLiteral("(objectclass=*)"))
+ {
+ // if urlFilter isn't parenthesized, we need to add in parens so that
+ // the filter works as a term to &
+ //
+ if (urlFilter[0] != '(')
+ {
+ searchFilter = NS_LITERAL_CSTRING("(&(");
+ searchFilter.Append(urlFilter);
+ searchFilter.AppendLiteral(")");
+ }
+ else
+ {
+ searchFilter = NS_LITERAL_CSTRING("(&");
+ searchFilter.Append(urlFilter);
+ }
+
+ searchFilter += filter;
+ searchFilter += ')';
+ }
+ else
+ searchFilter = filter;
+
+ rv = url->SetFilter(searchFilter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now formulate the search string
+
+ // Get the scope
+ int32_t scope;
+ bool doSubDirectories;
+ rv = aArguments->GetQuerySubDirectories (&doSubDirectories);
+ NS_ENSURE_SUCCESS(rv, rv);
+ scope = doSubDirectories ? nsILDAPURL::SCOPE_SUBTREE :
+ nsILDAPURL::SCOPE_ONELEVEL;
+
+ rv = url->SetScope(scope);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // too soon? Do we need a new listener?
+ // If we already have a connection, and don't need to re-do it, give it the
+ // new search details and go for it...
+ if (!redoConnection)
+ {
+ nsAbQueryLDAPMessageListener *msgListener =
+ static_cast<nsAbQueryLDAPMessageListener *>(static_cast<nsILDAPMessageListener *>(mListener.get()));
+ if (msgListener)
+ {
+ // Ensure the urls are correct
+ msgListener->mDirectoryUrl = mDirectoryUrl;
+ msgListener->mSearchUrl = url;
+ // Also ensure we set the correct result limit
+ msgListener->mResultLimit = aResultLimit;
+ return msgListener->DoTask();
+ }
+ }
+
+ nsCOMPtr<nsIAbLDAPDirectory> abLDAPDir = do_QueryInterface(aDirectory, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> serverSearchControls;
+ rv = abLDAPDir->GetSearchServerControls(getter_AddRefs(serverSearchControls));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> clientSearchControls;
+ rv = abLDAPDir->GetSearchClientControls(getter_AddRefs(clientSearchControls));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create the new connection (which cause the old one to be dropped if necessary)
+ mConnection = do_CreateInstance(NS_LDAPCONNECTION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectoryQueryResultListener> resultListener =
+ do_QueryInterface((nsIAbDirectoryQuery*)this, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Initiate LDAP message listener
+ nsAbQueryLDAPMessageListener* _messageListener =
+ new nsAbQueryLDAPMessageListener(resultListener, mDirectoryUrl, url,
+ mConnection, aArguments,
+ serverSearchControls, clientSearchControls,
+ mCurrentLogin, mCurrentMechanism,
+ aResultLimit, aTimeOut);
+ if (_messageListener == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mListener = _messageListener;
+ *_retval = 1;
+
+ // Now lets initialize the LDAP connection properly. We'll kick
+ // off the bind operation in the callback function, |OnLDAPInit()|.
+ rv = mConnection->Init(mDirectoryUrl, mCurrentLogin,
+ mListener, nullptr, mCurrentProtocolVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return rv;
+}
+
+/* void stopQuery (in long contextID); */
+NS_IMETHODIMP nsAbLDAPDirectoryQuery::StopQuery(int32_t contextID)
+{
+ mInitialized = true;
+
+ if (!mListener)
+ return NS_OK;
+
+ nsAbQueryLDAPMessageListener *listener =
+ static_cast<nsAbQueryLDAPMessageListener *>(static_cast<nsILDAPMessageListener *>(mListener.get()));
+ if (listener)
+ return listener->Cancel();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectoryQuery::OnQueryFoundCard(nsIAbCard *aCard)
+{
+ aCard->SetDirectoryId(mDirectoryId);
+
+ for (int32_t i = 0; i < mListeners.Count(); ++i)
+ mListeners[i]->OnSearchFoundCard(aCard);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectoryQuery::OnQueryResult(int32_t aResult,
+ int32_t aErrorCode)
+{
+ uint32_t count = mListeners.Count();
+
+ // XXX: Temporary fix for crasher needs reviewing as part of bug 135231.
+ // Temporarily add a reference to ourselves, in case the only thing
+ // keeping us alive is the link with the listener.
+ NS_ADDREF_THIS();
+
+ for (int32_t i = count - 1; i >= 0; --i)
+ {
+ mListeners[i]->OnSearchFinished(aResult, EmptyString());
+ mListeners.RemoveObjectAt(i);
+ }
+
+ NS_RELEASE_THIS();
+
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.h b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.h
new file mode 100644
index 000000000..2c11c2ab9
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbLDAPDirectoryQuery_h__
+#define nsAbLDAPDirectoryQuery_h__
+
+#include "nsIAbDirectoryQuery.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPMessageListener.h"
+#include "nsILDAPURL.h"
+#include "nsWeakReference.h"
+
+#include "nsStringGlue.h"
+#include "nsCOMArray.h"
+
+class nsAbLDAPDirectoryQuery : public nsIAbDirectoryQuery,
+ public nsIAbDirectoryQueryResultListener
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABDIRECTORYQUERY
+ NS_DECL_NSIABDIRECTORYQUERYRESULTLISTENER
+
+ nsAbLDAPDirectoryQuery();
+
+protected:
+ nsCOMPtr<nsILDAPMessageListener> mListener;
+
+private:
+ virtual ~nsAbLDAPDirectoryQuery();
+ nsCOMPtr<nsILDAPConnection> mConnection;
+ nsCOMPtr<nsILDAPURL> mDirectoryUrl;
+ nsCString mDirectoryId;
+ nsCOMArray<nsIAbDirSearchListener> mListeners;
+ nsCString mCurrentLogin;
+ nsCString mCurrentMechanism;
+ uint32_t mCurrentProtocolVersion;
+
+ bool mInitialized;
+};
+
+#endif // nsAbLDAPDirectoryQuery_h__
diff --git a/mailnews/addrbook/src/nsAbLDAPListenerBase.cpp b/mailnews/addrbook/src/nsAbLDAPListenerBase.cpp
new file mode 100644
index 000000000..e5465a7f5
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPListenerBase.cpp
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbLDAPListenerBase.h"
+#include "nsIWindowWatcher.h"
+#include "nsIWindowMediator.h"
+#include "mozIDOMWindow.h"
+#include "nsIAuthPrompt.h"
+#include "nsIStringBundle.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPErrors.h"
+#include "nsILoginManager.h"
+#include "nsILoginInfo.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMemory.h"
+#include "mozilla/Services.h"
+
+using namespace mozilla;
+
+nsAbLDAPListenerBase::nsAbLDAPListenerBase(nsILDAPURL* url,
+ nsILDAPConnection* connection,
+ const nsACString &login,
+ const int32_t timeOut) :
+ mDirectoryUrl(url), mConnection(connection), mLogin(login),
+ mTimeOut(timeOut), mBound(false), mInitialized(false),
+ mLock("nsAbLDAPListenerBase.mLock")
+{
+}
+
+nsAbLDAPListenerBase::~nsAbLDAPListenerBase()
+{
+}
+
+nsresult nsAbLDAPListenerBase::Initiate()
+{
+ if (!mConnection || !mDirectoryUrl)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mInitialized)
+ return NS_OK;
+
+ mInitialized = true;
+
+ return NS_OK;
+}
+
+// If something fails in this function, we must call InitFailed() so that the
+// derived class (and listener) knows to cancel what its doing as there is
+// a problem.
+NS_IMETHODIMP nsAbLDAPListenerBase::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus)
+{
+ if (!mConnection || !mDirectoryUrl)
+ {
+ InitFailed();
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsresult rv;
+ nsString passwd;
+
+ // Make sure that the Init() worked properly
+ if (NS_FAILED(aStatus))
+ {
+ InitFailed();
+ return NS_OK;
+ }
+
+ // If mLogin is set, we're expected to use it to get a password.
+ //
+ if (!mLogin.IsEmpty() && !mSaslMechanism.EqualsLiteral("GSSAPI"))
+ {
+ // get the string bundle service
+ //
+ nsCOMPtr<nsIStringBundleService> stringBundleSvc =
+ mozilla::services::GetStringBundleService();
+ if (!stringBundleSvc)
+ {
+ NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit():"
+ " error getting string bundle service");
+ InitFailed();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the LDAP string bundle
+ //
+ nsCOMPtr<nsIStringBundle> ldapBundle;
+ rv = stringBundleSvc->CreateBundle("chrome://mozldap/locale/ldap.properties",
+ getter_AddRefs(ldapBundle));
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit(): error creating string"
+ "bundle chrome://mozldap/locale/ldap.properties");
+ InitFailed();
+ return rv;
+ }
+
+ // get the title for the authentication prompt
+ //
+ nsString authPromptTitle;
+ rv = ldapBundle->GetStringFromName(u"authPromptTitle",
+ getter_Copies(authPromptTitle));
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit(): error getting"
+ "'authPromptTitle' string from bundle "
+ "chrome://mozldap/locale/ldap.properties");
+ InitFailed();
+ return rv;
+ }
+
+ // get the host name for the auth prompt
+ //
+ nsAutoCString host;
+ rv = mDirectoryUrl->GetAsciiHost(host);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit(): error getting ascii host"
+ "name from directory url");
+ InitFailed();
+ return rv;
+ }
+
+ // hostTemp is only necessary to work around a code-generation
+ // bug in egcs 1.1.2 (the version of gcc that comes with Red Hat 6.2),
+ // which is the default compiler for Mozilla on linux at the moment.
+ //
+ NS_ConvertASCIItoUTF16 hostTemp(host);
+ const char16_t *hostArray[1] = { hostTemp.get() };
+
+ // format the hostname into the authprompt text string
+ //
+ nsString authPromptText;
+ rv = ldapBundle->FormatStringFromName(u"authPromptText",
+ hostArray,
+ sizeof(hostArray) / sizeof(const char16_t *),
+ getter_Copies(authPromptText));
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit():"
+ "error getting 'authPromptText' string from bundle "
+ "chrome://mozldap/locale/ldap.properties");
+ InitFailed();
+ return rv;
+ }
+
+ // get the window mediator service, so we can get an auth prompter
+ //
+ nsCOMPtr<nsIWindowMediator> windowMediator =
+ do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit():"
+ " couldn't get window mediator service.");
+ InitFailed();
+ return rv;
+ }
+
+ // get the addressbook window, as it will be used to parent the auth
+ // prompter dialog
+ //
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ rv = windowMediator->GetMostRecentWindow(nullptr,
+ getter_AddRefs(window));
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit():"
+ " error getting most recent window");
+ InitFailed();
+ return rv;
+ }
+
+ // get the window watcher service, so we can get an auth prompter
+ //
+ nsCOMPtr<nsIWindowWatcher> windowWatcherSvc =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit():"
+ " couldn't get window watcher service.");
+ InitFailed();
+ return rv;
+ }
+
+ // get the auth prompter itself
+ //
+ nsCOMPtr<nsIAuthPrompt> authPrompter;
+ rv = windowWatcherSvc->GetNewAuthPrompter(window,
+ getter_AddRefs(authPrompter));
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit():"
+ " error getting auth prompter");
+ InitFailed();
+ return rv;
+ }
+
+ // get authentication password, prompting the user if necessary
+ //
+ // we're going to use the URL spec of the server as the "realm" for
+ // wallet to remember the password by / for.
+
+ // Get the specification
+ nsCString spec;
+ rv = mDirectoryUrl->GetSpec(spec);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit():"
+ " error getting directory url spec");
+ InitFailed();
+ return rv;
+ }
+
+ bool status;
+ rv = authPrompter->PromptPassword(authPromptTitle.get(),
+ authPromptText.get(),
+ NS_ConvertUTF8toUTF16(spec).get(),
+ nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
+ getter_Copies(passwd),
+ &status);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit(): failed to prompt for"
+ " password");
+ InitFailed();
+ return rv;
+ }
+ else if (!status)
+ {
+ InitFailed(true);
+ return NS_OK;
+ }
+ }
+
+ // Initiate the LDAP operation
+ mOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit(): failed to create ldap operation");
+ InitFailed();
+ return rv;
+ }
+
+ rv = mOperation->Init(mConnection, this, nullptr);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit(): failed to Initialise operation");
+ InitFailed();
+ return rv;
+ }
+
+ // Try non-password mechanisms first
+ if (mSaslMechanism.EqualsLiteral("GSSAPI"))
+ {
+ nsAutoCString service;
+ rv = mDirectoryUrl->GetAsciiHost(service);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ service.Insert(NS_LITERAL_CSTRING("ldap@"), 0);
+
+ nsCOMPtr<nsIAuthModule> authModule =
+ do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sasl-gssapi", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mOperation->SaslBind(service, mSaslMechanism, authModule);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit(): "
+ "failed to perform GSSAPI bind");
+ mOperation = nullptr; // Break Listener -> Operation -> Listener ref cycle
+ InitFailed();
+ }
+ return rv;
+ }
+
+ // Bind
+ rv = mOperation->SimpleBind(NS_ConvertUTF16toUTF8(passwd));
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit(): failed to perform bind operation");
+ mOperation = nullptr; // Break Listener->Operation->Listener reference cycle
+ InitFailed();
+ }
+ return rv;
+}
+
+nsresult nsAbLDAPListenerBase::OnLDAPMessageBind(nsILDAPMessage *aMessage)
+{
+ if (mBound)
+ return NS_OK;
+
+ // see whether the bind actually succeeded
+ //
+ int32_t errCode;
+ nsresult rv = aMessage->GetErrorCode(&errCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (errCode != nsILDAPErrors::SUCCESS)
+ {
+ // if the login failed, tell the wallet to forget this password
+ //
+ if (errCode == nsILDAPErrors::INAPPROPRIATE_AUTH ||
+ errCode == nsILDAPErrors::INVALID_CREDENTIALS)
+ {
+ // Login failed, so try again - but first remove the existing login(s)
+ // so that the user gets prompted. This may not be the best way of doing
+ // things, we need to review that later.
+
+ nsCOMPtr<nsILoginManager> loginMgr =
+ do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString spec;
+ rv = mDirectoryUrl->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString prePath;
+ rv = mDirectoryUrl->GetPrePath(prePath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t count;
+ nsILoginInfo** logins;
+
+ rv = loginMgr->FindLogins(&count, NS_ConvertUTF8toUTF16(prePath),
+ EmptyString(),
+ NS_ConvertUTF8toUTF16(spec), &logins);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Typically there should only be one-login stored for this url, however,
+ // just in case there isn't.
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ rv = loginMgr->RemoveLogin(logins[i]);
+ if (NS_FAILED(rv))
+ {
+ NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
+ return rv;
+ }
+ }
+ NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
+
+ // XXX We should probably pop up an error dialog telling
+ // the user that the login failed here, rather than just bringing
+ // up the password dialog again, which is what calling OnLDAPInit()
+ // does.
+ return OnLDAPInit(nullptr, NS_OK);
+ }
+
+ // Don't know how to handle this, so use the message error code in
+ // the failure return value so we hopefully get it back to the UI.
+ return NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, errCode);
+ }
+
+ mBound = true;
+ return DoTask();
+}
diff --git a/mailnews/addrbook/src/nsAbLDAPListenerBase.h b/mailnews/addrbook/src/nsAbLDAPListenerBase.h
new file mode 100644
index 000000000..aa292df71
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPListenerBase.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbLDAPListenerBase_h__
+#define nsAbLDAPListenerBase_h__
+
+#include "mozilla/Attributes.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPMessageListener.h"
+#include "nsILDAPURL.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPOperation.h"
+#include "nsStringGlue.h"
+#include "mozilla/Mutex.h"
+
+class nsAbLDAPListenerBase : public nsILDAPMessageListener
+{
+public:
+ // Note that the directoryUrl is the details of the ldap directory
+ // without any search params or attributes specified.
+ nsAbLDAPListenerBase(nsILDAPURL* directoryUrl = nullptr,
+ nsILDAPConnection* connection = nullptr,
+ const nsACString &login = EmptyCString(),
+ const int32_t timeOut = 0);
+ virtual ~nsAbLDAPListenerBase();
+
+ NS_IMETHOD OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus) override;
+
+protected:
+ nsresult OnLDAPMessageBind(nsILDAPMessage *aMessage);
+
+ nsresult Initiate();
+
+ // Called if an LDAP initialization fails.
+ virtual void InitFailed(bool aCancelled = false) = 0;
+
+ // Called to start off the required task after a bind.
+ virtual nsresult DoTask() = 0;
+
+ nsCOMPtr<nsILDAPURL> mDirectoryUrl;
+ nsCOMPtr<nsILDAPOperation> mOperation; // current ldap op
+ nsILDAPConnection* mConnection;
+ nsCString mLogin;
+ nsCString mSaslMechanism;
+ int32_t mTimeOut;
+ bool mBound;
+ bool mInitialized;
+
+ mozilla::Mutex mLock;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp b/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp
new file mode 100644
index 000000000..faa8cdd23
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp
@@ -0,0 +1,489 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsILDAPMessage.h"
+#include "nsAbLDAPReplicationData.h"
+#include "nsIAbCard.h"
+#include "nsAbBaseCID.h"
+#include "nsAbUtils.h"
+#include "nsAbLDAPReplicationQuery.h"
+#include "nsILDAPErrors.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+
+// once bug # 101252 gets fixed, this should be reverted back to be non threadsafe
+// implementation is not really thread safe since each object should exist
+// independently along with its related independent nsAbLDAPReplicationQuery object.
+NS_IMPL_ISUPPORTS(nsAbLDAPProcessReplicationData, nsIAbLDAPProcessReplicationData, nsILDAPMessageListener)
+
+nsAbLDAPProcessReplicationData::nsAbLDAPProcessReplicationData() :
+ nsAbLDAPListenerBase(),
+ mState(kIdle),
+ mProtocol(-1),
+ mCount(0),
+ mDBOpen(false),
+ mInitialized(false)
+{
+}
+
+nsAbLDAPProcessReplicationData::~nsAbLDAPProcessReplicationData()
+{
+ /* destructor code */
+ if(mDBOpen && mReplicationDB)
+ mReplicationDB->Close(false);
+}
+
+NS_IMETHODIMP nsAbLDAPProcessReplicationData::Init(
+ nsIAbLDAPDirectory *aDirectory,
+ nsILDAPConnection *aConnection,
+ nsILDAPURL* aURL,
+ nsIAbLDAPReplicationQuery *aQuery,
+ nsIWebProgressListener *aProgressListener)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+ NS_ENSURE_ARG_POINTER(aConnection);
+ NS_ENSURE_ARG_POINTER(aURL);
+ NS_ENSURE_ARG_POINTER(aQuery);
+
+ mDirectory = aDirectory;
+ mConnection = aConnection;
+ mDirectoryUrl = aURL;
+ mQuery = aQuery;
+
+ mListener = aProgressListener;
+
+ nsresult rv = mDirectory->GetAttributeMap(getter_AddRefs(mAttrMap));
+ if (NS_FAILED(rv)) {
+ mQuery = nullptr;
+ return rv;
+ }
+
+ rv = mDirectory->GetAuthDn(mLogin);
+ if (NS_FAILED(rv)) {
+ mQuery = nullptr;
+ return rv;
+ }
+
+ rv = mDirectory->GetSaslMechanism(mSaslMechanism);
+ if (NS_FAILED(rv)) {
+ mQuery = nullptr;
+ return rv;
+ }
+
+ mInitialized = true;
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbLDAPProcessReplicationData::GetReplicationState(int32_t *aReplicationState)
+{
+ NS_ENSURE_ARG_POINTER(aReplicationState);
+ *aReplicationState = mState;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPProcessReplicationData::GetProtocolUsed(int32_t *aProtocolUsed)
+{
+ NS_ENSURE_ARG_POINTER(aProtocolUsed);
+ *aProtocolUsed = mProtocol;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPProcessReplicationData::OnLDAPMessage(nsILDAPMessage *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ int32_t messageType;
+ nsresult rv = aMessage->GetType(&messageType);
+ if (NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+
+ switch (messageType)
+ {
+ case nsILDAPMessage::RES_BIND:
+ rv = OnLDAPMessageBind(aMessage);
+ if (NS_FAILED(rv))
+ rv = Abort();
+ break;
+ case nsILDAPMessage::RES_SEARCH_ENTRY:
+ rv = OnLDAPSearchEntry(aMessage);
+ break;
+ case nsILDAPMessage::RES_SEARCH_RESULT:
+ rv = OnLDAPSearchResult(aMessage);
+ break;
+ default:
+ // for messageTypes we do not handle return NS_OK to LDAP and move ahead.
+ rv = NS_OK;
+ break;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbLDAPProcessReplicationData::Abort()
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_OK;
+
+ if (mState != kIdle && mOperation) {
+ rv = mOperation->AbandonExt();
+ if (NS_SUCCEEDED(rv))
+ mState = kIdle;
+ }
+
+ if (mReplicationDB && mDBOpen) {
+ // force close since we need to delete the file.
+ mReplicationDB->ForceClosed();
+ mDBOpen = false;
+
+ // delete the unsaved replication file
+ if (mReplicationFile) {
+ rv = mReplicationFile->Remove(false);
+ if (NS_SUCCEEDED(rv) && mDirectory) {
+ nsAutoCString fileName;
+ rv = mDirectory->GetReplicationFileName(fileName);
+ // now put back the backed up replicated file if aborted
+ if (NS_SUCCEEDED(rv) && mBackupReplicationFile)
+ rv = mBackupReplicationFile->MoveToNative(nullptr, fileName);
+ }
+ }
+ }
+
+ Done(false);
+
+ return rv;
+}
+
+nsresult nsAbLDAPProcessReplicationData::DoTask()
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = OpenABForReplicatedDir(true);
+ if (NS_FAILED(rv))
+ // do not call done here since it is called by OpenABForReplicationDir
+ return rv;
+
+ mOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mOperation->Init(mConnection, this, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get the relevant attributes associated with the directory server url
+ nsAutoCString urlFilter;
+ rv = mDirectoryUrl->GetFilter(urlFilter);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString dn;
+ rv = mDirectoryUrl->GetDn(dn);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (dn.IsEmpty())
+ return NS_ERROR_UNEXPECTED;
+
+ int32_t scope;
+ rv = mDirectoryUrl->GetScope(&scope);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString attributes;
+ rv = mDirectoryUrl->GetAttributes(attributes);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mState = kReplicatingAll;
+
+ if (mListener && NS_SUCCEEDED(rv))
+ // XXX Cast from bool to nsresult
+ mListener->OnStateChange(nullptr, nullptr,
+ nsIWebProgressListener::STATE_START,
+ static_cast<nsresult>(true));
+
+ return mOperation->SearchExt(dn, scope, urlFilter, attributes, 0, 0);
+}
+
+void nsAbLDAPProcessReplicationData::InitFailed(bool aCancelled)
+{
+ // Just call Done() which will ensure everything is tidied up nicely.
+ Done(false);
+}
+
+nsresult nsAbLDAPProcessReplicationData::OnLDAPSearchEntry(nsILDAPMessage *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+ // since this runs on the main thread and is single threaded, this will
+ // take care of entries returned by LDAP Connection thread after Abort.
+ if (!mReplicationDB || !mDBOpen)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = NS_OK;
+
+ // Although we would may naturally create an nsIAbLDAPCard here, we don't
+ // need to as we are writing this straight to the database, so just create
+ // the database version instead.
+ nsCOMPtr<nsIAbCard> newCard(do_CreateInstance(NS_ABMDBCARD_CONTRACTID,
+ &rv));
+ if (NS_FAILED(rv)) {
+ Abort();
+ return rv;
+ }
+
+ rv = mAttrMap->SetCardPropertiesFromLDAPMessage(aMessage, newCard);
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("nsAbLDAPProcessReplicationData::OnLDAPSearchEntry"
+ "No card properties could be set");
+ // if some entries are bogus for us, continue with next one
+ return NS_OK;
+ }
+
+ rv = mReplicationDB->CreateNewCardAndAddToDB(newCard, false, nullptr);
+ if(NS_FAILED(rv)) {
+ Abort();
+ return rv;
+ }
+
+ // now set the attribute for the DN of the entry in the card in the DB
+ nsAutoCString authDN;
+ rv = aMessage->GetDn(authDN);
+ if(NS_SUCCEEDED(rv) && !authDN.IsEmpty())
+ {
+ newCard->SetPropertyAsAUTF8String("_DN", authDN);
+ }
+
+ rv = mReplicationDB->EditCard(newCard, false, nullptr);
+ if(NS_FAILED(rv)) {
+ Abort();
+ return rv;
+ }
+
+
+ mCount ++;
+
+ if (mListener && !(mCount % 10)) // inform the listener every 10 entries
+ {
+ mListener->OnProgressChange(nullptr,nullptr,mCount, -1, mCount, -1);
+ // in case if the LDAP Connection thread is starved and causes problem
+ // uncomment this one and try.
+ // PR_Sleep(PR_INTERVAL_NO_WAIT); // give others a chance
+ }
+
+ return rv;
+}
+
+
+nsresult nsAbLDAPProcessReplicationData::OnLDAPSearchResult(nsILDAPMessage *aMessage)
+{
+#ifdef DEBUG_rdayal
+ printf("LDAP Replication : Got Results for Completion");
+#endif
+
+ NS_ENSURE_ARG_POINTER(aMessage);
+ if(!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ int32_t errorCode;
+ nsresult rv = aMessage->GetErrorCode(&errorCode);
+
+ if(NS_SUCCEEDED(rv)) {
+ // We are done with the LDAP search for all entries.
+ if(errorCode == nsILDAPErrors::SUCCESS || errorCode == nsILDAPErrors::SIZELIMIT_EXCEEDED) {
+ Done(true);
+ if(mReplicationDB && mDBOpen) {
+ rv = mReplicationDB->Close(true);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication DB Close on Success failed");
+ mDBOpen = false;
+ // once we have saved the new replication file, delete the backup file
+ if(mBackupReplicationFile)
+ {
+ rv = mBackupReplicationFile->Remove(false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication BackupFile Remove on Success failed");
+ }
+ }
+ return NS_OK;
+ }
+ }
+
+ // in case if GetErrorCode returned error or errorCode is not SUCCESS / SIZELIMIT_EXCEEDED
+ if(mReplicationDB && mDBOpen) {
+ // if error result is returned close the DB without saving ???
+ // should we commit anyway ??? whatever is returned is not lost then !!
+ rv = mReplicationDB->ForceClosed(); // force close since we need to delete the file.
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication DB ForceClosed on Failure failed");
+ mDBOpen = false;
+ // if error result is returned remove the replicated file
+ if(mReplicationFile) {
+ rv = mReplicationFile->Remove(false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication File Remove on Failure failed");
+ if(NS_SUCCEEDED(rv)) {
+ // now put back the backed up replicated file
+ if(mBackupReplicationFile && mDirectory)
+ {
+ nsAutoCString fileName;
+ rv = mDirectory->GetReplicationFileName(fileName);
+ if (NS_SUCCEEDED(rv) && !fileName.IsEmpty())
+ {
+ rv = mBackupReplicationFile->MoveToNative(nullptr, fileName);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Replication Backup File Move back on Failure failed");
+ }
+ }
+ }
+ }
+ Done(false);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsAbLDAPProcessReplicationData::OpenABForReplicatedDir(bool aCreate)
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = mDirectory->GetReplicationFile(getter_AddRefs(mReplicationFile));
+ if (NS_FAILED(rv))
+ {
+ Done(false);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCString fileName;
+ rv = mReplicationFile->GetNativeLeafName(fileName);
+ if (NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+
+ // if the AB DB already exists backup existing one,
+ // in case if the user cancels or Abort put back the backed up file
+ bool fileExists;
+ rv = mReplicationFile->Exists(&fileExists);
+ if(NS_SUCCEEDED(rv) && fileExists) {
+ // create the backup file object same as the Replication file object.
+ // we create a backup file here since we need to cleanup the existing file
+ // for create and then commit so instead of deleting existing cards we just
+ // clone the existing one for a much better performance - for Download All.
+ // And also important in case if replication fails we donot lose user's existing
+ // replicated data for both Download all and Changelog.
+ nsCOMPtr<nsIFile> clone;
+ rv = mReplicationFile->Clone(getter_AddRefs(clone));
+ if(NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+ mBackupReplicationFile = do_QueryInterface(clone, &rv);
+ if(NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+ rv = mBackupReplicationFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0777);
+ if(NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+ nsAutoString backupFileLeafName;
+ rv = mBackupReplicationFile->GetLeafName(backupFileLeafName);
+ if(NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+ // remove the newly created unique backup file so that move and copy succeeds.
+ rv = mBackupReplicationFile->Remove(false);
+ if(NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+
+ if(aCreate) {
+ // set backup file to existing replication file for move
+ mBackupReplicationFile->SetNativeLeafName(fileName);
+
+ rv = mBackupReplicationFile->MoveTo(nullptr, backupFileLeafName);
+ // set the backup file leaf name now
+ if (NS_SUCCEEDED(rv))
+ mBackupReplicationFile->SetLeafName(backupFileLeafName);
+ }
+ else {
+ // set backup file to existing replication file for copy
+ mBackupReplicationFile->SetNativeLeafName(fileName);
+
+ // specify the parent here specifically,
+ // passing nullptr to copy to the same dir actually renames existing file
+ // instead of making another copy of the existing file.
+ nsCOMPtr<nsIFile> parent;
+ rv = mBackupReplicationFile->GetParent(getter_AddRefs(parent));
+ if (NS_SUCCEEDED(rv))
+ rv = mBackupReplicationFile->CopyTo(parent, backupFileLeafName);
+ // set the backup file leaf name now
+ if (NS_SUCCEEDED(rv))
+ mBackupReplicationFile->SetLeafName(backupFileLeafName);
+ }
+ if(NS_FAILED(rv)) {
+ Done(false);
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsIAddrDatabase> addrDBFactory =
+ do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
+ if(NS_FAILED(rv)) {
+ if (mBackupReplicationFile)
+ mBackupReplicationFile->Remove(false);
+ Done(false);
+ return rv;
+ }
+
+ rv = addrDBFactory->Open(mReplicationFile, aCreate, true, getter_AddRefs(mReplicationDB));
+ if(NS_FAILED(rv)) {
+ Done(false);
+ if (mBackupReplicationFile)
+ mBackupReplicationFile->Remove(false);
+ return rv;
+ }
+
+ mDBOpen = true; // replication DB is now Open
+ return rv;
+}
+
+void nsAbLDAPProcessReplicationData::Done(bool aSuccess)
+{
+ if (!mInitialized)
+ return;
+
+ mState = kReplicationDone;
+
+ if (mQuery)
+ mQuery->Done(aSuccess);
+
+ if (mListener)
+ // XXX Cast from bool to nsresult
+ mListener->OnStateChange(nullptr, nullptr,
+ nsIWebProgressListener::STATE_STOP,
+ static_cast<nsresult>(aSuccess));
+
+ // since this is called when all is done here, either on success,
+ // failure or abort release the query now.
+ mQuery = nullptr;
+}
+
+nsresult nsAbLDAPProcessReplicationData::DeleteCard(nsString & aDn)
+{
+ nsCOMPtr<nsIAbCard> cardToDelete;
+ mReplicationDB->GetCardFromAttribute(nullptr, "_DN", NS_ConvertUTF16toUTF8(aDn),
+ false, getter_AddRefs(cardToDelete));
+ return mReplicationDB->DeleteCard(cardToDelete, false, nullptr);
+}
diff --git a/mailnews/addrbook/src/nsAbLDAPReplicationData.h b/mailnews/addrbook/src/nsAbLDAPReplicationData.h
new file mode 100644
index 000000000..546ce659e
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPReplicationData.h
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef nsAbLDAPReplicationData_h__
+#define nsAbLDAPReplicationData_h__
+
+#include "mozilla/Attributes.h"
+#include "nsIAbLDAPReplicationData.h"
+#include "nsIWebProgressListener.h"
+#include "nsIAbLDAPReplicationQuery.h"
+#include "nsAbLDAPListenerBase.h"
+#include "nsIAddrDatabase.h"
+#include "nsIFile.h"
+#include "nsDirPrefs.h"
+#include "nsIAbLDAPAttributeMap.h"
+#include "nsIAbLDAPDirectory.h"
+#include "nsStringGlue.h"
+
+class nsAbLDAPProcessReplicationData : public nsIAbLDAPProcessReplicationData,
+ public nsAbLDAPListenerBase
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABLDAPPROCESSREPLICATIONDATA
+
+ nsAbLDAPProcessReplicationData();
+
+ // nsILDAPMessageListener
+ NS_IMETHOD OnLDAPMessage(nsILDAPMessage *aMessage) override;
+
+protected:
+ virtual ~nsAbLDAPProcessReplicationData();
+ virtual nsresult DoTask() override;
+ virtual void InitFailed(bool aCancelled = false) override;
+
+ // pointer to the interfaces used by this object
+ nsCOMPtr<nsIWebProgressListener> mListener;
+ // pointer to the query to call back to once we've finished
+ nsCOMPtr<nsIAbLDAPReplicationQuery> mQuery;
+
+ nsCOMPtr<nsIAddrDatabase> mReplicationDB;
+ nsCOMPtr <nsIFile> mReplicationFile;
+ nsCOMPtr <nsIFile> mBackupReplicationFile;
+
+ // state of processing, protocol used and count of results
+ int32_t mState;
+ int32_t mProtocol;
+ int32_t mCount;
+ bool mDBOpen;
+ bool mInitialized;
+
+ nsCOMPtr<nsIAbLDAPDirectory> mDirectory;
+ nsCOMPtr<nsIAbLDAPAttributeMap> mAttrMap; // maps ab properties to ldap attrs
+
+ virtual nsresult OnLDAPSearchEntry(nsILDAPMessage *aMessage);
+ virtual nsresult OnLDAPSearchResult(nsILDAPMessage *aMessage);
+
+ nsresult OpenABForReplicatedDir(bool bCreate);
+ nsresult DeleteCard(nsString & aDn);
+ void Done(bool aSuccess);
+};
+
+
+#endif // nsAbLDAPReplicationData_h__
diff --git a/mailnews/addrbook/src/nsAbLDAPReplicationQuery.cpp b/mailnews/addrbook/src/nsAbLDAPReplicationQuery.cpp
new file mode 100644
index 000000000..d82a8336c
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPReplicationQuery.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsCOMPtr.h"
+#include "nsAbLDAPReplicationQuery.h"
+#include "nsAbLDAPReplicationService.h"
+#include "nsAbLDAPReplicationData.h"
+#include "nsILDAPURL.h"
+#include "nsAbBaseCID.h"
+#include "nsAbUtils.h"
+#include "nsDirPrefs.h"
+#include "prmem.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+
+NS_IMPL_ISUPPORTS(nsAbLDAPReplicationQuery,
+ nsIAbLDAPReplicationQuery)
+
+nsAbLDAPReplicationQuery::nsAbLDAPReplicationQuery()
+ : mInitialized(false)
+{
+}
+
+nsresult nsAbLDAPReplicationQuery::InitLDAPData()
+{
+ nsAutoCString fileName;
+ nsresult rv = mDirectory->GetReplicationFileName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // this is done here to take care of the problem related to bug # 99124.
+ // earlier versions of Mozilla could have the fileName associated with the directory
+ // to be abook.mab which is the profile's personal addressbook. If the pref points to
+ // it, calls nsDirPrefs to generate a new server filename.
+ if (fileName.IsEmpty() || fileName.EqualsLiteral(kPersonalAddressbook))
+ {
+ // Ensure fileName is empty for DIR_GenerateAbFileName to work
+ // correctly.
+ fileName.Truncate();
+
+ nsCOMPtr<nsIAbDirectory> standardDir(do_QueryInterface(mDirectory, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString dirPrefId;
+ rv = standardDir->GetDirPrefId(dirPrefId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX This should be replaced by a local function at some stage.
+ // For now we'll continue using the nsDirPrefs version.
+ DIR_Server* server = DIR_GetServerFromList(dirPrefId.get());
+ if (server)
+ {
+ DIR_SetServerFileName(server);
+ // Now ensure the prefs are saved
+ DIR_SavePrefsForOneServer(server);
+ }
+ }
+
+ rv = mDirectory->SetReplicationFileName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mDirectory->GetLDAPURL(getter_AddRefs(mURL));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mDirectory->GetAuthDn(mLogin);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mConnection = do_CreateInstance(NS_LDAPCONNECTION_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
+
+ return rv;
+}
+
+nsresult nsAbLDAPReplicationQuery::ConnectToLDAPServer()
+{
+ if (!mInitialized || !mURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv;
+ nsCOMPtr<nsILDAPMessageListener> mDp = do_QueryInterface(mDataProcessor,
+ &rv);
+ if (NS_FAILED(rv))
+ return NS_ERROR_UNEXPECTED;
+
+ // this could be a rebind call
+ int32_t replicationState = nsIAbLDAPProcessReplicationData::kIdle;
+ rv = mDataProcessor->GetReplicationState(&replicationState);
+ if (NS_FAILED(rv) ||
+ replicationState != nsIAbLDAPProcessReplicationData::kIdle)
+ return rv;
+
+ uint32_t protocolVersion;
+ rv = mDirectory->GetProtocolVersion(&protocolVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // initialize the LDAP connection
+ return mConnection->Init(mURL, mLogin, mDp, nullptr, protocolVersion);
+}
+
+NS_IMETHODIMP nsAbLDAPReplicationQuery::Init(nsIAbLDAPDirectory *aDirectory,
+ nsIWebProgressListener *aProgressListener)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+
+ mDirectory = aDirectory;
+
+ nsresult rv = InitLDAPData();
+ if (NS_FAILED(rv))
+ return rv;
+
+ mDataProcessor =
+ do_CreateInstance(NS_ABLDAP_PROCESSREPLICATIONDATA_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // 'this' initialized
+ mInitialized = true;
+
+ return mDataProcessor->Init(mDirectory, mConnection, mURL, this,
+ aProgressListener);
+}
+
+NS_IMETHODIMP nsAbLDAPReplicationQuery::DoReplicationQuery()
+{
+ return ConnectToLDAPServer();
+}
+
+NS_IMETHODIMP nsAbLDAPReplicationQuery::CancelQuery()
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mDataProcessor->Abort();
+}
+
+NS_IMETHODIMP nsAbLDAPReplicationQuery::Done(bool aSuccess)
+{
+ if (!mInitialized)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIAbLDAPReplicationService> replicationService =
+ do_GetService(NS_ABLDAP_REPLICATIONSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ replicationService->Done(aSuccess);
+
+ return rv;
+}
diff --git a/mailnews/addrbook/src/nsAbLDAPReplicationQuery.h b/mailnews/addrbook/src/nsAbLDAPReplicationQuery.h
new file mode 100644
index 000000000..f5d7cdda7
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPReplicationQuery.h
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef nsAbLDAPReplicationQuery_h__
+#define nsAbLDAPReplicationQuery_h__
+
+#include "nsIWebProgressListener.h"
+#include "nsIAbLDAPReplicationQuery.h"
+#include "nsIAbLDAPReplicationData.h"
+#include "nsIAbLDAPDirectory.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPOperation.h"
+#include "nsILDAPURL.h"
+#include "nsDirPrefs.h"
+#include "nsStringGlue.h"
+
+class nsAbLDAPReplicationQuery final : public nsIAbLDAPReplicationQuery
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABLDAPREPLICATIONQUERY
+
+ nsAbLDAPReplicationQuery();
+
+ nsresult InitLDAPData();
+ nsresult ConnectToLDAPServer();
+
+protected :
+ ~nsAbLDAPReplicationQuery() {}
+ // pointer to interfaces used by this object
+ nsCOMPtr<nsILDAPConnection> mConnection;
+ nsCOMPtr<nsILDAPOperation> mOperation;
+ nsCOMPtr<nsILDAPURL> mURL;
+ nsCOMPtr<nsIAbLDAPDirectory> mDirectory;
+
+ nsCOMPtr<nsIAbLDAPProcessReplicationData> mDataProcessor;
+
+ bool mInitialized;
+ nsCString mLogin;
+};
+
+#endif // nsAbLDAPReplicationQuery_h__
diff --git a/mailnews/addrbook/src/nsAbLDAPReplicationService.cpp b/mailnews/addrbook/src/nsAbLDAPReplicationService.cpp
new file mode 100644
index 000000000..0ee53d719
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPReplicationService.cpp
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsCOMPtr.h"
+#include "nsAbLDAPReplicationService.h"
+#include "nsAbLDAPReplicationQuery.h"
+#include "nsAbBaseCID.h"
+#include "nsIWebProgressListener.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+// XXX Change log replication doesn't work. Bug 311632 should fix it.
+//#include "nsAbLDAPChangeLogQuery.h"
+#include "nsIAbLDAPReplicationData.h"
+
+/*** implementation of the service ******/
+
+NS_IMPL_ISUPPORTS(nsAbLDAPReplicationService, nsIAbLDAPReplicationService)
+
+nsAbLDAPReplicationService::nsAbLDAPReplicationService()
+ : mReplicating(false)
+{
+}
+
+nsAbLDAPReplicationService::~nsAbLDAPReplicationService()
+{
+}
+
+/* void startReplication(in string aURI, in nsIWebProgressListener progressListener); */
+NS_IMETHODIMP nsAbLDAPReplicationService::StartReplication(nsIAbLDAPDirectory *aDirectory,
+ nsIWebProgressListener *progressListener)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+
+#ifdef DEBUG_rdayal
+ printf("Start Replication called");
+#endif
+
+ // Makes sure to allow only one replication at a time.
+ if(mReplicating)
+ return NS_ERROR_FAILURE;
+
+ mDirectory = aDirectory;
+
+ nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
+
+ switch (DecideProtocol())
+ {
+ case nsIAbLDAPProcessReplicationData::kDefaultDownloadAll:
+ mQuery = do_CreateInstance(NS_ABLDAP_REPLICATIONQUERY_CONTRACTID, &rv);
+ break;
+// XXX Change log replication doesn't work. Bug 311632 should fix it.
+//case nsIAbLDAPProcessReplicationData::kChangeLogProtocol:
+// mQuery = do_CreateInstance (NS_ABLDAP_CHANGELOGQUERY_CONTRACTID, &rv);
+// break;
+ default:
+ break;
+ }
+
+ if (NS_SUCCEEDED(rv) && mQuery)
+ {
+ rv = mQuery->Init(mDirectory, progressListener);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = mQuery->DoReplicationQuery();
+ if (NS_SUCCEEDED(rv))
+ {
+ mReplicating = true;
+ return rv;
+ }
+ }
+ }
+
+ if (progressListener && NS_FAILED(rv))
+ progressListener->OnStateChange(nullptr, nullptr,
+ nsIWebProgressListener::STATE_STOP,
+ NS_OK);
+
+ if (NS_FAILED(rv))
+ {
+ mDirectory = nullptr;
+ mQuery = nullptr;
+ }
+
+ return rv;
+}
+
+/* void cancelReplication(in string aURI); */
+NS_IMETHODIMP nsAbLDAPReplicationService::CancelReplication(nsIAbLDAPDirectory *aDirectory)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (aDirectory == mDirectory)
+ {
+ if (mQuery && mReplicating)
+ rv = mQuery->CancelQuery();
+ }
+
+ // If query has been cancelled successfully
+ if (NS_SUCCEEDED(rv))
+ Done(false);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbLDAPReplicationService::Done(bool aSuccess)
+{
+ mReplicating = false;
+ if (mQuery)
+ {
+ mQuery = nullptr; // Release query obj
+ mDirectory = nullptr; // Release directory
+ }
+
+ return NS_OK;
+}
+
+
+// XXX: This method should query the RootDSE for the changeLog attribute,
+// if it exists ChangeLog protocol is supported.
+int32_t nsAbLDAPReplicationService::DecideProtocol()
+{
+ // Do the changeLog, it will decide if there is a need to replicate all
+ // entries or only update existing DB and will do the appropriate thing.
+ //
+ // XXX: Bug 231965 changed this from kChangeLogProtocol to
+ // kDefaultDownloadAll because of a problem with ldap replication not
+ // working correctly. We need to change this back at some stage (bug 311632).
+ return nsIAbLDAPProcessReplicationData::kDefaultDownloadAll;
+}
+
+
diff --git a/mailnews/addrbook/src/nsAbLDAPReplicationService.h b/mailnews/addrbook/src/nsAbLDAPReplicationService.h
new file mode 100644
index 000000000..07fb768f7
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPReplicationService.h
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+
+#ifndef nsAbLDAPReplicationService_h___
+#define nsAbLDAPReplicationService_h___
+
+#include "nsIAbLDAPReplicationService.h"
+#include "nsIAbLDAPReplicationQuery.h"
+#include "nsStringGlue.h"
+
+class nsAbLDAPReplicationService : public nsIAbLDAPReplicationService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABLDAPREPLICATIONSERVICE
+
+ nsAbLDAPReplicationService();
+
+ int32_t DecideProtocol();
+
+protected:
+ virtual ~nsAbLDAPReplicationService();
+ nsCOMPtr<nsIAbLDAPReplicationQuery> mQuery;
+ bool mReplicating;
+ nsCOMPtr<nsIAbLDAPDirectory> mDirectory;
+
+};
+
+
+#endif /* nsAbLDAPReplicationService_h___ */
diff --git a/mailnews/addrbook/src/nsAbLDIFService.cpp b/mailnews/addrbook/src/nsAbLDIFService.cpp
new file mode 100644
index 000000000..95eb4b96a
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDIFService.cpp
@@ -0,0 +1,868 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsIAddrDatabase.h"
+#include "nsStringGlue.h"
+#include "nsAbLDIFService.h"
+#include "nsIFile.h"
+#include "nsILineInputStream.h"
+#include "nsIInputStream.h"
+#include "nsNetUtil.h"
+#include "nsISeekableStream.h"
+#include "mdb.h"
+#include "plstr.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "nsCRTGlue.h"
+#include "nsTArray.h"
+
+#include <ctype.h>
+
+NS_IMPL_ISUPPORTS(nsAbLDIFService, nsIAbLDIFService)
+
+// If we get a line longer than 32K it's just toooooo bad!
+#define kTextAddressBufferSz (64 * 1024)
+
+nsAbLDIFService::nsAbLDIFService()
+{
+ mStoreLocAsHome = false;
+ mLFCount = 0;
+ mCRCount = 0;
+}
+
+nsAbLDIFService::~nsAbLDIFService()
+{
+}
+
+#define RIGHT2 0x03
+#define RIGHT4 0x0f
+#define CONTINUED_LINE_MARKER '\001'
+
+// XXX TODO fix me
+// use the NSPR base64 library. see plbase64.h
+// see bug #145367
+static unsigned char b642nib[0x80] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+NS_IMETHODIMP nsAbLDIFService::ImportLDIFFile(nsIAddrDatabase *aDb, nsIFile *aSrc, bool aStoreLocAsHome, uint32_t *aProgress)
+{
+ NS_ENSURE_ARG_POINTER(aSrc);
+ NS_ENSURE_ARG_POINTER(aDb);
+
+ mStoreLocAsHome = aStoreLocAsHome;
+
+ char buf[1024];
+ char* pBuf = &buf[0];
+ int32_t startPos = 0;
+ uint32_t len = 0;
+ nsTArray<int32_t> listPosArray; // where each list/group starts in ldif file
+ nsTArray<int32_t> listSizeArray; // size of the list/group info
+ int32_t savedStartPos = 0;
+ int32_t filePos = 0;
+ uint64_t bytesLeft = 0;
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aSrc);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Initialize the parser for a run...
+ mLdifLine.Truncate();
+
+ while (NS_SUCCEEDED(inputStream->Available(&bytesLeft)) && bytesLeft > 0)
+ {
+ if (NS_SUCCEEDED(inputStream->Read(pBuf, sizeof(buf), &len)) && len > 0)
+ {
+ startPos = 0;
+
+ while (NS_SUCCEEDED(GetLdifStringRecord(buf, len, startPos)))
+ {
+ if (mLdifLine.Find("groupOfNames") == -1)
+ AddLdifRowToDatabase(aDb, false);
+ else
+ {
+ //keep file position for mailing list
+ listPosArray.AppendElement(savedStartPos);
+ listSizeArray.AppendElement(filePos + startPos-savedStartPos);
+ ClearLdifRecordBuffer();
+ }
+ savedStartPos = filePos + startPos;
+ }
+ filePos += len;
+ if (aProgress)
+ *aProgress = (uint32_t)filePos;
+ }
+ }
+ //last row
+ if (!mLdifLine.IsEmpty() && mLdifLine.Find("groupOfNames") == -1)
+ AddLdifRowToDatabase(aDb, false);
+
+ // mail Lists
+ int32_t i, pos;
+ uint32_t size;
+ int32_t listTotal = listPosArray.Length();
+ char *listBuf;
+ ClearLdifRecordBuffer(); // make sure the buffer is clean
+
+ nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(inputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (i = 0; i < listTotal; i++)
+ {
+ pos = listPosArray[i];
+ size = listSizeArray[i];
+ if (NS_SUCCEEDED(seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, pos)))
+ {
+ // Allocate enough space for the lists/groups as the size varies.
+ listBuf = (char *) PR_Malloc(size);
+ if (!listBuf)
+ continue;
+ if (NS_SUCCEEDED(inputStream->Read(listBuf, size, &len)) && len > 0)
+ {
+ startPos = 0;
+
+ while (NS_SUCCEEDED(GetLdifStringRecord(listBuf, len, startPos)))
+ {
+ if (mLdifLine.Find("groupOfNames") != -1)
+ {
+ AddLdifRowToDatabase(aDb, true);
+ if (NS_SUCCEEDED(seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, 0)))
+ break;
+ }
+ }
+ }
+ PR_FREEIF(listBuf);
+ }
+ }
+
+ rv = inputStream->Close();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Finally commit everything to the database and return.
+ return aDb->Commit(nsAddrDBCommitType::kLargeCommit);
+}
+
+/*
+ * str_parse_line - takes a line of the form "type:[:] value" and splits it
+ * into components "type" and "value". if a double colon separates type from
+ * value, then value is encoded in base 64, and parse_line un-decodes it
+ * (in place) before returning.
+ * in LDIF, non-ASCII data is treated as base64 encoded UTF-8
+ */
+
+nsresult nsAbLDIFService::str_parse_line(char *line, char **type, char **value, int *vlen) const
+{
+ char *p, *s, *d, *byte, *stop;
+ char nib;
+ int i, b64;
+
+ /* skip any leading space */
+ while ( isspace( *line ) ) {
+ line++;
+ }
+ *type = line;
+
+ for ( s = line; *s && *s != ':'; s++ )
+ ; /* NULL */
+ if ( *s == '\0' ) {
+ return NS_ERROR_FAILURE;
+ }
+
+ /* trim any space between type and : */
+ for ( p = s - 1; p > line && isspace( *p ); p-- ) {
+ *p = '\0';
+ }
+ *s++ = '\0';
+
+ /* check for double : - indicates base 64 encoded value */
+ if ( *s == ':' ) {
+ s++;
+ b64 = 1;
+ /* single : - normally encoded value */
+ } else {
+ b64 = 0;
+ }
+
+ /* skip space between : and value */
+ while ( isspace( *s ) ) {
+ s++;
+ }
+
+ /* if no value is present, error out */
+ if ( *s == '\0' ) {
+ return NS_ERROR_FAILURE;
+ }
+
+ /* check for continued line markers that should be deleted */
+ for ( p = s, d = s; *p; p++ ) {
+ if ( *p != CONTINUED_LINE_MARKER )
+ *d++ = *p;
+ }
+ *d = '\0';
+
+ *value = s;
+ if ( b64 ) {
+ stop = PL_strchr( s, '\0' );
+ byte = s;
+ for ( p = s, *vlen = 0; p < stop; p += 4, *vlen += 3 ) {
+ for ( i = 0; i < 3; i++ ) {
+ if ( p[i] != '=' && (p[i] & 0x80 ||
+ b642nib[ p[i] & 0x7f ] > 0x3f) ) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ /* first digit */
+ nib = b642nib[ p[0] & 0x7f ];
+ byte[0] = nib << 2;
+ /* second digit */
+ nib = b642nib[ p[1] & 0x7f ];
+ byte[0] |= nib >> 4;
+ byte[1] = (nib & RIGHT4) << 4;
+ /* third digit */
+ if ( p[2] == '=' ) {
+ *vlen += 1;
+ break;
+ }
+ nib = b642nib[ p[2] & 0x7f ];
+ byte[1] |= nib >> 2;
+ byte[2] = (nib & RIGHT2) << 6;
+ /* fourth digit */
+ if ( p[3] == '=' ) {
+ *vlen += 2;
+ break;
+ }
+ nib = b642nib[ p[3] & 0x7f ];
+ byte[2] |= nib;
+
+ byte += 3;
+ }
+ s[ *vlen ] = '\0';
+ } else {
+ *vlen = (int) (d - s);
+ }
+ return NS_OK;
+}
+
+/*
+ * str_getline - return the next "line" (minus newline) of input from a
+ * string buffer of lines separated by newlines, terminated by \n\n
+ * or \0. this routine handles continued lines, bundling them into
+ * a single big line before returning. if a line begins with a white
+ * space character, it is a continuation of the previous line. the white
+ * space character (nb: only one char), and preceeding newline are changed
+ * into CONTINUED_LINE_MARKER chars, to be deleted later by the
+ * str_parse_line() routine above.
+ *
+ * it takes a pointer to a pointer to the buffer on the first call,
+ * which it updates and must be supplied on subsequent calls.
+ */
+
+char* nsAbLDIFService::str_getline(char **next) const
+{
+ char *lineStr;
+ char c;
+
+ if ( *next == nullptr || **next == '\n' || **next == '\0' ) {
+ return( nullptr);
+ }
+
+ lineStr = *next;
+ while ( (*next = PL_strchr( *next, '\n' )) != NULL ) {
+ c = *(*next + 1);
+ if ( isspace( c ) && c != '\n' ) {
+ **next = CONTINUED_LINE_MARKER;
+ *(*next+1) = CONTINUED_LINE_MARKER;
+ } else {
+ *(*next)++ = '\0';
+ break;
+ }
+ }
+
+ return( lineStr );
+}
+
+nsresult nsAbLDIFService::GetLdifStringRecord(char* buf, int32_t len, int32_t& stopPos)
+{
+ for (; stopPos < len; stopPos++)
+ {
+ char c = buf[stopPos];
+
+ if (c == 0xA)
+ {
+ mLFCount++;
+ }
+ else if (c == 0xD)
+ {
+ mCRCount++;
+ }
+ else
+ {
+ if (mLFCount == 0 && mCRCount == 0)
+ mLdifLine.Append(c);
+ else if (( mLFCount > 1) || ( mCRCount > 2 && mLFCount ) ||
+ ( !mLFCount && mCRCount > 1 ))
+ {
+ return NS_OK;
+ }
+ else if ((mLFCount == 1 || mCRCount == 1))
+ {
+ mLdifLine.Append('\n');
+ mLdifLine.Append(c);
+ mLFCount = 0;
+ mCRCount = 0;
+ }
+ }
+ }
+
+ if (((stopPos == len) && (mLFCount > 1)) || (mCRCount > 2 && mLFCount) ||
+ (!mLFCount && mCRCount > 1))
+ return NS_OK;
+
+ return NS_ERROR_FAILURE;
+}
+
+void nsAbLDIFService::AddLdifRowToDatabase(nsIAddrDatabase *aDatabase,
+ bool bIsList)
+{
+ // If no data to process then reset CR/LF counters and return.
+ if (mLdifLine.IsEmpty())
+ {
+ mLFCount = 0;
+ mCRCount = 0;
+ return;
+ }
+
+ nsCOMPtr <nsIMdbRow> newRow;
+ if (aDatabase)
+ {
+ if (bIsList)
+ aDatabase->GetNewListRow(getter_AddRefs(newRow));
+ else
+ aDatabase->GetNewRow(getter_AddRefs(newRow));
+
+ if (!newRow)
+ return;
+ }
+ else
+ return;
+
+ char* cursor = ToNewCString(mLdifLine);
+ char* saveCursor = cursor; /* keep for deleting */
+ char* line = 0;
+ char* typeSlot = 0;
+ char* valueSlot = 0;
+ int length = 0; // the length of an ldif attribute
+ while ( (line = str_getline(&cursor)) != nullptr)
+ {
+ if (NS_SUCCEEDED(str_parse_line(line, &typeSlot, &valueSlot, &length))) {
+ AddLdifColToDatabase(aDatabase, newRow, typeSlot, valueSlot, bIsList);
+ }
+ else
+ continue; // parse error: continue with next loop iteration
+ }
+ free(saveCursor);
+ aDatabase->AddCardRowToDB(newRow);
+
+ if (bIsList)
+ aDatabase->AddListDirNode(newRow);
+
+ // Clear buffer for next record
+ ClearLdifRecordBuffer();
+}
+
+void nsAbLDIFService::AddLdifColToDatabase(nsIAddrDatabase *aDatabase,
+ nsIMdbRow* newRow, char* typeSlot,
+ char* valueSlot, bool bIsList)
+{
+ nsAutoCString colType(typeSlot);
+ nsAutoCString column(valueSlot);
+
+ // 4.x exports attributes like "givenname",
+ // mozilla does "givenName" to be compliant with RFC 2798
+ ToLowerCase(colType);
+
+ mdb_u1 firstByte = (mdb_u1)(colType.get())[0];
+ switch ( firstByte )
+ {
+ case 'b':
+ if (colType.EqualsLiteral("birthyear"))
+ aDatabase->AddBirthYear(newRow, column.get());
+ else if (colType.EqualsLiteral("birthmonth"))
+ aDatabase->AddBirthMonth(newRow, column.get());
+ else if (colType.EqualsLiteral("birthday"))
+ aDatabase->AddBirthDay(newRow, column.get());
+ break; // 'b'
+
+ case 'c':
+ if (colType.EqualsLiteral("cn") || colType.EqualsLiteral("commonname"))
+ {
+ if (bIsList)
+ aDatabase->AddListName(newRow, column.get());
+ else
+ aDatabase->AddDisplayName(newRow, column.get());
+ }
+ else if (colType.EqualsLiteral("c") || colType.EqualsLiteral("countryname"))
+ {
+ if (mStoreLocAsHome )
+ aDatabase->AddHomeCountry(newRow, column.get());
+ else
+ aDatabase->AddWorkCountry(newRow, column.get());
+ }
+
+ else if (colType.EqualsLiteral("cellphone") )
+ aDatabase->AddCellularNumber(newRow, column.get());
+
+ else if (colType.EqualsLiteral("carphone"))
+ aDatabase->AddCellularNumber(newRow, column.get());
+
+ else if (colType.EqualsLiteral("custom1"))
+ aDatabase->AddCustom1(newRow, column.get());
+
+ else if (colType.EqualsLiteral("custom2"))
+ aDatabase->AddCustom2(newRow, column.get());
+
+ else if (colType.EqualsLiteral("custom3"))
+ aDatabase->AddCustom3(newRow, column.get());
+
+ else if (colType.EqualsLiteral("custom4"))
+ aDatabase->AddCustom4(newRow, column.get());
+
+ else if (colType.EqualsLiteral("company"))
+ aDatabase->AddCompany(newRow, column.get());
+ break; // 'c'
+
+ case 'd':
+ if (colType.EqualsLiteral("description"))
+ {
+ if (bIsList)
+ aDatabase->AddListDescription(newRow, column.get());
+ else
+ aDatabase->AddNotes(newRow, column.get());
+ }
+
+ else if (colType.EqualsLiteral("department"))
+ aDatabase->AddDepartment(newRow, column.get());
+
+ else if (colType.EqualsLiteral("displayname"))
+ {
+ if (bIsList)
+ aDatabase->AddListName(newRow, column.get());
+ else
+ aDatabase->AddDisplayName(newRow, column.get());
+ }
+ break; // 'd'
+
+ case 'f':
+
+ if (colType.EqualsLiteral("fax") ||
+ colType.EqualsLiteral("facsimiletelephonenumber"))
+ aDatabase->AddFaxNumber(newRow, column.get());
+ break; // 'f'
+
+ case 'g':
+ if (colType.EqualsLiteral("givenname"))
+ aDatabase->AddFirstName(newRow, column.get());
+
+ break; // 'g'
+
+ case 'h':
+ if (colType.EqualsLiteral("homephone"))
+ aDatabase->AddHomePhone(newRow, column.get());
+
+ else if (colType.EqualsLiteral("homestreet"))
+ aDatabase->AddHomeAddress(newRow, column.get());
+
+ else if (colType.EqualsLiteral("homeurl"))
+ aDatabase->AddWebPage2(newRow, column.get());
+ break; // 'h'
+
+ case 'l':
+ if (colType.EqualsLiteral("l") || colType.EqualsLiteral("locality"))
+ {
+ if (mStoreLocAsHome)
+ aDatabase->AddHomeCity(newRow, column.get());
+ else
+ aDatabase->AddWorkCity(newRow, column.get());
+ }
+ // labeledURI contains a URI and, optionally, a label
+ // This will remove the label and place the URI as the work URL
+ else if (colType.EqualsLiteral("labeleduri"))
+ {
+ int32_t index = column.FindChar(' ');
+ if (index != -1)
+ column.SetLength(index);
+
+ aDatabase->AddWebPage1(newRow, column.get());
+ }
+
+ break; // 'l'
+
+ case 'm':
+ if (colType.EqualsLiteral("mail"))
+ aDatabase->AddPrimaryEmail(newRow, column.get());
+
+ else if (colType.EqualsLiteral("member") && bIsList)
+ aDatabase->AddLdifListMember(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mobile"))
+ aDatabase->AddCellularNumber(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozilla_aimscreenname"))
+ aDatabase->AddAimScreenName(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillacustom1"))
+ aDatabase->AddCustom1(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillacustom2"))
+ aDatabase->AddCustom2(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillacustom3"))
+ aDatabase->AddCustom3(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillacustom4"))
+ aDatabase->AddCustom4(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillahomecountryname"))
+ aDatabase->AddHomeCountry(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillahomelocalityname"))
+ aDatabase->AddHomeCity(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillahomestate"))
+ aDatabase->AddHomeState(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillahomestreet"))
+ aDatabase->AddHomeAddress(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillahomestreet2"))
+ aDatabase->AddHomeAddress2(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillahomepostalcode"))
+ aDatabase->AddHomeZipCode(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillahomeurl"))
+ aDatabase->AddWebPage2(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillanickname"))
+ {
+ if (bIsList)
+ aDatabase->AddListNickName(newRow, column.get());
+ else
+ aDatabase->AddNickName(newRow, column.get());
+ }
+
+ else if (colType.EqualsLiteral("mozillasecondemail"))
+ aDatabase->Add2ndEmail(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillausehtmlmail"))
+ {
+ ToLowerCase(column);
+ if (-1 != column.Find("true"))
+ aDatabase->AddPreferMailFormat(newRow, nsIAbPreferMailFormat::html);
+ else if (-1 != column.Find("false"))
+ aDatabase->AddPreferMailFormat(newRow, nsIAbPreferMailFormat::plaintext);
+ else
+ aDatabase->AddPreferMailFormat(newRow, nsIAbPreferMailFormat::unknown);
+ }
+
+ else if (colType.EqualsLiteral("mozillaworkstreet2"))
+ aDatabase->AddWorkAddress2(newRow, column.get());
+
+ else if (colType.EqualsLiteral("mozillaworkurl"))
+ aDatabase->AddWebPage1(newRow, column.get());
+
+ break; // 'm'
+
+ case 'n':
+ if (colType.EqualsLiteral("notes"))
+ aDatabase->AddNotes(newRow, column.get());
+
+ else if (colType.EqualsLiteral("nscpaimscreenname") ||
+ colType.EqualsLiteral("nsaimid"))
+ aDatabase->AddAimScreenName(newRow, column.get());
+
+ break; // 'n'
+
+ case 'o':
+ if (colType.EqualsLiteral("objectclass"))
+ break;
+
+ else if (colType.EqualsLiteral("ou") || colType.EqualsLiteral("orgunit"))
+ aDatabase->AddDepartment(newRow, column.get());
+
+ else if (colType.EqualsLiteral("o")) // organization
+ aDatabase->AddCompany(newRow, column.get());
+
+ break; // 'o'
+
+ case 'p':
+ if (colType.EqualsLiteral("postalcode"))
+ {
+ if (mStoreLocAsHome)
+ aDatabase->AddHomeZipCode(newRow, column.get());
+ else
+ aDatabase->AddWorkZipCode(newRow, column.get());
+ }
+
+ else if (colType.EqualsLiteral("postofficebox"))
+ {
+ nsAutoCString workAddr1, workAddr2;
+ SplitCRLFAddressField(column, workAddr1, workAddr2);
+ aDatabase->AddWorkAddress(newRow, workAddr1.get());
+ aDatabase->AddWorkAddress2(newRow, workAddr2.get());
+ }
+ else if (colType.EqualsLiteral("pager") || colType.EqualsLiteral("pagerphone"))
+ aDatabase->AddPagerNumber(newRow, column.get());
+
+ break; // 'p'
+
+ case 'r':
+ if (colType.EqualsLiteral("region"))
+ {
+ aDatabase->AddWorkState(newRow, column.get());
+ }
+
+ break; // 'r'
+
+ case 's':
+ if (colType.EqualsLiteral("sn") || colType.EqualsLiteral("surname"))
+ aDatabase->AddLastName(newRow, column.get());
+
+ else if (colType.EqualsLiteral("street"))
+ aDatabase->AddWorkAddress(newRow, column.get());
+
+ else if (colType.EqualsLiteral("streetaddress"))
+ {
+ nsAutoCString addr1, addr2;
+ SplitCRLFAddressField(column, addr1, addr2);
+ if (mStoreLocAsHome)
+ {
+ aDatabase->AddHomeAddress(newRow, addr1.get());
+ aDatabase->AddHomeAddress2(newRow, addr2.get());
+ }
+ else
+ {
+ aDatabase->AddWorkAddress(newRow, addr1.get());
+ aDatabase->AddWorkAddress2(newRow, addr2.get());
+ }
+ }
+ else if (colType.EqualsLiteral("st"))
+ {
+ if (mStoreLocAsHome)
+ aDatabase->AddHomeState(newRow, column.get());
+ else
+ aDatabase->AddWorkState(newRow, column.get());
+ }
+
+ break; // 's'
+
+ case 't':
+ if (colType.EqualsLiteral("title"))
+ aDatabase->AddJobTitle(newRow, column.get());
+
+ else if (colType.EqualsLiteral("telephonenumber") )
+ {
+ aDatabase->AddWorkPhone(newRow, column.get());
+ }
+
+ break; // 't'
+
+ case 'u':
+
+ if (colType.EqualsLiteral("uniquemember") && bIsList)
+ aDatabase->AddLdifListMember(newRow, column.get());
+
+ break; // 'u'
+
+ case 'w':
+ if (colType.EqualsLiteral("workurl"))
+ aDatabase->AddWebPage1(newRow, column.get());
+
+ break; // 'w'
+
+ case 'x':
+ if (colType.EqualsLiteral("xmozillanickname"))
+ {
+ if (bIsList)
+ aDatabase->AddListNickName(newRow, column.get());
+ else
+ aDatabase->AddNickName(newRow, column.get());
+ }
+
+ else if (colType.EqualsLiteral("xmozillausehtmlmail"))
+ {
+ ToLowerCase(column);
+ if (-1 != column.Find("true"))
+ aDatabase->AddPreferMailFormat(newRow, nsIAbPreferMailFormat::html);
+ else if (-1 != column.Find("false"))
+ aDatabase->AddPreferMailFormat(newRow, nsIAbPreferMailFormat::plaintext);
+ else
+ aDatabase->AddPreferMailFormat(newRow, nsIAbPreferMailFormat::unknown);
+ }
+
+ break; // 'x'
+
+ case 'z':
+ if (colType.EqualsLiteral("zip")) // alias for postalcode
+ {
+ if (mStoreLocAsHome)
+ aDatabase->AddHomeZipCode(newRow, column.get());
+ else
+ aDatabase->AddWorkZipCode(newRow, column.get());
+ }
+
+ break; // 'z'
+
+ default:
+ break; // default
+ }
+}
+
+void nsAbLDIFService::ClearLdifRecordBuffer()
+{
+ if (!mLdifLine.IsEmpty())
+ {
+ mLdifLine.Truncate();
+ mLFCount = 0;
+ mCRCount = 0;
+ }
+}
+
+// Some common ldif fields, it an ldif file has NONE of these entries
+// then it is most likely NOT an ldif file!
+static const char *const sLDIFFields[] = {
+ "objectclass",
+ "sn",
+ "dn",
+ "cn",
+ "givenName",
+ "mail",
+ nullptr
+};
+#define kMaxLDIFLen 14
+
+// Count total number of legal ldif fields and records in the first 100 lines of the
+// file and if the average legal ldif field is 3 or higher than it's a valid ldif file.
+NS_IMETHODIMP nsAbLDIFService::IsLDIFFile(nsIFile *pSrc, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(pSrc);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = false;
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIInputStream> fileStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), pSrc);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILineInputStream> lineInputStream(do_QueryInterface(fileStream, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t lineLen = 0;
+ int32_t lineCount = 0;
+ int32_t ldifFields = 0; // total number of legal ldif fields.
+ char field[kMaxLDIFLen];
+ int32_t fLen = 0;
+ const char *pChar;
+ int32_t recCount = 0; // total number of records.
+ int32_t i;
+ bool gotLDIF = false;
+ bool more = true;
+ nsCString line;
+
+ while (more && NS_SUCCEEDED(rv) && (lineCount < 100))
+ {
+ rv = lineInputStream->ReadLine(line, &more);
+
+ if (NS_SUCCEEDED(rv) && more)
+ {
+ pChar = line.get();
+ lineLen = line.Length();
+ if (!lineLen && gotLDIF)
+ {
+ recCount++;
+ gotLDIF = false;
+ }
+
+ if (lineLen && (*pChar != ' ') && (*pChar != '\t'))
+ {
+ fLen = 0;
+
+ while (lineLen && (fLen < (kMaxLDIFLen - 1)) && (*pChar != ':'))
+ {
+ field[fLen] = *pChar;
+ pChar++;
+ fLen++;
+ lineLen--;
+ }
+
+ field[fLen] = 0;
+
+ if (lineLen && (*pChar == ':') && (fLen < (kMaxLDIFLen - 1)))
+ {
+ // see if this is an ldif field (case insensitive)?
+ i = 0;
+ while (sLDIFFields[i])
+ {
+ if (!PL_strcasecmp( sLDIFFields[i], field))
+ {
+ ldifFields++;
+ gotLDIF = true;
+ break;
+ }
+ i++;
+ }
+ }
+ }
+ }
+ lineCount++;
+ }
+
+ // If we just saw ldif address, increment recCount.
+ if (gotLDIF)
+ recCount++;
+
+ rv = fileStream->Close();
+
+ if (recCount > 1)
+ ldifFields /= recCount;
+
+ // If the average field number >= 3 then it's a good ldif file.
+ if (ldifFields >= 3)
+ {
+ *_retval = true;
+ }
+
+ return rv;
+}
+
+void nsAbLDIFService::SplitCRLFAddressField(nsCString &inputAddress, nsCString &outputLine1, nsCString &outputLine2) const
+{
+ int32_t crlfPos = inputAddress.Find("\r\n");
+ if (crlfPos != -1)
+ {
+ outputLine1 = Substring(inputAddress, 0, crlfPos);
+ outputLine2 = Substring(inputAddress, crlfPos + 2);
+ }
+ else
+ outputLine1.Assign(inputAddress);
+}
+
diff --git a/mailnews/addrbook/src/nsAbLDIFService.h b/mailnews/addrbook/src/nsAbLDIFService.h
new file mode 100644
index 000000000..8f50559c9
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDIFService.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef __nsAbLDIFService_h
+#define __nsAbLDIFService_h
+
+#include "nsIAbLDIFService.h"
+#include "nsCOMPtr.h"
+
+class nsIMdbRow;
+
+class nsAbLDIFService : public nsIAbLDIFService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABLDIFSERVICE
+
+ nsAbLDIFService();
+private:
+ virtual ~nsAbLDIFService();
+ nsresult str_parse_line(char *line, char **type, char **value, int *vlen) const;
+ char * str_getline(char **next) const;
+ nsresult GetLdifStringRecord(char* buf, int32_t len, int32_t& stopPos);
+ void AddLdifRowToDatabase(nsIAddrDatabase *aDatabase, bool aIsList);
+ void AddLdifColToDatabase(nsIAddrDatabase *aDatabase, nsIMdbRow* newRow,
+ char* typeSlot, char* valueSlot, bool bIsList);
+ void ClearLdifRecordBuffer();
+ void SplitCRLFAddressField(nsCString &inputAddress, nsCString &outputLine1, nsCString &outputLine2) const;
+
+ bool mStoreLocAsHome;
+ nsCString mLdifLine;
+ int32_t mLFCount;
+ int32_t mCRCount;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbMDBCard.cpp b/mailnews/addrbook/src/nsAbMDBCard.cpp
new file mode 100644
index 000000000..14af6a875
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbMDBCard.cpp
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbMDBCard.h"
+
+nsAbMDBCard::nsAbMDBCard(void)
+{
+}
+
+nsAbMDBCard::~nsAbMDBCard(void)
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsAbMDBCard, nsAbCardProperty)
+
+NS_IMETHODIMP nsAbMDBCard::Equals(nsIAbCard *card, bool *result)
+{
+ NS_ENSURE_ARG_POINTER(card);
+ NS_ENSURE_ARG_POINTER(result);
+
+ if (this == card) {
+ *result = true;
+ return NS_OK;
+ }
+
+ // If we have the same directory, we will equal the other card merely given
+ // the row IDs. If not, we are never equal. But we are dumb in that we don't
+ // know who our directory is, which may change in the future. For now,
+ // however, the only known users of this method are for locating us in a list
+ // of cards, most commonly mailing lists; a warning on the IDL has also
+ // notified consumers that this method is not generally safe to use. In this
+ // respect, it is safe to assume that the directory portion is satisfied when
+ // making this call.
+ // However, if we make the wrong assumption, one of two things will happen.
+ // If the other directory is a local address book, we could return a spurious
+ // true result. If not, then DbRowID should be unset and we can definitively
+ // return false.
+
+ uint32_t row;
+ nsresult rv = card->GetPropertyAsUint32("DbRowID", &row);
+ if (NS_FAILED(rv))
+ {
+ *result = false;
+ return NS_OK;
+ }
+
+ uint32_t ourRow;
+ rv = GetPropertyAsUint32("DbRowID", &ourRow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *result = (row == ourRow);
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbMDBCard.h b/mailnews/addrbook/src/nsAbMDBCard.h
new file mode 100644
index 000000000..0d9123bc4
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbMDBCard.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbMDBCard_h__
+#define nsAbMDBCard_h__
+
+#include "mozilla/Attributes.h"
+#include "nsAbCardProperty.h"
+#include "nsCOMPtr.h"
+
+class nsAbMDBCard: public nsAbCardProperty
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsAbMDBCard(void);
+
+ NS_IMETHOD Equals(nsIAbCard *card, bool *result) override;
+
+private:
+ virtual ~nsAbMDBCard();
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbMDBDirFactory.cpp b/mailnews/addrbook/src/nsAbMDBDirFactory.cpp
new file mode 100644
index 000000000..73ecb32fc
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbMDBDirFactory.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbMDBDirFactory.h"
+#include "nsAbUtils.h"
+#include "nsStringGlue.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIFile.h"
+#include "nsIAbManager.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsAbMDBDirFactory.h"
+#include "nsIAddrDBListener.h"
+#include "nsIAddrDatabase.h"
+#include "nsEnumeratorUtils.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "nsAbBaseCID.h"
+
+NS_IMPL_ISUPPORTS(nsAbMDBDirFactory, nsIAbDirFactory)
+
+nsAbMDBDirFactory::nsAbMDBDirFactory()
+{
+}
+
+nsAbMDBDirFactory::~nsAbMDBDirFactory()
+{
+}
+
+NS_IMETHODIMP nsAbMDBDirFactory::GetDirectories(const nsAString &aDirName,
+ const nsACString &aURI,
+ const nsACString &aPrefName,
+ nsISimpleEnumerator **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsresult rv;
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(aURI, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = directory->SetDirPrefId(aPrefName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> dbPath;
+ rv = abManager->GetUserProfileDirectory(getter_AddRefs(dbPath));
+
+ nsCOMPtr<nsIAddrDatabase> listDatabase;
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString fileName;
+
+ if (StringBeginsWith(aURI, NS_LITERAL_CSTRING(kMDBDirectoryRoot)))
+ fileName = Substring(aURI, kMDBDirectoryRootLen, aURI.Length() - kMDBDirectoryRootLen);
+
+ rv = dbPath->AppendNative(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAddrDatabase> addrDBFactory = do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = addrDBFactory->Open(dbPath, true, true, getter_AddRefs(listDatabase));
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = listDatabase->GetMailingListsFromDB(directory);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_NewSingletonEnumerator(_retval, directory);
+}
+
+/* void deleteDirectory (in nsIAbDirectory directory); */
+NS_IMETHODIMP nsAbMDBDirFactory::DeleteDirectory(nsIAbDirectory *directory)
+{
+ if (!directory)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIMutableArray> pAddressLists;
+ rv = directory->GetAddressLists(getter_AddRefs(pAddressLists));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t total;
+ rv = pAddressLists->GetLength(&total);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t i = 0; i < total; i++)
+ {
+ nsCOMPtr<nsIAbDirectory> listDir(do_QueryElementAt(pAddressLists, i, &rv));
+ if (NS_FAILED(rv))
+ break;
+
+ nsCOMPtr<nsIAbMDBDirectory> dblistDir(do_QueryInterface(listDir, &rv));
+ if (NS_FAILED(rv))
+ break;
+
+ rv = directory->DeleteDirectory(listDir);
+ if (NS_FAILED(rv))
+ break;
+
+ rv = dblistDir->RemoveElementsFromAddressList();
+ if (NS_FAILED(rv))
+ break;
+ }
+ pAddressLists->Clear();
+
+ nsCOMPtr<nsIAbMDBDirectory> dbdirectory(do_QueryInterface(directory, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return dbdirectory->ClearDatabase();
+}
+
diff --git a/mailnews/addrbook/src/nsAbMDBDirFactory.h b/mailnews/addrbook/src/nsAbMDBDirFactory.h
new file mode 100644
index 000000000..200175f62
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbMDBDirFactory.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbMDBDirFactory_h__
+#define nsAbMDBDirFactory_h__
+
+#include "nsIAbDirFactory.h"
+
+class nsAbMDBDirFactory : public nsIAbDirFactory
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABDIRFACTORY
+
+ nsAbMDBDirFactory();
+
+private:
+ virtual ~nsAbMDBDirFactory();
+};
+
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbMDBDirProperty.cpp b/mailnews/addrbook/src/nsAbMDBDirProperty.cpp
new file mode 100644
index 000000000..7df904d87
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbMDBDirProperty.cpp
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbMDBDirProperty.h"
+#include "nsIServiceManager.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsAbBaseCID.h"
+#include "nsAddrDatabase.h"
+#include "nsIAbCard.h"
+#include "nsIAbListener.h"
+#include "nsArrayUtils.h"
+#include "mdb.h"
+#include "nsComponentManagerUtils.h"
+
+nsAbMDBDirProperty::nsAbMDBDirProperty(void)
+ : nsAbDirProperty()
+{
+ m_dbRowID = 0;
+}
+
+nsAbMDBDirProperty::~nsAbMDBDirProperty(void)
+{
+}
+
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAbMDBDirProperty, nsAbDirProperty,
+ nsIAbDirectory,
+ nsISupportsWeakReference, nsIAbMDBDirectory)
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+// nsIAbMDBDirectory attributes
+
+NS_IMETHODIMP nsAbMDBDirProperty::GetDbRowID(uint32_t *aDbRowID)
+{
+ *aDbRowID = m_dbRowID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirProperty::SetDbRowID(uint32_t aDbRowID)
+{
+ m_dbRowID = aDbRowID;
+ return NS_OK;
+}
+
+
+// nsIAbMDBDirectory methods
+
+/* add mailing list to the parent directory */
+NS_IMETHODIMP nsAbMDBDirProperty::AddMailListToDirectory(nsIAbDirectory *mailList)
+{
+ if (!m_AddressList)
+ {
+ nsresult rv;
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ uint32_t position;
+ if (NS_FAILED(m_AddressList->IndexOf(0, mailList, &position)))
+ m_AddressList->AppendElement(mailList, false);
+
+ return NS_OK;
+}
+
+/* add addresses to the mailing list */
+NS_IMETHODIMP nsAbMDBDirProperty::AddAddressToList(nsIAbCard *card)
+{
+ if (!m_AddressList)
+ {
+ nsresult rv;
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ uint32_t position;
+ if (NS_FAILED(m_AddressList->IndexOf(0, card, &position)))
+ m_AddressList->AppendElement(card, false);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirProperty::CopyDBMailList(nsIAbMDBDirectory* srcListDB)
+{
+ nsresult err = NS_OK;
+ nsCOMPtr<nsIAbDirectory> srcList(do_QueryInterface(srcListDB));
+ if (NS_FAILED(err))
+ return NS_ERROR_NULL_POINTER;
+
+ CopyMailList (srcList);
+
+ uint32_t rowID;
+ srcListDB->GetDbRowID(&rowID);
+ SetDbRowID(rowID);
+
+ return NS_OK;
+}
+
+
+// nsIAbMDBDirectory NOT IMPLEMENTED methods
+
+/* nsIAbDirectory addDirectory (in string uriName); */
+NS_IMETHODIMP nsAbMDBDirProperty::AddDirectory(const char *uriName, nsIAbDirectory **_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* [noscript] void removeElementsFromAddressList (); */
+NS_IMETHODIMP nsAbMDBDirProperty::RemoveElementsFromAddressList()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void removeEmailAddressAt (in unsigned long aIndex); */
+NS_IMETHODIMP nsAbMDBDirProperty::RemoveEmailAddressAt(uint32_t aIndex)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* [noscript] void notifyDirItemAdded (in nsISupports item); */
+NS_IMETHODIMP nsAbMDBDirProperty::NotifyDirItemAdded(nsISupports *item)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* [noscript] void clearDatabase (); */
+NS_IMETHODIMP nsAbMDBDirProperty::ClearDatabase()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbMDBDirProperty::GetDatabaseFile(nsIFile **aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbMDBDirProperty::GetDatabase(nsIAddrDatabase **aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/mailnews/addrbook/src/nsAbMDBDirProperty.h b/mailnews/addrbook/src/nsAbMDBDirProperty.h
new file mode 100644
index 000000000..3ee90d17e
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbMDBDirProperty.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/********************************************************************************************************
+
+ Interface for representing Address Book Directory
+
+*********************************************************************************************************/
+
+#ifndef nsAbMDBDirProperty_h__
+#define nsAbMDBDirProperty_h__
+
+#include "nsIAbMDBDirectory.h"
+#include "nsAbDirProperty.h"
+#include "nsIAbCard.h"
+#include "nsCOMPtr.h"
+#include "nsDirPrefs.h"
+#include "nsIAddrDatabase.h"
+
+ /*
+ * Address Book Directory
+ */
+
+class nsAbMDBDirProperty: public nsIAbMDBDirectory, public nsAbDirProperty
+{
+public:
+ nsAbMDBDirProperty(void);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABMDBDIRECTORY
+
+protected:
+ virtual ~nsAbMDBDirProperty();
+
+ uint32_t m_dbRowID;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbMDBDirectory.cpp b/mailnews/addrbook/src/nsAbMDBDirectory.cpp
new file mode 100644
index 000000000..be4799cf1
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbMDBDirectory.cpp
@@ -0,0 +1,1125 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbMDBDirectory.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsAbBaseCID.h"
+#include "nsAddrDatabase.h"
+#include "nsIAbListener.h"
+#include "nsIAbManager.h"
+#include "nsIURL.h"
+#include "nsNetCID.h"
+#include "nsAbDirectoryQuery.h"
+#include "nsIAbDirectoryQueryProxy.h"
+#include "nsAbQueryStringToExpression.h"
+#include "nsIMutableArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsEnumeratorUtils.h"
+#include "mdb.h"
+#include "prprf.h"
+#include "nsIPrefService.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIFile.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMemory.h"
+#include "nsArrayUtils.h"
+#include "nsUnicharUtils.h"
+#include "mozilla/DebugOnly.h"
+
+nsAbMDBDirectory::nsAbMDBDirectory(void):
+ nsAbMDBDirProperty(),
+ mPerformingQuery(false)
+{
+}
+
+nsAbMDBDirectory::~nsAbMDBDirectory(void)
+{
+ if (mDatabase) {
+ mDatabase->RemoveListener(this);
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAbMDBDirectory, nsAbMDBDirProperty,
+ nsIAbDirSearchListener,
+ nsIAbDirectorySearch,
+ nsIAddrDBListener)
+
+NS_IMETHODIMP nsAbMDBDirectory::Init(const char *aUri)
+{
+ // We need to ensure that the m_DirPrefId is initialized properly
+ nsDependentCString uri(aUri);
+
+ // Find the first ? (of the search params) if there is one.
+ // We know we can start at the end of the moz-abmdbdirectory:// because
+ // that's the URI we should have been passed.
+ int32_t searchCharLocation = uri.FindChar('?', kMDBDirectoryRootLen);
+ nsAutoCString URINoQuery;
+ if (searchCharLocation != kNotFound)
+ {
+ URINoQuery = Substring(uri, 0, searchCharLocation);
+ } else {
+ URINoQuery.Assign(uri);
+ }
+
+ // In the non-query part of the URI, check if we are a mailinglist
+ if (URINoQuery.Find("MailList") != kNotFound)
+ m_IsMailList = true;
+
+ // Mailing lists don't have their own prefs.
+ if (m_DirPrefId.IsEmpty() && !m_IsMailList)
+ {
+ nsAutoCString filename;
+
+ // Extract the filename from the uri.
+ filename = Substring(URINoQuery, kMDBDirectoryRootLen);
+
+ // Get the pref servers and the address book directory branch
+ nsresult rv;
+ nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ rv = prefService->GetBranch(NS_LITERAL_CSTRING(PREF_LDAP_SERVER_TREE_NAME ".").get(),
+ getter_AddRefs(prefBranch));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char** childArray;
+ uint32_t childCount, i;
+ int32_t dotOffset;
+ nsCString childValue;
+ nsDependentCString child;
+
+ rv = prefBranch->GetChildList("", &childCount, &childArray);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (i = 0; i < childCount; ++i)
+ {
+ child.Assign(childArray[i]);
+
+ if (StringEndsWith(child, NS_LITERAL_CSTRING(".filename")))
+ {
+ if (NS_SUCCEEDED(prefBranch->GetCharPref(child.get(),
+ getter_Copies(childValue))))
+ {
+ if (childValue == filename)
+ {
+ dotOffset = child.RFindChar('.');
+ if (dotOffset != -1)
+ {
+ nsAutoCString prefName(StringHead(child, dotOffset));
+ m_DirPrefId.AssignLiteral(PREF_LDAP_SERVER_TREE_NAME ".");
+ m_DirPrefId.Append(prefName);
+ }
+ }
+ }
+ }
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(childCount, childArray);
+
+ NS_ASSERTION(!m_DirPrefId.IsEmpty(),
+ "Error, Could not set m_DirPrefId in nsAbMDBDirectory::Init");
+ }
+
+ return nsAbDirProperty::Init(aUri);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult nsAbMDBDirectory::RemoveCardFromAddressList(nsIAbCard* card)
+{
+ nsresult rv = NS_OK;
+ uint32_t listTotal;
+ int32_t i, j;
+
+ // These checks ensure we don't run into null pointers
+ // as we did when we caused bug 280463.
+ if (!mDatabase)
+ {
+ rv = GetAbDatabase();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (!m_AddressList)
+ {
+ rv = mDatabase->GetMailingListsFromDB(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If the previous call didn't gives us an m_AddressList (and succeeded)
+ // then we haven't got any mailing lists to try and remove the card from.
+ // So just return without doing anything
+ if (!m_AddressList)
+ return NS_OK;
+ }
+
+ rv = m_AddressList->GetLength(&listTotal);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ for (i = listTotal - 1; i >= 0; i--)
+ {
+ nsCOMPtr<nsIAbDirectory> listDir(do_QueryElementAt(m_AddressList, i, &rv));
+ if (listDir)
+ {
+ // First remove the instance in the database
+ mDatabase->DeleteCardFromMailList(listDir, card, false);
+
+ // Now remove the instance in any lists we hold.
+ nsCOMPtr<nsIMutableArray> pAddressLists;
+ listDir->GetAddressLists(getter_AddRefs(pAddressLists));
+ if (pAddressLists)
+ {
+ uint32_t total;
+ rv = pAddressLists->GetLength(&total);
+ for (j = total - 1; j >= 0; j--)
+ {
+ nsCOMPtr<nsIAbCard> cardInList(do_QueryElementAt(pAddressLists, j, &rv));
+ bool equals;
+ rv = cardInList->Equals(card, &equals); // should we checking email?
+ if (NS_SUCCEEDED(rv) && equals)
+ pAddressLists->RemoveElementAt(j);
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::DeleteDirectory(nsIAbDirectory *directory)
+{
+ NS_ENSURE_ARG_POINTER(directory);
+
+ nsCOMPtr<nsIAddrDatabase> database;
+ nsresult rv = GetDatabase(getter_AddRefs(database));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = database->DeleteMailList(directory, this);
+
+ if (NS_SUCCEEDED(rv))
+ database->Commit(nsAddrDBCommitType::kLargeCommit);
+
+ uint32_t dirIndex;
+ if (m_AddressList && NS_SUCCEEDED(m_AddressList->IndexOf(0, directory, &dirIndex)))
+ m_AddressList->RemoveElementAt(dirIndex);
+ // XXX Cast from bool to nsresult
+ rv = static_cast<nsresult>(mSubDirectories.RemoveObject(directory));
+
+ NotifyItemDeleted(directory);
+ return rv;
+}
+
+nsresult nsAbMDBDirectory::NotifyItemChanged(nsISupports *item)
+{
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = abManager->NotifyItemPropertyChanged(item, nullptr, nullptr, nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+nsresult nsAbMDBDirectory::NotifyPropertyChanged(nsIAbDirectory *list, const char *property, const char16_t* oldValue, const char16_t* newValue)
+{
+ nsresult rv;
+ nsCOMPtr<nsISupports> supports = do_QueryInterface(list, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = abManager->NotifyItemPropertyChanged(supports, property, oldValue, newValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+nsresult nsAbMDBDirectory::NotifyItemAdded(nsISupports *item)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ if(NS_SUCCEEDED(rv))
+ abManager->NotifyDirectoryItemAdded(this, item);
+ return NS_OK;
+}
+
+nsresult nsAbMDBDirectory::NotifyItemDeleted(nsISupports *item)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ if(NS_SUCCEEDED(rv))
+ abManager->NotifyDirectoryItemDeleted(this, item);
+
+ return NS_OK;
+}
+
+// nsIAbMDBDirectory methods
+
+NS_IMETHODIMP nsAbMDBDirectory::ClearDatabase()
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ if (mDatabase)
+ {
+ mDatabase->RemoveListener(this);
+ mDatabase = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::RemoveElementsFromAddressList()
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ if (m_AddressList)
+ {
+ uint32_t count;
+ mozilla::DebugOnly<nsresult> rv = m_AddressList->GetLength(&count);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
+ int32_t i;
+ for (i = count - 1; i >= 0; i--)
+ m_AddressList->RemoveElementAt(i);
+ }
+ m_AddressList = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::RemoveEmailAddressAt(uint32_t aIndex)
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ if (m_AddressList)
+ {
+ return m_AddressList->RemoveElementAt(aIndex);
+ }
+ else
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::AddDirectory(const char *uriName, nsIAbDirectory **childDir)
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ if (!childDir || !uriName)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mURI.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(nsDependentCString(uriName), getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mSubDirectories.IndexOf(directory) == -1)
+ mSubDirectories.AppendObject(directory);
+ NS_IF_ADDREF(*childDir = directory);
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::GetDatabaseFile(nsIFile **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsCString fileName;
+ nsresult rv = GetStringValue("filename", EmptyCString(), fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (fileName.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsCOMPtr<nsIFile> dbFile;
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(dbFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = dbFile->AppendNative(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ADDREF(*aResult = dbFile);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::GetDatabase(nsIAddrDatabase **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> databaseFile;
+ rv = GetDatabaseFile(getter_AddRefs(databaseFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAddrDatabase> addrDBFactory =
+ do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return addrDBFactory->Open(databaseFile, false /* no create */, true,
+ aResult);
+}
+
+// nsIAbDirectory methods
+
+NS_IMETHODIMP nsAbMDBDirectory::GetURI(nsACString &aURI)
+{
+ if (mURI.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ aURI = mURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::GetChildNodes(nsISimpleEnumerator* *aResult)
+{
+ if (mIsQueryURI)
+ return NS_NewEmptyEnumerator(aResult);
+
+ return NS_NewArrayEnumerator(aResult, mSubDirectories);
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::GetChildCards(nsISimpleEnumerator* *result)
+{
+ nsresult rv;
+
+ if (mIsQueryURI)
+ {
+ rv = StartSearch();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // TODO
+ // Search is synchronous so need to return
+ // results after search is complete
+ nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ for (auto iter = mSearchCache.Iter(); !iter.Done(); iter.Next()) {
+ array->AppendElement(iter.Data(), false);
+ }
+ return NS_NewArrayEnumerator(result, array);
+ }
+
+ rv = GetAbDatabase();
+
+ if (NS_FAILED(rv) || !mDatabase)
+ return rv;
+
+ return m_IsMailList ? mDatabase->EnumerateListAddresses(this, result) :
+ mDatabase->EnumerateCards(this, result);
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::GetIsQuery(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mIsQueryURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::DeleteCards(nsIArray *aCards)
+{
+ NS_ENSURE_ARG_POINTER(aCards);
+ nsresult rv = NS_OK;
+
+ if (mIsQueryURI) {
+ // if this is a query, delete the cards from the directory (without the query)
+ // before we do the delete, make this directory (which represents the search)
+ // a listener on the database, so that it will get notified when the cards are deleted
+ // after delete, remove this query as a listener.
+ nsCOMPtr<nsIAddrDatabase> database;
+ rv = GetDatabase(getter_AddRefs(database));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = database->AddListener(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbManager> abManager =
+ do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(mURINoQuery, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = directory->DeleteCards(aCards);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = database->RemoveListener(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+ }
+
+ if (!mDatabase)
+ rv = GetAbDatabase();
+
+ if (NS_SUCCEEDED(rv) && mDatabase)
+ {
+ uint32_t cardCount;
+ uint32_t i;
+ rv = aCards->GetLength(&cardCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ for (i = 0; i < cardCount; i++)
+ {
+ nsCOMPtr<nsIAbCard> card(do_QueryElementAt(aCards, i, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (card)
+ {
+ uint32_t rowID;
+ rv = card->GetPropertyAsUint32("DbRowID", &rowID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (m_IsMailList)
+ {
+ mDatabase->DeleteCardFromMailList(this, card, true);
+
+ uint32_t cardTotal = 0;
+ int32_t i;
+ if (m_AddressList)
+ rv = m_AddressList->GetLength(&cardTotal);
+ for (i = cardTotal - 1; i >= 0; i--)
+ {
+ nsCOMPtr<nsIAbCard> arrayCard(do_QueryElementAt(m_AddressList, i, &rv));
+ if (arrayCard)
+ {
+ // No card can have a row ID of 0
+ uint32_t arrayRowID = 0;
+ arrayCard->GetPropertyAsUint32("DbRowID", &arrayRowID);
+ if (rowID == arrayRowID)
+ m_AddressList->RemoveElementAt(i);
+ }
+ }
+ }
+ else
+ {
+ mDatabase->DeleteCard(card, true, this);
+ bool bIsMailList = false;
+ card->GetIsMailList(&bIsMailList);
+ if (bIsMailList)
+ {
+ //to do, get mailing list dir side uri and notify nsIAbManager to remove it
+ nsAutoCString listUri(mURI);
+ listUri.AppendLiteral("/MailList");
+ listUri.AppendInt(rowID);
+ if (!listUri.IsEmpty())
+ {
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIAbManager> abManager =
+ do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> listDir;
+ rv = abManager->GetDirectory(listUri, getter_AddRefs(listDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t dirIndex;
+ if (m_AddressList && NS_SUCCEEDED(m_AddressList->IndexOf(0, listDir, &dirIndex)))
+ m_AddressList->RemoveElementAt(dirIndex);
+
+ mSubDirectories.RemoveObject(listDir);
+
+ if (listDir)
+ NotifyItemDeleted(listDir);
+ }
+ }
+ else
+ {
+ rv = RemoveCardFromAddressList(card);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ }
+ }
+ mDatabase->Commit(nsAddrDBCommitType::kLargeCommit);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::HasCard(nsIAbCard *cards, bool *hasCard)
+{
+ if(!hasCard)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mIsQueryURI)
+ {
+ *hasCard = mSearchCache.Get(cards, nullptr);
+ return NS_OK;
+ }
+
+ nsresult rv = NS_OK;
+ if (!mDatabase)
+ rv = GetAbDatabase();
+
+ if(NS_SUCCEEDED(rv) && mDatabase)
+ {
+ if(NS_SUCCEEDED(rv))
+ rv = mDatabase->ContainsCard(cards, hasCard);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::HasDirectory(nsIAbDirectory *dir, bool *hasDir)
+{
+ if (!hasDir)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbdir(do_QueryInterface(dir, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool bIsMailingList = false;
+ dir->GetIsMailList(&bIsMailingList);
+ if (bIsMailingList)
+ {
+ nsCOMPtr<nsIAddrDatabase> database;
+ rv = GetDatabase(getter_AddRefs(database));
+
+ if (NS_SUCCEEDED(rv))
+ rv = database->ContainsMailList(dir, hasDir);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::HasMailListWithName(const char16_t *aName, bool *aHasList)
+{
+ NS_ENSURE_ARG_POINTER(aHasList);
+
+ nsCOMPtr<nsIAddrDatabase> database;
+ nsresult rv = GetDatabase(getter_AddRefs(database));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = database->FindMailListbyUnicodeName(aName, aHasList);
+ if (NS_SUCCEEDED(rv) && *aHasList)
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::AddMailList(nsIAbDirectory *list, nsIAbDirectory **addedList)
+{
+ NS_ENSURE_ARG_POINTER(addedList);
+
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ nsresult rv = NS_OK;
+ if (!mDatabase)
+ rv = GetAbDatabase();
+
+ if (NS_FAILED(rv) || !mDatabase)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIAbMDBDirectory> dblist(do_QueryInterface(list, &rv));
+ if (NS_FAILED(rv))
+ {
+ nsCOMPtr<nsIAbDirectory> newlist(new nsAbMDBDirProperty);
+ if (!newlist)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = newlist->CopyMailList(list);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ dblist = do_QueryInterface(newlist, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mDatabase->CreateMailListAndAddToDB(newlist, true, this);
+ }
+ else
+ mDatabase->CreateMailListAndAddToDB(list, true, this);
+
+ mDatabase->Commit(nsAddrDBCommitType::kLargeCommit);
+
+ uint32_t dbRowID;
+ dblist->GetDbRowID(&dbRowID);
+
+ nsAutoCString listUri(mURI);
+ listUri.AppendLiteral("/MailList");
+ listUri.AppendInt(dbRowID);
+
+ nsCOMPtr<nsIAbDirectory> newList;
+ rv = AddDirectory(listUri.get(), getter_AddRefs(newList));
+ if (NS_SUCCEEDED(rv) && newList)
+ {
+ nsCOMPtr<nsIAbMDBDirectory> dbnewList(do_QueryInterface(newList, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ dbnewList->CopyDBMailList(dblist);
+ AddMailListToDirectory(newList);
+ NotifyItemAdded(newList);
+ }
+
+ NS_IF_ADDREF(*addedList = newList);
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::AddCard(nsIAbCard* card, nsIAbCard **addedCard)
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ nsresult rv = NS_OK;
+ if (!mDatabase)
+ rv = GetAbDatabase();
+
+ if (NS_FAILED(rv) || !mDatabase)
+ return NS_ERROR_FAILURE;
+
+ if (m_IsMailList)
+ rv = mDatabase->CreateNewListCardAndAddToDB(this, m_dbRowID, card, true /* notify */);
+ else
+ rv = mDatabase->CreateNewCardAndAddToDB(card, true, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mDatabase->Commit(nsAddrDBCommitType::kLargeCommit);
+
+ NS_IF_ADDREF(*addedCard = card);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::ModifyCard(nsIAbCard *aModifiedCard)
+{
+ NS_ENSURE_ARG_POINTER(aModifiedCard);
+
+ nsresult rv;
+ if (!mDatabase)
+ {
+ rv = GetAbDatabase();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = mDatabase->EditCard(aModifiedCard, true, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return mDatabase->Commit(nsAddrDBCommitType::kLargeCommit);
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::DropCard(nsIAbCard* aCard, bool needToCopyCard)
+{
+ NS_ENSURE_ARG_POINTER(aCard);
+
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ nsresult rv = NS_OK;
+
+ if (!mDatabase)
+ rv = GetAbDatabase();
+
+ if (NS_FAILED(rv) || !mDatabase)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIAbCard> newCard;
+
+ if (needToCopyCard) {
+ newCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = newCard->Copy(aCard);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ newCard = aCard;
+ }
+
+ if (m_IsMailList) {
+ if (needToCopyCard) {
+ nsCOMPtr <nsIMdbRow> cardRow;
+ // if card doesn't exist in db, add the card to the directory that
+ // contains the mailing list.
+ mDatabase->FindRowByCard(newCard, getter_AddRefs(cardRow));
+ if (!cardRow)
+ mDatabase->CreateNewCardAndAddToDB(newCard, true /* notify */, this);
+ else
+ mDatabase->InitCardFromRow(newCard, cardRow);
+ }
+ // since we didn't copy the card, we don't have to notify that it was inserted
+ mDatabase->CreateNewListCardAndAddToDB(this, m_dbRowID, newCard, false /* notify */);
+ }
+ else {
+ mDatabase->CreateNewCardAndAddToDB(newCard, true /* notify */, this);
+ }
+ mDatabase->Commit(nsAddrDBCommitType::kLargeCommit);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::EditMailListToDatabase(nsIAbCard *listCard)
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ if (!m_IsMailList)
+ return NS_ERROR_UNEXPECTED;
+
+ nsresult rv = GetAbDatabase();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mDatabase->EditMailList(this, listCard, true);
+ mDatabase->Commit(nsAddrDBCommitType::kLargeCommit);
+
+ return NS_OK;
+}
+
+static bool ContainsDirectory(nsIAbDirectory *parent, nsIAbDirectory *directory)
+{
+ // If parent is a maillist, 'addressLists' contains AbCards.
+ bool bIsMailList = false;
+ nsresult rv = parent->GetIsMailList(&bIsMailList);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (bIsMailList)
+ return false;
+
+ nsCOMPtr<nsIMutableArray> pAddressLists;
+ parent->GetAddressLists(getter_AddRefs(pAddressLists));
+ if (pAddressLists)
+ {
+ uint32_t total;
+ rv = pAddressLists->GetLength(&total);
+ for (uint32_t i = 0; i < total; ++i)
+ {
+ nsCOMPtr<nsIAbDirectory> pList(do_QueryElementAt(pAddressLists, i, &rv));
+
+ if (directory == pList)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// nsIAddrDBListener methods
+
+NS_IMETHODIMP nsAbMDBDirectory::OnCardAttribChange(uint32_t abCode)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::OnCardEntryChange
+(uint32_t aAbCode, nsIAbCard *aCard, nsIAbDirectory *aParent)
+{
+ // Don't notify AbManager unless we have the parent
+ if (!aParent)
+ return NS_OK;
+
+ NS_ENSURE_ARG_POINTER(aCard);
+ nsCOMPtr<nsISupports> cardSupports(do_QueryInterface(aCard));
+ nsresult rv;
+
+ // Notify when
+ // - any operation is done to a card belonging to this
+ // => if <this> is <aParent>, or
+ // - a card belonging to a directory which is parent of this is deleted
+ // => if aAbCode is AB_NotifyDeleted && <this> is child of <aParent>, or
+ // - a card belonging to a directory which is child of this is added/modified
+ // => if aAbCode is !AB_NotifyDeleted && <this> is parent of <aParent>
+
+ if (aParent != this)
+ {
+ bool isChild = false;
+ if (aAbCode != AB_NotifyDeleted)
+ isChild = ContainsDirectory(this, aParent);
+ else
+ isChild = ContainsDirectory(aParent, this);
+
+ if (!isChild)
+ return NS_OK;
+ }
+
+ switch (aAbCode) {
+ case AB_NotifyInserted:
+ rv = NotifyItemAdded(cardSupports);
+ break;
+ case AB_NotifyDeleted:
+ rv = NotifyItemDeleted(cardSupports);
+ break;
+ case AB_NotifyPropertyChanged:
+ rv = NotifyItemChanged(cardSupports);
+ break;
+ default:
+ rv = NS_ERROR_UNEXPECTED;
+ break;
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::OnListEntryChange
+(uint32_t abCode, nsIAbDirectory *list)
+{
+ nsresult rv = NS_OK;
+
+ if (abCode == AB_NotifyPropertyChanged && list)
+ {
+ bool bIsMailList = false;
+ rv = list->GetIsMailList(&bIsMailList);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIAbMDBDirectory> dblist(do_QueryInterface(list, &rv));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (bIsMailList) {
+ nsString listName;
+ rv = list->GetDirName(listName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = NotifyPropertyChanged(list, "DirName", nullptr, listName.get());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::OnAnnouncerGoingAway()
+{
+ if (mDatabase)
+ mDatabase->RemoveListener(this);
+ return NS_OK;
+}
+
+// nsIAbDirectorySearch methods
+
+NS_IMETHODIMP nsAbMDBDirectory::StartSearch()
+{
+ if (!mIsQueryURI)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+
+ mPerformingQuery = true;
+ mSearchCache.Clear();
+
+ nsCOMPtr<nsIAbDirectoryQueryArguments> arguments = do_CreateInstance(NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID,&rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbBooleanExpression> expression;
+ rv = nsAbQueryStringToExpression::Convert(mQueryString,
+ getter_AddRefs(expression));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = arguments->SetExpression(expression);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // don't search the subdirectories
+ // if the current directory is a mailing list, it won't have any subdirectories
+ // if the current directory is a addressbook, searching both it
+ // and the subdirectories (the mailing lists), will yield duplicate results
+ // because every entry in a mailing list will be an entry in the parent addressbook
+ rv = arguments->SetQuerySubDirectories(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbManager> abManager =
+ do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the directory without the query
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(mURINoQuery, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Bug 280232 - something was causing continuous loops in searching. Add a
+ // check here for the directory to search not being a query uri as well in
+ // the hopes that will at least break us out of the continuous loop even if
+ // we don't know how we got into it.
+ bool isQuery;
+ rv = directory->GetIsQuery(&isQuery);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isQuery)
+ {
+ NS_ERROR("Attempting to search a directory within a search");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Initiate the proxy query with the no query directory
+ nsCOMPtr<nsIAbDirectoryQueryProxy> queryProxy =
+ do_CreateInstance(NS_ABDIRECTORYQUERYPROXY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = queryProxy->Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = queryProxy->DoQuery(directory, arguments, this, -1, 0, &mContext);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::StopSearch()
+{
+ if (!mIsQueryURI)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+// nsAbDirSearchListenerContext methods
+
+NS_IMETHODIMP nsAbMDBDirectory::OnSearchFinished(int32_t aResult,
+ const nsAString &aErrorMsg)
+{
+ mPerformingQuery = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::OnSearchFoundCard(nsIAbCard* card)
+{
+ mSearchCache.Put(card, card);
+
+ // TODO
+ // Search is synchronous so asserting on the
+ // datasource will not work since the getChildCards
+ // method will not have returned with results.
+ // NotifyItemAdded (card);
+ return NS_OK;
+}
+
+nsresult nsAbMDBDirectory::GetAbDatabase()
+{
+ if (mURI.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (mDatabase)
+ return NS_OK;
+
+ nsresult rv;
+
+ if (m_IsMailList)
+ {
+ // Get the database of the parent directory.
+ nsAutoCString parentURI(mURINoQuery);
+
+ int32_t pos = parentURI.RFindChar('/');
+
+ // If we didn't find a / something really bad has happened
+ if (pos == -1)
+ return NS_ERROR_FAILURE;
+
+ parentURI = StringHead(parentURI, pos);
+
+ nsCOMPtr<nsIAbManager> abManager =
+ do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(parentURI, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbMDBDirectory> mdbDir(do_QueryInterface(directory, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mdbDir->GetDatabase(getter_AddRefs(mDatabase));
+ }
+ else
+ rv = GetDatabase(getter_AddRefs(mDatabase));
+
+ if (NS_SUCCEEDED(rv))
+ rv = mDatabase->AddListener(this);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::CardForEmailAddress(const nsACString &aEmailAddress, nsIAbCard ** aAbCard)
+{
+ NS_ENSURE_ARG_POINTER(aAbCard);
+
+ *aAbCard = nullptr;
+
+ // Ensure that if we've not been given an email address we never match
+ // so that we don't fail out unnecessarily and we don't match a blank email
+ // address against random cards that the user hasn't supplied an email for.
+ if (aEmailAddress.IsEmpty())
+ return NS_OK;
+
+ nsresult rv = NS_OK;
+ if (!mDatabase)
+ rv = GetAbDatabase();
+ if (rv == NS_ERROR_FILE_NOT_FOUND)
+ {
+ // If file wasn't found, the card cannot exist.
+ return NS_OK;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Convert Email to lower case in UTF-16 format. This correctly lower-cases
+ // it and doing this change means that we can use a hash lookup in the
+ // database rather than searching and comparing each line individually.
+ NS_ConvertUTF8toUTF16 lowerEmail(aEmailAddress);
+ ToLowerCase(lowerEmail);
+
+ // If lower email is empty, something went wrong somewhere, e.g. the conversion.
+ // Hence, don't go looking for a card with no email address. Something is wrong.
+ if (lowerEmail.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ mDatabase->GetCardFromAttribute(this, kLowerPriEmailColumn,
+ NS_ConvertUTF16toUTF8(lowerEmail),
+ false, aAbCard);
+ if (!*aAbCard)
+ {
+ mDatabase->GetCardFromAttribute(this, kLower2ndEmailColumn,
+ NS_ConvertUTF16toUTF8(lowerEmail),
+ false, aAbCard);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::GetCardFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool caseSensitive,
+ nsIAbCard **result)
+{
+ NS_ENSURE_ARG(aProperty);
+ NS_ENSURE_ARG_POINTER(result);
+
+ *result = nullptr;
+
+ // If the value is empty, don't match.
+ if (aValue.IsEmpty())
+ return NS_OK;
+
+ nsresult rv;
+ if (!mDatabase)
+ {
+ rv = GetAbDatabase();
+ // We can't find the database file, so we can't find the card at all.
+ if (rv == NS_ERROR_FILE_NOT_FOUND)
+ return NS_OK;
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // nsIAddrDatabase has aCaseInsensitive as its parameter
+ return mDatabase->GetCardFromAttribute(this, aProperty, aValue,
+ !caseSensitive, result);
+}
+
+NS_IMETHODIMP nsAbMDBDirectory::GetCardsFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool caseSensitive,
+ nsISimpleEnumerator **result)
+{
+ NS_ENSURE_ARG(aProperty);
+ NS_ENSURE_ARG_POINTER(result);
+
+ *result = nullptr;
+
+ if (aValue.IsEmpty())
+ return NS_OK;
+
+ if (!mDatabase)
+ {
+ nsresult rv = GetAbDatabase();
+ if (rv == NS_ERROR_FILE_NOT_FOUND)
+ return NS_OK;
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return mDatabase->GetCardsFromAttribute(this, aProperty, aValue,
+ !caseSensitive, result);
+}
diff --git a/mailnews/addrbook/src/nsAbMDBDirectory.h b/mailnews/addrbook/src/nsAbMDBDirectory.h
new file mode 100644
index 000000000..fb25c5708
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbMDBDirectory.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/********************************************************************************************************
+
+ Interface for representing Address Book Directory
+
+*********************************************************************************************************/
+
+#ifndef nsAbMDBDirectory_h__
+#define nsAbMDBDirectory_h__
+
+#include "mozilla/Attributes.h"
+#include "nsAbMDBDirProperty.h"
+#include "nsIAbCard.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsDirPrefs.h"
+#include "nsIAbDirectorySearch.h"
+#include "nsIAbDirSearchListener.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIAddrDBListener.h"
+
+/*
+ * Address Book Directory
+ */
+
+class nsAbMDBDirectory:
+ public nsAbMDBDirProperty, // nsIAbDirectory, nsIAbMDBDirectory
+ public nsIAbDirSearchListener,
+ public nsIAddrDBListener,
+ public nsIAbDirectorySearch
+{
+public:
+ nsAbMDBDirectory(void);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIADDRDBLISTENER
+
+ // Override nsAbMDBDirProperty::Init
+ NS_IMETHOD Init(const char *aUri) override;
+
+ // nsIAbMDBDirectory methods
+ NS_IMETHOD GetURI(nsACString &aURI) override;
+ NS_IMETHOD ClearDatabase() override;
+ NS_IMETHOD NotifyDirItemAdded(nsISupports *item) override { return NotifyItemAdded(item);}
+ NS_IMETHOD RemoveElementsFromAddressList() override;
+ NS_IMETHOD RemoveEmailAddressAt(uint32_t aIndex) override;
+ NS_IMETHOD AddDirectory(const char *uriName, nsIAbDirectory **childDir) override;
+ NS_IMETHOD GetDatabaseFile(nsIFile **aResult) override;
+ NS_IMETHOD GetDatabase(nsIAddrDatabase **aResult) override;
+
+ // nsIAbDirectory methods:
+ NS_IMETHOD GetChildNodes(nsISimpleEnumerator* *result) override;
+ NS_IMETHOD GetChildCards(nsISimpleEnumerator* *result) override;
+ NS_IMETHOD GetIsQuery(bool *aResult) override;
+ NS_IMETHOD DeleteDirectory(nsIAbDirectory *directory) override;
+ NS_IMETHOD DeleteCards(nsIArray *cards) override;
+ NS_IMETHOD HasCard(nsIAbCard *cards, bool *hasCard) override;
+ NS_IMETHOD HasDirectory(nsIAbDirectory *dir, bool *hasDir) override;
+ NS_IMETHOD HasMailListWithName(const char16_t *aName, bool *aHasList) override;
+ NS_IMETHOD AddMailList(nsIAbDirectory *list, nsIAbDirectory **addedList) override;
+ NS_IMETHOD AddCard(nsIAbCard *card, nsIAbCard **addedCard) override;
+ NS_IMETHOD ModifyCard(nsIAbCard *aModifiedCard) override;
+ NS_IMETHOD DropCard(nsIAbCard *card, bool needToCopyCard) override;
+ NS_IMETHOD EditMailListToDatabase(nsIAbCard *listCard) override;
+ NS_IMETHOD CardForEmailAddress(const nsACString &aEmailAddress,
+ nsIAbCard ** aAbCard) override;
+ NS_IMETHOD GetCardFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool caseSensitive, nsIAbCard **result) override;
+ NS_IMETHOD GetCardsFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool caseSensitive,
+ nsISimpleEnumerator **result) override;
+
+ // nsIAbDirectorySearch methods
+ NS_DECL_NSIABDIRECTORYSEARCH
+
+ // nsIAbDirSearchListener methods
+ NS_DECL_NSIABDIRSEARCHLISTENER
+
+protected:
+ virtual ~nsAbMDBDirectory();
+ nsresult NotifyPropertyChanged(nsIAbDirectory *list, const char *property, const char16_t* oldValue, const char16_t* newValue);
+ nsresult NotifyItemAdded(nsISupports *item);
+ nsresult NotifyItemDeleted(nsISupports *item);
+ nsresult NotifyItemChanged(nsISupports *item);
+ nsresult RemoveCardFromAddressList(nsIAbCard* card);
+
+ nsresult GetAbDatabase();
+ nsCOMPtr<nsIAddrDatabase> mDatabase;
+
+ nsCOMArray<nsIAbDirectory> mSubDirectories;
+
+ int32_t mContext;
+ bool mPerformingQuery;
+
+ nsInterfaceHashtable<nsISupportsHashKey, nsIAbCard> mSearchCache;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbManager.cpp b/mailnews/addrbook/src/nsAbManager.cpp
new file mode 100644
index 000000000..2de1b4468
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbManager.cpp
@@ -0,0 +1,1422 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbManager.h"
+#include "nsAbBaseCID.h"
+#include "nsAddrDatabase.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsIOutputStream.h"
+#include "nsNetUtil.h"
+#include "nsMsgI18N.h"
+#include "nsIStringBundle.h"
+#include "nsMsgUtils.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "plstr.h"
+#include "prmem.h"
+#include "nsIServiceManager.h"
+#include "mozIDOMWindow.h"
+#include "nsIFilePicker.h"
+#include "plbase64.h"
+#include "nsIWindowWatcher.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsVCard.h"
+#include "nsVCardObj.h"
+#include "nsIAbLDAPAttributeMap.h"
+#include "nsICommandLine.h"
+#include "nsIFile.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIObserverService.h"
+#include "nsDirPrefs.h"
+#include "nsThreadUtils.h"
+#include "nsIAbDirFactory.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIIOService.h"
+#include "nsAbQueryStringToExpression.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Services.h"
+using namespace mozilla;
+
+struct ExportAttributesTableStruct
+{
+ const char* abPropertyName;
+ uint32_t plainTextStringID;
+};
+
+// our schema is not fixed yet, but we still want some sort of objectclass
+// for now, use obsolete in the class name, hinting that this will change
+// see bugs bug #116692 and #118454
+#define MOZ_AB_OBJECTCLASS "mozillaAbPersonAlpha"
+
+// for now, the oder of the attributes with true for includeForPlainText
+// should be in the same order as they are in the import code
+// see importMsgProperties and nsImportStringBundle.
+//
+// XXX todo, merge with what's in nsAbLDAPProperties.cpp, so we can
+// use this for LDAP and LDIF export
+//
+// here's how we're coming up with the ldapPropertyName values
+// if they are specified in RFC 2798, use them
+// else use the 4.x LDIF attribute names (for example, "xmozillanickname"
+// as we want to allow export from mozilla back to 4.x, and other apps
+// are probably out there that can handle 4.x LDIF)
+// else use the MOZ_AB_LDIF_PREFIX prefix, see nsIAddrDatabase.idl
+
+const ExportAttributesTableStruct EXPORT_ATTRIBUTES_TABLE[] = {
+ {kFirstNameProperty, 2100},
+ {kLastNameProperty, 2101},
+ {kDisplayNameProperty, 2102},
+ {kNicknameProperty, 2103},
+ {kPriEmailProperty, 2104},
+ {k2ndEmailProperty, 2105},
+ {kScreenNameProperty, 2136},
+ {kPreferMailFormatProperty, 0},
+ {kLastModifiedDateProperty, 0},
+ {kWorkPhoneProperty, 2106},
+ {kWorkPhoneTypeProperty, 0},
+ {kHomePhoneProperty, 2107},
+ {kHomePhoneTypeProperty, 0},
+ {kFaxProperty, 2108},
+ {kFaxTypeProperty, 0},
+ {kPagerProperty, 2109},
+ {kPagerTypeProperty, 0},
+ {kCellularProperty, 2110},
+ {kCellularTypeProperty, 0},
+ {kHomeAddressProperty, 2111},
+ {kHomeAddress2Property, 2112},
+ {kHomeCityProperty, 2113},
+ {kHomeStateProperty, 2114},
+ {kHomeZipCodeProperty, 2115},
+ {kHomeCountryProperty, 2116},
+ {kWorkAddressProperty, 2117},
+ {kWorkAddress2Property, 2118},
+ {kWorkCityProperty, 2119},
+ {kWorkStateProperty, 2120},
+ {kWorkZipCodeProperty, 2121},
+ {kWorkCountryProperty, 2122},
+ {kJobTitleProperty, 2123},
+ {kDepartmentProperty, 2124},
+ {kCompanyProperty, 2125},
+ {kWorkWebPageProperty, 2126},
+ {kHomeWebPageProperty, 2127},
+ {kBirthYearProperty, 2128}, // unused for now
+ {kBirthMonthProperty, 2129}, // unused for now
+ {kBirthDayProperty, 2130}, // unused for now
+ {kCustom1Property, 2131},
+ {kCustom2Property, 2132},
+ {kCustom3Property, 2133},
+ {kCustom4Property, 2134},
+ {kNotesProperty, 2135},
+ {kAnniversaryYearProperty, 0},
+ {kAnniversaryMonthProperty, 0},
+ {kAnniversaryDayProperty, 0},
+ {kSpouseNameProperty, 0},
+ {kFamilyNameProperty, 0},
+};
+
+//
+// nsAbManager
+//
+nsAbManager::nsAbManager()
+{
+}
+
+nsAbManager::~nsAbManager()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsAbManager, nsIAbManager, nsICommandLineHandler,
+ nsIObserver)
+
+nsresult nsAbManager::Init()
+{
+ NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED);
+
+ nsresult rv = observerService->AddObserver(this, "profile-do-change", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::Observe(nsISupports *aSubject, const char *aTopic,
+ const char16_t *someData)
+{
+ // The nsDirPrefs code caches all the directories that it got
+ // from the first profiles prefs.js.
+ // When we profile switch, we need to force it to shut down.
+ // We'll re-load all the directories from the second profiles prefs.js
+ // that happens in nsAbBSDirectory::GetChildNodes()
+ // when we call DIR_GetDirectories().
+ if (!strcmp(aTopic, "profile-do-change"))
+ {
+ DIR_ShutDown();
+ return NS_OK;
+ }
+
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID))
+ {
+ DIR_ShutDown();
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED);
+
+ nsresult rv = observerService->RemoveObserver(this, "profile-do-change");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+//
+// nsIAbManager
+//
+
+NS_IMETHODIMP nsAbManager::GetDirectories(nsISimpleEnumerator **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // We cache the top level AB to ensure that nsIAbDirectory items are not
+ // created and dumped every time GetDirectories is called. This was causing
+ // performance problems, especially with the content policy on messages
+ // with lots of urls.
+ nsresult rv;
+ nsCOMPtr<nsIAbDirectory> rootAddressBook;
+ rv = GetRootDirectory(getter_AddRefs(rootAddressBook));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return rootAddressBook->GetChildNodes(aResult);
+}
+
+nsresult
+nsAbManager::GetRootDirectory(nsIAbDirectory **aResult)
+{
+ // We cache the top level AB to ensure that nsIAbDirectory items are not
+ // created and dumped every time GetDirectories is called. This was causing
+ // performance problems, especially with the content policy on messages
+ // with lots of urls.
+ nsresult rv;
+
+ if (!mCacheTopLevelAb)
+ {
+ nsCOMPtr<nsIAbDirectory> rootAddressBook(do_GetService(NS_ABDIRECTORY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCacheTopLevelAb = rootAddressBook;
+ }
+
+ NS_IF_ADDREF(*aResult = mCacheTopLevelAb);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::GetDirectoryFromId(const nsACString &aDirPrefId,
+ nsIAbDirectory **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ nsresult rv = GetDirectories(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsISupports> support;
+ nsCOMPtr<nsIAbDirectory> directory;
+
+ bool hasMore = false;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
+ rv = enumerator->GetNext(getter_AddRefs(support));
+ NS_ENSURE_SUCCESS(rv, rv);
+ directory = do_QueryInterface(support, &rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Unable to select Address book nsAbManager::GetDirectoryFromId()");
+ continue;
+ }
+
+ nsCString dirPrefId;
+ directory->GetDirPrefId(dirPrefId);
+ if (dirPrefId.Equals(aDirPrefId)) {
+ directory.forget(aResult);
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::GetDirectory(const nsACString &aURI,
+ nsIAbDirectory **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsresult rv;
+ nsCOMPtr<nsIAbDirectory> directory;
+
+ // Was the directory root requested?
+ if (aURI.EqualsLiteral(kAllDirectoryRoot))
+ {
+ rv = GetRootDirectory(getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_IF_ADDREF(*aResult = directory);
+ return NS_OK;
+ }
+
+ // Do we have a copy of this directory already within our look-up table?
+ if (!mAbStore.Get(aURI, getter_AddRefs(directory)))
+ {
+ // The directory wasn't in our look-up table, so we need to instantiate
+ // it. First, extract the scheme from the URI...
+
+ nsAutoCString scheme;
+
+ int32_t colon = aURI.FindChar(':');
+ if (colon <= 0)
+ return NS_ERROR_MALFORMED_URI;
+ scheme = Substring(aURI, 0, colon);
+
+ // Construct the appropriate nsIAbDirectory...
+ nsAutoCString contractID;
+ contractID.AssignLiteral(NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX);
+ contractID.Append(scheme);
+ directory = do_CreateInstance(contractID.get(), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Init it with the URI
+ rv = directory->Init(PromiseFlatCString(aURI).get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Check if this directory was initiated with a search query. If so,
+ // we don't cache it.
+ bool isQuery = false;
+ rv = directory->GetIsQuery(&isQuery);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!isQuery)
+ mAbStore.Put(aURI, directory);
+ }
+ NS_IF_ADDREF(*aResult = directory);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::NewAddressBook(const nsAString &aDirName,
+ const nsACString &aURI,
+ const uint32_t aType,
+ const nsACString &aPrefName,
+ nsACString &aResult)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIAbDirectory> parentDir;
+ rv = GetRootDirectory(getter_AddRefs(parentDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return parentDir->CreateNewDirectory(aDirName, aURI, aType, aPrefName, aResult);
+}
+
+NS_IMETHODIMP nsAbManager::DeleteAddressBook(const nsACString &aURI)
+{
+ // Find the address book
+ nsresult rv;
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = GetDirectory(aURI, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> rootDirectory;
+ rv = GetRootDirectory(getter_AddRefs(rootDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Go through each of the children of the address book
+ // (so, the mailing lists) and remove their entries from
+ // the look up table.
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = directory->GetChildNodes(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> item;
+ nsCOMPtr<nsIAbDirectory> childDirectory;
+ bool hasMore = false;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ rv = enumerator->GetNext(getter_AddRefs(item));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ childDirectory = do_QueryInterface(item, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCString childURI;
+ rv = childDirectory->GetURI(childURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mAbStore.Remove(childURI);
+ }
+ }
+
+ mAbStore.Remove(aURI);
+
+ bool isMailList;
+ rv = directory->GetIsMailList(&isMailList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!isMailList)
+ // If we're not a mailing list, then our parent
+ // must be the root address book directory.
+ return rootDirectory->DeleteDirectory(directory);
+
+ nsCString parentUri;
+ parentUri.Append(aURI);
+ int32_t pos = parentUri.RFindChar('/');
+
+ // If we didn't find a /, we're in trouble.
+ if (pos == -1)
+ return NS_ERROR_FAILURE;
+
+ parentUri = StringHead(parentUri, pos);
+ nsCOMPtr<nsIAbDirectory> parentDirectory;
+ rv = GetDirectory(parentUri, getter_AddRefs(parentDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return parentDirectory->DeleteDirectory(directory);
+}
+
+NS_IMETHODIMP nsAbManager::AddAddressBookListener(nsIAbListener *aListener,
+ abListenerNotifyFlagValue aNotifyFlags)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ abListener newListener(aListener, aNotifyFlags);
+ mListeners.AppendElementUnlessExists(newListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::RemoveAddressBookListener(nsIAbListener *aListener)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ mListeners.RemoveElement(aListener);
+ return NS_OK;
+}
+
+#define NOTIFY_AB_LISTENERS(propertyflag_, propertyfunc_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<abListener>::ForwardIterator iter(mListeners); \
+ while (iter.HasMore()) { \
+ const abListener &abL = iter.GetNext(); \
+ if (abL.mNotifyFlags & nsIAbListener::propertyflag_) \
+ abL.mListener->propertyfunc_ params_; \
+ } \
+ PR_END_MACRO
+
+NS_IMETHODIMP nsAbManager::NotifyItemPropertyChanged(nsISupports *aItem,
+ const char *aProperty,
+ const char16_t* aOldValue,
+ const char16_t* aNewValue)
+{
+ NOTIFY_AB_LISTENERS(itemChanged, OnItemPropertyChanged,
+ (aItem, aProperty, aOldValue, aNewValue));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::NotifyDirectoryItemAdded(nsIAbDirectory *aParentDirectory,
+ nsISupports *aItem)
+{
+ NOTIFY_AB_LISTENERS(itemAdded, OnItemAdded, (aParentDirectory, aItem));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::NotifyDirectoryItemDeleted(nsIAbDirectory *aParentDirectory,
+ nsISupports *aItem)
+{
+ NOTIFY_AB_LISTENERS(directoryItemRemoved, OnItemRemoved,
+ (aParentDirectory, aItem));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::NotifyDirectoryDeleted(nsIAbDirectory *aParentDirectory,
+ nsISupports *aDirectory)
+{
+ NOTIFY_AB_LISTENERS(directoryRemoved, OnItemRemoved,
+ (aParentDirectory, aDirectory));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::GetUserProfileDirectory(nsIFile **userDir)
+{
+ NS_ENSURE_ARG_POINTER(userDir);
+ *userDir = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> profileDir;
+ nsAutoCString pathBuf;
+
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ profileDir.forget(userDir);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbManager::MailListNameExists(const char16_t *aName, bool *aExists)
+{
+ nsresult rv;
+ NS_ENSURE_ARG_POINTER(aExists);
+
+ *aExists = false;
+
+ // now get the top-level book
+ nsCOMPtr<nsIAbDirectory> topDirectory;
+ rv = GetRootDirectory(getter_AddRefs(topDirectory));
+
+ // now go through the address books
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = topDirectory->GetChildNodes(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> item;
+ rv = enumerator->GetNext(getter_AddRefs(item));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory = do_QueryInterface(item, &rv);
+ if (NS_FAILED(rv))
+ continue;
+
+ rv = directory->HasMailListWithName(aName, aExists);
+ if (NS_SUCCEEDED(rv) && *aExists)
+ return NS_OK;
+ }
+
+ *aExists = false;
+ return NS_OK;
+}
+
+#define CSV_DELIM ","
+#define CSV_DELIM_LEN 1
+#define TAB_DELIM "\t"
+#define TAB_DELIM_LEN 1
+
+#define CSV_FILE_EXTENSION ".csv"
+#define TAB_FILE_EXTENSION ".tab"
+#define TXT_FILE_EXTENSION ".txt"
+#define VCF_FILE_EXTENSION ".vcf"
+#define LDIF_FILE_EXTENSION ".ldi"
+#define LDIF_FILE_EXTENSION2 ".ldif"
+
+enum ADDRESSBOOK_EXPORT_FILE_TYPE
+{
+ CSV_EXPORT_TYPE = 0,
+ CSV_EXPORT_TYPE_UTF8 = 1,
+ TAB_EXPORT_TYPE = 2,
+ TAB_EXPORT_TYPE_UTF8 = 3,
+ VCF_EXPORT_TYPE = 4,
+ LDIF_EXPORT_TYPE = 5,
+};
+
+NS_IMETHODIMP nsAbManager::ExportAddressBook(mozIDOMWindowProxy *aParentWin, nsIAbDirectory *aDirectory)
+{
+ NS_ENSURE_ARG_POINTER(aParentWin);
+
+ nsresult rv;
+ nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString dirName;
+ aDirectory->GetDirName(dirName);
+ const char16_t *formatStrings[] = { dirName.get() };
+
+ nsString title;
+ rv = bundle->FormatStringFromName(u"ExportAddressBookNameTitle", formatStrings,
+ ArrayLength(formatStrings), getter_Copies(title));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = filePicker->Init(aParentWin, title, nsIFilePicker::modeSave);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ filePicker->SetDefaultString(dirName);
+
+ nsString filterString;
+
+ // CSV: System charset and UTF-8.
+ rv = bundle->GetStringFromName(u"CSVFilesSysCharset", getter_Copies(filterString));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.csv"));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = bundle->GetStringFromName(u"CSVFilesUTF8", getter_Copies(filterString));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.csv"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Tab separated: System charset and UTF-8.
+ rv = bundle->GetStringFromName(u"TABFilesSysCharset", getter_Copies(filterString));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.tab; *.txt"));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = bundle->GetStringFromName(u"TABFilesUTF8", getter_Copies(filterString));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.tab; *.txt"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = bundle->GetStringFromName(u"VCFFiles", getter_Copies(filterString));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.vcf"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = bundle->GetStringFromName(u"LDIFFiles", getter_Copies(filterString));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.ldi; *.ldif"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int16_t dialogResult;
+ filePicker->Show(&dialogResult);
+
+ if (dialogResult == nsIFilePicker::returnCancel)
+ return rv;
+
+ nsCOMPtr<nsIFile> localFile;
+ rv = filePicker->GetFile(getter_AddRefs(localFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (dialogResult == nsIFilePicker::returnReplace) {
+ // be extra safe and only delete when the file is really a file
+ bool isFile;
+ rv = localFile->IsFile(&isFile);
+ if (NS_SUCCEEDED(rv) && isFile) {
+ rv = localFile->Remove(false /* recursive delete */);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ // The type of export is determined by the drop-down in
+ // the file picker dialog.
+ int32_t exportType;
+ rv = filePicker->GetFilterIndex(&exportType);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsAutoString fileName;
+ rv = localFile->GetLeafName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ switch ( exportType )
+ {
+ default:
+ case LDIF_EXPORT_TYPE: // ldif
+ // If filename does not have the correct ext, add one.
+ if ((MsgFind(fileName, LDIF_FILE_EXTENSION, true, fileName.Length() - strlen(LDIF_FILE_EXTENSION)) == -1) &&
+ (MsgFind(fileName, LDIF_FILE_EXTENSION2, true, fileName.Length() - strlen(LDIF_FILE_EXTENSION2)) == -1)) {
+
+ // Add the extension and build a new localFile.
+ fileName.AppendLiteral(LDIF_FILE_EXTENSION2);
+ localFile->SetLeafName(fileName);
+ }
+ rv = ExportDirectoryToLDIF(aDirectory, localFile);
+ break;
+
+ case CSV_EXPORT_TYPE: // csv
+ case CSV_EXPORT_TYPE_UTF8:
+ // If filename does not have the correct ext, add one.
+ if (MsgFind(fileName, CSV_FILE_EXTENSION, true, fileName.Length() - strlen(CSV_FILE_EXTENSION)) == -1) {
+
+ // Add the extension and build a new localFile.
+ fileName.AppendLiteral(CSV_FILE_EXTENSION);
+ localFile->SetLeafName(fileName);
+ }
+ rv = ExportDirectoryToDelimitedText(aDirectory, CSV_DELIM, CSV_DELIM_LEN, localFile,
+ exportType==CSV_EXPORT_TYPE_UTF8);
+ break;
+
+ case TAB_EXPORT_TYPE: // tab & text
+ case TAB_EXPORT_TYPE_UTF8:
+ // If filename does not have the correct ext, add one.
+ if ((MsgFind(fileName, TXT_FILE_EXTENSION, true, fileName.Length() - strlen(TXT_FILE_EXTENSION)) == -1) &&
+ (MsgFind(fileName, TAB_FILE_EXTENSION, true, fileName.Length() - strlen(TAB_FILE_EXTENSION)) == -1)) {
+
+ // Add the extension and build a new localFile.
+ fileName.AppendLiteral(TXT_FILE_EXTENSION);
+ localFile->SetLeafName(fileName);
+ }
+ rv = ExportDirectoryToDelimitedText(aDirectory, TAB_DELIM, TAB_DELIM_LEN, localFile,
+ exportType==TAB_EXPORT_TYPE_UTF8);
+ break;
+
+ case VCF_EXPORT_TYPE: // vCard
+ // If filename does not have the correct ext, add one.
+ if (MsgFind(fileName, VCF_FILE_EXTENSION, true, fileName.Length() - strlen(VCF_FILE_EXTENSION)) == -1) {
+
+ // Add the extension and build a new localFile.
+ fileName.AppendLiteral(VCF_FILE_EXTENSION);
+ localFile->SetLeafName(fileName);
+ }
+ rv = ExportDirectoryToVCard(aDirectory, localFile);
+ break;
+ };
+
+ return rv;
+}
+
+nsresult
+nsAbManager::ExportDirectoryToDelimitedText(nsIAbDirectory *aDirectory,
+ const char *aDelim,
+ uint32_t aDelimLen,
+ nsIFile *aLocalFile,
+ bool useUTF8)
+{
+ nsCOMPtr <nsISimpleEnumerator> cardsEnumerator;
+ nsCOMPtr <nsIAbCard> card;
+
+ nsresult rv;
+
+ nsCOMPtr <nsIOutputStream> outputStream;
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream),
+ aLocalFile,
+ PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
+ 0664);
+
+ // the desired file may be read only
+ if (NS_FAILED(rv))
+ return rv;
+
+ uint32_t i;
+ uint32_t writeCount;
+ uint32_t length;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/importMsgs.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString revisedName;
+ nsString columnName;
+
+ for (i = 0; i < ArrayLength(EXPORT_ATTRIBUTES_TABLE); i++) {
+ if (EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID != 0) {
+
+ // We don't need to truncate the string here as getter_Copies will
+ // do that for us.
+ if (NS_FAILED(bundle->GetStringFromID(EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID, getter_Copies(columnName))))
+ columnName.AppendInt(EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID);
+
+ rv = nsMsgI18NConvertFromUnicode(useUTF8 ? "UTF-8" : nsMsgI18NFileSystemCharset(),
+ columnName, revisedName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = outputStream->Write(revisedName.get(),
+ revisedName.Length(),
+ &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (revisedName.Length() != writeCount)
+ return NS_ERROR_FAILURE;
+
+ if (i < ArrayLength(EXPORT_ATTRIBUTES_TABLE) - 1) {
+ rv = outputStream->Write(aDelim, aDelimLen, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (aDelimLen != writeCount)
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+ rv = outputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (MSG_LINEBREAK_LEN != writeCount)
+ return NS_ERROR_FAILURE;
+
+ rv = aDirectory->GetChildCards(getter_AddRefs(cardsEnumerator));
+ if (NS_SUCCEEDED(rv) && cardsEnumerator) {
+ nsCOMPtr<nsISupports> item;
+ bool more;
+ while (NS_SUCCEEDED(cardsEnumerator->HasMoreElements(&more)) && more) {
+ rv = cardsEnumerator->GetNext(getter_AddRefs(item));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr <nsIAbCard> card = do_QueryInterface(item, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool isMailList;
+ rv = card->GetIsMailList(&isMailList);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+
+ if (isMailList) {
+ // .tab, .txt and .csv aren't able to export mailing lists
+ // use LDIF for that.
+ }
+ else {
+ nsString value;
+ nsCString valueCStr;
+
+ for (i = 0; i < ArrayLength(EXPORT_ATTRIBUTES_TABLE); i++) {
+ if (EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID != 0) {
+ rv = card->GetPropertyAsAString(EXPORT_ATTRIBUTES_TABLE[i].abPropertyName, value);
+ if (NS_FAILED(rv))
+ value.Truncate();
+
+ // If a string contains at least one comma, tab or double quote then
+ // we need to quote the entire string. Also if double quote is part
+ // of the string we need to quote the double quote(s) as well.
+ nsAutoString newValue(value);
+ bool needsQuotes = false;
+ if(newValue.FindChar('"') != -1)
+ {
+ needsQuotes = true;
+
+ int32_t match = 0;
+ uint32_t offset = 0;
+ nsString oldSubstr = NS_LITERAL_STRING("\"");
+ nsString newSubstr = NS_LITERAL_STRING("\"\"");
+ while (offset < newValue.Length()) {
+ match = newValue.Find(oldSubstr, offset);
+ if (match == -1)
+ break;
+
+ newValue.Replace(offset + match, oldSubstr.Length(), newSubstr);
+ offset += (match + newSubstr.Length());
+ }
+ }
+ if (!needsQuotes && (newValue.FindChar(',') != -1 || newValue.FindChar('\x09') != -1))
+ needsQuotes = true;
+
+ // Make sure we quote if containing CR/LF.
+ if (newValue.FindChar('\r') != -1 ||
+ newValue.FindChar('\n') != -1)
+ needsQuotes = true;
+
+ if (needsQuotes)
+ {
+ newValue.Insert(NS_LITERAL_STRING("\""), 0);
+ newValue.AppendLiteral("\"");
+ }
+
+ rv = nsMsgI18NConvertFromUnicode(useUTF8 ? "UTF-8" : nsMsgI18NFileSystemCharset(),
+ newValue, valueCStr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (NS_FAILED(rv)) {
+ NS_ERROR("failed to convert string to system charset. use LDIF");
+ valueCStr = "?";
+ }
+
+ length = valueCStr.Length();
+ if (length) {
+ rv = outputStream->Write(valueCStr.get(), length, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (length != writeCount)
+ return NS_ERROR_FAILURE;
+ }
+ valueCStr = "";
+ }
+ else {
+ // something we don't support for the current export
+ // for example, .tab doesn't export preferred html format
+ continue; // go to next field
+ }
+
+ if (i < ArrayLength(EXPORT_ATTRIBUTES_TABLE) - 1) {
+ rv = outputStream->Write(aDelim, aDelimLen, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (aDelimLen != writeCount)
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // write out the linebreak that separates the cards
+ rv = outputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (MSG_LINEBREAK_LEN != writeCount)
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+ }
+
+ rv = outputStream->Flush();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = outputStream->Close();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return NS_OK;
+}
+
+nsresult
+nsAbManager::ExportDirectoryToVCard(nsIAbDirectory *aDirectory, nsIFile *aLocalFile)
+{
+ nsCOMPtr <nsISimpleEnumerator> cardsEnumerator;
+ nsCOMPtr <nsIAbCard> card;
+
+ nsresult rv;
+
+ nsCOMPtr <nsIOutputStream> outputStream;
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream),
+ aLocalFile,
+ PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
+ 0664);
+
+ // the desired file may be read only
+ if (NS_FAILED(rv))
+ return rv;
+
+ uint32_t writeCount;
+ uint32_t length;
+
+ rv = aDirectory->GetChildCards(getter_AddRefs(cardsEnumerator));
+ if (NS_SUCCEEDED(rv) && cardsEnumerator) {
+ nsCOMPtr<nsISupports> item;
+ bool more;
+ while (NS_SUCCEEDED(cardsEnumerator->HasMoreElements(&more)) && more) {
+ rv = cardsEnumerator->GetNext(getter_AddRefs(item));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr <nsIAbCard> card = do_QueryInterface(item, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool isMailList;
+ rv = card->GetIsMailList(&isMailList);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (isMailList) {
+ // we don't know how to export mailing lists to vcf
+ // use LDIF for that.
+ }
+ else {
+ nsCString escapedValue;
+ rv = card->TranslateTo(NS_LITERAL_CSTRING("vcard"), escapedValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCString valueCStr;
+ MsgUnescapeString(escapedValue, 0, valueCStr);
+
+ length = valueCStr.Length();
+ rv = outputStream->Write(valueCStr.get(), length, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (length != writeCount)
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+ }
+
+ rv = outputStream->Flush();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = outputStream->Close();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return NS_OK;
+}
+
+
+nsresult
+nsAbManager::ExportDirectoryToLDIF(nsIAbDirectory *aDirectory, nsIFile *aLocalFile)
+{
+ nsCOMPtr <nsISimpleEnumerator> cardsEnumerator;
+ nsCOMPtr <nsIAbCard> card;
+
+ nsresult rv;
+
+ nsCOMPtr <nsIOutputStream> outputStream;
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream),
+ aLocalFile,
+ PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
+ 0664);
+
+ // the desired file may be read only
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Get the default attribute map for ldap. We use the default attribute
+ // map rather than one for a specific server because if people want an
+ // ldif export using a servers specific schema, then they can use ldapsearch
+ nsCOMPtr<nsIAbLDAPAttributeMapService> mapSrv =
+ do_GetService("@mozilla.org/addressbook/ldap-attribute-map-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbLDAPAttributeMap> attrMap;
+ rv = mapSrv->GetMapForPrefBranch(NS_LITERAL_CSTRING("ldap_2.servers.default.attrmap"),
+ getter_AddRefs(attrMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t i;
+ uint32_t writeCount;
+ uint32_t length;
+
+ rv = aDirectory->GetChildCards(getter_AddRefs(cardsEnumerator));
+ if (NS_SUCCEEDED(rv) && cardsEnumerator) {
+ nsCOMPtr<nsISupports> item;
+ bool more;
+ while (NS_SUCCEEDED(cardsEnumerator->HasMoreElements(&more)) && more) {
+ rv = cardsEnumerator->GetNext(getter_AddRefs(item));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr <nsIAbCard> card = do_QueryInterface(item, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool isMailList;
+ rv = card->GetIsMailList(&isMailList);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (isMailList) {
+ nsCString mailListCStr;
+
+ rv = AppendLDIFForMailList(card, attrMap, mailListCStr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ length = mailListCStr.Length();
+ rv = outputStream->Write(mailListCStr.get(), length, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (length != writeCount)
+ return NS_ERROR_FAILURE;
+ }
+ else {
+ nsString value;
+ nsCString valueCStr;
+
+ rv = AppendBasicLDIFForCard(card, attrMap, valueCStr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ length = valueCStr.Length();
+ rv = outputStream->Write(valueCStr.get(), length, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (length != writeCount)
+ return NS_ERROR_FAILURE;
+
+ valueCStr.Truncate();
+
+ nsAutoCString ldapAttribute;
+
+ for (i = 0; i < ArrayLength(EXPORT_ATTRIBUTES_TABLE); i++) {
+ if (NS_SUCCEEDED(attrMap->GetFirstAttribute(nsDependentCString(EXPORT_ATTRIBUTES_TABLE[i].abPropertyName),
+ ldapAttribute)) &&
+ !ldapAttribute.IsEmpty()) {
+
+ rv = card->GetPropertyAsAString(EXPORT_ATTRIBUTES_TABLE[i].abPropertyName, value);
+ if (NS_FAILED(rv))
+ value.Truncate();
+
+ if (!PL_strcmp(EXPORT_ATTRIBUTES_TABLE[i].abPropertyName, kPreferMailFormatProperty)) {
+ if (value.EqualsLiteral("html"))
+ value.AssignLiteral("true");
+ else if (value.EqualsLiteral("plaintext"))
+ value.AssignLiteral("false");
+ else
+ value.Truncate(); // unknown.
+ }
+
+ if (!value.IsEmpty()) {
+ rv = AppendProperty(ldapAttribute.get(), value.get(), valueCStr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ valueCStr += MSG_LINEBREAK;
+ }
+ else
+ valueCStr.Truncate();
+
+ length = valueCStr.Length();
+ if (length) {
+ rv = outputStream->Write(valueCStr.get(), length, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (length != writeCount)
+ return NS_ERROR_FAILURE;
+ }
+ valueCStr.Truncate();
+ }
+ else {
+ // something we don't support yet
+ // ldif doesn't export multiple addresses
+ }
+ }
+
+ // write out the linebreak that separates the cards
+ rv = outputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &writeCount);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (MSG_LINEBREAK_LEN != writeCount)
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+ }
+
+ rv = outputStream->Flush();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = outputStream->Close();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return NS_OK;
+}
+
+nsresult nsAbManager::AppendLDIFForMailList(nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult)
+{
+ nsresult rv;
+ nsString attrValue;
+
+ rv = AppendDNForCard("dn", aCard, aAttrMap, aResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ aResult += MSG_LINEBREAK \
+ "objectclass: top" MSG_LINEBREAK \
+ "objectclass: groupOfNames" MSG_LINEBREAK;
+
+ rv = aCard->GetDisplayName(attrValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsAutoCString ldapAttributeName;
+
+ rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kDisplayNameProperty),
+ ldapAttributeName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = AppendProperty(ldapAttributeName.get(), attrValue.get(), aResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+ aResult += MSG_LINEBREAK;
+
+ rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kNicknameProperty),
+ ldapAttributeName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aCard->GetPropertyAsAString(kNicknameProperty, attrValue);
+ if (NS_SUCCEEDED(rv) && !attrValue.IsEmpty()) {
+ rv = AppendProperty(ldapAttributeName.get(), attrValue.get(), aResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+ aResult += MSG_LINEBREAK;
+ }
+
+ rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kNotesProperty),
+ ldapAttributeName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aCard->GetPropertyAsAString(kNotesProperty, attrValue);
+ if (NS_SUCCEEDED(rv) && !attrValue.IsEmpty()) {
+ rv = AppendProperty(ldapAttributeName.get(), attrValue.get(), aResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+ aResult += MSG_LINEBREAK;
+ }
+
+ nsCString mailListURI;
+ rv = aCard->GetMailListURI(getter_Copies(mailListURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIAbDirectory> mailList;
+ rv = GetDirectory(mailListURI, getter_AddRefs(mailList));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIMutableArray> addresses;
+ rv = mailList->GetAddressLists(getter_AddRefs(addresses));
+ if (addresses) {
+ uint32_t total = 0;
+ addresses->GetLength(&total);
+ if (total) {
+ uint32_t i;
+ for (i = 0; i < total; i++) {
+ nsCOMPtr <nsIAbCard> listCard = do_QueryElementAt(addresses, i, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AppendDNForCard("member", listCard, aAttrMap, aResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ aResult += MSG_LINEBREAK;
+ }
+ }
+ }
+
+ aResult += MSG_LINEBREAK;
+ return NS_OK;
+}
+
+nsresult nsAbManager::AppendDNForCard(const char *aProperty, nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult)
+{
+ nsString email;
+ nsString displayName;
+ nsAutoCString ldapAttributeName;
+
+ nsresult rv = aCard->GetPrimaryEmail(email);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = aCard->GetDisplayName(displayName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsString cnStr;
+
+ rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kDisplayNameProperty),
+ ldapAttributeName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!displayName.IsEmpty()) {
+ cnStr += NS_ConvertUTF8toUTF16(ldapAttributeName).get();
+ cnStr.AppendLiteral("=");
+ cnStr.Append(displayName);
+ if (!email.IsEmpty()) {
+ cnStr.AppendLiteral(",");
+ }
+ }
+
+ rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kPriEmailProperty),
+ ldapAttributeName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!email.IsEmpty()) {
+ cnStr += NS_ConvertUTF8toUTF16(ldapAttributeName).get();
+ cnStr.AppendLiteral("=");
+ cnStr.Append(email);
+ }
+
+ rv = AppendProperty(aProperty, cnStr.get(), aResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+nsresult nsAbManager::AppendBasicLDIFForCard(nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult)
+{
+ nsresult rv = AppendDNForCard("dn", aCard, aAttrMap, aResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+ aResult += MSG_LINEBREAK \
+ "objectclass: top" MSG_LINEBREAK \
+ "objectclass: person" MSG_LINEBREAK \
+ "objectclass: organizationalPerson" MSG_LINEBREAK \
+ "objectclass: inetOrgPerson" MSG_LINEBREAK \
+ "objectclass: " MOZ_AB_OBJECTCLASS MSG_LINEBREAK;
+ return rv;
+}
+
+bool nsAbManager::IsSafeLDIFString(const char16_t *aStr)
+{
+ // follow RFC 2849 to determine if something is safe "as is" for LDIF
+ if (aStr[0] == char16_t(' ') ||
+ aStr[0] == char16_t(':') ||
+ aStr[0] == char16_t('<'))
+ return false;
+
+ uint32_t i;
+ uint32_t len = NS_strlen(aStr);
+ for (i=0; i<len; i++) {
+ // If string contains CR or LF, it is not safe for LDIF
+ // and MUST be base64 encoded
+ if ((aStr[i] == char16_t('\n')) ||
+ (aStr[i] == char16_t('\r')) ||
+ (!NS_IsAscii(aStr[i])))
+ return false;
+ }
+ return true;
+}
+
+nsresult nsAbManager::AppendProperty(const char *aProperty, const char16_t *aValue, nsACString &aResult)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ aResult += aProperty;
+
+ // if the string is not safe "as is", base64 encode it
+ if (IsSafeLDIFString(aValue)) {
+ aResult.AppendLiteral(": ");
+ aResult.Append(NS_LossyConvertUTF16toASCII(aValue));
+ }
+ else {
+ char *base64Str = PL_Base64Encode(NS_ConvertUTF16toUTF8(aValue).get(), 0, nullptr);
+ if (!base64Str)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ aResult.AppendLiteral(":: ");
+ aResult.Append(nsDependentCString(base64Str));
+ PR_Free(base64Str);
+ }
+
+ return NS_OK;
+}
+
+char *getCString(VObject *vObj)
+{
+ if (VALUE_TYPE(vObj) == VCVT_USTRINGZ)
+ return fakeCString(vObjectUStringZValue(vObj));
+ if (VALUE_TYPE(vObj) == VCVT_STRINGZ)
+ return PL_strdup(vObjectStringZValue(vObj));
+ return NULL;
+}
+
+static void convertNameValue(VObject *vObj, nsIAbCard *aCard)
+{
+ const char *cardPropName = NULL;
+
+ // if the vCard property is not a root property then we need to determine its exact property.
+ // a good example of this is VCTelephoneProp, this prop has four objects underneath it:
+ // fax, work and home and cellular.
+ if (PL_strcasecmp(VCCityProp, vObjectName(vObj)) == 0)
+ cardPropName = kWorkCityProperty;
+ else if (PL_strcasecmp(VCTelephoneProp, vObjectName(vObj)) == 0)
+ {
+ if (isAPropertyOf(vObj, VCFaxProp))
+ cardPropName = kFaxProperty;
+ else if (isAPropertyOf(vObj, VCWorkProp))
+ cardPropName = kWorkPhoneProperty;
+ else if (isAPropertyOf(vObj, VCHomeProp))
+ cardPropName = kHomePhoneProperty;
+ else if (isAPropertyOf(vObj, VCCellularProp))
+ cardPropName = kCellularProperty;
+ else if (isAPropertyOf(vObj, VCPagerProp))
+ cardPropName = kPagerProperty;
+ else
+ return;
+ }
+ else if (PL_strcasecmp(VCEmailAddressProp, vObjectName(vObj)) == 0)
+ cardPropName = kPriEmailProperty;
+ else if (PL_strcasecmp(VCFamilyNameProp, vObjectName(vObj)) == 0)
+ cardPropName = kLastNameProperty;
+ else if (PL_strcasecmp(VCFullNameProp, vObjectName(vObj)) == 0)
+ cardPropName = kDisplayNameProperty;
+ else if (PL_strcasecmp(VCGivenNameProp, vObjectName(vObj)) == 0)
+ cardPropName = kFirstNameProperty;
+ else if (PL_strcasecmp(VCOrgNameProp, vObjectName(vObj)) == 0)
+ cardPropName = kCompanyProperty;
+ else if (PL_strcasecmp(VCOrgUnitProp, vObjectName(vObj)) == 0)
+ cardPropName = kDepartmentProperty;
+ else if (PL_strcasecmp(VCPostalCodeProp, vObjectName(vObj)) == 0)
+ cardPropName = kWorkZipCodeProperty;
+ else if (PL_strcasecmp(VCRegionProp, vObjectName(vObj)) == 0)
+ cardPropName = kWorkStateProperty;
+ else if (PL_strcasecmp(VCStreetAddressProp, vObjectName(vObj)) == 0)
+ cardPropName = kWorkAddressProperty;
+ else if (PL_strcasecmp(VCPostalBoxProp, vObjectName(vObj)) == 0)
+ cardPropName = kWorkAddress2Property;
+ else if (PL_strcasecmp(VCCountryNameProp, vObjectName(vObj)) == 0)
+ cardPropName = kWorkCountryProperty;
+ else if (PL_strcasecmp(VCTitleProp, vObjectName(vObj)) == 0)
+ cardPropName = kJobTitleProperty;
+ else if (PL_strcasecmp(VCUseHTML, vObjectName(vObj)) == 0)
+ cardPropName = kPreferMailFormatProperty;
+ else if (PL_strcasecmp(VCNoteProp, vObjectName(vObj)) == 0)
+ cardPropName = kNotesProperty;
+ else if (PL_strcasecmp(VCURLProp, vObjectName(vObj)) == 0)
+ cardPropName = kWorkWebPageProperty;
+ else
+ return;
+
+ if (!VALUE_TYPE(vObj))
+ return;
+
+ char *cardPropValue = getCString(vObj);
+ if (PL_strcmp(cardPropName, kPreferMailFormatProperty)) {
+ aCard->SetPropertyAsAUTF8String(cardPropName, nsDependentCString(cardPropValue));
+ } else {
+ if (!PL_strcmp(cardPropValue, "TRUE"))
+ aCard->SetPropertyAsUint32(cardPropName, nsIAbPreferMailFormat::html);
+ else if (!PL_strcmp(cardPropValue, "FALSE"))
+ aCard->SetPropertyAsUint32(cardPropName, nsIAbPreferMailFormat::plaintext);
+ else
+ aCard->SetPropertyAsUint32(cardPropName, nsIAbPreferMailFormat::unknown);
+ }
+ PR_FREEIF(cardPropValue);
+ return;
+}
+
+static void convertFromVObject(VObject *vObj, nsIAbCard *aCard)
+{
+ if (vObj)
+ {
+ VObjectIterator t;
+
+ convertNameValue(vObj, aCard);
+
+ initPropIterator(&t, vObj);
+ while (moreIteration(&t))
+ {
+ VObject * nextObject = nextVObject(&t);
+ convertFromVObject(nextObject, aCard);
+ }
+ }
+ return;
+}
+
+NS_IMETHODIMP nsAbManager::EscapedVCardToAbCard(const char *aEscapedVCardStr, nsIAbCard **aCard)
+{
+ NS_ENSURE_ARG_POINTER(aEscapedVCardStr);
+ NS_ENSURE_ARG_POINTER(aCard);
+
+ nsCOMPtr <nsIAbCard> cardFromVCard = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID);
+ if (!cardFromVCard)
+ return NS_ERROR_FAILURE;
+
+ // aEscapedVCardStr will be "" the first time, before you have a vCard
+ if (*aEscapedVCardStr != '\0') {
+ nsCString unescapedData;
+ MsgUnescapeString(nsDependentCString(aEscapedVCardStr), 0, unescapedData);
+
+ VObject *vObj = parse_MIME(unescapedData.get(), unescapedData.Length());
+ if (vObj)
+ {
+ convertFromVObject(vObj, cardFromVCard);
+
+ cleanVObject(vObj);
+ }
+ else
+ NS_WARNING("Parse of vCard failed");
+ }
+
+ NS_IF_ADDREF(*aCard = cardFromVCard);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbManager::Handle(nsICommandLine* aCmdLine)
+{
+ nsresult rv;
+ bool found;
+
+ rv = aCmdLine->HandleFlag(NS_LITERAL_STRING("addressbook"), false, &found);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!found)
+ return NS_OK;
+
+ nsCOMPtr<nsIWindowWatcher> wwatch (do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ NS_ENSURE_TRUE(wwatch, NS_ERROR_FAILURE);
+
+ nsCOMPtr<mozIDOMWindowProxy> opened;
+ wwatch->OpenWindow(nullptr,
+ "chrome://messenger/content/addressbook/addressbook.xul",
+ "_blank",
+ "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar",
+ nullptr, getter_AddRefs(opened));
+ aCmdLine->SetPreventDefault(true);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbManager::GetHelpInfo(nsACString& aResult)
+{
+ aResult.Assign(NS_LITERAL_CSTRING(" -addressbook Open the address book at startup.\n"));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbManager::GenerateUUID(const nsACString &aDirectoryId,
+ const nsACString &aLocalId, nsACString &uuid)
+{
+ uuid.Assign(aDirectoryId);
+ uuid.Append('#');
+ uuid.Append(aLocalId);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbManager::ConvertQueryStringToExpression(const nsACString &aQueryString,
+ nsIAbBooleanExpression **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ return nsAbQueryStringToExpression::Convert(aQueryString,
+ _retval);
+}
diff --git a/mailnews/addrbook/src/nsAbManager.h b/mailnews/addrbook/src/nsAbManager.h
new file mode 100644
index 000000000..066fa8a08
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbManager.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsAbManager_h
+#define __nsAbManager_h
+
+#include "nsIAbManager.h"
+#include "nsTObserverArray.h"
+#include "nsCOMPtr.h"
+#include "nsICommandLineHandler.h"
+#include "nsIObserver.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIAbDirFactoryService.h"
+#include "nsIAbDirectory.h"
+
+class nsIAbLDAPAttributeMap;
+
+class nsAbManager : public nsIAbManager,
+ public nsICommandLineHandler,
+ public nsIObserver
+{
+
+public:
+ nsAbManager();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIABMANAGER
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSICOMMANDLINEHANDLER
+
+ nsresult Init();
+
+private:
+ virtual ~nsAbManager();
+ nsresult GetRootDirectory(nsIAbDirectory **aResult);
+ nsresult ExportDirectoryToDelimitedText(nsIAbDirectory *aDirectory, const char *aDelim,
+ uint32_t aDelimLen, nsIFile *aLocalFile, bool useUTF8);
+ nsresult ExportDirectoryToVCard(nsIAbDirectory *aDirectory, nsIFile *aLocalFile);
+ nsresult ExportDirectoryToLDIF(nsIAbDirectory *aDirectory, nsIFile *aLocalFile);
+ nsresult AppendLDIFForMailList(nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult);
+ nsresult AppendDNForCard(const char *aProperty, nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult);
+ nsresult AppendBasicLDIFForCard(nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult);
+ nsresult AppendProperty(const char *aProperty, const char16_t *aValue, nsACString &aResult);
+ bool IsSafeLDIFString(const char16_t *aStr);
+
+ struct abListener {
+ nsCOMPtr<nsIAbListener> mListener;
+ uint32_t mNotifyFlags;
+
+ abListener(nsIAbListener *aListener, uint32_t aNotifyFlags)
+ : mListener(aListener), mNotifyFlags(aNotifyFlags) {}
+ abListener(const abListener &aListener)
+ : mListener(aListener.mListener), mNotifyFlags(aListener.mNotifyFlags) {}
+ ~abListener() {}
+
+ int operator==(nsIAbListener* aListener) const {
+ return mListener == aListener;
+ }
+ int operator==(const abListener &aListener) const {
+ return mListener == aListener.mListener;
+ }
+ };
+
+ nsTObserverArray<abListener> mListeners;
+ nsCOMPtr<nsIAbDirectory> mCacheTopLevelAb;
+ nsInterfaceHashtable<nsCStringHashKey, nsIAbDirectory> mAbStore;
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbOSXCard.h b/mailnews/addrbook/src/nsAbOSXCard.h
new file mode 100644
index 000000000..51f41c1d7
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOSXCard.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbOSXCard_h___
+#define nsAbOSXCard_h___
+
+#include "mozilla/Attributes.h"
+#include "nsAbCardProperty.h"
+
+#define NS_ABOSXCARD_URI_PREFIX NS_ABOSXCARD_PREFIX "://"
+
+#define NS_IABOSXCARD_IID \
+ { 0xa7e5b697, 0x772d, 0x4fb5, \
+ { 0x81, 0x16, 0x23, 0xb7, 0x5a, 0xac, 0x94, 0x56 } }
+
+class nsIAbOSXCard : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IABOSXCARD_IID)
+
+ virtual nsresult Init(const char *aUri) = 0;
+ virtual nsresult Update(bool aNotify) = 0;
+ virtual nsresult GetURI(nsACString &aURI) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIAbOSXCard, NS_IABOSXCARD_IID)
+
+class nsAbOSXCard : public nsAbCardProperty,
+ public nsIAbOSXCard
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsresult Update(bool aNotify) override;
+ nsresult GetURI(nsACString &aURI) override;
+ nsresult Init(const char *aUri) override;
+ // this is needed so nsAbOSXUtils.mm can get at nsAbCardProperty
+ friend class nsAbOSXUtils;
+private:
+ nsCString mURI;
+
+ virtual ~nsAbOSXCard() {}
+};
+
+#endif // nsAbOSXCard_h___
diff --git a/mailnews/addrbook/src/nsAbOSXCard.mm b/mailnews/addrbook/src/nsAbOSXCard.mm
new file mode 100644
index 000000000..28f978c24
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOSXCard.mm
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbOSXCard.h"
+#include "nsAbOSXDirectory.h"
+#include "nsAbOSXUtils.h"
+#include "nsAutoPtr.h"
+#include "nsIAbManager.h"
+#include "nsObjCExceptions.h"
+#include "nsServiceManagerUtils.h"
+
+#include <AddressBook/AddressBook.h>
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAbOSXCard,
+ nsAbCardProperty,
+ nsIAbOSXCard)
+
+#ifdef DEBUG
+static ABPropertyType
+GetPropertType(ABRecord *aCard, NSString *aProperty)
+{
+ ABPropertyType propertyType = kABErrorInProperty;
+ if ([aCard isKindOfClass:[ABPerson class]])
+ propertyType = [ABPerson typeOfProperty:aProperty];
+ else if ([aCard isKindOfClass:[ABGroup class]])
+ propertyType = [ABGroup typeOfProperty:aProperty];
+ return propertyType;
+}
+#endif
+
+static void
+SetStringProperty(nsAbOSXCard *aCard, const nsString &aValue,
+ const char *aMemberName, bool aNotify,
+ nsIAbManager *aAbManager)
+{
+ nsString oldValue;
+ nsresult rv = aCard->GetPropertyAsAString(aMemberName, oldValue);
+ if (NS_FAILED(rv))
+ oldValue.Truncate();
+
+ if (!aNotify) {
+ aCard->SetPropertyAsAString(aMemberName, aValue);
+ }
+ else if (!oldValue.Equals(aValue)) {
+ aCard->SetPropertyAsAString(aMemberName, aValue);
+
+ nsISupports *supports = NS_ISUPPORTS_CAST(nsAbCardProperty*, aCard);
+
+ aAbManager->NotifyItemPropertyChanged(supports, aMemberName,
+ oldValue.get(), aValue.get());
+ }
+}
+
+static void
+SetStringProperty(nsAbOSXCard *aCard, NSString *aValue, const char *aMemberName,
+ bool aNotify, nsIAbManager *aAbManager)
+{
+ nsAutoString value;
+ if (aValue)
+ AppendToString(aValue, value);
+
+ SetStringProperty(aCard, value, aMemberName, aNotify, aAbManager);
+}
+
+static void
+MapStringProperty(nsAbOSXCard *aCard, ABRecord *aOSXCard, NSString *aProperty,
+ const char *aMemberName, bool aNotify,
+ nsIAbManager *aAbManager)
+{
+ NS_ASSERTION(aProperty, "This is bad! You asked for an unresolved symbol.");
+ NS_ASSERTION(GetPropertType(aOSXCard, aProperty) == kABStringProperty,
+ "Wrong type!");
+
+ SetStringProperty(aCard, [aOSXCard valueForProperty:aProperty], aMemberName,
+ aNotify, aAbManager);
+}
+
+static ABMutableMultiValue*
+GetMultiValue(ABRecord *aCard, NSString *aProperty)
+{
+ NS_ASSERTION(aProperty, "This is bad! You asked for an unresolved symbol.");
+ NS_ASSERTION(GetPropertType(aCard, aProperty) & kABMultiValueMask,
+ "Wrong type!");
+
+ return [aCard valueForProperty:aProperty];
+}
+
+static void
+MapDate(nsAbOSXCard *aCard, NSDate *aDate, const char *aYearPropName,
+ const char *aMonthPropName, const char *aDayPropName, bool aNotify,
+ nsIAbManager *aAbManager)
+{
+ // XXX Should we pass a format and timezone?
+ NSCalendarDate *date = [aDate dateWithCalendarFormat:nil timeZone:nil];
+
+ nsAutoString value;
+ value.AppendInt(static_cast<int32_t>([date yearOfCommonEra]));
+ SetStringProperty(aCard, value, aYearPropName, aNotify, aAbManager);
+ value.Truncate();
+ value.AppendInt(static_cast<int32_t>([date monthOfYear]));
+ SetStringProperty(aCard, value, aMonthPropName, aNotify, aAbManager);
+ value.Truncate();
+ value.AppendInt(static_cast<int32_t>([date dayOfMonth]));
+ SetStringProperty(aCard, value, aDayPropName, aNotify, aAbManager);
+}
+
+static bool
+MapMultiValue(nsAbOSXCard *aCard, ABRecord *aOSXCard,
+ const nsAbOSXPropertyMap &aMap, bool aNotify,
+ nsIAbManager *aAbManager)
+{
+ ABMultiValue *value = GetMultiValue(aOSXCard, aMap.mOSXProperty);
+ if (value) {
+ unsigned int j;
+ unsigned int count = [value count];
+ for (j = 0; j < count; ++j) {
+ if ([[value labelAtIndex:j] isEqualToString:aMap.mOSXLabel]) {
+ NSString *stringValue = (aMap.mOSXKey)
+ ? [[value valueAtIndex:j] objectForKey:aMap.mOSXKey]
+ : [value valueAtIndex:j];
+
+ SetStringProperty(aCard, stringValue, aMap.mPropertyName, aNotify,
+ aAbManager);
+
+ return true;
+ }
+ }
+ }
+ // String wasn't found, set value of card to empty if it was set previously
+ SetStringProperty(aCard, EmptyString(), aMap.mPropertyName, aNotify,
+ aAbManager);
+
+ return false;
+}
+
+// Maps Address Book's instant messenger name to the corresponding nsIAbCard field name.
+static const char*
+InstantMessengerFieldName(NSString* aInstantMessengerName)
+{
+ if ([aInstantMessengerName isEqualToString:@"AIMInstant"]) {
+ return "_AimScreenName";
+ }
+ if ([aInstantMessengerName isEqualToString:@"GoogleTalkInstant"]) {
+ return "_GoogleTalk";
+ }
+ if ([aInstantMessengerName isEqualToString:@"ICQInstant"]) {
+ return "_ICQ";
+ }
+ if ([aInstantMessengerName isEqualToString:@"JabberInstant"]) {
+ return "_JabberId";
+ }
+ if ([aInstantMessengerName isEqualToString:@"MSNInstant"]) {
+ return "_MSN";
+ }
+ if ([aInstantMessengerName isEqualToString:@"QQInstant"]) {
+ return "_QQ";
+ }
+ if ([aInstantMessengerName isEqualToString:@"SkypeInstant"]) {
+ return "_Skype";
+ }
+ if ([aInstantMessengerName isEqualToString:@"YahooInstant"]) {
+ return "_Yahoo";
+ }
+
+ // Fall back to AIM for everything else.
+ // We don't have nsIAbCard fields for FacebookInstant and GaduGaduInstant.
+ return "_AimScreenName";
+}
+
+nsresult
+nsAbOSXCard::Init(const char *aUri)
+{
+ if (strncmp(aUri, NS_ABOSXCARD_URI_PREFIX,
+ sizeof(NS_ABOSXCARD_URI_PREFIX) - 1) != 0)
+ return NS_ERROR_FAILURE;
+
+ mURI = aUri;
+
+ SetLocalId(nsDependentCString(aUri));
+
+ return Update(false);
+}
+
+nsresult
+nsAbOSXCard::GetURI(nsACString &aURI)
+{
+ if (mURI.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ aURI = mURI;
+ return NS_OK;
+}
+
+nsresult
+nsAbOSXCard::Update(bool aNotify)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
+
+ const char *uid = &((mURI.get())[16]);
+ ABRecord *card = [addressBook recordForUniqueId:[NSString stringWithUTF8String:uid]];
+ NS_ENSURE_TRUE(card, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIAbManager> abManager;
+ nsresult rv;
+ if (aNotify) {
+ abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if ([card isKindOfClass:[ABGroup class]]) {
+ m_IsMailList = true;
+ m_MailListURI.AssignLiteral(NS_ABOSXDIRECTORY_URI_PREFIX);
+ m_MailListURI.Append(uid);
+ MapStringProperty(this, card, kABGroupNameProperty, "DisplayName", aNotify,
+ abManager);
+ MapStringProperty(this, card, kABGroupNameProperty, "LastName", aNotify,
+ abManager);
+
+ return NS_OK;
+ }
+
+ bool foundHome = false, foundWork = false;
+
+ uint32_t i;
+ for (i = 0; i < nsAbOSXUtils::kPropertyMapSize; ++i) {
+ const nsAbOSXPropertyMap &propertyMap = nsAbOSXUtils::kPropertyMap[i];
+ if (!propertyMap.mOSXProperty)
+ continue;
+
+ if (propertyMap.mOSXLabel) {
+ if (MapMultiValue(this, card, propertyMap, aNotify,
+ abManager) && propertyMap.mOSXProperty == kABAddressProperty) {
+ if (propertyMap.mOSXLabel == kABAddressHomeLabel)
+ foundHome = true;
+ else
+ foundWork = true;
+ }
+ }
+ else {
+ MapStringProperty(this, card, propertyMap.mOSXProperty,
+ propertyMap.mPropertyName, aNotify, abManager);
+ }
+ }
+
+ int flags = 0;
+ if (kABPersonFlags)
+ flags = [[card valueForProperty:kABPersonFlags] intValue];
+
+#define SET_STRING(_value, _name, _notify, _session) \
+ SetStringProperty(this, _value, #_name, _notify, _session)
+
+ // If kABShowAsCompany is set we use the company name as display name.
+ if (kABPersonFlags && (flags & kABShowAsCompany)) {
+ nsString company;
+ nsresult rv = GetPropertyAsAString(kCompanyProperty, company);
+ if (NS_FAILED(rv))
+ company.Truncate();
+ SET_STRING(company, DisplayName, aNotify, abManager);
+ }
+ else {
+ // Use the order used in the OS X address book to set DisplayName.
+ int order = kABPersonFlags && (flags & kABNameOrderingMask);
+ if (kABPersonFlags && (order == kABDefaultNameOrdering)) {
+ order = [addressBook defaultNameOrdering];
+ }
+
+ nsAutoString displayName, tempName;
+ if (kABPersonFlags && (order == kABFirstNameFirst)) {
+ GetFirstName(tempName);
+ displayName.Append(tempName);
+
+ GetLastName(tempName);
+
+ // Only append a space if the last name and the first name are not empty
+ if (!tempName.IsEmpty() && !displayName.IsEmpty())
+ displayName.Append(' ');
+
+ displayName.Append(tempName);
+ }
+ else {
+ GetLastName(tempName);
+ displayName.Append(tempName);
+
+ GetFirstName(tempName);
+
+ // Only append a space if the last name and the first name are not empty
+ if (!tempName.IsEmpty() && !displayName.IsEmpty())
+ displayName.Append(' ');
+
+ displayName.Append(tempName);
+ }
+ SET_STRING(displayName, DisplayName, aNotify, abManager);
+ }
+
+ ABMultiValue *value = GetMultiValue(card, kABEmailProperty);
+ if (value) {
+ unsigned int count = [value count];
+ if (count > 0) {
+ unsigned int j = [value indexForIdentifier:[value primaryIdentifier]];
+
+ if (j < count)
+ SET_STRING([value valueAtIndex:j], PrimaryEmail, aNotify,
+ abManager);
+
+ // If j is 0 (first in the list) we want the second in the list
+ // (index 1), if j is anything else we want the first in the list
+ // (index 0).
+ j = (j == 0);
+ if (j < count)
+ SET_STRING([value valueAtIndex:j], SecondEmail, aNotify,
+ abManager);
+ }
+ }
+
+ // We map the first home address we can find and the first work address
+ // we can find. If we find none, we map the primary address to the home
+ // address.
+ if (!foundHome && !foundWork) {
+ value = GetMultiValue(card, kABAddressProperty);
+ if (value) {
+ unsigned int count = [value count];
+ unsigned int j = [value indexForIdentifier:[value primaryIdentifier]];
+
+ if (j < count) {
+ NSDictionary *address = [value valueAtIndex:j];
+ if (address) {
+ SET_STRING([address objectForKey:kABAddressStreetKey],
+ HomeAddress, aNotify, abManager);
+ SET_STRING([address objectForKey:kABAddressCityKey],
+ HomeCity, aNotify, abManager);
+ SET_STRING([address objectForKey:kABAddressStateKey],
+ HomeState, aNotify, abManager);
+ SET_STRING([address objectForKey:kABAddressZIPKey],
+ HomeZipCode, aNotify, abManager);
+ SET_STRING([address objectForKey:kABAddressCountryKey],
+ HomeCountry, aNotify, abManager);
+ }
+ }
+ }
+ }
+ // This was kABAIMInstantProperty previously, but it was deprecated in OS X 10.7.
+ value = GetMultiValue(card, kABInstantMessageProperty);
+ if (value) {
+ unsigned int count = [value count];
+ for (size_t i = 0; i < count; i++) {
+ id imValue = [value valueAtIndex:i];
+ // Depending on the macOS version, imValue can be an NSString or an NSDictionary.
+ if ([imValue isKindOfClass:[NSString class]]) {
+ if (i == [value indexForIdentifier:[value primaryIdentifier]]) {
+ SET_STRING(imValue, _AimScreenName, aNotify, abManager);
+ }
+ } else if ([imValue isKindOfClass:[NSDictionary class]]) {
+ NSString* instantMessageService = [imValue objectForKey:@"InstantMessageService"];
+ const char* fieldName = InstantMessengerFieldName(instantMessageService);
+ NSString* userName = [imValue objectForKey:@"InstantMessageUsername"];
+ SetStringProperty(this, userName, fieldName, aNotify, abManager);
+ }
+ }
+ }
+
+#define MAP_DATE(_date, _name, _notify, _session) \
+ MapDate(this, _date, #_name"Year", #_name"Month", #_name"Day", _notify, \
+ _session)
+
+ NSDate *date = [card valueForProperty:kABBirthdayProperty];
+ if (date)
+ MAP_DATE(date, Birth, aNotify, abManager);
+
+ if (kABOtherDatesProperty) {
+ value = GetMultiValue(card, kABOtherDatesProperty);
+ if (value) {
+ unsigned int j, count = [value count];
+ for (j = 0; j < count; ++j) {
+ if ([[value labelAtIndex:j] isEqualToString:kABAnniversaryLabel]) {
+ date = [value valueAtIndex:j];
+ if (date) {
+ MAP_DATE(date, Anniversary, aNotify, abManager);
+
+ break;
+ }
+ }
+ }
+ }
+ }
+#undef MAP_DATE
+#undef SET_STRING
+
+ date = [card valueForProperty:kABModificationDateProperty];
+ if (date)
+ SetPropertyAsUint32("LastModifiedDate",
+ uint32_t([date timeIntervalSince1970]));
+ // XXX No way to notify about this?
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
diff --git a/mailnews/addrbook/src/nsAbOSXDirFactory.cpp b/mailnews/addrbook/src/nsAbOSXDirFactory.cpp
new file mode 100644
index 000000000..e9f2d7d3b
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOSXDirFactory.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbOSXDirFactory.h"
+#include "nsAbBaseCID.h"
+#include "nsEnumeratorUtils.h"
+#include "nsIAbDirectory.h"
+#include "nsIAbManager.h"
+#include "nsStringGlue.h"
+#include "nsServiceManagerUtils.h"
+#include "nsAbOSXDirectory.h"
+
+NS_IMPL_ISUPPORTS(nsAbOSXDirFactory, nsIAbDirFactory)
+
+NS_IMETHODIMP
+nsAbOSXDirFactory::GetDirectories(const nsAString &aDirName,
+ const nsACString &aURI,
+ const nsACString &aPrefName,
+ nsISimpleEnumerator **aDirectories)
+{
+ NS_ENSURE_ARG_POINTER(aDirectories);
+
+ *aDirectories = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX "/"),
+ getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbOSXDirectory> osxDirectory(do_QueryInterface(directory, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = osxDirectory->AssertChildNodes();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_NewSingletonEnumerator(aDirectories, osxDirectory);
+}
+
+// No actual deletion, since you cannot create the address books from Mozilla.
+NS_IMETHODIMP
+nsAbOSXDirFactory::DeleteDirectory(nsIAbDirectory *aDirectory)
+{
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbOSXDirFactory.h b/mailnews/addrbook/src/nsAbOSXDirFactory.h
new file mode 100644
index 000000000..4c86211d3
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOSXDirFactory.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbOSXDirFactory_h___
+#define nsAbOSXDirFactory_h___
+
+#include "nsIAbDirFactory.h"
+
+class nsAbOSXDirFactory final : public nsIAbDirFactory
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABDIRFACTORY
+
+private:
+ ~nsAbOSXDirFactory() {}
+};
+
+#endif // nsAbOSXDirFactory_h___
diff --git a/mailnews/addrbook/src/nsAbOSXDirectory.h b/mailnews/addrbook/src/nsAbOSXDirectory.h
new file mode 100644
index 000000000..7e3fad96c
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOSXDirectory.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbOSXDirectory_h___
+#define nsAbOSXDirectory_h___
+
+#include "mozilla/Attributes.h"
+#include "nsISupports.h"
+#include "nsAbBaseCID.h"
+#include "nsAbDirProperty.h"
+#include "nsIAbDirectoryQuery.h"
+#include "nsIAbDirectorySearch.h"
+#include "nsIAbDirSearchListener.h"
+#include "nsIMutableArray.h"
+#include "nsInterfaceHashtable.h"
+#include "nsAbOSXCard.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+class nsIAbManager;
+class nsIAbBooleanExpression;
+
+#define NS_ABOSXDIRECTORY_URI_PREFIX NS_ABOSXDIRECTORY_PREFIX "://"
+
+#define NS_IABOSXDIRECTORY_IID \
+{ 0x87ee4bd9, 0x8552, 0x498f, \
+ { 0x80, 0x85, 0x34, 0xf0, 0x2a, 0xbb, 0x56, 0x16 } }
+
+class nsIAbOSXDirectory : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IABOSXDIRECTORY_IID)
+
+ virtual nsresult AssertChildNodes() = 0;
+ virtual nsresult Update() = 0;
+ virtual nsresult AssertDirectory(nsIAbManager *aManager,
+ nsIAbDirectory *aDirectory) = 0;
+ virtual nsresult AssertCard(nsIAbManager *aManager,
+ nsIAbCard *aCard) = 0;
+ virtual nsresult UnassertCard(nsIAbManager *aManager,
+ nsIAbCard *aCard,
+ nsIMutableArray *aCardList) = 0;
+ virtual nsresult UnassertDirectory(nsIAbManager *aManager,
+ nsIAbDirectory *aDirectory) = 0;
+ virtual nsresult DeleteUid(const nsACString &aUid) = 0;
+ virtual nsresult GetURI(nsACString &aURI) = 0;
+ virtual nsresult Init(const char *aUri) = 0;
+ virtual nsresult GetCardByUri(const nsACString &aUri, nsIAbOSXCard **aResult) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIAbOSXDirectory, NS_IABOSXDIRECTORY_IID)
+
+class nsAbOSXDirectory final : public nsAbDirProperty,
+public nsIAbDirSearchListener,
+public nsIAbOSXDirectory
+{
+public:
+ nsAbOSXDirectory();
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIABDIRSEARCHLISTENER
+
+ // nsIAbOSXDirectory method
+ NS_IMETHOD Init(const char *aUri) override;
+
+ // nsAbDirProperty methods
+ NS_IMETHOD GetReadOnly(bool *aReadOnly) override;
+ NS_IMETHOD GetChildCards(nsISimpleEnumerator **aCards) override;
+ NS_IMETHOD GetChildNodes(nsISimpleEnumerator **aNodes) override;
+ NS_IMETHOD GetIsQuery(bool *aResult) override;
+ NS_IMETHOD HasCard(nsIAbCard *aCard, bool *aHasCard) override;
+ NS_IMETHOD HasDirectory(nsIAbDirectory *aDirectory, bool *aHasDirectory) override;
+ NS_IMETHOD GetURI(nsACString &aURI) override;
+ NS_IMETHOD GetCardFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool caseSensitive,
+ nsIAbCard **aResult) override;
+ NS_IMETHOD GetCardsFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool aCaseSensitive,
+ nsISimpleEnumerator **aResult) override;
+ NS_IMETHOD CardForEmailAddress(const nsACString &aEmailAddress,
+ nsIAbCard **aResult) override;
+
+ // nsIAbOSXDirectory
+ nsresult AssertChildNodes() override;
+ nsresult AssertDirectory(nsIAbManager *aManager,
+ nsIAbDirectory *aDirectory) override;
+ nsresult AssertCard(nsIAbManager *aManager,
+ nsIAbCard *aCard) override;
+ nsresult UnassertCard(nsIAbManager *aManager,
+ nsIAbCard *aCard,
+ nsIMutableArray *aCardList) override;
+ nsresult UnassertDirectory(nsIAbManager *aManager,
+ nsIAbDirectory *aDirectory) override;
+
+ nsresult Update() override;
+
+ nsresult DeleteUid(const nsACString &aUid) override;
+
+ nsresult GetCardByUri(const nsACString &aUri, nsIAbOSXCard **aResult) override;
+
+ nsresult GetRootOSXDirectory(nsIAbOSXDirectory **aResult);
+
+private:
+ ~nsAbOSXDirectory();
+ nsresult FallbackSearch(nsIAbBooleanExpression *aExpression,
+ nsISimpleEnumerator **aCards);
+
+ // This is a list of nsIAbCards, kept separate from m_AddressList because:
+ // - nsIAbDirectory items that are mailing lists, must keep a list of
+ // nsIAbCards in m_AddressList, however
+ // - nsIAbDirectory items that are address books, must keep a list of
+ // nsIAbDirectory (i.e. mailing lists) in m_AddressList, AND no nsIAbCards.
+ //
+ // This wasn't too bad for mork, as that just gets a list from its database,
+ // but because we store our own copy of the list, we must store a separate
+ // list of nsIAbCards here. nsIMutableArray is used, because then it is
+ // interchangeable with m_AddressList.
+ nsCOMPtr<nsIMutableArray> mCardList;
+ nsInterfaceHashtable<nsCStringHashKey, nsIAbOSXCard> mCardStore;
+ nsCOMPtr<nsIAbOSXDirectory> mCacheTopLevelOSXAb;
+};
+
+#endif // nsAbOSXDirectory_h___
diff --git a/mailnews/addrbook/src/nsAbOSXDirectory.mm b/mailnews/addrbook/src/nsAbOSXDirectory.mm
new file mode 100644
index 000000000..e195fe3a7
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOSXDirectory.mm
@@ -0,0 +1,1374 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbOSXDirectory.h"
+#include "nsAbOSXCard.h"
+#include "nsAbOSXUtils.h"
+#include "nsAbQueryStringToExpression.h"
+#include "nsArrayEnumerator.h"
+#include "nsAutoPtr.h"
+#include "nsCOMArray.h"
+#include "nsEnumeratorUtils.h"
+#include "nsIAbDirectoryQueryProxy.h"
+#include "nsIAbManager.h"
+#include "nsObjCExceptions.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "nsIAbBooleanExpression.h"
+#include "nsComponentManagerUtils.h"
+#include "nsISimpleEnumerator.h"
+
+#include <AddressBook/AddressBook.h>
+
+#define kABDeletedRecords (kABDeletedRecords? kABDeletedRecords : @"ABDeletedRecords")
+#define kABUpdatedRecords (kABUpdatedRecords ? kABUpdatedRecords : @"ABUpdatedRecords")
+#define kABInsertedRecords (kABInsertedRecords ? kABInsertedRecords : @"ABInsertedRecords")
+
+static nsresult
+GetOrCreateGroup(NSString *aUid, nsIAbDirectory **aResult)
+{
+ NS_ASSERTION(aUid, "No UID for group!.");
+
+ nsAutoCString uri(NS_ABOSXDIRECTORY_URI_PREFIX);
+ AppendToCString(aUid, uri);
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(uri, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*aResult = directory);
+ return NS_OK;
+}
+
+static nsresult
+GetCard(ABRecord *aRecord, nsIAbCard **aResult, nsIAbOSXDirectory *osxDirectory)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NSString *uid = [aRecord uniqueId];
+ NS_ASSERTION(uid, "No UID for card!.");
+ if (!uid)
+ return NS_ERROR_FAILURE;
+
+ nsAutoCString uri(NS_ABOSXCARD_URI_PREFIX);
+ AppendToCString(uid, uri);
+ nsCOMPtr<nsIAbOSXCard> osxCard;
+ nsresult rv = osxDirectory->GetCardByUri(uri, getter_AddRefs(osxCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> card = do_QueryInterface(osxCard, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*aResult = card);
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+static nsresult
+CreateCard(ABRecord *aRecord, nsIAbCard **aResult)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NSString *uid = [aRecord uniqueId];
+ NS_ASSERTION(uid, "No UID for card!.");
+ if (!uid)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbOSXCard> osxCard = do_CreateInstance(NS_ABOSXCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString uri(NS_ABOSXCARD_URI_PREFIX);
+ AppendToCString(uid, uri);
+
+ rv = osxCard->Init(uri.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> card = do_QueryInterface(osxCard, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*aResult = card);
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+static nsresult
+Sync(NSString *aUid)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
+ ABRecord *card = [addressBook recordForUniqueId:aUid];
+ if ([card isKindOfClass:[ABGroup class]])
+ {
+ nsCOMPtr<nsIAbDirectory> directory;
+ GetOrCreateGroup(aUid, getter_AddRefs(directory));
+ nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
+ do_QueryInterface(directory);
+
+ if (osxDirectory) {
+ osxDirectory->Update();
+ }
+ }
+ else {
+ nsCOMPtr<nsIAbCard> abCard;
+ nsresult rv;
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX"/"),
+ getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
+ do_QueryInterface(directory, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetCard(card, getter_AddRefs(abCard), osxDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbOSXCard> osxCard = do_QueryInterface(abCard);
+ osxCard->Update(true);
+ }
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+@interface ABChangedMonitor : NSObject
+-(void)ABChanged:(NSNotification *)aNotification;
+@end
+
+@implementation ABChangedMonitor
+-(void)ABChanged:(NSNotification *)aNotification
+{
+ NSDictionary *changes = [aNotification userInfo];
+
+ nsresult rv;
+ NSArray *inserted = [changes objectForKey:kABInsertedRecords];
+
+ if (inserted) {
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX"/"),
+ getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
+ do_QueryInterface(directory, &rv);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ unsigned int i, count = [inserted count];
+ for (i = 0; i < count; ++i) {
+ ABAddressBook *addressBook =
+ [ABAddressBook sharedAddressBook];
+ ABRecord *card =
+ [addressBook recordForUniqueId:[inserted objectAtIndex:i]];
+ if ([card isKindOfClass:[ABGroup class]]) {
+ nsCOMPtr<nsIAbDirectory> directory;
+ GetOrCreateGroup([inserted objectAtIndex:i],
+ getter_AddRefs(directory));
+
+ rv = osxDirectory->AssertDirectory(abManager, directory);
+ NS_ENSURE_SUCCESS_VOID(rv);
+ }
+ else {
+ nsCOMPtr<nsIAbCard> abCard;
+ // Construct a card
+ nsresult rv = CreateCard(card, getter_AddRefs(abCard));
+ NS_ENSURE_SUCCESS_VOID(rv);
+ rv = osxDirectory->AssertCard(abManager, abCard);
+ NS_ENSURE_SUCCESS_VOID(rv);
+ }
+ }
+ }
+
+ NSArray *updated = [changes objectForKey:kABUpdatedRecords];
+ if (updated) {
+ unsigned int i, count = [updated count];
+ for (i = 0; i < count; ++i) {
+ NSString *uid = [updated objectAtIndex:i];
+ Sync(uid);
+ }
+ }
+
+ NSArray *deleted = [changes objectForKey:kABDeletedRecords];
+ if (deleted) {
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX"/"),
+ getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
+ do_QueryInterface(directory, &rv);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ unsigned int i, count = [deleted count];
+ for (i = 0; i < count; ++i) {
+ NSString *deletedUid = [deleted objectAtIndex:i];
+
+ nsAutoCString uid;
+ AppendToCString(deletedUid, uid);
+
+ rv = osxDirectory->DeleteUid(uid);
+ NS_ENSURE_SUCCESS_VOID(rv);
+ }
+ }
+
+ if (!inserted && !updated && !deleted) {
+ // XXX This is supposed to mean "everything was updated", but we get
+ // this whenever something has changed, so not sure what to do.
+ }
+}
+@end
+
+static nsresult
+MapConditionString(nsIAbBooleanConditionString *aCondition, bool aNegate,
+ bool &aCanHandle, ABSearchElement **aResult)
+{
+ aCanHandle = false;
+
+ nsAbBooleanConditionType conditionType = 0;
+ nsresult rv = aCondition->GetCondition(&conditionType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ABSearchComparison comparison;
+ switch (conditionType) {
+ case nsIAbBooleanConditionTypes::Contains:
+ {
+ if (!aNegate) {
+ comparison = kABContainsSubString;
+ aCanHandle = true;
+ }
+ break;
+ }
+ case nsIAbBooleanConditionTypes::DoesNotContain:
+ {
+ if (aNegate) {
+ comparison = kABContainsSubString;
+ aCanHandle = true;
+ }
+ break;
+ }
+ case nsIAbBooleanConditionTypes::Is:
+ {
+ comparison = aNegate ? kABNotEqual : kABEqual;
+ aCanHandle = true;
+ break;
+ }
+ case nsIAbBooleanConditionTypes::IsNot:
+ {
+ comparison = aNegate ? kABEqual : kABNotEqual;
+ aCanHandle = true;
+ break;
+ }
+ case nsIAbBooleanConditionTypes::BeginsWith:
+ {
+ if (!aNegate) {
+ comparison = kABPrefixMatch;
+ aCanHandle = true;
+ }
+ break;
+ }
+ case nsIAbBooleanConditionTypes::EndsWith:
+ {
+ //comparison = kABSuffixMatch;
+ break;
+ }
+ case nsIAbBooleanConditionTypes::LessThan:
+ {
+ comparison = aNegate ? kABGreaterThanOrEqual : kABLessThan;
+ aCanHandle = true;
+ break;
+ }
+ case nsIAbBooleanConditionTypes::GreaterThan:
+ {
+ comparison = aNegate ? kABLessThanOrEqual : kABGreaterThan;
+ aCanHandle = true;
+ break;
+ }
+ }
+
+ if (!aCanHandle)
+ return NS_OK;
+
+ nsCString name;
+ rv = aCondition->GetName(getter_Copies(name));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString value;
+ rv = aCondition->GetValue(getter_Copies(value));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t length = value.Length();
+
+ uint32_t i;
+ for (i = 0; i < nsAbOSXUtils::kPropertyMapSize; ++i) {
+ if (name.Equals(nsAbOSXUtils::kPropertyMap[i].mPropertyName)) {
+ *aResult =
+ [ABPerson searchElementForProperty:nsAbOSXUtils::kPropertyMap[i].mOSXProperty
+ label:nsAbOSXUtils::kPropertyMap[i].mOSXLabel
+ key:nsAbOSXUtils::kPropertyMap[i].mOSXKey
+ value:[NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.get()) length:length]
+ comparison:comparison];
+
+ return NS_OK;
+ }
+ }
+
+ if (name.EqualsLiteral("DisplayName") && comparison == kABContainsSubString) {
+ ABSearchElement *first =
+ [ABPerson searchElementForProperty:kABFirstNameProperty
+ label:nil
+ key:nil
+ value:[NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.get()) length:length]
+ comparison:comparison];
+ ABSearchElement *second =
+ [ABPerson searchElementForProperty:kABLastNameProperty
+ label:nil
+ key:nil
+ value:[NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.get()) length:length]
+ comparison:comparison];
+ ABSearchElement *third =
+ [ABGroup searchElementForProperty:kABGroupNameProperty
+ label:nil
+ key:nil
+ value:[NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.get()) length:length]
+ comparison:comparison];
+
+ *aResult = [ABSearchElement searchElementForConjunction:kABSearchOr children:[NSArray arrayWithObjects:first, second, third, nil]];
+
+ return NS_OK;
+ }
+
+ aCanHandle = false;
+
+ return NS_OK;
+}
+
+static nsresult
+BuildSearchElements(nsIAbBooleanExpression *aExpression,
+ bool &aCanHandle,
+ ABSearchElement **aResult)
+{
+ aCanHandle = true;
+
+ nsCOMPtr<nsIArray> expressions;
+ nsresult rv = aExpression->GetExpressions(getter_AddRefs(expressions));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAbBooleanOperationType operation;
+ rv = aExpression->GetOperation(&operation);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t count;
+ rv = expressions->GetLength(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(count > 1 && operation != nsIAbBooleanOperationTypes::NOT,
+ "This doesn't make sense!");
+
+ NSMutableArray *array = nullptr;
+ if (count > 1)
+ array = [[NSMutableArray alloc] init];
+
+ uint32_t i;
+ nsCOMPtr<nsIAbBooleanConditionString> condition;
+ nsCOMPtr<nsIAbBooleanExpression> subExpression;
+ for (i = 0; i < count; ++i) {
+ ABSearchElement *element = nullptr;
+
+ condition = do_QueryElementAt(expressions, i);
+ if (condition) {
+ rv = MapConditionString(condition, operation == nsIAbBooleanOperationTypes::NOT, aCanHandle, &element);
+ if (NS_FAILED(rv))
+ break;
+ }
+ else {
+ subExpression = do_QueryElementAt(expressions, i);
+ if (subExpression) {
+ rv = BuildSearchElements(subExpression, aCanHandle, &element);
+ if (NS_FAILED(rv))
+ break;
+ }
+ }
+
+ if (!aCanHandle) {
+ // remember to free the array when returning early
+ [array release];
+ return NS_OK;
+ }
+
+ if (element) {
+ if (array)
+ [array addObject:element];
+ else
+ *aResult = element;
+ }
+ }
+
+ if (array) {
+ if (NS_SUCCEEDED(rv)) {
+ ABSearchConjunction conjunction = operation == nsIAbBooleanOperationTypes::AND ? kABSearchAnd : kABSearchOr;
+ *aResult = [ABSearchElement searchElementForConjunction:conjunction children:array];
+ }
+ [array release];
+ }
+
+ return rv;
+}
+
+static bool
+Search(nsIAbBooleanExpression *aExpression, NSArray **aResult)
+{
+ bool canHandle = false;
+ ABSearchElement *searchElement;
+ nsresult rv = BuildSearchElements(aExpression, canHandle, &searchElement);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (canHandle)
+ *aResult = [[ABAddressBook sharedAddressBook] recordsMatchingSearchElement:searchElement];
+
+ return canHandle;
+}
+
+static uint32_t sObserverCount = 0;
+static ABChangedMonitor *sObserver = nullptr;
+
+nsAbOSXDirectory::nsAbOSXDirectory()
+{
+}
+
+nsAbOSXDirectory::~nsAbOSXDirectory()
+{
+ if (--sObserverCount == 0) {
+ [[NSNotificationCenter defaultCenter] removeObserver:sObserver];
+ [sObserver release];
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAbOSXDirectory,
+ nsAbDirProperty,
+ nsIAbOSXDirectory,
+ nsIAbDirSearchListener)
+
+NS_IMETHODIMP
+nsAbOSXDirectory::Init(const char *aUri)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsresult rv;
+ rv = nsAbDirProperty::Init(aUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
+ if (sObserverCount == 0) {
+ sObserver = [[ABChangedMonitor alloc] init];
+ [[NSNotificationCenter defaultCenter] addObserver:(ABChangedMonitor*)sObserver
+ selector:@selector(ABChanged:)
+ name:kABDatabaseChangedExternallyNotification
+ object:nil];
+ }
+ ++sObserverCount;
+
+ NSArray *cards;
+ nsCOMPtr<nsIMutableArray> cardList;
+ bool isRootOSXDirectory = false;
+
+ if (!mIsQueryURI && mURINoQuery.Length() <= sizeof(NS_ABOSXDIRECTORY_URI_PREFIX))
+ isRootOSXDirectory = true;
+
+ if (mIsQueryURI || isRootOSXDirectory)
+ {
+ m_DirPrefId.AssignLiteral("ldap_2.servers.osx");
+
+ cards = [[addressBook people] arrayByAddingObjectsFromArray:[addressBook groups]];
+ if (!mCardList)
+ mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ else
+ rv = mCardList->Clear();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ cardList = mCardList;
+ }
+ else
+ {
+ nsAutoCString uid(Substring(mURINoQuery, sizeof(NS_ABOSXDIRECTORY_URI_PREFIX) - 1));
+ ABRecord *card = [addressBook recordForUniqueId:[NSString stringWithUTF8String:uid.get()]];
+ NS_ASSERTION([card isKindOfClass:[ABGroup class]], "Huh.");
+
+ m_IsMailList = true;
+ AppendToString([card valueForProperty:kABGroupNameProperty], m_ListDirName);
+
+ ABGroup *group = (ABGroup*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsAutoCString(Substring(mURINoQuery, 21)).get()]];
+ cards = [[group members] arrayByAddingObjectsFromArray:[group subgroups]];
+
+ if (!m_AddressList)
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ else
+ rv = m_AddressList->Clear();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ cardList = m_AddressList;
+ }
+
+
+ nsAutoCString ourUuid;
+ GetUuid(ourUuid);
+
+ unsigned int nbCards = [cards count];
+ nsCOMPtr<nsIAbCard> card;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbOSXDirectory> rootOSXDirectory;
+ if (!isRootOSXDirectory)
+ {
+ rv = GetRootOSXDirectory(getter_AddRefs(rootOSXDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ for (unsigned int i = 0; i < nbCards; ++i)
+ {
+ // If we're a Group, it's likely that the cards we're going
+ // to create were already created in the root nsAbOSXDirectory,
+ if (!isRootOSXDirectory)
+ rv = GetCard([cards objectAtIndex:i], getter_AddRefs(card),
+ rootOSXDirectory);
+ else
+ {
+ // If we're not a Group, that means we're the root nsAbOSXDirectory,
+ // which means we have to create the cards from scratch.
+ rv = CreateCard([cards objectAtIndex:i], getter_AddRefs(card));
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we're not a query directory, we're going to want to
+ // tell the AB Manager that we've added some cards so that they
+ // show up in the address book views.
+ if (!mIsQueryURI)
+ AssertCard(abManager, card);
+
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::GetURI(nsACString &aURI)
+{
+ if (mURI.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ aURI = mURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::GetReadOnly(bool *aReadOnly)
+{
+ NS_ENSURE_ARG_POINTER(aReadOnly);
+
+ *aReadOnly = true;
+ return NS_OK;
+}
+
+static bool
+CheckRedundantCards(nsIAbManager *aManager, nsIAbDirectory *aDirectory,
+ nsIAbCard *aCard, NSMutableArray *aCardList)
+{
+ nsresult rv;
+ nsCOMPtr<nsIAbOSXCard> osxCard = do_QueryInterface(aCard, &rv);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ nsAutoCString uri;
+ rv = osxCard->GetURI(uri);
+ NS_ENSURE_SUCCESS(rv, false);
+ NSString *uid = [NSString stringWithUTF8String:(uri.get() + 21)];
+
+ unsigned int i, count = [aCardList count];
+ for (i = 0; i < count; ++i) {
+ if ([[[aCardList objectAtIndex:i] uniqueId] isEqualToString:uid]) {
+ [aCardList removeObjectAtIndex:i];
+ break;
+ }
+ }
+
+ if (i == count) {
+ aManager->NotifyDirectoryItemDeleted(aDirectory, aCard);
+ return true;
+ }
+
+ return false;
+}
+
+nsresult
+nsAbOSXDirectory::GetRootOSXDirectory(nsIAbOSXDirectory **aResult)
+{
+ if (!mCacheTopLevelOSXAb)
+ {
+ // Attempt to get card from the toplevel directories
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX"/"),
+ getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
+ do_QueryInterface(directory, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCacheTopLevelOSXAb = osxDirectory;
+ }
+
+ NS_IF_ADDREF(*aResult = mCacheTopLevelOSXAb);
+ return NS_OK;
+}
+
+nsresult
+nsAbOSXDirectory::Update()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mIsQueryURI) {
+ return NS_OK;
+ }
+
+ ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
+ // Due to the horrible way the address book code works wrt mailing lists
+ // we have to use a different list depending on what we are. This pointer
+ // holds a reference to that list.
+ nsIMutableArray* cardList;
+ NSArray *groups, *cards;
+ if (m_IsMailList) {
+ ABGroup *group = (ABGroup*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsAutoCString(Substring(mURINoQuery, 21)).get()]];
+ groups = nil;
+ cards = [[group members] arrayByAddingObjectsFromArray:[group subgroups]];
+
+ if (!m_AddressList)
+ {
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ // For mailing lists, store the cards in m_AddressList
+ cardList = m_AddressList;
+ }
+ else {
+ groups = [addressBook groups];
+ cards = [[addressBook people] arrayByAddingObjectsFromArray:groups];
+
+ if (!mCardList)
+ {
+ mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ // For directories, store the cards in mCardList
+ cardList = mCardList;
+ }
+
+ NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:cards];
+ uint32_t addressCount;
+ rv = cardList->GetLength(&addressCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ while (addressCount--)
+ {
+ nsCOMPtr<nsIAbCard> card(do_QueryElementAt(cardList, addressCount, &rv));
+ if (NS_FAILED(rv))
+ break;
+
+ if (CheckRedundantCards(abManager, this, card, mutableArray))
+ cardList->RemoveElementAt(addressCount);
+ }
+
+ NSEnumerator *enumerator = [mutableArray objectEnumerator];
+ ABRecord *card;
+ nsCOMPtr<nsIAbCard> abCard;
+ nsCOMPtr<nsIAbOSXDirectory> rootOSXDirectory;
+ rv = GetRootOSXDirectory(getter_AddRefs(rootOSXDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ while ((card = [enumerator nextObject]))
+ {
+ rv = GetCard(card, getter_AddRefs(abCard), rootOSXDirectory);
+ if (NS_FAILED(rv))
+ rv = CreateCard(card, getter_AddRefs(abCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ AssertCard(abManager, abCard);
+ }
+
+ card = (ABRecord*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsAutoCString(Substring(mURINoQuery, 21)).get()]];
+ NSString * stringValue = [card valueForProperty:kABGroupNameProperty];
+ if (![stringValue isEqualToString:WrapString(m_ListDirName)])
+ {
+ nsAutoString oldValue(m_ListDirName);
+ AssignToString(stringValue, m_ListDirName);
+ nsCOMPtr<nsISupports> supports = do_QueryInterface(static_cast<nsIAbDirectory *>(this), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ abManager->NotifyItemPropertyChanged(supports, "DirName",
+ oldValue.get(), m_ListDirName.get());
+ }
+
+ if (groups)
+ {
+ mutableArray = [NSMutableArray arrayWithArray:groups];
+ nsCOMPtr<nsIAbDirectory> directory;
+ // It is ok to use m_AddressList here as only top-level directories have
+ // groups, and they will be in m_AddressList
+ if (m_AddressList)
+ {
+ rv = m_AddressList->GetLength(&addressCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ while (addressCount--)
+ {
+ directory = do_QueryElementAt(m_AddressList, addressCount, &rv);
+ if (NS_FAILED(rv))
+ continue;
+
+ nsAutoCString uri;
+ directory->GetURI(uri);
+ uri.Cut(0, 21);
+ NSString *uid = [NSString stringWithUTF8String:uri.get()];
+
+ unsigned int j, arrayCount = [mutableArray count];
+ for (j = 0; j < arrayCount; ++j) {
+ if ([[[mutableArray objectAtIndex:j] uniqueId] isEqualToString:uid]) {
+ [mutableArray removeObjectAtIndex:j];
+ break;
+ }
+ }
+
+ if (j == arrayCount) {
+ UnassertDirectory(abManager, directory);
+ }
+ }
+ }
+
+ enumerator = [mutableArray objectEnumerator];
+ while ((card = [enumerator nextObject])) {
+ rv = GetOrCreateGroup([card uniqueId], getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ AssertDirectory(abManager, directory);
+ }
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult
+nsAbOSXDirectory::AssertChildNodes()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ // Queries and mailing lists can't have childnodes.
+ if (mIsQueryURI || m_IsMailList) {
+ return NS_OK;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager =
+ do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NSArray *groups = [[ABAddressBook sharedAddressBook] groups];
+
+ unsigned int i, count = [groups count];
+
+ if (count > 0 && !m_AddressList) {
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ for (i = 0; i < count; ++i) {
+ rv = GetOrCreateGroup([[groups objectAtIndex:i] uniqueId],
+ getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = AssertDirectory(abManager, directory);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult
+nsAbOSXDirectory::AssertDirectory(nsIAbManager *aManager,
+ nsIAbDirectory *aDirectory)
+{
+ uint32_t pos;
+ if (m_AddressList &&
+ NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos)))
+ // We already have this directory, so no point in adding it again.
+ return NS_OK;
+
+ nsresult rv;
+ if (!m_AddressList) {
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = m_AddressList->AppendElement(aDirectory, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aManager->NotifyDirectoryItemAdded(this, aDirectory);
+}
+
+nsresult
+nsAbOSXDirectory::AssertCard(nsIAbManager *aManager,
+ nsIAbCard *aCard)
+{
+ nsAutoCString ourUuid;
+ GetUuid(ourUuid);
+ aCard->SetDirectoryId(ourUuid);
+
+ nsresult rv = m_IsMailList ? m_AddressList->AppendElement(aCard, false) :
+ mCardList->AppendElement(aCard, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the card's URI and add it to our card store
+ nsCOMPtr<nsIAbOSXCard> osxCard = do_QueryInterface(aCard, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString uri;
+ rv = osxCard->GetURI(uri);
+
+ nsCOMPtr<nsIAbOSXCard> retrievedCard;
+ if (!mCardStore.Get(uri, getter_AddRefs(retrievedCard)))
+ mCardStore.Put(uri, osxCard);
+
+ return aManager->NotifyDirectoryItemAdded(this, aCard);
+}
+
+nsresult
+nsAbOSXDirectory::UnassertCard(nsIAbManager *aManager,
+ nsIAbCard *aCard,
+ nsIMutableArray *aCardList)
+{
+ nsresult rv;
+ uint32_t pos;
+
+ if (NS_SUCCEEDED(aCardList->IndexOf(0, aCard, &pos)))
+ rv = aCardList->RemoveElementAt(pos);
+
+ return aManager->NotifyDirectoryItemDeleted(this, aCard);
+}
+
+nsresult
+nsAbOSXDirectory::UnassertDirectory(nsIAbManager *aManager,
+ nsIAbDirectory *aDirectory)
+{
+ NS_ENSURE_TRUE(m_AddressList, NS_ERROR_NULL_POINTER);
+
+ uint32_t pos;
+ if (NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos)))
+ {
+ nsresult rv = m_AddressList->RemoveElementAt(pos);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return aManager->NotifyDirectoryItemDeleted(this, aDirectory);
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::GetChildNodes(nsISimpleEnumerator **aNodes)
+{
+ NS_ENSURE_ARG_POINTER(aNodes);
+
+ // Queries don't have childnodes.
+ if (mIsQueryURI || m_IsMailList || !m_AddressList)
+ return NS_NewEmptyEnumerator(aNodes);
+
+ return NS_NewArrayEnumerator(aNodes, m_AddressList);
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::GetChildCards(nsISimpleEnumerator **aCards)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_ARG_POINTER(aCards);
+
+ nsresult rv;
+ NSArray *cards;
+ if (mIsQueryURI)
+ {
+ nsCOMPtr<nsIAbBooleanExpression> expression;
+ rv = nsAbQueryStringToExpression::Convert(mQueryString,
+ getter_AddRefs(expression));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool canHandle = !m_IsMailList && Search(expression, &cards);
+ if (!canHandle)
+ return FallbackSearch(expression, aCards);
+
+ if (!mCardList)
+ mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ else
+ mCardList->Clear();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The uuid for initializing cards
+ nsAutoCString ourUuid;
+ GetUuid(ourUuid);
+
+ // Fill the results array and update the card list
+ unsigned int nbCards = [cards count];
+
+ unsigned int i;
+ nsCOMPtr<nsIAbCard> card;
+ nsCOMPtr<nsIAbOSXDirectory> rootOSXDirectory;
+ rv = GetRootOSXDirectory(getter_AddRefs(rootOSXDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (i = 0; i < nbCards; ++i)
+ {
+ rv = GetCard([cards objectAtIndex:i], getter_AddRefs(card),
+ rootOSXDirectory);
+
+ if (NS_FAILED(rv))
+ rv = CreateCard([cards objectAtIndex:i],
+ getter_AddRefs(card));
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ card->SetDirectoryId(ourUuid);
+
+ mCardList->AppendElement(card, false);
+ }
+
+ return NS_NewArrayEnumerator(aCards, mCardList);
+ }
+
+ // Not a search, so just return the appropriate list of items.
+ return m_IsMailList ? NS_NewArrayEnumerator(aCards, m_AddressList) :
+ NS_NewArrayEnumerator(aCards, mCardList);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::GetIsQuery(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mIsQueryURI;
+ return NS_OK;
+}
+
+/* Recursive method that searches for a child card by URI. If it cannot find
+ * it within this directory, it checks all subfolders.
+ */
+NS_IMETHODIMP
+nsAbOSXDirectory::GetCardByUri(const nsACString &aUri, nsIAbOSXCard **aResult)
+{
+ nsCOMPtr<nsIAbOSXCard> osxCard;
+
+ // Base Case
+ if (mCardStore.Get(aUri, getter_AddRefs(osxCard)))
+ {
+ NS_IF_ADDREF(*aResult = osxCard);
+ return NS_OK;
+ }
+ // Search children
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ nsresult rv = this->GetChildNodes(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> item;
+ bool hasMore = false;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ rv = enumerator->GetNext(getter_AddRefs(item));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbOSXDirectory> childDirectory;
+ childDirectory = do_QueryInterface(item, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = childDirectory->GetCardByUri(aUri, getter_AddRefs(osxCard));
+ if (NS_SUCCEEDED(rv))
+ {
+ NS_IF_ADDREF(*aResult = osxCard);
+ return NS_OK;
+ }
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::GetCardFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool aCaseSensitive,
+ nsIAbCard **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (aValue.IsEmpty())
+ return NS_OK;
+
+ nsIMutableArray *list = m_IsMailList ? m_AddressList : mCardList;
+
+ if (!list)
+ return NS_OK;
+
+ uint32_t length;
+ nsresult rv = list->GetLength(&length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> card;
+ nsAutoCString cardValue;
+
+ for (uint32_t i = 0; i < length && !*aResult; ++i)
+ {
+ card = do_QueryElementAt(list, i, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = card->GetPropertyAsAUTF8String(aProperty, cardValue);
+ if (NS_SUCCEEDED(rv))
+ {
+#ifdef MOZILLA_INTERNAL_API
+ bool equal = aCaseSensitive ? cardValue.Equals(aValue) :
+ cardValue.Equals(aValue, nsCaseInsensitiveCStringComparator());
+#else
+ bool equal = aCaseSensitive ? cardValue.Equals(aValue) :
+ cardValue.Equals(aValue, CaseInsensitiveCompare);
+#endif
+ if (equal)
+ NS_IF_ADDREF(*aResult = card);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::GetCardsFromProperty(const char *aProperty,
+ const nsACString &aValue,
+ bool aCaseSensitive,
+ nsISimpleEnumerator **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (aValue.IsEmpty())
+ return NS_NewEmptyEnumerator(aResult);
+
+ nsIMutableArray *list = m_IsMailList ? m_AddressList : mCardList;
+
+ if (!list)
+ return NS_NewEmptyEnumerator(aResult);
+
+ uint32_t length;
+ nsresult rv = list->GetLength(&length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMArray<nsIAbCard> resultArray;
+ nsCOMPtr<nsIAbCard> card;
+ nsAutoCString cardValue;
+
+ for (uint32_t i = 0; i < length; ++i)
+ {
+ card = do_QueryElementAt(list, i, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = card->GetPropertyAsAUTF8String(aProperty, cardValue);
+ if (NS_SUCCEEDED(rv))
+ {
+#ifdef MOZILLA_INTERNAL_API
+ bool equal = aCaseSensitive ? cardValue.Equals(aValue) :
+ cardValue.Equals(aValue, nsCaseInsensitiveCStringComparator());
+#else
+ bool equal = aCaseSensitive ? cardValue.Equals(aValue) :
+ cardValue.Equals(aValue, CaseInsensitiveCompare);
+#endif
+ if (equal)
+ resultArray.AppendObject(card);
+ }
+ }
+ }
+
+ return NS_NewArrayEnumerator(aResult, resultArray);
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::CardForEmailAddress(const nsACString &aEmailAddress,
+ nsIAbCard **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (aEmailAddress.IsEmpty())
+ return NS_OK;
+
+ nsIMutableArray *list = m_IsMailList ? m_AddressList : mCardList;
+
+ if (!list)
+ return NS_OK;
+
+ uint32_t length;
+ nsresult rv = list->GetLength(&length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> card;
+
+ for (uint32_t i = 0; i < length && !*aResult; ++i)
+ {
+ card = do_QueryElementAt(list, i, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ bool hasEmailAddress = false;
+
+ rv = card->HasEmailAddress(aEmailAddress, &hasEmailAddress);
+ if (NS_SUCCEEDED(rv) && hasEmailAddress)
+ NS_IF_ADDREF(*aResult = card);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::HasCard(nsIAbCard *aCard, bool *aHasCard)
+{
+ NS_ENSURE_ARG_POINTER(aCard);
+ NS_ENSURE_ARG_POINTER(aHasCard);
+
+ nsresult rv = NS_OK;
+ uint32_t index;
+ if (m_IsMailList)
+ {
+ if (m_AddressList)
+ rv = m_AddressList->IndexOf(0, aCard, &index);
+ }
+ else if (mCardList)
+ rv = mCardList->IndexOf(0, aCard, &index);
+
+ *aHasCard = NS_SUCCEEDED(rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::HasDirectory(nsIAbDirectory *aDirectory,
+ bool *aHasDirectory)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+ NS_ENSURE_ARG_POINTER(aHasDirectory);
+
+ *aHasDirectory = false;
+
+ uint32_t pos;
+ if (m_AddressList && NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos)))
+ *aHasDirectory = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::OnSearchFinished(int32_t aResult, const nsAString &aErrorMsg)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAbOSXDirectory::OnSearchFoundCard(nsIAbCard *aCard)
+{
+ nsresult rv;
+ if (!m_AddressList) {
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (!mCardList) {
+ mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = m_AddressList->AppendElement(aCard, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mCardList->AppendElement(aCard, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString ourUuid;
+ GetUuid(ourUuid);
+ aCard->SetDirectoryId(ourUuid);
+
+ return NS_OK;
+}
+
+nsresult
+nsAbOSXDirectory::FallbackSearch(nsIAbBooleanExpression *aExpression,
+ nsISimpleEnumerator **aCards)
+{
+ nsresult rv;
+
+ if (mCardList)
+ rv = mCardList->Clear();
+ else
+ mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (m_AddressList) {
+ m_AddressList->Clear();
+ }
+ else {
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIAbDirectoryQueryArguments> arguments =
+ do_CreateInstance(NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = arguments->SetExpression(aExpression);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Don't search the subdirectories. If the current directory is a mailing
+ // list, it won't have any subdirectories. If the current directory is an
+ // addressbook, searching both it and the subdirectories (the mailing
+ // lists), will yield duplicate results because every entry in a mailing
+ // list will be an entry in the parent addressbook.
+ rv = arguments->SetQuerySubDirectories(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the directory without the query
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(mURINoQuery, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Initiate the proxy query with the no query directory
+ nsCOMPtr<nsIAbDirectoryQueryProxy> queryProxy =
+ do_CreateInstance(NS_ABDIRECTORYQUERYPROXY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = queryProxy->Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t context = 0;
+ rv = queryProxy->DoQuery(directory, arguments, this, -1, 0, &context);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_NewArrayEnumerator(aCards, m_AddressList);
+}
+
+nsresult nsAbOSXDirectory::DeleteUid(const nsACString &aUid)
+{
+ if (!m_AddressList)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager =
+ do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // At this stage we don't know if aUid represents a card or group. The OS X
+ // interfaces don't give us chance to find out, so we have to go through
+ // our lists to find it.
+
+ // First, we'll see if its in the group list as it is likely to be shorter.
+
+ // See if this item is in our address list
+ uint32_t addressCount;
+ rv = m_AddressList->GetLength(&addressCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString uri(NS_ABOSXDIRECTORY_URI_PREFIX);
+ uri.Append(aUid);
+
+ // Iterate backwards in case we remove something
+ while (addressCount--)
+ {
+ nsCOMPtr<nsIAbItem> abItem(do_QueryElementAt(m_AddressList,
+ addressCount, &rv));
+ if (NS_FAILED(rv))
+ continue;
+
+ nsCOMPtr<nsIAbDirectory> directory(do_QueryInterface(abItem, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString dirUri;
+ directory->GetURI(dirUri);
+ if (uri.Equals(dirUri))
+ return UnassertDirectory(abManager, directory);
+ } else {
+ nsCOMPtr<nsIAbOSXCard> osxCard(do_QueryInterface(abItem, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString cardUri;
+ osxCard->GetURI(cardUri);
+ if (uri.Equals(cardUri))
+ {
+ nsCOMPtr<nsIAbCard> card(do_QueryInterface(osxCard, &rv));
+ if (NS_SUCCEEDED(rv))
+ return UnassertCard(abManager, card, m_AddressList);
+ }
+ }
+ }
+ }
+
+ // Second, see if it is one of the cards.
+ if (!mCardList)
+ return NS_ERROR_FAILURE;
+
+ uri = NS_ABOSXCARD_URI_PREFIX;
+ uri.Append(aUid);
+
+ rv = mCardList->GetLength(&addressCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ while (addressCount--)
+ {
+ nsCOMPtr<nsIAbOSXCard> osxCard(do_QueryElementAt(mCardList, addressCount, &rv));
+ if (NS_FAILED(rv))
+ continue;
+
+ nsAutoCString cardUri;
+ osxCard->GetURI(cardUri);
+
+ if (uri.Equals(cardUri)) {
+ nsCOMPtr<nsIAbCard> card(do_QueryInterface(osxCard, &rv));
+ if (NS_SUCCEEDED(rv))
+ return UnassertCard(abManager, card, mCardList);
+ }
+ }
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbOSXUtils.h b/mailnews/addrbook/src/nsAbOSXUtils.h
new file mode 100644
index 000000000..fc2f2a546
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOSXUtils.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbOSXUtils_h___
+#define nsAbOSXUtils_h___
+
+#include <Foundation/NSString.h>
+#include "nsStringGlue.h"
+
+class nsString;
+class nsCString;
+class nsAbCardProperty;
+
+NSString *WrapString(const nsString &aString);
+void AppendToString(const NSString *aString, nsString &aResult);
+void AssignToString(const NSString *aString, nsString &aResult);
+void AppendToCString(const NSString *aString, nsCString &aResult);
+
+struct nsAbOSXPropertyMap
+{
+ NSString * const mOSXProperty;
+ NSString * const mOSXLabel;
+ NSString * const mOSXKey;
+ const char *mPropertyName;
+};
+
+class nsAbOSXUtils
+{
+public:
+ static const nsAbOSXPropertyMap kPropertyMap[];
+ static const uint32_t kPropertyMapSize;
+};
+
+#endif // nsAbOSXUtils_h___
diff --git a/mailnews/addrbook/src/nsAbOSXUtils.mm b/mailnews/addrbook/src/nsAbOSXUtils.mm
new file mode 100644
index 000000000..60802d772
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOSXUtils.mm
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbOSXUtils.h"
+#include "nsStringGlue.h"
+#include "nsAbOSXCard.h"
+#include "nsMemory.h"
+#include "mozilla/ArrayUtils.h"
+using namespace mozilla;
+
+#include <AddressBook/AddressBook.h>
+#define kABDepartmentProperty (kABDepartmentProperty ? kABDepartmentProperty : @"ABDepartment")
+
+NSString*
+WrapString(const nsString &aString)
+{
+ unichar* chars = reinterpret_cast<unichar*>(const_cast<char16_t*>(aString.get()));
+
+ return [NSString stringWithCharacters:chars
+ length:aString.Length()];
+}
+
+void
+AppendToString(const NSString *aString, nsString &aResult)
+{
+ if (aString) {
+ const char *chars = [aString UTF8String];
+ if (chars) {
+ aResult.Append(NS_ConvertUTF8toUTF16(chars));
+ }
+ }
+}
+
+void
+AssignToString(const NSString *aString, nsString &aResult)
+{
+ if (aString) {
+ const char *chars = [aString UTF8String];
+ if (chars)
+ CopyUTF8toUTF16(nsDependentCString(chars), aResult);
+ }
+}
+
+void
+AppendToCString(const NSString *aString, nsCString &aResult)
+{
+ if (aString) {
+ const char *chars = [aString UTF8String];
+ if (chars) {
+ aResult.Append(chars);
+ }
+ }
+}
+
+// Some properties can't be easily mapped back and forth.
+#define DONT_MAP(moz_name, osx_property, osx_label, osx_key)
+
+#define DEFINE_PROPERTY(moz_name, osx_property, osx_label, osx_key) \
+ { osx_property, osx_label, osx_key, #moz_name },
+
+const nsAbOSXPropertyMap nsAbOSXUtils::kPropertyMap[] = {
+ DEFINE_PROPERTY(FirstName, kABFirstNameProperty, nil, nil)
+ DEFINE_PROPERTY(LastName, kABLastNameProperty, nil, nil)
+ DONT_MAP("DisplayName", nil, nil, nil)
+ DEFINE_PROPERTY(PhoneticFirstName, kABFirstNamePhoneticProperty, nil, nil)
+ DEFINE_PROPERTY(PhoneticLastName, kABLastNamePhoneticProperty, nil, nil)
+ DEFINE_PROPERTY(NickName, kABNicknameProperty, nil, nil)
+ DONT_MAP(PrimaryEmail, kABEmailProperty, nil, nil)
+ DONT_MAP(SecondEmail, kABEmailProperty, nil, nil)
+ DEFINE_PROPERTY(WorkPhone, kABPhoneProperty, kABPhoneWorkLabel, nil)
+ DEFINE_PROPERTY(HomePhone, kABPhoneProperty, kABPhoneHomeLabel, nil)
+ DEFINE_PROPERTY(FaxNumber, kABPhoneProperty, kABPhoneWorkFAXLabel, nil)
+ DEFINE_PROPERTY(PagerNumber, kABPhoneProperty, kABPhonePagerLabel, nil)
+ DEFINE_PROPERTY(CellularNumber, kABPhoneProperty, kABPhoneMobileLabel, nil)
+ DEFINE_PROPERTY(HomeAddress, kABAddressProperty, kABAddressHomeLabel,
+ kABAddressStreetKey)
+ DEFINE_PROPERTY(HomeCity, kABAddressProperty, kABAddressHomeLabel,
+ kABAddressCityKey)
+ DEFINE_PROPERTY(HomeState, kABAddressProperty, kABAddressHomeLabel,
+ kABAddressStateKey)
+ DEFINE_PROPERTY(HomeZipCode, kABAddressProperty, kABAddressHomeLabel,
+ kABAddressZIPKey)
+ DEFINE_PROPERTY(HomeCountry, kABAddressProperty, kABAddressHomeLabel,
+ kABAddressCountryKey)
+ DEFINE_PROPERTY(WorkAddress, kABAddressProperty, kABAddressWorkLabel,
+ kABAddressStreetKey)
+ DEFINE_PROPERTY(WorkCity, kABAddressProperty, kABAddressWorkLabel,
+ kABAddressCityKey)
+ DEFINE_PROPERTY(WorkState, kABAddressProperty, kABAddressWorkLabel,
+ kABAddressStateKey)
+ DEFINE_PROPERTY(WorkZipCode, kABAddressProperty, kABAddressWorkLabel,
+ kABAddressZIPKey)
+ DEFINE_PROPERTY(WorkCountry, kABAddressProperty, kABAddressWorkLabel,
+ kABAddressCountryKey)
+ DEFINE_PROPERTY(JobTitle, kABJobTitleProperty, nil, nil)
+ DEFINE_PROPERTY(Department, kABDepartmentProperty, nil, nil)
+ DEFINE_PROPERTY(Company, kABOrganizationProperty, nil, nil)
+ // This was kABAIMInstantProperty previously, but it was deprecated in OS X 10.7.
+ DONT_MAP(_AimScreenName, kABInstantMessageProperty, nil, nil)
+ DEFINE_PROPERTY(WebPage1, kABHomePageProperty, nil, nil)
+ DONT_MAP(WebPage2, kABHomePageProperty, nil, nil)
+ DONT_MAP(BirthYear, "birthyear", nil, nil)
+ DONT_MAP(BirthMonth, "birthmonth", nil, nil)
+ DONT_MAP(BirthDay, "birthday", nil, nil)
+ DONT_MAP(Custom1, "custom1", nil, nil)
+ DONT_MAP(Custom2, "custom2", nil, nil)
+ DONT_MAP(Custom3, "custom3", nil, nil)
+ DONT_MAP(Custom4, "custom4", nil, nil)
+ DEFINE_PROPERTY(Note, kABNoteProperty, nil, nil)
+ DONT_MAP("PreferMailFormat", nil, nil, nil)
+ DONT_MAP("LastModifiedDate", modifytimestamp, nil, nil)
+};
+
+const uint32_t nsAbOSXUtils::kPropertyMapSize =
+ ArrayLength(nsAbOSXUtils::kPropertyMap);
diff --git a/mailnews/addrbook/src/nsAbOutlookDirFactory.cpp b/mailnews/addrbook/src/nsAbOutlookDirFactory.cpp
new file mode 100644
index 000000000..164e5a475
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOutlookDirFactory.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsAbOutlookDirFactory.h"
+#include "nsAbWinHelper.h"
+#include "nsIAbDirectory.h"
+#include "nsIAbManager.h"
+#include "nsEnumeratorUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIMutableArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsAbBaseCID.h"
+#include "mozilla/Logging.h"
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo* gAbOutlookDirFactoryLog
+ = PR_NewLogModule("nsAbOutlookDirFactoryLog");
+#endif
+
+#define PRINTF(args) MOZ_LOG(nsAbOutlookDirFactoryLog, mozilla::LogLevel::Debug, args)
+
+
+NS_IMPL_ISUPPORTS(nsAbOutlookDirFactory, nsIAbDirFactory)
+
+nsAbOutlookDirFactory::nsAbOutlookDirFactory(void)
+{
+}
+
+nsAbOutlookDirFactory::~nsAbOutlookDirFactory(void)
+{
+}
+
+extern const char *kOutlookDirectoryScheme;
+
+NS_IMETHODIMP
+nsAbOutlookDirFactory::GetDirectories(const nsAString &aDirName,
+ const nsACString &aURI,
+ const nsACString &aPrefName,
+ nsISimpleEnumerator **aDirectories)
+{
+ NS_ENSURE_ARG_POINTER(aDirectories);
+
+ *aDirectories = nullptr;
+ nsresult rv = NS_OK;
+ nsCString stub;
+ nsCString entry;
+ nsAbWinType abType = getAbWinType(kOutlookDirectoryScheme,
+ nsCString(aURI).get(), stub, entry);
+
+ if (abType == nsAbWinType_Unknown) {
+ return NS_ERROR_FAILURE;
+ }
+ nsAbWinHelperGuard mapiAddBook(abType);
+ nsMapiEntryArray folders;
+ ULONG nbFolders = 0;
+ nsCOMPtr<nsIMutableArray> directories(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!mapiAddBook->IsOK() || !mapiAddBook->GetFolders(folders)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString entryId;
+ nsAutoCString uri;
+
+ for (ULONG i = 0; i < folders.mNbEntries; ++i) {
+ folders.mEntries[i].ToString(entryId);
+ buildAbWinUri(kOutlookDirectoryScheme, abType, uri);
+ uri.Append(entryId);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(uri, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+ directories->AppendElement(directory, false);
+ }
+ return NS_NewArrayEnumerator(aDirectories, directories);
+}
+
+// No actual deletion, since you cannot create the address books from Mozilla.
+NS_IMETHODIMP nsAbOutlookDirFactory::DeleteDirectory(nsIAbDirectory *aDirectory)
+{
+ return NS_OK;
+}
+
diff --git a/mailnews/addrbook/src/nsAbOutlookDirFactory.h b/mailnews/addrbook/src/nsAbOutlookDirFactory.h
new file mode 100644
index 000000000..c66a9c904
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOutlookDirFactory.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsAbOutlookDirFactory_h___
+#define nsAbOutlookDirFactory_h___
+
+#include "nsIAbDirFactory.h"
+
+class nsAbOutlookDirFactory : public nsIAbDirFactory
+{
+public:
+ nsAbOutlookDirFactory(void);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABDIRFACTORY
+
+private:
+ virtual ~nsAbOutlookDirFactory(void);
+};
+
+#endif // nsAbOutlookDirFactory_h___
diff --git a/mailnews/addrbook/src/nsAbOutlookDirectory.cpp b/mailnews/addrbook/src/nsAbOutlookDirectory.cpp
new file mode 100644
index 000000000..0d39d1d17
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOutlookDirectory.cpp
@@ -0,0 +1,1539 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsAbOutlookDirectory.h"
+#include "nsAbWinHelper.h"
+
+#include "nsAbBaseCID.h"
+#include "nsIAbCard.h"
+#include "nsStringGlue.h"
+#include "nsAbDirectoryQuery.h"
+#include "nsIAbBooleanExpression.h"
+#include "nsIAbManager.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsAbQueryStringToExpression.h"
+#include "nsAbUtils.h"
+#include "nsEnumeratorUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "mozilla/Logging.h"
+#include "prthread.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsCRTGlue.h"
+#include "nsArrayUtils.h"
+#include "nsArrayEnumerator.h"
+#include "nsMsgUtils.h"
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo* gAbOutlookDirectoryLog
+ = PR_NewLogModule("nsAbOutlookDirectoryLog");
+#endif
+
+#define PRINTF(args) MOZ_LOG(gAbOutlookDirectoryLog, mozilla::LogLevel::Debug, args)
+
+nsAbOutlookDirectory::nsAbOutlookDirectory(void)
+ : nsAbDirProperty(),
+ mCurrentQueryId(0), mSearchContext(-1),
+ mAbWinType(nsAbWinType_Unknown), mMapiData(nullptr)
+{
+ mMapiData = new nsMapiEntry ;
+ mProtector = PR_NewLock() ;
+}
+
+nsAbOutlookDirectory::~nsAbOutlookDirectory(void)
+{
+ if (mMapiData) { delete mMapiData ; }
+ if (mProtector) { PR_DestroyLock(mProtector) ; }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAbOutlookDirectory, nsAbDirProperty,
+ nsIAbDirectoryQuery, nsIAbDirectorySearch,
+ nsIAbDirSearchListener)
+
+NS_IMETHODIMP nsAbOutlookDirectory::Init(const char *aUri)
+{
+ nsresult rv = nsAbDirProperty::Init(aUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString entry;
+ nsAutoCString stub;
+
+ mAbWinType = getAbWinType(kOutlookDirectoryScheme, mURINoQuery.get(), stub, entry);
+ if (mAbWinType == nsAbWinType_Unknown) {
+ PRINTF(("Huge problem URI=%s.\n", mURINoQuery));
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsAbWinHelperGuard mapiAddBook (mAbWinType);
+ nsString prefix;
+ nsAutoString unichars;
+ ULONG objectType = 0;
+
+ if (!mapiAddBook->IsOK())
+ return NS_ERROR_FAILURE;
+
+ mMapiData->Assign(entry);
+ if (!mapiAddBook->GetPropertyLong(*mMapiData, PR_OBJECT_TYPE, objectType)) {
+ PRINTF(("Cannot get type.\n"));
+ return NS_ERROR_FAILURE;
+ }
+ if (!mapiAddBook->GetPropertyUString(*mMapiData, PR_DISPLAY_NAME_W, unichars)) {
+ PRINTF(("Cannot get name.\n"));
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mAbWinType == nsAbWinType_Outlook)
+ prefix.AssignLiteral("OP ");
+ else
+ prefix.AssignLiteral("OE ");
+ prefix.Append(unichars);
+
+ if (objectType == MAPI_DISTLIST) {
+ m_IsMailList = true;
+ SetDirName(unichars);
+ }
+ else {
+ m_IsMailList = false;
+ SetDirName(prefix);
+ }
+
+ return UpdateAddressList();
+}
+
+// nsIAbDirectory methods
+
+NS_IMETHODIMP nsAbOutlookDirectory::GetDirType(int32_t *aDirType)
+{
+ NS_ENSURE_ARG_POINTER(aDirType);
+ *aDirType = MAPIDirectory;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::GetURI(nsACString &aURI)
+{
+ if (mURI.IsEmpty())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ aURI = mURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::GetChildNodes(nsISimpleEnumerator **aNodes)
+{
+ NS_ENSURE_ARG_POINTER(aNodes);
+
+ *aNodes = nullptr;
+
+ if (mIsQueryURI) {
+ return NS_NewEmptyEnumerator(aNodes);
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> nodeList(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetChildNodes(nodeList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_NewArrayEnumerator(aNodes, nodeList);
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::GetChildCards(nsISimpleEnumerator **aCards)
+{
+ NS_ENSURE_ARG_POINTER(aCards);
+ *aCards = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> cardList(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCardList.Clear();
+
+ rv = mIsQueryURI ? StartSearch() : GetChildCards(cardList, nullptr);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!m_AddressList)
+ {
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Fill the results array and update the card list
+ // Also update the address list and notify any changes.
+ uint32_t nbCards = 0;
+
+ NS_NewArrayEnumerator(aCards, cardList);
+ cardList->GetLength(&nbCards);
+
+ nsCOMPtr<nsIAbCard> card;
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+
+ for (uint32_t i = 0; i < nbCards; ++i)
+ {
+ card = do_QueryElementAt(cardList, i, &rv);
+ if (NS_FAILED(rv))
+ continue;
+
+ if (!mCardList.Get(card, nullptr))
+ {
+ // We are dealing with a new element (probably directly
+ // added from Outlook), we may need to sync m_AddressList
+ mCardList.Put(card, card);
+
+ bool isMailList = false;
+
+ rv = card->GetIsMailList(&isMailList);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (isMailList)
+ {
+ // We can have mailing lists only in folder,
+ // we must add the directory to m_AddressList
+ nsCString mailListUri;
+ rv = card->GetMailListURI(getter_Copies(mailListUri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIAbDirectory> mailList;
+ rv = abManager->GetDirectory(mailListUri, getter_AddRefs(mailList));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_AddressList->AppendElement(mailList, false);
+ NotifyItemAddition(mailList);
+ }
+ else if (m_IsMailList)
+ {
+ m_AddressList->AppendElement(card, false);
+ NotifyItemAddition(card);
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::GetIsQuery(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mIsQueryURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::HasCard(nsIAbCard *aCard, bool *aHasCard)
+{
+ if (!aCard || !aHasCard)
+ return NS_ERROR_NULL_POINTER;
+
+ *aHasCard = mCardList.Get(aCard, nullptr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::HasDirectory(nsIAbDirectory *aDirectory, bool *aHasDirectory)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+ NS_ENSURE_ARG_POINTER(aHasDirectory);
+
+ *aHasDirectory = false;
+
+ uint32_t pos;
+ if (m_AddressList && NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos)))
+ *aHasDirectory = true;
+
+ return NS_OK;
+}
+
+
+static nsresult ExtractCardEntry(nsIAbCard *aCard, nsCString& aEntry)
+{
+ aEntry.Truncate();
+
+ nsCString uri;
+ aCard->GetPropertyAsAUTF8String("OutlookEntryURI", uri);
+
+ // If we don't have a URI, uri will be empty. getAbWinType doesn't set
+ // aEntry to anything if uri is empty, so it will be truncated, allowing us
+ // to accept cards not initialized by us.
+ nsAutoCString stub;
+ getAbWinType(kOutlookCardScheme, uri.get(), stub, aEntry);
+ return NS_OK;
+}
+
+static nsresult ExtractDirectoryEntry(nsIAbDirectory *aDirectory, nsCString& aEntry)
+{
+ aEntry.Truncate();
+ nsCString uri;
+ nsresult rv = aDirectory->GetURI(uri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString stub;
+ nsAbWinType objType = getAbWinType(kOutlookDirectoryScheme, uri.get(), stub, aEntry);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::DeleteCards(nsIArray *aCardList)
+{
+ if (mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
+ if (!aCardList) { return NS_ERROR_NULL_POINTER ; }
+ uint32_t nbCards = 0 ;
+ nsresult retCode = NS_OK ;
+ nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
+
+ if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
+
+ retCode = aCardList->GetLength(&nbCards);
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ uint32_t i = 0 ;
+ nsAutoCString entryString ;
+ nsMapiEntry cardEntry ;
+
+ for (i = 0 ; i < nbCards ; ++ i) {
+ nsCOMPtr<nsIAbCard> card(do_QueryElementAt(aCardList, i, &retCode));
+ NS_ENSURE_SUCCESS(retCode, retCode);
+
+ retCode = ExtractCardEntry(card, entryString) ;
+ if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) {
+ card->SetDirectoryId(EmptyCString());
+
+ cardEntry.Assign(entryString) ;
+ if (!mapiAddBook->DeleteEntry(*mMapiData, cardEntry)) {
+ PRINTF(("Cannot delete card %s.\n", entryString.get())) ;
+ }
+ else {
+ mCardList.Remove(card);
+ if (m_IsMailList && m_AddressList)
+ {
+ uint32_t pos;
+ if (NS_SUCCEEDED(m_AddressList->IndexOf(0, card, &pos)))
+ m_AddressList->RemoveElementAt(pos);
+ }
+ retCode = NotifyItemDeletion(card);
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ }
+ }
+ else {
+ PRINTF(("Card doesn't belong in this directory.\n")) ;
+ }
+ }
+ return NS_OK ;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::DeleteDirectory(nsIAbDirectory *aDirectory)
+{
+ if (mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
+ if (!aDirectory) { return NS_ERROR_NULL_POINTER ; }
+ nsresult retCode = NS_OK ;
+ nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
+ nsAutoCString entryString ;
+
+ if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
+ retCode = ExtractDirectoryEntry(aDirectory, entryString) ;
+ if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) {
+ nsMapiEntry directoryEntry ;
+
+ directoryEntry.Assign(entryString) ;
+ if (!mapiAddBook->DeleteEntry(*mMapiData, directoryEntry)) {
+ PRINTF(("Cannot delete directory %s.\n", entryString.get())) ;
+ }
+ else {
+ uint32_t pos;
+ if (m_AddressList && NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos)))
+ m_AddressList->RemoveElementAt(pos);
+
+ retCode = NotifyItemDeletion(aDirectory);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+ }
+ }
+ else {
+ PRINTF(("Directory doesn't belong to this folder.\n")) ;
+ }
+ return retCode ;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::AddCard(nsIAbCard *aData, nsIAbCard **addedCard)
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ NS_ENSURE_ARG_POINTER(aData);
+
+ nsresult retCode = NS_OK ;
+ bool hasCard = false ;
+
+ retCode = HasCard(aData, &hasCard) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ if (hasCard) {
+ PRINTF(("Has card.\n")) ;
+ NS_IF_ADDREF(*addedCard = aData);
+ return NS_OK ;
+ }
+ retCode = CreateCard(aData, addedCard) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+
+ mCardList.Put(*addedCard, *addedCard);
+
+ if (!m_AddressList)
+ {
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &retCode);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+ }
+
+ if (m_IsMailList)
+ m_AddressList->AppendElement(*addedCard, false);
+ NotifyItemAddition(*addedCard) ;
+ return retCode ;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::DropCard(nsIAbCard *aData, bool needToCopyCard)
+{
+ nsCOMPtr <nsIAbCard> addedCard;
+ return AddCard(aData, getter_AddRefs(addedCard));
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::AddMailList(nsIAbDirectory *aMailList, nsIAbDirectory **addedList)
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+ NS_ENSURE_ARG_POINTER(aMailList);
+ NS_ENSURE_ARG_POINTER(addedList);
+ if (m_IsMailList)
+ return NS_OK;
+ nsAbWinHelperGuard mapiAddBook (mAbWinType);
+ nsAutoCString entryString;
+ nsMapiEntry newEntry;
+ bool didCopy = false;
+
+ if (!mapiAddBook->IsOK())
+ return NS_ERROR_FAILURE;
+ nsresult rv = ExtractDirectoryEntry(aMailList, entryString);
+ if (NS_SUCCEEDED(rv) && !entryString.IsEmpty())
+ {
+ nsMapiEntry sourceEntry;
+
+ sourceEntry.Assign(entryString);
+ mapiAddBook->CopyEntry(*mMapiData, sourceEntry, newEntry);
+ }
+ if (newEntry.mByteCount == 0)
+ {
+ if (!mapiAddBook->CreateDistList(*mMapiData, newEntry))
+ return NS_ERROR_FAILURE;
+ }
+ else {
+ didCopy = true;
+ }
+ newEntry.ToString(entryString);
+ nsAutoCString uri;
+
+ buildAbWinUri(kOutlookDirectoryScheme, mAbWinType, uri);
+ uri.Append(entryString);
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> newList;
+ rv = abManager->GetDirectory(uri, getter_AddRefs(newList));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!didCopy)
+ {
+ rv = newList->CopyMailList(aMailList);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = newList->EditMailListToDatabase(nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (!m_AddressList)
+ {
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ m_AddressList->AppendElement(newList, false);
+ NotifyItemAddition(newList);
+ NS_IF_ADDREF(*addedList = newList);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::EditMailListToDatabase(nsIAbCard *listCard)
+{
+ if (mIsQueryURI)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ nsresult rv;
+ nsString name;
+ nsAbWinHelperGuard mapiAddBook(mAbWinType);
+
+ if (!mapiAddBook->IsOK())
+ return NS_ERROR_FAILURE;
+
+ rv = GetDirName(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mapiAddBook->SetPropertyUString(*mMapiData, PR_DISPLAY_NAME_W,
+ name.get()))
+ return NS_ERROR_FAILURE;
+
+ return CommitAddressList();
+}
+
+struct OutlookTableAttr
+{
+ const char *mOuterName ;
+ ULONG mMapiProp ;
+} ;
+
+// Here, we are forced to use the Ascii versions of the properties
+// instead of the widechar ones, because the content restriction
+// operators do not work on unicode strings in mapi.
+static const OutlookTableAttr OutlookTableStringToProp [] =
+{
+ {kFirstNameProperty, PR_GIVEN_NAME_A},
+ {kLastNameProperty, PR_SURNAME_A},
+ {kDisplayNameProperty, PR_DISPLAY_NAME_A},
+ {kNicknameProperty, PR_NICKNAME_A},
+ {kPriEmailProperty, PR_EMAIL_ADDRESS_A},
+ {kWorkPhoneProperty, PR_BUSINESS_TELEPHONE_NUMBER_A},
+ {kHomePhoneProperty, PR_HOME_TELEPHONE_NUMBER_A},
+ {kFaxProperty, PR_BUSINESS_FAX_NUMBER_A},
+ {kPagerProperty, PR_PAGER_TELEPHONE_NUMBER_A},
+ {kCellularProperty, PR_MOBILE_TELEPHONE_NUMBER_A},
+ {kHomeAddressProperty, PR_HOME_ADDRESS_STREET_A},
+ {kHomeCityProperty, PR_HOME_ADDRESS_CITY_A},
+ {kHomeStateProperty, PR_HOME_ADDRESS_STATE_OR_PROVINCE_A},
+ {kHomeZipCodeProperty, PR_HOME_ADDRESS_POSTAL_CODE_A},
+ {kHomeCountryProperty, PR_HOME_ADDRESS_COUNTRY_A},
+ {kWorkAddressProperty, PR_BUSINESS_ADDRESS_STREET_A},
+ {kWorkCityProperty, PR_BUSINESS_ADDRESS_CITY_A},
+ {kWorkStateProperty, PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A},
+ {kWorkZipCodeProperty, PR_BUSINESS_ADDRESS_POSTAL_CODE_A},
+ {kWorkCountryProperty, PR_BUSINESS_ADDRESS_COUNTRY_A},
+ {kJobTitleProperty, PR_TITLE_A},
+ {kDepartmentProperty, PR_DEPARTMENT_NAME_A},
+ {kCompanyProperty, PR_COMPANY_NAME_A},
+ {kWorkWebPageProperty, PR_BUSINESS_HOME_PAGE_A},
+ {kHomeWebPageProperty, PR_PERSONAL_HOME_PAGE_A},
+ // For the moment, we don't support querying on the birthday
+ // sub-elements.
+#if 0
+ {kBirthYearProperty, PR_BIRTHDAY},
+ {kBirthMonthProperty, PR_BIRTHDAY},
+ {kBirthDayProperty, PR_BIRTHDAY},
+#endif // 0
+ {kNotesProperty, PR_COMMENT_A}
+} ;
+
+static const uint32_t OutlookTableNbProps = sizeof(OutlookTableStringToProp) /
+ sizeof(OutlookTableStringToProp [0]) ;
+
+static ULONG findPropertyTag(const char *aName) {
+ uint32_t i = 0 ;
+
+ for (i = 0 ; i < OutlookTableNbProps ; ++ i) {
+ if (strcmp(aName, OutlookTableStringToProp [i].mOuterName) == 0) {
+ return OutlookTableStringToProp [i].mMapiProp ;
+ }
+ }
+ return 0 ;
+}
+
+static nsresult BuildRestriction(nsIAbBooleanConditionString *aCondition,
+ SRestriction& aRestriction,
+ bool& aSkipItem)
+{
+ if (!aCondition) { return NS_ERROR_NULL_POINTER ; }
+ aSkipItem = false ;
+ nsAbBooleanConditionType conditionType = 0 ;
+ nsresult retCode = NS_OK ;
+ nsCString name;
+ nsString value;
+ ULONG propertyTag = 0 ;
+ nsAutoCString valueAscii ;
+
+ retCode = aCondition->GetCondition(&conditionType) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ retCode = aCondition->GetName(getter_Copies(name)) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ retCode = aCondition->GetValue(getter_Copies(value)) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ LossyCopyUTF16toASCII(value, valueAscii);
+ propertyTag = findPropertyTag(name.get()) ;
+ if (propertyTag == 0) {
+ aSkipItem = true ;
+ return retCode ;
+ }
+ switch (conditionType) {
+ case nsIAbBooleanConditionTypes::Exists :
+ aRestriction.rt = RES_EXIST ;
+ aRestriction.res.resExist.ulPropTag = propertyTag ;
+ break ;
+ case nsIAbBooleanConditionTypes::DoesNotExist :
+ aRestriction.rt = RES_NOT ;
+ aRestriction.res.resNot.lpRes = new SRestriction ;
+ aRestriction.res.resNot.lpRes->rt = RES_EXIST ;
+ aRestriction.res.resNot.lpRes->res.resExist.ulPropTag = propertyTag ;
+ break ;
+ case nsIAbBooleanConditionTypes::Contains :
+ aRestriction.rt = RES_CONTENT ;
+ aRestriction.res.resContent.ulFuzzyLevel = FL_SUBSTRING | FL_LOOSE ;
+ aRestriction.res.resContent.ulPropTag = propertyTag ;
+ aRestriction.res.resContent.lpProp = new SPropValue ;
+ aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+ break ;
+ case nsIAbBooleanConditionTypes::DoesNotContain :
+ aRestriction.rt = RES_NOT ;
+ aRestriction.res.resNot.lpRes = new SRestriction ;
+ aRestriction.res.resNot.lpRes->rt = RES_CONTENT ;
+ aRestriction.res.resNot.lpRes->res.resContent.ulFuzzyLevel = FL_SUBSTRING | FL_LOOSE ;
+ aRestriction.res.resNot.lpRes->res.resContent.ulPropTag = propertyTag ;
+ aRestriction.res.resNot.lpRes->res.resContent.lpProp = new SPropValue ;
+ aRestriction.res.resNot.lpRes->res.resContent.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resNot.lpRes->res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+ break ;
+ case nsIAbBooleanConditionTypes::Is :
+ aRestriction.rt = RES_CONTENT ;
+ aRestriction.res.resContent.ulFuzzyLevel = FL_FULLSTRING | FL_LOOSE ;
+ aRestriction.res.resContent.ulPropTag = propertyTag ;
+ aRestriction.res.resContent.lpProp = new SPropValue ;
+ aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+ break ;
+ case nsIAbBooleanConditionTypes::IsNot :
+ aRestriction.rt = RES_NOT ;
+ aRestriction.res.resNot.lpRes = new SRestriction ;
+ aRestriction.res.resNot.lpRes->rt = RES_CONTENT ;
+ aRestriction.res.resNot.lpRes->res.resContent.ulFuzzyLevel = FL_FULLSTRING | FL_LOOSE ;
+ aRestriction.res.resNot.lpRes->res.resContent.ulPropTag = propertyTag ;
+ aRestriction.res.resNot.lpRes->res.resContent.lpProp = new SPropValue ;
+ aRestriction.res.resNot.lpRes->res.resContent.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resNot.lpRes->res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+ break ;
+ case nsIAbBooleanConditionTypes::BeginsWith :
+ aRestriction.rt = RES_CONTENT ;
+ aRestriction.res.resContent.ulFuzzyLevel = FL_PREFIX | FL_LOOSE ;
+ aRestriction.res.resContent.ulPropTag = propertyTag ;
+ aRestriction.res.resContent.lpProp = new SPropValue ;
+ aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+ break ;
+ case nsIAbBooleanConditionTypes::EndsWith :
+ // This condition should be implemented through regular expressions,
+ // but MAPI doesn't match them correctly.
+#if 0
+ aRestriction.rt = RES_PROPERTY ;
+ aRestriction.res.resProperty.relop = RELOP_RE ;
+ aRestriction.res.resProperty.ulPropTag = propertyTag ;
+ aRestriction.res.resProperty.lpProp = new SPropValue ;
+ aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resProperty.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+#else
+ aSkipItem = true ;
+#endif // 0
+ break ;
+ case nsIAbBooleanConditionTypes::SoundsLike :
+ // This condition cannot be implemented in MAPI.
+ aSkipItem = true ;
+ break ;
+ case nsIAbBooleanConditionTypes::RegExp :
+ // This condition should be implemented this way, but the following
+ // code will never match (through MAPI's fault).
+#if 0
+ aRestriction.rt = RES_PROPERTY ;
+ aRestriction.res.resProperty.relop = RELOP_RE ;
+ aRestriction.res.resProperty.ulPropTag = propertyTag ;
+ aRestriction.res.resProperty.lpProp = new SPropValue ;
+ aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resProperty.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+#else
+ aSkipItem = true ;
+#endif // 0
+ break ;
+ case nsIAbBooleanConditionTypes::LessThan :
+ aRestriction.rt = RES_PROPERTY ;
+ aRestriction.res.resProperty.relop = RELOP_LT ;
+ aRestriction.res.resProperty.ulPropTag = propertyTag ;
+ aRestriction.res.resProperty.lpProp = new SPropValue ;
+ aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resProperty.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+ break ;
+ case nsIAbBooleanConditionTypes::GreaterThan :
+ aRestriction.rt = RES_PROPERTY ;
+ aRestriction.res.resProperty.relop = RELOP_GT ;
+ aRestriction.res.resProperty.ulPropTag = propertyTag ;
+ aRestriction.res.resProperty.lpProp = new SPropValue ;
+ aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ;
+ aRestriction.res.resProperty.lpProp->Value.lpszA = strdup(valueAscii.get()) ;
+ break ;
+ default :
+ aSkipItem = true ;
+ break ;
+ }
+ return retCode ;
+}
+
+static nsresult BuildRestriction(nsIAbBooleanExpression *aLevel,
+ SRestriction& aRestriction)
+{
+ if (!aLevel) { return NS_ERROR_NULL_POINTER ; }
+ aRestriction.rt = RES_COMMENT ;
+ nsresult retCode = NS_OK ;
+ nsAbBooleanOperationType operationType = 0 ;
+ uint32_t nbExpressions = 0 ;
+ nsCOMPtr<nsIArray> expressions;
+
+ retCode = aLevel->GetOperation(&operationType);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+ retCode = aLevel->GetExpressions(getter_AddRefs(expressions));
+ NS_ENSURE_SUCCESS(retCode, retCode);
+ retCode = expressions->GetLength(&nbExpressions);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+ if (nbExpressions == 0) {
+ PRINTF(("Error, no expressions.\n")) ;
+ return NS_OK ;
+ }
+ if (operationType == nsIAbBooleanOperationTypes::NOT && nbExpressions != 1) {
+ PRINTF(("Error, unary operation NOT with multiple operands.\n")) ;
+ return NS_OK ;
+ }
+ LPSRestriction restrictionArray = new SRestriction [nbExpressions] ;
+ uint32_t realNbExpressions = 0 ;
+ bool skipItem = false ;
+ uint32_t i = 0 ;
+
+ nsCOMPtr<nsIAbBooleanConditionString> condition;
+ nsCOMPtr<nsIAbBooleanExpression> subExpression;
+
+ for (i = 0; i < nbExpressions; ++i) {
+ condition = do_QueryElementAt(expressions, i, &retCode);
+
+ if (NS_SUCCEEDED(retCode)) {
+ retCode = BuildRestriction(condition, *restrictionArray, skipItem);
+ if (NS_SUCCEEDED(retCode)) {
+ if (!skipItem) {
+ ++restrictionArray;
+ ++realNbExpressions;
+ }
+ }
+ else
+ PRINTF(("Cannot build restriction for item %d %08x.\n", i, retCode));
+ }
+ else {
+ subExpression = do_QueryElementAt(expressions, i, &retCode);
+
+ if (NS_SUCCEEDED(retCode)) {
+ retCode = BuildRestriction(subExpression, *restrictionArray);
+ if (NS_SUCCEEDED(retCode)) {
+ if (restrictionArray->rt != RES_COMMENT) {
+ ++restrictionArray;
+ ++realNbExpressions;
+ }
+ }
+ }
+ else
+ PRINTF(("Cannot get interface for item %d %08x.\n", i, retCode));
+ }
+ }
+
+ restrictionArray -= realNbExpressions ;
+ if (realNbExpressions > 1) {
+ if (operationType == nsIAbBooleanOperationTypes::OR) {
+ aRestriction.rt = RES_OR ;
+ aRestriction.res.resOr.lpRes = restrictionArray ;
+ aRestriction.res.resOr.cRes = realNbExpressions ;
+ }
+ else if (operationType == nsIAbBooleanOperationTypes::AND) {
+ aRestriction.rt = RES_AND ;
+ aRestriction.res.resAnd.lpRes = restrictionArray ;
+ aRestriction.res.resAnd.cRes = realNbExpressions ;
+ }
+ else {
+ PRINTF(("Unsupported operation %d.\n", operationType)) ;
+ }
+ }
+ else if (realNbExpressions == 1) {
+ if (operationType == nsIAbBooleanOperationTypes::NOT) {
+ aRestriction.rt = RES_NOT ;
+ // This copy is to ensure that every NOT restriction is being
+ // allocated by new and not new[] (see destruction of restriction)
+ aRestriction.res.resNot.lpRes = new SRestriction ;
+ memcpy(aRestriction.res.resNot.lpRes, restrictionArray, sizeof(SRestriction)) ;
+ }
+ else {
+ // Case where the restriction array is redundant,
+ // we need to fill the restriction directly.
+ memcpy(&aRestriction, restrictionArray, sizeof(SRestriction)) ;
+ }
+ delete [] restrictionArray ;
+ }
+ if (aRestriction.rt == RES_COMMENT) {
+ // This means we haven't really built any useful expression
+ delete [] restrictionArray ;
+ }
+ return NS_OK ;
+}
+
+static nsresult BuildRestriction(nsIAbDirectoryQueryArguments *aArguments,
+ SRestriction& aRestriction)
+{
+ if (!aArguments) { return NS_ERROR_NULL_POINTER ; }
+ nsresult retCode = NS_OK ;
+
+ nsCOMPtr<nsISupports> supports ;
+ retCode = aArguments->GetExpression(getter_AddRefs(supports)) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ nsCOMPtr<nsIAbBooleanExpression> booleanQuery =
+ do_QueryInterface(supports, &retCode) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ retCode = BuildRestriction(booleanQuery, aRestriction) ;
+ return retCode ;
+}
+
+static void DestroyRestriction(SRestriction& aRestriction)
+{
+ switch(aRestriction.rt) {
+ case RES_AND :
+ case RES_OR :
+ {
+ for (ULONG i = 0 ; i < aRestriction.res.resAnd.cRes ; ++ i) {
+ DestroyRestriction(aRestriction.res.resAnd.lpRes [i]) ;
+ }
+ delete [] aRestriction.res.resAnd.lpRes ;
+ }
+ break ;
+ case RES_COMMENT :
+ break ;
+ case RES_CONTENT :
+ if (PROP_TYPE(aRestriction.res.resContent.ulPropTag) == PT_UNICODE) {
+ NS_Free(aRestriction.res.resContent.lpProp->Value.lpszW) ;
+ }
+ else if (PROP_TYPE(aRestriction.res.resContent.ulPropTag) == PT_STRING8) {
+ NS_Free(aRestriction.res.resContent.lpProp->Value.lpszA) ;
+ }
+ delete aRestriction.res.resContent.lpProp ;
+ break ;
+ case RES_EXIST :
+ break ;
+ case RES_NOT :
+ DestroyRestriction(*aRestriction.res.resNot.lpRes) ;
+ delete aRestriction.res.resNot.lpRes ;
+ break ;
+ case RES_BITMASK :
+ case RES_COMPAREPROPS :
+ break ;
+ case RES_PROPERTY :
+ if (PROP_TYPE(aRestriction.res.resProperty.ulPropTag) == PT_UNICODE) {
+ NS_Free(aRestriction.res.resProperty.lpProp->Value.lpszW) ;
+ }
+ else if (PROP_TYPE(aRestriction.res.resProperty.ulPropTag) == PT_STRING8) {
+ NS_Free(aRestriction.res.resProperty.lpProp->Value.lpszA) ;
+ }
+ delete aRestriction.res.resProperty.lpProp ;
+ case RES_SIZE :
+ case RES_SUBRESTRICTION :
+ break ;
+ }
+}
+
+struct QueryThreadArgs
+{
+ nsAbOutlookDirectory *mThis ;
+ SRestriction mRestriction ;
+ nsCOMPtr<nsIAbDirSearchListener> mListener ;
+ int32_t mResultLimit ;
+ int32_t mTimeout ;
+ int32_t mThreadId ;
+} ;
+
+static void QueryThreadFunc(void *aArguments)
+{
+ QueryThreadArgs *arguments = reinterpret_cast<QueryThreadArgs *>(aArguments) ;
+
+ if (!aArguments) { return ; }
+ arguments->mThis->ExecuteQuery(arguments->mRestriction, arguments->mListener,
+ arguments->mResultLimit, arguments->mTimeout,
+ arguments->mThreadId) ;
+ DestroyRestriction(arguments->mRestriction) ;
+ delete arguments ;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::DoQuery(nsIAbDirectory *aDirectory,
+ nsIAbDirectoryQueryArguments *aArguments,
+ nsIAbDirSearchListener *aListener,
+ int32_t aResultLimit, int32_t aTimeout,
+ int32_t *aReturnValue)
+{
+ if (!aArguments || !aListener || !aReturnValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aReturnValue = -1;
+
+ QueryThreadArgs *threadArgs = new QueryThreadArgs;
+ PRThread *newThread = nullptr;
+
+ if (!threadArgs)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = BuildRestriction(aArguments, threadArgs->mRestriction);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ threadArgs->mThis = this;
+ threadArgs->mListener = aListener;
+ threadArgs->mResultLimit = aResultLimit;
+ threadArgs->mTimeout = aTimeout;
+
+ PR_Lock(mProtector);
+ *aReturnValue = ++mCurrentQueryId;
+ PR_Unlock(mProtector);
+
+ threadArgs->mThreadId = *aReturnValue;
+ newThread = PR_CreateThread(PR_USER_THREAD,
+ QueryThreadFunc,
+ threadArgs,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ if (!newThread ) {
+ DestroyRestriction(threadArgs->mRestriction);
+ delete threadArgs;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mQueryThreads.Put(*aReturnValue, newThread);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::StopQuery(int32_t aContext)
+{
+ PRThread *queryThread;
+ if (mQueryThreads.Get(aContext, &queryThread)) {
+ PR_Interrupt(queryThread);
+ mQueryThreads.Remove(aContext);
+ }
+ return NS_OK;
+}
+
+// nsIAbDirectorySearch methods
+NS_IMETHODIMP nsAbOutlookDirectory::StartSearch(void)
+{
+ if (!mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
+ nsresult retCode = NS_OK ;
+
+ retCode = StopSearch() ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ mCardList.Clear();
+
+ nsCOMPtr<nsIAbBooleanExpression> expression ;
+
+ nsCOMPtr<nsIAbDirectoryQueryArguments> arguments = do_CreateInstance(NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID,&retCode);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+
+ retCode = nsAbQueryStringToExpression::Convert(mQueryString, getter_AddRefs(expression)) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ retCode = arguments->SetExpression(expression) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+
+ retCode = arguments->SetQuerySubDirectories(true) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+
+ return DoQuery(this, arguments, this, -1, 0, &mSearchContext);
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::StopSearch(void)
+{
+ if (!mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
+ return StopQuery(mSearchContext) ;
+}
+
+// nsIAbDirSearchListener
+NS_IMETHODIMP nsAbOutlookDirectory::OnSearchFinished(int32_t aResult,
+ const nsAString &aErrorMsg)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::OnSearchFoundCard(nsIAbCard *aCard)
+{
+ mCardList.Put(aCard, aCard);
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv))
+ rv = abManager->NotifyDirectoryItemAdded(this, aCard);
+
+ return rv;
+}
+
+nsresult nsAbOutlookDirectory::ExecuteQuery(SRestriction &aRestriction,
+ nsIAbDirSearchListener *aListener,
+ int32_t aResultLimit, int32_t aTimeout,
+ int32_t aThreadId)
+
+{
+ if (!aListener)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult retCode = NS_OK;
+
+ nsCOMPtr<nsIMutableArray> resultsArray(do_CreateInstance(NS_ARRAY_CONTRACTID,
+ &retCode));
+ NS_ENSURE_SUCCESS(retCode, retCode);
+
+ retCode = GetChildCards(resultsArray,
+ aRestriction.rt == RES_COMMENT ? nullptr : &aRestriction);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+
+ uint32_t nbResults = 0;
+ retCode = resultsArray->GetLength(&nbResults);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+
+ if (aResultLimit > 0 && nbResults > static_cast<uint32_t>(aResultLimit)) {
+ nbResults = static_cast<uint32_t>(aResultLimit) ;
+ }
+
+ uint32_t i = 0;
+ nsCOMPtr<nsIAbCard> card;
+
+ for (i = 0 ; i < nbResults ; ++ i) {
+ card = do_QueryElementAt(resultsArray, i, &retCode);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+
+ aListener->OnSearchFoundCard(card);
+ }
+
+ mQueryThreads.Remove(aThreadId);
+
+ aListener->OnSearchFinished(nsIAbDirectoryQueryResultListener::queryResultComplete,
+ EmptyString());
+ return retCode;
+}
+
+// This function expects the aCards array to already be created.
+nsresult nsAbOutlookDirectory::GetChildCards(nsIMutableArray *aCards,
+ void *aRestriction)
+{
+ nsAbWinHelperGuard mapiAddBook(mAbWinType);
+
+ if (!mapiAddBook->IsOK())
+ return NS_ERROR_FAILURE;
+
+ nsMapiEntryArray cardEntries;
+ LPSRestriction restriction = (LPSRestriction) aRestriction;
+
+ if (!mapiAddBook->GetCards(*mMapiData, restriction, cardEntries)) {
+ PRINTF(("Cannot get cards.\n"));
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString ourUuid;
+ GetUuid(ourUuid);
+
+ nsAutoCString entryId;
+ nsAutoCString uriName;
+ nsCOMPtr<nsIAbCard> childCard;
+ nsresult rv;
+
+ for (ULONG card = 0; card < cardEntries.mNbEntries; ++card) {
+ cardEntries.mEntries[card].ToString(entryId);
+ buildAbWinUri(kOutlookCardScheme, mAbWinType, uriName);
+ uriName.Append(entryId);
+
+ rv = OutlookCardForURI(uriName, getter_AddRefs(childCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ childCard->SetDirectoryId(ourUuid);
+
+ aCards->AppendElement(childCard, false);
+ }
+ return rv;
+}
+
+nsresult nsAbOutlookDirectory::GetChildNodes(nsIMutableArray* aNodes)
+{
+ NS_ENSURE_ARG_POINTER(aNodes);
+
+ aNodes->Clear();
+
+ nsAbWinHelperGuard mapiAddBook(mAbWinType);
+ nsMapiEntryArray nodeEntries;
+
+ if (!mapiAddBook->IsOK())
+ return NS_ERROR_FAILURE;
+
+ if (!mapiAddBook->GetNodes(*mMapiData, nodeEntries))
+ {
+ PRINTF(("Cannot get nodes.\n"));
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString entryId;
+ nsAutoCString uriName;
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (ULONG node = 0; node < nodeEntries.mNbEntries; ++node)
+ {
+ nodeEntries.mEntries[node].ToString(entryId);
+ buildAbWinUri(kOutlookDirectoryScheme, mAbWinType, uriName);
+ uriName.Append(entryId);
+
+ nsCOMPtr <nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(uriName, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aNodes->AppendElement(directory, false);
+ }
+ return rv;
+}
+
+nsresult nsAbOutlookDirectory::NotifyItemDeletion(nsISupports *aItem)
+{
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv))
+ rv = abManager->NotifyDirectoryItemDeleted(this, aItem);
+
+ return rv;
+}
+
+nsresult nsAbOutlookDirectory::NotifyItemAddition(nsISupports *aItem)
+{
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = abManager->NotifyDirectoryItemAdded(this, aItem);
+
+ return rv;
+}
+
+// This is called from EditMailListToDatabase.
+// We got m_AddressList containing the list of cards the mailing
+// list is supposed to contain at the end.
+nsresult nsAbOutlookDirectory::CommitAddressList(void)
+{
+ if (!m_IsMailList) {
+ PRINTF(("We are not in a mailing list, no commit can be done.\n"));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv;
+ uint32_t i = 0;
+ nsCOMPtr<nsIMutableArray> oldList(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetChildCards(oldList, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!m_AddressList)
+ return NS_ERROR_NULL_POINTER;
+
+ uint32_t nbCards = 0;
+ rv = m_AddressList->GetLength(&nbCards);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> element;
+ nsCOMPtr<nsIAbCard> newCard;
+ uint32_t pos;
+
+ for (i = 0; i < nbCards; ++i) {
+ element = do_QueryElementAt(m_AddressList, i, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (NS_SUCCEEDED(oldList->IndexOf(0, element, &pos))) {
+ rv = oldList->RemoveElementAt(pos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The entry was not already there
+ nsCOMPtr<nsIAbCard> card(do_QueryInterface(element, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = CreateCard(card, getter_AddRefs(newCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ m_AddressList->ReplaceElementAt(newCard, i, false);
+ }
+ }
+ return DeleteCards(oldList);
+}
+
+nsresult nsAbOutlookDirectory::UpdateAddressList(void)
+{
+ if (!m_AddressList)
+ {
+ nsresult rv;
+ m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return m_IsMailList ? GetChildCards(m_AddressList, nullptr) :
+ GetChildNodes(m_AddressList);
+}
+
+nsresult nsAbOutlookDirectory::CreateCard(nsIAbCard *aData, nsIAbCard **aNewCard)
+{
+ if (!aData || !aNewCard) { return NS_ERROR_NULL_POINTER ; }
+ *aNewCard = nullptr ;
+ nsresult retCode = NS_OK ;
+ nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
+ nsMapiEntry newEntry ;
+ nsAutoCString entryString ;
+ bool didCopy = false ;
+
+ if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
+ // If we get an nsIAbCard that maps onto an Outlook card uri
+ // we simply copy the contents of the Outlook card.
+ retCode = ExtractCardEntry(aData, entryString) ;
+ if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) {
+ nsMapiEntry sourceEntry ;
+
+
+ sourceEntry.Assign(entryString) ;
+ if (m_IsMailList) {
+ // In the case of a mailing list, we can use the address
+ // as a direct template to build the new one (which is done
+ // by CopyEntry).
+ mapiAddBook->CopyEntry(*mMapiData, sourceEntry, newEntry) ;
+ didCopy = true ;
+ }
+ else {
+ // Else, we have to create a temporary address and copy the
+ // source into it. Yes it's silly.
+ mapiAddBook->CreateEntry(*mMapiData, newEntry) ;
+ }
+ }
+ // If this approach doesn't work, well we're back to creating and copying.
+ if (newEntry.mByteCount == 0) {
+ // In the case of a mailing list, we cannot directly create a new card,
+ // we have to create a temporary one in a real folder (to be able to use
+ // templates) and then copy it to the mailing list.
+ if (m_IsMailList) {
+ nsMapiEntry parentEntry ;
+ nsMapiEntry temporaryEntry ;
+
+ if (!mapiAddBook->GetDefaultContainer(parentEntry)) {
+ return NS_ERROR_FAILURE ;
+ }
+ if (!mapiAddBook->CreateEntry(parentEntry, temporaryEntry)) {
+ return NS_ERROR_FAILURE ;
+ }
+ if (!mapiAddBook->CopyEntry(*mMapiData, temporaryEntry, newEntry)) {
+ return NS_ERROR_FAILURE ;
+ }
+ if (!mapiAddBook->DeleteEntry(parentEntry, temporaryEntry)) {
+ return NS_ERROR_FAILURE ;
+ }
+ }
+ // If we're on a real address book folder, we can directly create an
+ // empty card.
+ else if (!mapiAddBook->CreateEntry(*mMapiData, newEntry)) {
+ return NS_ERROR_FAILURE ;
+ }
+ }
+ newEntry.ToString(entryString) ;
+ nsAutoCString uri ;
+
+ buildAbWinUri(kOutlookCardScheme, mAbWinType, uri) ;
+ uri.Append(entryString) ;
+
+ nsCOMPtr<nsIAbCard> newCard;
+ retCode = OutlookCardForURI(uri, getter_AddRefs(newCard));
+ NS_ENSURE_SUCCESS(retCode, retCode);
+
+ nsAutoCString ourUuid;
+ GetUuid(ourUuid);
+ newCard->SetDirectoryId(ourUuid);
+
+ if (!didCopy) {
+ retCode = newCard->Copy(aData) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ retCode = ModifyCard(newCard) ;
+ NS_ENSURE_SUCCESS(retCode, retCode) ;
+ }
+ *aNewCard = newCard ;
+ NS_ADDREF(*aNewCard) ;
+ return retCode ;
+}
+
+static void UnicodeToWord(const char16_t *aUnicode, WORD& aWord)
+{
+ aWord = 0 ;
+ if (aUnicode == nullptr || *aUnicode == 0) { return ; }
+ nsresult errorCode = NS_OK;
+ nsAutoString unichar (aUnicode) ;
+
+ aWord = static_cast<WORD>(unichar.ToInteger(&errorCode));
+ if (NS_FAILED(errorCode)) {
+ PRINTF(("Error conversion string %S: %08x.\n", unichar.get(), errorCode)) ;
+ }
+}
+
+#define PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST "mail.addr_book.lastnamefirst"
+
+
+NS_IMETHODIMP nsAbOutlookDirectory::ModifyCard(nsIAbCard *aModifiedCard)
+{
+ NS_ENSURE_ARG_POINTER(aModifiedCard);
+
+ nsString *properties = nullptr;
+ nsAutoString utility;
+ nsAbWinHelperGuard mapiAddBook(mAbWinType);
+
+ if (!mapiAddBook->IsOK())
+ return NS_ERROR_FAILURE;
+
+ nsCString entry;
+ nsresult retCode = ExtractCardEntry(aModifiedCard, entry);
+ NS_ENSURE_SUCCESS(retCode, retCode);
+ // If we don't have the card entry, we can't work.
+ if (entry.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ nsMapiEntry mapiData;
+ mapiData.Assign(entry);
+
+ // First, all the standard properties in one go
+ properties = new nsString[index_LastProp];
+ if (!properties) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ aModifiedCard->GetFirstName(properties[index_FirstName]);
+ aModifiedCard->GetLastName(properties[index_LastName]);
+ // This triple search for something to put in the name
+ // is because in the case of a mailing list edition in
+ // Mozilla, the display name will not be provided, and
+ // MAPI doesn't allow that, so we fall back on an optional
+ // name, and when all fails, on the email address.
+ aModifiedCard->GetDisplayName(properties[index_DisplayName]);
+ if (properties[index_DisplayName].IsEmpty()) {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch =
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ int32_t format;
+ rv = prefBranch->GetIntPref(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, &format);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = aModifiedCard->GenerateName(format, nullptr,
+ properties[index_DisplayName]);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (properties[index_DisplayName].IsEmpty()) {
+ aModifiedCard->GetPrimaryEmail(properties[index_DisplayName]);
+ }
+ }
+ aModifiedCard->SetDisplayName(properties[index_DisplayName]);
+ aModifiedCard->GetPrimaryEmail(properties[index_EmailAddress]);
+ aModifiedCard->GetPropertyAsAString(kNicknameProperty, properties[index_NickName]);
+ aModifiedCard->GetPropertyAsAString(kWorkPhoneProperty, properties[index_WorkPhoneNumber]);
+ aModifiedCard->GetPropertyAsAString(kHomePhoneProperty, properties[index_HomePhoneNumber]);
+ aModifiedCard->GetPropertyAsAString(kFaxProperty, properties[index_WorkFaxNumber]);
+ aModifiedCard->GetPropertyAsAString(kPagerProperty, properties[index_PagerNumber]);
+ aModifiedCard->GetPropertyAsAString(kCellularProperty, properties[index_MobileNumber]);
+ aModifiedCard->GetPropertyAsAString(kHomeCityProperty, properties[index_HomeCity]);
+ aModifiedCard->GetPropertyAsAString(kHomeStateProperty, properties[index_HomeState]);
+ aModifiedCard->GetPropertyAsAString(kHomeZipCodeProperty, properties[index_HomeZip]);
+ aModifiedCard->GetPropertyAsAString(kHomeCountryProperty, properties[index_HomeCountry]);
+ aModifiedCard->GetPropertyAsAString(kWorkCityProperty, properties[index_WorkCity]);
+ aModifiedCard->GetPropertyAsAString(kWorkStateProperty, properties[index_WorkState]);
+ aModifiedCard->GetPropertyAsAString(kWorkZipCodeProperty, properties[index_WorkZip]);
+ aModifiedCard->GetPropertyAsAString(kWorkCountryProperty, properties[index_WorkCountry]);
+ aModifiedCard->GetPropertyAsAString(kJobTitleProperty, properties[index_JobTitle]);
+ aModifiedCard->GetPropertyAsAString(kDepartmentProperty, properties[index_Department]);
+ aModifiedCard->GetPropertyAsAString(kCompanyProperty, properties[index_Company]);
+ aModifiedCard->GetPropertyAsAString(kWorkWebPageProperty, properties[index_WorkWebPage]);
+ aModifiedCard->GetPropertyAsAString(kHomeWebPageProperty, properties[index_HomeWebPage]);
+ aModifiedCard->GetPropertyAsAString(kNotesProperty, properties[index_Comments]);
+ if (!mapiAddBook->SetPropertiesUString(mapiData, OutlookCardMAPIProps,
+ index_LastProp, properties)) {
+ PRINTF(("Cannot set general properties.\n")) ;
+ }
+
+ delete [] properties;
+ nsString unichar;
+ nsString unichar2;
+ WORD year = 0;
+ WORD month = 0;
+ WORD day = 0;
+
+ aModifiedCard->GetPropertyAsAString(kHomeAddressProperty, unichar);
+ aModifiedCard->GetPropertyAsAString(kHomeAddress2Property, unichar2);
+
+ utility.Assign(unichar.get());
+ if (!utility.IsEmpty())
+ utility.AppendLiteral("\r\n");
+
+ utility.Append(unichar2.get());
+ if (!mapiAddBook->SetPropertyUString(mapiData, PR_HOME_ADDRESS_STREET_W, utility.get())) {
+ PRINTF(("Cannot set home address.\n")) ;
+ }
+
+ unichar.Truncate();
+ aModifiedCard->GetPropertyAsAString(kWorkAddressProperty, unichar);
+ unichar2.Truncate();
+ aModifiedCard->GetPropertyAsAString(kWorkAddress2Property, unichar2);
+
+ utility.Assign(unichar.get());
+ if (!utility.IsEmpty())
+ utility.AppendLiteral("\r\n");
+
+ utility.Append(unichar2.get());
+ if (!mapiAddBook->SetPropertyUString(mapiData, PR_BUSINESS_ADDRESS_STREET_W, utility.get())) {
+ PRINTF(("Cannot set work address.\n")) ;
+ }
+
+ unichar.Truncate();
+ aModifiedCard->GetPropertyAsAString(kBirthYearProperty, unichar);
+ UnicodeToWord(unichar.get(), year);
+ unichar.Truncate();
+ aModifiedCard->GetPropertyAsAString(kBirthMonthProperty, unichar);
+ UnicodeToWord(unichar.get(), month);
+ unichar.Truncate();
+ aModifiedCard->GetPropertyAsAString(kBirthDayProperty, unichar);
+ UnicodeToWord(unichar.get(), day);
+ if (!mapiAddBook->SetPropertyDate(mapiData, PR_BIRTHDAY, year, month, day)) {
+ PRINTF(("Cannot set date.\n")) ;
+ }
+
+ return retCode;
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::OnQueryFoundCard(nsIAbCard *aCard)
+{
+ return OnSearchFoundCard(aCard);
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::OnQueryResult(int32_t aResult,
+ int32_t aErrorCode)
+{
+ return OnSearchFinished(aResult, EmptyString());
+}
+
+NS_IMETHODIMP nsAbOutlookDirectory::UseForAutocomplete(const nsACString &aIdentityKey, bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+ return NS_OK;
+}
+
+static void splitString(nsString& aSource, nsString& aTarget)
+{
+ aTarget.Truncate();
+ int32_t offset = aSource.FindChar('\n');
+
+ if (offset >= 0)
+ {
+ const char16_t *source = aSource.get() + offset + 1;
+ while (*source)
+ {
+ if (*source == '\n' || *source == '\r')
+ aTarget.Append(char16_t(' '));
+ else
+ aTarget.Append(*source);
+ ++source;
+ }
+ aSource.SetLength(offset);
+ }
+}
+
+nsresult OutlookCardForURI(const nsACString &aUri, nsIAbCard **newCard)
+{
+ NS_ENSURE_ARG_POINTER(newCard);
+
+ nsAutoCString entry;
+ nsAutoCString stub;
+ uint32_t abWinType = getAbWinType(kOutlookCardScheme,
+ PromiseFlatCString(aUri).get(), stub, entry);
+ if (abWinType == nsAbWinType_Unknown)
+ {
+ PRINTF(("Huge problem URI=%s.\n", PromiseFlatCString(aUri).get()));
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsAbWinHelperGuard mapiAddBook(abWinType);
+ if (!mapiAddBook->IsOK())
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbCard> card = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ card->SetPropertyAsAUTF8String("OutlookEntryURI", aUri);
+ card->SetLocalId(aUri);
+
+ nsMapiEntry mapiData;
+ mapiData.Assign(entry);
+
+ nsString unichars[index_LastProp];
+
+ if (mapiAddBook->GetPropertiesUString(mapiData, OutlookCardMAPIProps,
+ index_LastProp, unichars))
+ {
+ card->SetFirstName(unichars[index_FirstName]);
+ card->SetLastName(unichars[index_LastName]);
+ card->SetDisplayName(unichars[index_DisplayName]);
+ card->SetPrimaryEmail(unichars[index_EmailAddress]);
+ card->SetPropertyAsAString(kNicknameProperty, unichars[index_NickName]);
+ card->SetPropertyAsAString(kWorkPhoneProperty, unichars[index_WorkPhoneNumber]);
+ card->SetPropertyAsAString(kHomePhoneProperty, unichars[index_HomePhoneNumber]);
+ card->SetPropertyAsAString(kFaxProperty, unichars[index_WorkFaxNumber]);
+ card->SetPropertyAsAString(kPagerProperty, unichars[index_PagerNumber]);
+ card->SetPropertyAsAString(kCellularProperty, unichars[index_MobileNumber]);
+ card->SetPropertyAsAString(kHomeCityProperty, unichars[index_HomeCity]);
+ card->SetPropertyAsAString(kHomeStateProperty, unichars[index_HomeState]);
+ card->SetPropertyAsAString(kHomeZipCodeProperty, unichars[index_HomeZip]);
+ card->SetPropertyAsAString(kHomeCountryProperty, unichars[index_HomeCountry]);
+ card->SetPropertyAsAString(kWorkCityProperty, unichars[index_WorkCity]);
+ card->SetPropertyAsAString(kWorkStateProperty, unichars[index_WorkState]);
+ card->SetPropertyAsAString(kWorkZipCodeProperty, unichars[index_WorkZip]);
+ card->SetPropertyAsAString(kWorkCountryProperty, unichars[index_WorkCountry]);
+ card->SetPropertyAsAString(kJobTitleProperty, unichars[index_JobTitle]);
+ card->SetPropertyAsAString(kDepartmentProperty, unichars[index_Department]);
+ card->SetPropertyAsAString(kCompanyProperty, unichars[index_Company]);
+ card->SetPropertyAsAString(kWorkWebPageProperty, unichars[index_WorkWebPage]);
+ card->SetPropertyAsAString(kHomeWebPageProperty, unichars[index_HomeWebPage]);
+ card->SetPropertyAsAString(kNotesProperty, unichars[index_Comments]);
+ }
+
+ ULONG cardType = 0;
+ if (mapiAddBook->GetPropertyLong(mapiData, PR_OBJECT_TYPE, cardType))
+ {
+ card->SetIsMailList(cardType == MAPI_DISTLIST);
+ if (cardType == MAPI_DISTLIST)
+ {
+ nsAutoCString normalChars;
+ buildAbWinUri(kOutlookDirectoryScheme, abWinType, normalChars);
+ normalChars.Append(entry);
+ card->SetMailListURI(normalChars.get());
+ }
+ }
+
+ nsAutoString unichar;
+ nsAutoString unicharBis;
+ if (mapiAddBook->GetPropertyUString(mapiData, PR_HOME_ADDRESS_STREET_W, unichar))
+ {
+ splitString(unichar, unicharBis);
+ card->SetPropertyAsAString(kHomeAddressProperty, unichar);
+ card->SetPropertyAsAString(kHomeAddress2Property, unicharBis);
+ }
+ if (mapiAddBook->GetPropertyUString(mapiData, PR_BUSINESS_ADDRESS_STREET_W,
+ unichar))
+ {
+ splitString(unichar, unicharBis);
+ card->SetPropertyAsAString(kWorkAddressProperty, unichar);
+ card->SetPropertyAsAString(kWorkAddress2Property, unicharBis);
+ }
+
+ WORD year = 0, month = 0, day = 0;
+ if (mapiAddBook->GetPropertyDate(mapiData, PR_BIRTHDAY, year, month, day))
+ {
+ card->SetPropertyAsUint32(kBirthYearProperty, year);
+ card->SetPropertyAsUint32(kBirthMonthProperty, month);
+ card->SetPropertyAsUint32(kBirthDayProperty, day);
+ }
+
+ card.swap(*newCard);
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbOutlookDirectory.h b/mailnews/addrbook/src/nsAbOutlookDirectory.h
new file mode 100644
index 000000000..116966ce3
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbOutlookDirectory.h
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsAbOutlookDirectory_h___
+#define nsAbOutlookDirectory_h___
+
+#include "mozilla/Attributes.h"
+#include "nsAbDirProperty.h"
+#include "nsIAbDirectoryQuery.h"
+#include "nsIAbDirectorySearch.h"
+#include "nsIAbDirSearchListener.h"
+#include "nsDataHashtable.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIMutableArray.h"
+#include "nsAbWinHelper.h"
+
+struct nsMapiEntry ;
+
+class nsAbOutlookDirectory : public nsAbDirProperty, // nsIAbDirectory
+ public nsIAbDirectoryQuery,
+ public nsIAbDirectorySearch,
+ public nsIAbDirSearchListener,
+ public nsIAbDirectoryQueryResultListener
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIABDIRSEARCHLISTENER
+ NS_DECL_NSIABDIRECTORYQUERYRESULTLISTENER
+
+ nsAbOutlookDirectory(void);
+
+ // nsAbDirProperty methods
+ NS_IMETHOD GetDirType(int32_t *aDirType) override;
+ NS_IMETHOD GetURI(nsACString &aURI) override;
+ NS_IMETHOD GetChildCards(nsISimpleEnumerator **aCards) override;
+ NS_IMETHOD GetChildNodes(nsISimpleEnumerator **aNodes) override;
+ NS_IMETHOD GetIsQuery(bool *aResult) override;
+ NS_IMETHOD HasCard(nsIAbCard *aCard, bool *aHasCard) override;
+ NS_IMETHOD HasDirectory(nsIAbDirectory *aDirectory, bool *aHasDirectory) override;
+ NS_IMETHOD DeleteCards(nsIArray *aCardList) override;
+ NS_IMETHOD DeleteDirectory(nsIAbDirectory *aDirectory) override;
+ NS_IMETHOD UseForAutocomplete(const nsACString &aIdentityKey, bool *aResult) override;
+ NS_IMETHOD AddCard(nsIAbCard *aData, nsIAbCard **addedCard) override;
+ NS_IMETHOD ModifyCard(nsIAbCard *aModifiedCard) override;
+ NS_IMETHOD DropCard(nsIAbCard *aData, bool needToCopyCard) override;
+ NS_IMETHOD AddMailList(nsIAbDirectory *aMailList, nsIAbDirectory **addedList) override;
+ NS_IMETHOD EditMailListToDatabase(nsIAbCard *listCard) override;
+
+ // nsAbDirProperty method
+ NS_IMETHOD Init(const char *aUri) override;
+ // nsIAbDirectoryQuery methods
+ NS_DECL_NSIABDIRECTORYQUERY
+ // nsIAbDirectorySearch methods
+ NS_DECL_NSIABDIRECTORYSEARCH
+ // Perform a MAPI query (function executed in a separate thread)
+ nsresult ExecuteQuery(SRestriction &aRestriction,
+ nsIAbDirSearchListener *aListener,
+ int32_t aResultLimit, int32_t aTimeout,
+ int32_t aThreadId);
+
+protected:
+ // Retrieve hierarchy as cards, with an optional restriction
+ nsresult GetChildCards(nsIMutableArray *aCards, void *aRestriction);
+ // Retrieve hierarchy as directories
+ nsresult GetChildNodes(nsIMutableArray *aNodes);
+ // Create a new card
+ nsresult CreateCard(nsIAbCard *aData, nsIAbCard **aNewCard);
+ // Notification for the UI
+ nsresult NotifyItemDeletion(nsISupports *aItem);
+ nsresult NotifyItemAddition(nsISupports *aItem);
+ // Force update of MAPI repository for mailing list
+ nsresult CommitAddressList(void);
+ // Read MAPI repository
+ nsresult UpdateAddressList(void);
+
+ nsMapiEntry *mMapiData;
+ // Container for the query threads
+ nsDataHashtable<nsUint32HashKey, PRThread*> mQueryThreads;
+ int32_t mCurrentQueryId;
+ PRLock *mProtector;
+ // Data for the search interfaces
+ nsInterfaceHashtable<nsISupportsHashKey, nsIAbCard> mCardList;
+ int32_t mSearchContext;
+ // Windows AB type
+ uint32_t mAbWinType;
+
+private:
+ virtual ~nsAbOutlookDirectory(void);
+
+};
+
+enum
+{
+ index_DisplayName = 0,
+ index_EmailAddress,
+ index_FirstName,
+ index_LastName,
+ index_NickName,
+ index_WorkPhoneNumber,
+ index_HomePhoneNumber,
+ index_WorkFaxNumber,
+ index_PagerNumber,
+ index_MobileNumber,
+ index_HomeCity,
+ index_HomeState,
+ index_HomeZip,
+ index_HomeCountry,
+ index_WorkCity,
+ index_WorkState,
+ index_WorkZip,
+ index_WorkCountry,
+ index_JobTitle,
+ index_Department,
+ index_Company,
+ index_WorkWebPage,
+ index_HomeWebPage,
+ index_Comments,
+ index_LastProp
+};
+
+static const ULONG OutlookCardMAPIProps[] =
+{
+ PR_DISPLAY_NAME_W,
+ PR_EMAIL_ADDRESS_W,
+ PR_GIVEN_NAME_W,
+ PR_SURNAME_W,
+ PR_NICKNAME_W,
+ PR_BUSINESS_TELEPHONE_NUMBER_W,
+ PR_HOME_TELEPHONE_NUMBER_W,
+ PR_BUSINESS_FAX_NUMBER_W,
+ PR_PAGER_TELEPHONE_NUMBER_W,
+ PR_MOBILE_TELEPHONE_NUMBER_W,
+ PR_HOME_ADDRESS_CITY_W,
+ PR_HOME_ADDRESS_STATE_OR_PROVINCE_W,
+ PR_HOME_ADDRESS_POSTAL_CODE_W,
+ PR_HOME_ADDRESS_COUNTRY_W,
+ PR_BUSINESS_ADDRESS_CITY_W,
+ PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_W,
+ PR_BUSINESS_ADDRESS_POSTAL_CODE_W,
+ PR_BUSINESS_ADDRESS_COUNTRY_W,
+ PR_TITLE_W,
+ PR_DEPARTMENT_NAME_W,
+ PR_COMPANY_NAME_W,
+ PR_BUSINESS_HOME_PAGE_W,
+ PR_PERSONAL_HOME_PAGE_W,
+ PR_COMMENT_W
+};
+
+nsresult OutlookCardForURI(const nsACString &aUri, nsIAbCard **card);
+
+#endif // nsAbOutlookDirectory_h___
diff --git a/mailnews/addrbook/src/nsAbQueryStringToExpression.cpp b/mailnews/addrbook/src/nsAbQueryStringToExpression.cpp
new file mode 100644
index 000000000..fe1f22e00
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbQueryStringToExpression.cpp
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAbQueryStringToExpression.h"
+
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsITextToSubURI.h"
+#include "nsAbBooleanExpression.h"
+#include "nsAbBaseCID.h"
+#include "plstr.h"
+#include "nsIMutableArray.h"
+
+/**
+ * This code parses the query expression passed in as an addressbook URI.
+ * The expression takes the form:
+ * (BOOL1(FIELD1,OP1,VALUE1)..(FIELDn,OPn,VALUEn)(BOOL2(FIELD1,OP1,VALUE1)...)...)
+ *
+ * BOOLn A boolean operator joining subsequent terms delimited by ().
+ * For possible values see CreateBooleanExpression().
+ * FIELDn An addressbook card data field.
+ * OPn An operator for the search term.
+ * For possible values see CreateBooleanConditionString().
+ * VALUEn The value to be matched in the FIELDn via the OPn operator.
+ * The value must be URL encoded by the caller, if it contains any special
+ * characters including '(' and ')'.
+ */
+nsresult nsAbQueryStringToExpression::Convert (
+ const nsACString &aQueryString,
+ nsIAbBooleanExpression** expression)
+{
+ nsresult rv;
+
+ nsAutoCString q(aQueryString);
+ q.StripWhitespace();
+ const char *queryChars = q.get();
+
+ nsCOMPtr<nsISupports> s;
+ rv = ParseExpression(&queryChars, getter_AddRefs(s));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Case: Not end of string
+ if (*queryChars != 0)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIAbBooleanExpression> e(do_QueryInterface(s, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*expression = e);
+ return rv;
+}
+
+nsresult nsAbQueryStringToExpression::ParseExpression (
+ const char** index,
+ nsISupports** expression)
+{
+ nsresult rv;
+
+ if (**index != '(')
+ return NS_ERROR_FAILURE;
+
+ const char* indexBracket = *index + 1;
+ while (*indexBracket &&
+ *indexBracket != '(' && *indexBracket != ')')
+ indexBracket++;
+
+ // Case: End of string
+ if (*indexBracket == 0)
+ return NS_ERROR_FAILURE;
+
+ // Case: "((" or "()"
+ if (indexBracket == *index + 1)
+ {
+ return NS_ERROR_FAILURE;
+ }
+ // Case: "(*("
+ else if (*indexBracket == '(')
+ {
+ // printf ("Case: (*(: %s\n", *index);
+
+ nsCString operation;
+ rv = ParseOperationEntry (
+ *index, indexBracket,
+ getter_Copies (operation));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbBooleanExpression> e;
+ rv = CreateBooleanExpression(operation.get(),
+ getter_AddRefs(e));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Case: "(*)(*)....(*))"
+ *index = indexBracket;
+ rv = ParseExpressions (index, e);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*expression = e);
+ }
+ // Case" "(*)"
+ else if (*indexBracket == ')')
+ {
+ // printf ("Case: (*): %s\n", *index);
+
+ nsCOMPtr<nsIAbBooleanConditionString> conditionString;
+ rv = ParseCondition (index, indexBracket,
+ getter_AddRefs(conditionString));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*expression = conditionString);
+ }
+
+ if (**index != ')')
+ return NS_ERROR_FAILURE;
+
+ (*index)++;
+
+ return NS_OK;
+}
+
+
+nsresult nsAbQueryStringToExpression::ParseExpressions (
+ const char** index,
+ nsIAbBooleanExpression* expression)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> expressions(do_CreateInstance(NS_ARRAY_CONTRACTID,
+ &rv));
+ if (NS_FAILED(rv))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Case: ")(*)(*)....(*))"
+ // printf ("Case: )(*)(*)....(*)): %s\n", *index);
+ while (**index == '(')
+ {
+ nsCOMPtr<nsISupports> childExpression;
+ rv = ParseExpression(index, getter_AddRefs (childExpression));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ expressions->AppendElement(childExpression, false);
+ }
+
+ if (**index == 0)
+ return NS_ERROR_FAILURE;
+
+ // Case: "))"
+ // printf ("Case: )): %s\n", *index);
+
+ if (**index != ')')
+ return NS_ERROR_FAILURE;
+
+ expression->SetExpressions (expressions);
+
+ return NS_OK;
+}
+
+nsresult nsAbQueryStringToExpression::ParseCondition (
+ const char** index,
+ const char* indexBracketClose,
+ nsIAbBooleanConditionString** conditionString)
+{
+ nsresult rv;
+
+ (*index)++;
+
+ nsCString entries[3];
+ for (int i = 0; i < 3; i++)
+ {
+ rv = ParseConditionEntry (index, indexBracketClose,
+ getter_Copies (entries[i]));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (*index == indexBracketClose)
+ break;
+ }
+
+ if (*index != indexBracketClose)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIAbBooleanConditionString> c;
+ rv = CreateBooleanConditionString (
+ entries[0].get(),
+ entries[1].get(),
+ entries[2].get(),
+ getter_AddRefs (c));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*conditionString = c);
+ return NS_OK;
+}
+
+nsresult nsAbQueryStringToExpression::ParseConditionEntry (
+ const char** index,
+ const char* indexBracketClose,
+ char** entry)
+{
+ const char* indexDeliminator = *index;
+ while (indexDeliminator != indexBracketClose &&
+ *indexDeliminator != ',')
+ indexDeliminator++;
+
+ int entryLength = indexDeliminator - *index;
+ if (entryLength)
+ *entry = PL_strndup (*index, entryLength);
+ else
+ *entry = 0;
+
+ if (indexDeliminator != indexBracketClose)
+ *index = indexDeliminator + 1;
+ else
+ *index = indexDeliminator;
+
+ return NS_OK;
+}
+
+nsresult nsAbQueryStringToExpression::ParseOperationEntry (
+ const char* indexBracketOpen1,
+ const char* indexBracketOpen2,
+ char** operation)
+{
+ int operationLength = indexBracketOpen2 - indexBracketOpen1 - 1;
+ if (operationLength)
+ *operation = PL_strndup (indexBracketOpen1 + 1,
+ operationLength);
+ else
+ *operation = 0;
+
+ return NS_OK;
+}
+
+nsresult nsAbQueryStringToExpression::CreateBooleanExpression(
+ const char* operation,
+ nsIAbBooleanExpression** expression)
+{
+ nsAbBooleanOperationType op;
+ if (PL_strcasecmp (operation, "and") == 0)
+ op = nsIAbBooleanOperationTypes::AND;
+ else if (PL_strcasecmp (operation, "or") == 0)
+ op = nsIAbBooleanOperationTypes::OR;
+ else if (PL_strcasecmp (operation, "not") == 0)
+ op = nsIAbBooleanOperationTypes::NOT;
+ else
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+
+ nsCOMPtr <nsIAbBooleanExpression> expr = do_CreateInstance(NS_BOOLEANEXPRESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*expression = expr);
+
+ rv = expr->SetOperation (op);
+ return rv;
+}
+
+nsresult nsAbQueryStringToExpression::CreateBooleanConditionString (
+ const char* attribute,
+ const char* condition,
+ const char* value,
+ nsIAbBooleanConditionString** conditionString)
+{
+ if (attribute == 0 || condition == 0 || value == 0)
+ return NS_ERROR_FAILURE;
+
+ nsAbBooleanConditionType c;
+
+ if (PL_strcasecmp (condition, "=") == 0)
+ c = nsIAbBooleanConditionTypes::Is;
+ else if (PL_strcasecmp (condition, "!=") == 0)
+ c = nsIAbBooleanConditionTypes::IsNot;
+ else if (PL_strcasecmp (condition, "lt") == 0)
+ c = nsIAbBooleanConditionTypes::LessThan;
+ else if (PL_strcasecmp (condition, "gt") == 0)
+ c = nsIAbBooleanConditionTypes::GreaterThan;
+ else if (PL_strcasecmp (condition, "bw") == 0)
+ c = nsIAbBooleanConditionTypes::BeginsWith;
+ else if (PL_strcasecmp (condition, "ew") == 0)
+ c = nsIAbBooleanConditionTypes::EndsWith;
+ else if (PL_strcasecmp (condition, "c")== 0)
+ c = nsIAbBooleanConditionTypes::Contains;
+ else if (PL_strcasecmp (condition, "!c") == 0)
+ c = nsIAbBooleanConditionTypes::DoesNotContain;
+ else if (PL_strcasecmp (condition, "~=") == 0)
+ c = nsIAbBooleanConditionTypes::SoundsLike;
+ else if (PL_strcasecmp (condition, "regex") == 0)
+ c = nsIAbBooleanConditionTypes::RegExp;
+ else
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIAbBooleanConditionString> cs = do_CreateInstance(NS_BOOLEANCONDITIONSTRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = cs->SetCondition (c);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsITextToSubURI> textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID,&rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsString attributeUCS2;
+ nsString valueUCS2;
+
+ rv = textToSubURI->UnEscapeAndConvert("UTF-8",
+ attribute, getter_Copies(attributeUCS2));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = textToSubURI->UnEscapeAndConvert("UTF-8",
+ value, getter_Copies(valueUCS2));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertUTF16toUTF8 attributeUTF8(attributeUCS2);
+
+ rv = cs->SetName (attributeUTF8.get ());
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = cs->SetValue(valueUCS2.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else
+ {
+ NS_ConvertUTF8toUTF16 valueUCS2(value);
+
+ rv = cs->SetName (attribute);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = cs->SetValue (valueUCS2.get ());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+
+ NS_IF_ADDREF(*conditionString = cs);
+ return NS_OK;
+}
+
+
diff --git a/mailnews/addrbook/src/nsAbQueryStringToExpression.h b/mailnews/addrbook/src/nsAbQueryStringToExpression.h
new file mode 100644
index 000000000..dfd8da0c5
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbQueryStringToExpression.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbQueryStringToExpression_h__
+#define nsAbQueryStringToExpression_h__
+
+#include "nsIAbBooleanExpression.h"
+
+class nsAbQueryStringToExpression
+{
+public:
+ static nsresult Convert (
+ const nsACString &aQueryString,
+ nsIAbBooleanExpression** expression);
+
+protected:
+ static nsresult ParseExpression (
+ const char** index,
+ nsISupports** expression);
+ static nsresult ParseExpressions (
+ const char** index,
+ nsIAbBooleanExpression* expression);
+ static nsresult ParseCondition (
+ const char** index,
+ const char* indexBracketClose,
+ nsIAbBooleanConditionString** conditionString);
+
+ static nsresult ParseConditionEntry (
+ const char** index,
+ const char* indexBracketClose,
+ char** entry);
+ static nsresult ParseOperationEntry (
+ const char* indexBracketOpen1,
+ const char* indexBracketOpen2,
+ char** operation);
+
+ static nsresult CreateBooleanExpression(
+ const char* operation,
+ nsIAbBooleanExpression** expression);
+ static nsresult CreateBooleanConditionString (
+ const char* attribute,
+ const char* condition,
+ const char* value,
+ nsIAbBooleanConditionString** conditionString);
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAbUtils.h b/mailnews/addrbook/src/nsAbUtils.h
new file mode 100644
index 000000000..d6b8915e0
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbUtils.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAbUtils_h__
+#define nsAbUtils_h__
+
+#include "nsMemory.h"
+
+/*
+ * Wrapper class to automatically free an array of
+ * char* when class goes out of scope
+ */
+class CharPtrArrayGuard
+{
+public:
+ CharPtrArrayGuard (bool freeElements = true) :
+ mFreeElements (freeElements),
+ mArray (0),
+ mSize (0)
+ {
+ }
+
+ ~CharPtrArrayGuard ()
+ {
+ Free ();
+ }
+
+ char* operator[](int i)
+ {
+ return mArray[i];
+ }
+
+ uint32_t* GetSizeAddr(void)
+ {
+ return &mSize;
+ }
+
+ uint32_t GetSize(void)
+ {
+ return mSize;
+ }
+
+ char*** GetArrayAddr(void)
+ {
+ return &mArray;
+ }
+
+ const char** GetArray(void)
+ {
+ return (const char** ) mArray;
+ }
+
+public:
+
+private:
+ bool mFreeElements;
+ char **mArray;
+ uint32_t mSize;
+
+ void Free ()
+ {
+ if (!mArray)
+ return;
+
+ if (mFreeElements)
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mSize, mArray);
+ else
+ {
+ free(mArray);
+ }
+ }
+};
+
+/*
+ * Wrapper class to automatically free an array of
+ * char16_t* when class goes out of scope
+ */
+class PRUnicharPtrArrayGuard
+{
+public:
+ PRUnicharPtrArrayGuard (bool freeElements = true) :
+ mFreeElements (freeElements),
+ mArray (0),
+ mSize (0)
+ {
+ }
+
+ ~PRUnicharPtrArrayGuard ()
+ {
+ Free ();
+ }
+
+ char16_t* operator[](int i)
+ {
+ return mArray[i];
+ }
+
+ uint32_t* GetSizeAddr(void)
+ {
+ return &mSize;
+ }
+
+ uint32_t GetSize(void)
+ {
+ return mSize;
+ }
+
+ char16_t*** GetArrayAddr(void)
+ {
+ return &mArray;
+ }
+
+ const char16_t** GetArray(void)
+ {
+ return (const char16_t** ) mArray;
+ }
+
+public:
+
+private:
+ bool mFreeElements;
+ char16_t **mArray;
+ uint32_t mSize;
+ void Free ()
+ {
+ if (!mArray)
+ return;
+
+ if (mFreeElements)
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mSize, mArray);
+ else
+ {
+ free(mArray);
+ }
+ }
+};
+
+#endif /* nsAbUtils_h__ */
diff --git a/mailnews/addrbook/src/nsAbView.cpp b/mailnews/addrbook/src/nsAbView.cpp
new file mode 100644
index 000000000..77f3122df
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbView.cpp
@@ -0,0 +1,1451 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DebugOnly.h"
+
+#include "nsAbView.h"
+#include "nsISupports.h"
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsIAbCard.h"
+#include "nsILocale.h"
+#include "nsILocaleService.h"
+#include "prmem.h"
+#include "nsCollationCID.h"
+#include "nsIAbManager.h"
+#include "nsAbBaseCID.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsITreeColumns.h"
+#include "nsCRTGlue.h"
+#include "nsIMutableArray.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIStringBundle.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsArrayUtils.h"
+#include "nsIAddrDatabase.h" // for kPriEmailColumn
+#include "nsMsgUtils.h"
+#include "mozilla/Services.h"
+
+using namespace mozilla;
+
+#define CARD_NOT_FOUND -1
+#define ALL_ROWS -1
+
+#define PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST "mail.addr_book.lastnamefirst"
+#define PREF_MAIL_ADDR_BOOK_DISPLAYNAME_AUTOGENERATION "mail.addr_book.displayName.autoGeneration"
+#define PREF_MAIL_ADDR_BOOK_DISPLAYNAME_LASTNAMEFIRST "mail.addr_book.displayName.lastnamefirst"
+
+// Also, our default primary sort
+#define GENERATED_NAME_COLUMN_ID "GeneratedName"
+
+NS_IMPL_ISUPPORTS(nsAbView, nsIAbView, nsITreeView, nsIAbListener, nsIObserver)
+
+nsAbView::nsAbView() : mInitialized(false),
+ mIsAllDirectoryRootView(false),
+ mSuppressSelectionChange(false),
+ mSuppressCountChange(false),
+ mGeneratedNameFormat(0)
+{
+}
+
+nsAbView::~nsAbView()
+{
+ if (mInitialized) {
+ NS_ASSERTION(NS_SUCCEEDED(ClearView()), "failed to close view");
+ }
+}
+
+NS_IMETHODIMP nsAbView::ClearView()
+{
+ mDirectory = nullptr;
+ mAbViewListener = nullptr;
+ if (mTree)
+ mTree->SetView(nullptr);
+ mTree = nullptr;
+ mTreeSelection = nullptr;
+
+ if (mInitialized)
+ {
+ nsresult rv;
+ mInitialized = false;
+ nsCOMPtr<nsIPrefBranch> pbi(do_GetService(NS_PREFSERVICE_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = pbi->RemoveObserver(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = abManager->RemoveAddressBookListener(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ int32_t i = mCards.Length();
+ while(i-- > 0)
+ NS_ASSERTION(NS_SUCCEEDED(RemoveCardAt(i)), "remove card failed\n");
+
+ return NS_OK;
+}
+
+nsresult nsAbView::RemoveCardAt(int32_t row)
+{
+ nsresult rv;
+
+ AbCard *abcard = mCards.ElementAt(row);
+ NS_IF_RELEASE(abcard->card);
+ mCards.RemoveElementAt(row);
+ PR_FREEIF(abcard->primaryCollationKey);
+ PR_FREEIF(abcard->secondaryCollationKey);
+ PR_FREEIF(abcard);
+
+
+ // This needs to happen after we remove the card, as RowCountChanged() will call GetRowCount()
+ if (mTree) {
+ rv = mTree->RowCountChanged(row, -1);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ if (mAbViewListener && !mSuppressCountChange) {
+ rv = mAbViewListener->OnCountChanged(mCards.Length());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ return NS_OK;
+}
+
+nsresult nsAbView::SetGeneratedNameFormatFromPrefs()
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranchInt(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ return prefBranchInt->GetIntPref(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, &mGeneratedNameFormat);
+}
+
+nsresult nsAbView::Initialize()
+{
+ if (mInitialized)
+ return NS_OK;
+
+ mInitialized = true;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = abManager->AddAddressBookListener(this, nsIAbListener::all);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrefBranch> pbi(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = pbi->AddObserver(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, this, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mABBundle)
+ {
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(stringBundleService, NS_ERROR_UNEXPECTED);
+
+ rv = stringBundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(mABBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return SetGeneratedNameFormatFromPrefs();
+}
+
+NS_IMETHODIMP nsAbView::SetView(nsIAbDirectory *aAddressBook,
+ nsIAbViewListener *aAbViewListener,
+ const nsAString &aSortColumn,
+ const nsAString &aSortDirection,
+ nsAString &aResult)
+{
+ // Ensure we are initialized
+ nsresult rv = Initialize();
+
+ mAbViewListener = nullptr;
+ if (mTree)
+ {
+ // Try and speed deletion of old cards by disconnecting the tree from us.
+ mTreeSelection->ClearSelection();
+ mTree->SetView(nullptr);
+ }
+
+ // Clear out old cards
+ int32_t i = mCards.Length();
+ while(i-- > 0)
+ {
+ rv = RemoveCardAt(i);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "remove card failed\n");
+ }
+
+ // We replace all cards so any sorting is no longer valid.
+ mSortColumn.AssignLiteral("");
+ mSortDirection.AssignLiteral("");
+
+ nsCString uri;
+ aAddressBook->GetURI(uri);
+ int32_t searchBegin = uri.FindChar('?');
+ nsCString searchQuery(Substring(uri, searchBegin));
+ // This is a special case, a workaround basically, to just have all ABs.
+ if (searchQuery.EqualsLiteral("?")) {
+ searchQuery.AssignLiteral("");
+ }
+
+ if (Substring(uri, 0, searchBegin).EqualsLiteral(kAllDirectoryRoot)) {
+ mIsAllDirectoryRootView = true;
+ // We have special request case to search all addressbooks, so we need
+ // to iterate over all addressbooks.
+ // Since the request is for all addressbooks, the URI must have been
+ // passed with an extra '?'. We still check it for sanity and trim it here.
+ if (searchQuery.Find("??") == 0)
+ searchQuery = Substring(searchQuery, 1);
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = abManager->GetDirectories(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore = false;
+ nsCOMPtr<nsISupports> support;
+ nsCOMPtr<nsIAbDirectory> directory;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
+ rv = enumerator->GetNext(getter_AddRefs(support));
+ NS_ENSURE_SUCCESS(rv, rv);
+ directory = do_QueryInterface(support, &rv);
+
+ // If, for some reason, we are unable to get a directory, we continue.
+ if (NS_FAILED(rv))
+ continue;
+
+ // Get appropriate directory with search query.
+ nsCString uri;
+ directory->GetURI(uri);
+ rv = abManager->GetDirectory(uri + searchQuery, getter_AddRefs(directory));
+ mDirectory = directory;
+ rv = EnumerateCards();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ } else {
+ mIsAllDirectoryRootView = false;
+ mDirectory = aAddressBook;
+ rv = EnumerateCards();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ NS_NAMED_LITERAL_STRING(generatedNameColumnId, GENERATED_NAME_COLUMN_ID);
+
+ // See if the persisted sortColumn is valid.
+ // It may not be, if you migrated from older versions, or switched between
+ // a mozilla build and a commercial build, which have different columns.
+ nsAutoString actualSortColumn;
+ if (!generatedNameColumnId.Equals(aSortColumn) && mCards.Length()) {
+ nsIAbCard *card = mCards.ElementAt(0)->card;
+ nsString value;
+ // XXX todo
+ // Need to check if _Generic is valid. GetCardValue() will always return NS_OK for _Generic
+ // We're going to have to ask mDirectory if it is.
+ // It might not be. example: _ScreenName is valid in Netscape, but not Mozilla.
+ rv = GetCardValue(card, PromiseFlatString(aSortColumn).get(), value);
+ if (NS_FAILED(rv))
+ actualSortColumn = generatedNameColumnId;
+ else
+ actualSortColumn = aSortColumn;
+ }
+ else
+ actualSortColumn = aSortColumn;
+
+ rv = SortBy(actualSortColumn.get(), PromiseFlatString(aSortDirection).get(), false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mAbViewListener = aAbViewListener;
+ if (mAbViewListener && !mSuppressCountChange) {
+ rv = mAbViewListener->OnCountChanged(mCards.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ aResult = actualSortColumn;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetDirectory(nsIAbDirectory **aDirectory)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+ NS_IF_ADDREF(*aDirectory = mDirectory);
+ return NS_OK;
+}
+
+nsresult nsAbView::EnumerateCards()
+{
+ nsresult rv;
+ nsCOMPtr<nsISimpleEnumerator> cardsEnumerator;
+ nsCOMPtr<nsIAbCard> card;
+
+ if (!mDirectory)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = mDirectory->GetChildCards(getter_AddRefs(cardsEnumerator));
+ if (NS_SUCCEEDED(rv) && cardsEnumerator)
+ {
+ nsCOMPtr<nsISupports> item;
+ bool more;
+ while (NS_SUCCEEDED(cardsEnumerator->HasMoreElements(&more)) && more)
+ {
+ rv = cardsEnumerator->GetNext(getter_AddRefs(item));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr <nsIAbCard> card = do_QueryInterface(item);
+ // Malloc these from an arena
+ AbCard *abcard = (AbCard *) PR_Calloc(1, sizeof(struct AbCard));
+ if (!abcard)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ abcard->card = card;
+ NS_IF_ADDREF(abcard->card);
+
+ // XXX todo
+ // Would it be better to do an insertion sort, than append and sort?
+ // XXX todo
+ // If we knew how many cards there was going to be
+ // we could allocate an array of the size,
+ // instead of growing and copying as we append.
+ DebugOnly<bool> didAppend = mCards.AppendElement(abcard);
+ NS_ASSERTION(didAppend, "failed to append card");
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetRowCount(int32_t *aRowCount)
+{
+ *aRowCount = mCards.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetSelection(nsITreeSelection * *aSelection)
+{
+ NS_IF_ADDREF(*aSelection = mTreeSelection);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::SetSelection(nsITreeSelection * aSelection)
+{
+ mTreeSelection = aSelection;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetRowProperties(int32_t index, nsAString& properties)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetCellProperties(int32_t row, nsITreeColumn* col, nsAString& properties)
+{
+ NS_ENSURE_TRUE(row >= 0, NS_ERROR_UNEXPECTED);
+
+ if (mCards.Length() <= (size_t)row)
+ return NS_OK;
+
+ const char16_t* colID;
+ col->GetIdConst(&colID);
+ // "G" == "GeneratedName"
+ if (colID[0] != char16_t('G'))
+ return NS_OK;
+
+ nsIAbCard *card = mCards.ElementAt(row)->card;
+
+ bool isMailList;
+ nsresult rv = card->GetIsMailList(&isMailList);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (isMailList)
+ properties.AssignLiteral("MailList");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetColumnProperties(nsITreeColumn* col, nsAString& properties)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::IsContainer(int32_t index, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::IsContainerOpen(int32_t index, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::IsContainerEmpty(int32_t index, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::IsSeparator(int32_t index, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::IsSorted(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::CanDrop(int32_t index,
+ int32_t orientation,
+ nsIDOMDataTransfer *dataTransfer,
+ bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::Drop(int32_t row,
+ int32_t orientation,
+ nsIDOMDataTransfer *dataTransfer)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::GetParentIndex(int32_t rowIndex, int32_t *_retval)
+{
+ *_retval = -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::HasNextSibling(int32_t rowIndex, int32_t afterIndex, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::GetLevel(int32_t index, int32_t *_retval)
+{
+ *_retval = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetImageSrc(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetProgressMode(int32_t row, nsITreeColumn* col, int32_t* _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetCellValue(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ return NS_OK;
+}
+
+nsresult nsAbView::GetCardValue(nsIAbCard *card, const char16_t *colID,
+ nsAString &_retval)
+{
+ if (nsString(colID).EqualsLiteral("addrbook")) {
+ nsCString dirID;
+ nsresult rv = card->GetDirectoryId(dirID);
+ if (NS_SUCCEEDED(rv))
+ CopyUTF8toUTF16(Substring(dirID, dirID.FindChar('&') + 1), _retval);
+
+ return rv;
+ }
+
+ // "G" == "GeneratedName", "_P" == "_PhoneticName"
+ // else, standard column (like PrimaryEmail and _AimScreenName)
+ if (colID[0] == char16_t('G'))
+ return card->GenerateName(mGeneratedNameFormat, mABBundle, _retval);
+
+ if (colID[0] == char16_t('_') && colID[1] == char16_t('P'))
+ // Use LN/FN order for the phonetic name
+ return card->GeneratePhoneticName(true, _retval);
+
+ if (!NS_strcmp(colID, u"ChatName"))
+ return card->GenerateChatName(_retval);
+
+ nsresult rv = card->GetPropertyAsAString(NS_ConvertUTF16toUTF8(colID).get(), _retval);
+ if (rv == NS_ERROR_NOT_AVAILABLE) {
+ rv = NS_OK;
+ _retval.Truncate();
+ }
+ return rv;
+}
+
+nsresult nsAbView::RefreshTree()
+{
+ nsresult rv;
+
+ // The PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST pref affects how the GeneratedName column looks.
+ // so if the GeneratedName is our primary or secondary sort,
+ // we need to resort.
+ // the same applies for kPhoneticNameColumn
+ //
+ // XXX optimize me
+ // PrimaryEmail is always the secondary sort, unless it is currently the
+ // primary sort. So, if PrimaryEmail is the primary sort,
+ // GeneratedName might be the secondary sort.
+ //
+ // One day, we can get fancy and remember what the secondary sort is.
+ // We do that, we can fix this code. At best, it will turn a sort into a invalidate.
+ //
+ // If neither the primary nor the secondary sorts are GeneratedName (or kPhoneticNameColumn),
+ // all we have to do is invalidate (to show the new GeneratedNames),
+ // but the sort will not change.
+ if (mSortColumn.EqualsLiteral(GENERATED_NAME_COLUMN_ID) ||
+ mSortColumn.EqualsLiteral(kPriEmailProperty) ||
+ mSortColumn.EqualsLiteral(kPhoneticNameColumn)) {
+ rv = SortBy(mSortColumn.get(), mSortDirection.get(), true);
+ }
+ else {
+ rv = InvalidateTree(ALL_ROWS);
+
+ // Although the selection hasn't changed, the card that is selected may need
+ // to be displayed differently, therefore pretend that the selection has
+ // changed to force that update.
+ SelectionChanged();
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbView::GetCellText(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ NS_ENSURE_TRUE(row >= 0 && (size_t)row < mCards.Length(), NS_ERROR_UNEXPECTED);
+
+ nsIAbCard *card = mCards.ElementAt(row)->card;
+ const char16_t* colID;
+ col->GetIdConst(&colID);
+ return GetCardValue(card, colID, _retval);
+}
+
+NS_IMETHODIMP nsAbView::SetTree(nsITreeBoxObject *tree)
+{
+ mTree = tree;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::ToggleOpenState(int32_t index)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::CycleHeader(nsITreeColumn* col)
+{
+ return NS_OK;
+}
+
+nsresult nsAbView::InvalidateTree(int32_t row)
+{
+ if (!mTree)
+ return NS_OK;
+
+ if (row == ALL_ROWS)
+ return mTree->Invalidate();
+ else
+ return mTree->InvalidateRow(row);
+}
+
+NS_IMETHODIMP nsAbView::SelectionChanged()
+{
+ if (mAbViewListener && !mSuppressSelectionChange) {
+ nsresult rv = mAbViewListener->OnSelectionChanged();
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::CycleCell(int32_t row, nsITreeColumn* col)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::IsEditable(int32_t row, nsITreeColumn* col, bool* _retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::IsSelectable(int32_t row, nsITreeColumn* col, bool* _retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::SetCellValue(int32_t row, nsITreeColumn* col, const nsAString& value)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::SetCellText(int32_t row, nsITreeColumn* col, const nsAString& value)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::PerformAction(const char16_t *action)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::PerformActionOnRow(const char16_t *action, int32_t row)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::PerformActionOnCell(const char16_t *action, int32_t row, nsITreeColumn* col)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsAbView::GetCardFromRow(int32_t row, nsIAbCard **aCard)
+{
+ *aCard = nullptr;
+ NS_ENSURE_TRUE(row >= 0, NS_ERROR_UNEXPECTED);
+ if (mCards.Length() <= (size_t)row) {
+ return NS_OK;
+ }
+
+ AbCard *a = mCards.ElementAt(row);
+ if (!a)
+ return NS_OK;
+
+ NS_IF_ADDREF(*aCard = a->card);
+ return NS_OK;
+}
+
+#define DESCENDING_SORT_FACTOR -1
+#define ASCENDING_SORT_FACTOR 1
+
+typedef struct SortClosure
+{
+ const char16_t *colID;
+ int32_t factor;
+ nsAbView *abView;
+} SortClosure;
+
+static int
+inplaceSortCallback(const AbCard *card1, const AbCard *card2, SortClosure *closure)
+{
+ int32_t sortValue;
+
+ // If we are sorting the "PrimaryEmail", swap the collation keys, as the secondary is always the
+ // PrimaryEmail. Use the last primary key as the secondary key.
+ //
+ // "Pr" to distinguish "PrimaryEmail" from "PagerNumber"
+ if (closure->colID[0] == char16_t('P') && closure->colID[1] == char16_t('r')) {
+ sortValue = closure->abView->CompareCollationKeys(card1->secondaryCollationKey,card1->secondaryCollationKeyLen,card2->secondaryCollationKey,card2->secondaryCollationKeyLen);
+ if (sortValue)
+ return sortValue * closure->factor;
+ else
+ return closure->abView->CompareCollationKeys(card1->primaryCollationKey,card1->primaryCollationKeyLen,card2->primaryCollationKey,card2->primaryCollationKeyLen) * (closure->factor);
+ }
+ else {
+ sortValue = closure->abView->CompareCollationKeys(card1->primaryCollationKey,card1->primaryCollationKeyLen,card2->primaryCollationKey,card2->primaryCollationKeyLen);
+ if (sortValue)
+ return sortValue * (closure->factor);
+ else
+ return closure->abView->CompareCollationKeys(card1->secondaryCollationKey,card1->secondaryCollationKeyLen,card2->secondaryCollationKey,card2->secondaryCollationKeyLen) * (closure->factor);
+ }
+}
+
+static void SetSortClosure(const char16_t *sortColumn, const char16_t *sortDirection, nsAbView *abView, SortClosure *closure)
+{
+ closure->colID = sortColumn;
+
+ if (sortDirection && !NS_strcmp(sortDirection, u"descending"))
+ closure->factor = DESCENDING_SORT_FACTOR;
+ else
+ closure->factor = ASCENDING_SORT_FACTOR;
+
+ closure->abView = abView;
+ return;
+}
+
+class CardComparator
+{
+public:
+ void SetClosure(SortClosure *closure) { m_closure = closure; };
+
+ bool Equals(const AbCard *a, const AbCard *b) const {
+ return inplaceSortCallback(a, b, m_closure) == 0;
+ }
+ bool LessThan(const AbCard *a, const AbCard *b) const{
+ return inplaceSortCallback(a, b, m_closure) < 0;
+ }
+
+private:
+ SortClosure *m_closure;
+};
+
+NS_IMETHODIMP nsAbView::SortBy(const char16_t *colID, const char16_t *sortDir, bool aResort = false)
+{
+ nsresult rv;
+
+ int32_t count = mCards.Length();
+
+ nsAutoString sortColumn;
+ if (!colID)
+ sortColumn = NS_LITERAL_STRING(GENERATED_NAME_COLUMN_ID); // default sort column
+ else
+ sortColumn = colID;
+
+ nsAutoString sortDirection;
+ if (!sortDir)
+ sortDirection = NS_LITERAL_STRING("ascending"); // default direction
+ else
+ sortDirection = sortDir;
+
+ if (mSortColumn.Equals(sortColumn) && !aResort) {
+ if (mSortDirection.Equals(sortDir)) {
+ // If sortColumn and sortDirection are identical since the last call, do nothing.
+ return NS_OK;
+ } else {
+ // If we are sorting by how we are already sorted,
+ // and just the sort direction changes, just reverse.
+ int32_t halfPoint = count / 2;
+ for (int32_t i = 0; i < halfPoint; i++) {
+ // Swap the elements.
+ AbCard *ptr1 = mCards.ElementAt(i);
+ AbCard *ptr2 = mCards.ElementAt(count - i - 1);
+ mCards.ReplaceElementAt(i, ptr2);
+ mCards.ReplaceElementAt(count - i - 1, ptr1);
+ }
+ mSortDirection = sortDir;
+ }
+ }
+ else {
+ // Generate collation keys
+ for (int32_t i = 0; i < count; i++) {
+ AbCard *abcard = mCards.ElementAt(i);
+
+ rv = GenerateCollationKeysForCard(sortColumn.get(), abcard);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ // We need to do full sort.
+ SortClosure closure;
+ SetSortClosure(sortColumn.get(), sortDirection.get(), this, &closure);
+
+ nsCOMPtr<nsIMutableArray> selectedCards = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetSelectedCards(selectedCards);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> indexCard;
+
+ if (mTreeSelection) {
+ int32_t currentIndex = -1;
+
+ rv = mTreeSelection->GetCurrentIndex(&currentIndex);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (currentIndex != -1) {
+ rv = GetCardFromRow(currentIndex, getter_AddRefs(indexCard));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+
+ CardComparator cardComparator;
+ cardComparator.SetClosure(&closure);
+ mCards.Sort(cardComparator);
+
+ rv = ReselectCards(selectedCards, indexCard);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mSortColumn = sortColumn;
+ mSortDirection = sortDirection;
+ }
+
+ rv = InvalidateTree(ALL_ROWS);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+int32_t nsAbView::CompareCollationKeys(uint8_t *key1, uint32_t len1, uint8_t *key2, uint32_t len2)
+{
+ NS_ASSERTION(mCollationKeyGenerator, "no key generator");
+ if (!mCollationKeyGenerator)
+ return 0;
+
+ int32_t result;
+
+ nsresult rv = mCollationKeyGenerator->CompareRawSortKey(key1,len1,key2,len2,&result);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "key compare failed");
+ if (NS_FAILED(rv))
+ result = 0;
+ return result;
+}
+
+nsresult nsAbView::GenerateCollationKeysForCard(const char16_t *colID, AbCard *abcard)
+{
+ nsresult rv;
+ nsString value;
+
+ if (!mCollationKeyGenerator)
+ {
+ nsCOMPtr<nsILocaleService> localeSvc = do_GetService(NS_LOCALESERVICE_CONTRACTID,&rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILocale> locale;
+ rv = localeSvc->GetApplicationLocale(getter_AddRefs(locale));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsICollationFactory> factory = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = factory->CreateCollation(locale, getter_AddRefs(mCollationKeyGenerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = GetCardValue(abcard->card, colID, value);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ PR_FREEIF(abcard->primaryCollationKey);
+ rv = mCollationKeyGenerator->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive,
+ value, &(abcard->primaryCollationKey), &(abcard->primaryCollationKeyLen));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Hardcode email to be our secondary key. As we are doing this, just call
+ // the card's GetCardValue direct, rather than our own function which will
+ // end up doing the same as then we can save a bit of time.
+ rv = abcard->card->GetPrimaryEmail(value);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ PR_FREEIF(abcard->secondaryCollationKey);
+ rv = mCollationKeyGenerator->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive,
+ value, &(abcard->secondaryCollationKey), &(abcard->secondaryCollationKeyLen));
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+// A helper method currently returns true if the directory is an LDAP.
+// We can tweak this to return true for all Remote Address Books where the
+// search is asynchronous.
+bool isDirectoryRemote(nsCOMPtr<nsIAbDirectory> aDir)
+{
+ nsCString uri;
+ aDir->GetURI(uri);
+ return (uri.Find("moz-abldapdirectory") != kNotFound);
+}
+
+// A helper method to get the query string for nsIAbDirectory.
+nsCString getQuery(nsCOMPtr<nsIAbDirectory> aDir)
+{
+ nsCString uri;
+ aDir->GetURI(uri);
+ int32_t searchBegin = uri.FindChar('?');
+ if (searchBegin == kNotFound)
+ return EmptyCString();
+
+ return nsCString(Substring(uri, searchBegin));
+}
+
+NS_IMETHODIMP nsAbView::OnItemAdded(nsISupports *parentDir, nsISupports *item)
+{
+ nsresult rv;
+ nsCOMPtr <nsIAbDirectory> directory = do_QueryInterface(parentDir, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool isRemote = isDirectoryRemote(directory);
+ // If the search is performed on All Address books, its possible that the LDAP
+ // results start coming when mDirectory has changed (LDAP search works in an
+ // asynchronous manner).
+ // Since the listeners are being added to all nsAbView instances, we need to
+ // make sure that all the views aren't updated by the listeners.
+ bool isDirectoryQuery = false;
+ bool isMDirectoryQuery = false;
+ // See if current parent directory to which the item is added is a query
+ // directory.
+ directory->GetIsQuery(&isDirectoryQuery);
+ // Get the query string for the directory in Advanced AB Search window.
+ nsCString directoryQuery(getQuery(directory));
+ // See if the selected directory in Address book main window is a query
+ // directory.
+ mDirectory->GetIsQuery(&isMDirectoryQuery);
+ // Get the query string for the selected directory in the main AB window.
+ nsCString mDirectoryQuery(getQuery(mDirectory));
+ if ((mIsAllDirectoryRootView && isRemote &&
+ isDirectoryQuery && isMDirectoryQuery &&
+ directoryQuery.Equals(mDirectoryQuery)) ||
+ directory.get() == mDirectory.get()) {
+ nsCOMPtr <nsIAbCard> addedCard = do_QueryInterface(item);
+ if (addedCard) {
+ // Malloc these from an arena
+ AbCard *abcard = (AbCard *) PR_Calloc(1, sizeof(struct AbCard));
+ if (!abcard)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ abcard->card = addedCard;
+ NS_IF_ADDREF(abcard->card);
+
+ rv = GenerateCollationKeysForCard(mSortColumn.get(), abcard);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ int32_t index;
+ rv = AddCard(abcard, false /* select card */, &index);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAbView::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
+{
+ if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ if (nsDependentString(someData).EqualsLiteral(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST)) {
+ nsresult rv = SetGeneratedNameFormatFromPrefs();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = RefreshTree();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsAbView::AddCard(AbCard *abcard, bool selectCardAfterAdding, int32_t *index)
+{
+ nsresult rv = NS_OK;
+ NS_ENSURE_ARG_POINTER(abcard);
+
+ *index = FindIndexForInsert(abcard);
+ mCards.InsertElementAt(*index, abcard);
+
+ // This needs to happen after we insert the card, as RowCountChanged() will call GetRowCount()
+ if (mTree)
+ rv = mTree->RowCountChanged(*index, 1);
+
+ // Checking for mTree here works around core bug 399227
+ if (selectCardAfterAdding && mTreeSelection && mTree) {
+ mTreeSelection->SetCurrentIndex(*index);
+ mTreeSelection->RangedSelect(*index, *index, false /* augment */);
+ }
+
+ if (mAbViewListener && !mSuppressCountChange) {
+ rv = mAbViewListener->OnCountChanged(mCards.Length());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ return rv;
+}
+
+int32_t nsAbView::FindIndexForInsert(AbCard *abcard)
+{
+ int32_t count = mCards.Length();
+ int32_t i;
+
+ SortClosure closure;
+ SetSortClosure(mSortColumn.get(), mSortDirection.get(), this, &closure);
+
+ // XXX todo
+ // Make this a binary search
+ for (i=0; i < count; i++) {
+ int32_t value = inplaceSortCallback(abcard, mCards.ElementAt(i), &closure);
+ // XXX Fix me, this is not right for both ascending and descending
+ if (value <= 0)
+ break;
+ }
+ return i;
+}
+
+NS_IMETHODIMP nsAbView::OnItemRemoved(nsISupports *parentDir, nsISupports *item)
+{
+ nsresult rv;
+
+ nsCOMPtr <nsIAbDirectory> directory = do_QueryInterface(parentDir,&rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (directory.get() == mDirectory.get())
+ return RemoveCardAndSelectNextCard(item);
+
+ // The pointers aren't the same, are the URI strings similar? This is most
+ // likely the case if the current directory is a search on a directory.
+ nsCString currentURI;
+ rv = mDirectory->GetURI(currentURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If it is a search, it will have something like ?(or(PrimaryEmail...
+ // on the end of the string, so remove that before comparing
+ int32_t pos = currentURI.FindChar('?');
+ if (pos != -1)
+ currentURI.SetLength(pos);
+
+ nsCString notifiedURI;
+ rv = directory->GetURI(notifiedURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (currentURI.Equals(notifiedURI))
+ return RemoveCardAndSelectNextCard(item);
+
+ return NS_OK;
+}
+
+nsresult nsAbView::RemoveCardAndSelectNextCard(nsISupports *item)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr <nsIAbCard> card = do_QueryInterface(item);
+ if (card) {
+ int32_t index = FindIndexForCard(card);
+ if (index != CARD_NOT_FOUND) {
+ bool selectNextCard = false;
+ if (mTreeSelection) {
+ int32_t selectedIndex;
+ // XXX todo
+ // Make sure it works if nothing selected
+ mTreeSelection->GetCurrentIndex(&selectedIndex);
+ if (index == selectedIndex)
+ selectNextCard = true;
+ }
+
+ rv = RemoveCardAt(index);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (selectNextCard) {
+ int32_t count = mCards.Length();
+ if (count && mTreeSelection) {
+ // If we deleted the last card, adjust so we select the new "last" card
+ if (index >= (count - 1)) {
+ index = count -1;
+ }
+ mTreeSelection->SetCurrentIndex(index);
+ mTreeSelection->RangedSelect(index, index, false /* augment */);
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+int32_t nsAbView::FindIndexForCard(nsIAbCard *card)
+{
+ int32_t count = mCards.Length();
+ int32_t i;
+
+ // You can't implement the binary search here, as all you have is the nsIAbCard
+ // you might be here because one of the card properties has changed, and that property
+ // could be the collation key.
+ for (i=0; i < count; i++) {
+ AbCard *abcard = mCards.ElementAt(i);
+ bool equals;
+ nsresult rv = card->Equals(abcard->card, &equals);
+ if (NS_SUCCEEDED(rv) && equals) {
+ return i;
+ }
+ }
+ return CARD_NOT_FOUND;
+}
+
+NS_IMETHODIMP nsAbView::OnItemPropertyChanged(nsISupports *item, const char *property, const char16_t *oldValue, const char16_t *newValue)
+{
+ nsresult rv;
+
+ nsCOMPtr <nsIAbCard> card = do_QueryInterface(item);
+ if (!card)
+ return NS_OK;
+
+ int32_t index = FindIndexForCard(card);
+ if (index == -1)
+ return NS_OK;
+
+ AbCard *oldCard = mCards.ElementAt(index);
+
+ // Malloc these from an arena
+ AbCard *newCard = (AbCard *) PR_Calloc(1, sizeof(struct AbCard));
+ if (!newCard)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ newCard->card = card;
+ NS_IF_ADDREF(newCard->card);
+
+ rv = GenerateCollationKeysForCard(mSortColumn.get(), newCard);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool cardWasSelected = false;
+
+ if (mTreeSelection) {
+ rv = mTreeSelection->IsSelected(index, &cardWasSelected);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ if (!CompareCollationKeys(newCard->primaryCollationKey,newCard->primaryCollationKeyLen,oldCard->primaryCollationKey,oldCard->primaryCollationKeyLen)
+ && CompareCollationKeys(newCard->secondaryCollationKey,newCard->secondaryCollationKeyLen,oldCard->secondaryCollationKey,oldCard->secondaryCollationKeyLen)) {
+ // No need to remove and add, since the collation keys haven't changed.
+ // Since they haven't changed, the card will sort to the same place.
+ // We just need to clean up what we allocated.
+ NS_IF_RELEASE(newCard->card);
+ if (newCard->primaryCollationKey)
+ free(newCard->primaryCollationKey);
+ if (newCard->secondaryCollationKey)
+ free(newCard->secondaryCollationKey);
+ PR_FREEIF(newCard);
+
+ // Still need to invalidate, as the other columns may have changed.
+ rv = InvalidateTree(index);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ else {
+ mSuppressSelectionChange = true;
+ mSuppressCountChange = true;
+
+ // Remove the old card.
+ rv = RemoveCardAt(index);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "remove card failed\n");
+
+ // Add the card we created, and select it (to restore selection) if it was selected.
+ rv = AddCard(newCard, cardWasSelected /* select card */, &index);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "add card failed\n");
+
+ mSuppressSelectionChange = false;
+ mSuppressCountChange = false;
+
+ // Ensure restored selection is visible
+ if (cardWasSelected && mTree)
+ mTree->EnsureRowIsVisible(index);
+ }
+
+ // Although the selection hasn't changed, the card that is selected may need
+ // to be displayed differently, therefore pretend that the selection has
+ // changed to force that update.
+ if (cardWasSelected)
+ SelectionChanged();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::SelectAll()
+{
+ if (mTreeSelection && mTree) {
+ mTreeSelection->SelectAll();
+ mTree->Invalidate();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetSortDirection(nsAString & aDirection)
+{
+ aDirection = mSortDirection;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::GetSortColumn(nsAString & aColumn)
+{
+ aColumn = mSortColumn;
+ return NS_OK;
+}
+
+nsresult nsAbView::ReselectCards(nsIArray *aCards, nsIAbCard *aIndexCard)
+{
+ uint32_t count;
+ uint32_t i;
+
+ if (!mTreeSelection || !aCards)
+ return NS_OK;
+
+ nsresult rv = mTreeSelection->ClearSelection();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = aCards->GetLength(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we don't have any cards selected, nothing else to do.
+ if (!count)
+ return NS_OK;
+
+ for (i = 0; i < count; i++) {
+ nsCOMPtr<nsIAbCard> card = do_QueryElementAt(aCards, i);
+ if (card) {
+ int32_t index = FindIndexForCard(card);
+ if (index != CARD_NOT_FOUND) {
+ mTreeSelection->RangedSelect(index, index, true /* augment */);
+ }
+ }
+ }
+
+ // Reset the index card, and ensure it is visible.
+ if (aIndexCard) {
+ int32_t currentIndex = FindIndexForCard(aIndexCard);
+ rv = mTreeSelection->SetCurrentIndex(currentIndex);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mTree) {
+ rv = mTree->EnsureRowIsVisible(currentIndex);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::DeleteSelectedCards()
+{
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> cardsToDelete = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetSelectedCards(cardsToDelete);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // mDirectory should not be null
+ // Bullet proof (and assert) to help figure out bug #127748
+ NS_ENSURE_TRUE(mDirectory, NS_ERROR_UNEXPECTED);
+
+ rv = mDirectory->DeleteCards(cardsToDelete);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+nsresult nsAbView::GetSelectedCards(nsCOMPtr<nsIMutableArray> &aSelectedCards)
+{
+ if (!mTreeSelection)
+ return NS_OK;
+
+ int32_t selectionCount;
+ nsresult rv = mTreeSelection->GetRangeCount(&selectionCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!selectionCount)
+ return NS_OK;
+
+ for (int32_t i = 0; i < selectionCount; i++)
+ {
+ int32_t startRange;
+ int32_t endRange;
+ rv = mTreeSelection->GetRangeAt(i, &startRange, &endRange);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+ int32_t totalCards = mCards.Length();
+ if (startRange >= 0 && startRange < totalCards)
+ {
+ for (int32_t rangeIndex = startRange; rangeIndex <= endRange && rangeIndex < totalCards; rangeIndex++) {
+ nsCOMPtr<nsIAbCard> abCard;
+ rv = GetCardFromRow(rangeIndex, getter_AddRefs(abCard));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = aSelectedCards->AppendElement(abCard, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbView::SwapFirstNameLastName()
+{
+ if (!mTreeSelection)
+ return NS_OK;
+
+ int32_t selectionCount;
+ nsresult rv = mTreeSelection->GetRangeCount(&selectionCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!selectionCount)
+ return NS_OK;
+
+ // Prepare for displayname generation
+ // No cache for pref and bundle since the swap operation is not executed frequently
+ bool displayNameAutoGeneration;
+ bool displayNameLastnamefirst = false;
+
+ nsCOMPtr<nsIPrefBranch> pPrefBranchInt(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = pPrefBranchInt->GetBoolPref(PREF_MAIL_ADDR_BOOK_DISPLAYNAME_AUTOGENERATION, &displayNameAutoGeneration);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ if (displayNameAutoGeneration)
+ {
+ nsCOMPtr<nsIPrefLocalizedString> pls;
+ rv = pPrefBranchInt->GetComplexValue(PREF_MAIL_ADDR_BOOK_DISPLAYNAME_LASTNAMEFIRST,
+ NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(pls));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString str;
+ pls->ToString(getter_Copies(str));
+ displayNameLastnamefirst = str.EqualsLiteral("true");
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ for (int32_t i = 0; i < selectionCount; i++)
+ {
+ int32_t startRange;
+ int32_t endRange;
+ rv = mTreeSelection->GetRangeAt(i, &startRange, &endRange);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+ int32_t totalCards = mCards.Length();
+ if (startRange >= 0 && startRange < totalCards)
+ {
+ for (int32_t rangeIndex = startRange; rangeIndex <= endRange && rangeIndex < totalCards; rangeIndex++) {
+ nsCOMPtr<nsIAbCard> abCard;
+ rv = GetCardFromRow(rangeIndex, getter_AddRefs(abCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Swap FN/LN
+ nsAutoString fn, ln;
+ abCard->GetFirstName(fn);
+ abCard->GetLastName(ln);
+ if (!fn.IsEmpty() || !ln.IsEmpty())
+ {
+ abCard->SetFirstName(ln);
+ abCard->SetLastName(fn);
+
+ // Generate display name using the new order
+ if (displayNameAutoGeneration &&
+ !fn.IsEmpty() && !ln.IsEmpty())
+ {
+ nsString dnLnFn;
+ nsString dnFnLn;
+ const char16_t *nameString[2];
+ const char16_t *formatString;
+
+ // The format should stays the same before/after we swap the names
+ formatString = displayNameLastnamefirst ?
+ u"lastFirstFormat" :
+ u"firstLastFormat";
+
+ // Generate both ln/fn and fn/ln combination since we need both later
+ // to check to see if the current display name was edited
+ // note that fn/ln still hold the values before the swap
+ nameString[0] = ln.get();
+ nameString[1] = fn.get();
+ rv = bundle->FormatStringFromName(formatString,
+ nameString, 2, getter_Copies(dnLnFn));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nameString[0] = fn.get();
+ nameString[1] = ln.get();
+ rv = bundle->FormatStringFromName(formatString,
+ nameString, 2, getter_Copies(dnFnLn));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the current display name
+ nsAutoString dn;
+ rv = abCard->GetDisplayName(dn);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Swap the display name if not edited
+ if (displayNameLastnamefirst)
+ {
+ if (dn.Equals(dnLnFn))
+ abCard->SetDisplayName(dnFnLn);
+ }
+ else
+ {
+ if (dn.Equals(dnFnLn))
+ abCard->SetDisplayName(dnLnFn);
+ }
+ }
+
+ // Swap phonetic names
+ rv = abCard->GetPropertyAsAString(kPhoneticFirstNameProperty, fn);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = abCard->GetPropertyAsAString(kPhoneticLastNameProperty, ln);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!fn.IsEmpty() || !ln.IsEmpty())
+ {
+ abCard->SetPropertyAsAString(kPhoneticFirstNameProperty, ln);
+ abCard->SetPropertyAsAString(kPhoneticLastNameProperty, fn);
+ }
+ }
+ }
+ }
+ }
+ // Update the tree
+ // Re-sort if either generated or phonetic name is primary or secondary sort,
+ // otherwise invalidate to reflect the change
+ rv = RefreshTree();
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAbView::GetSelectedAddresses(nsIArray **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> selectedCards = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetSelectedCards(selectedCards);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> addresses = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t count;
+ selectedCards->GetLength(&count);
+
+ for (uint32_t i = 0; i < count; i++) {
+ nsCOMPtr<nsIAbCard> card(do_QueryElementAt(selectedCards, i, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isMailList;
+ card->GetIsMailList(&isMailList);
+ nsAutoString primaryEmail;
+ if (isMailList) {
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString mailListURI;
+ rv = card->GetMailListURI(getter_Copies(mailListURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> mailList;
+ rv = abManager->GetDirectory(mailListURI, getter_AddRefs(mailList));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> mailListAddresses;
+ rv = mailList->GetAddressLists(getter_AddRefs(mailListAddresses));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ uint32_t mailListCount = 0;
+ mailListAddresses->GetLength(&mailListCount);
+
+ for (uint32_t j = 0; j < mailListCount; j++) {
+ nsCOMPtr<nsIAbCard> mailListCard = do_QueryElementAt(mailListAddresses, j, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = mailListCard->GetPrimaryEmail(primaryEmail);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!primaryEmail.IsEmpty()) {
+ nsCOMPtr<nsISupportsString> supportsEmail(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ supportsEmail->SetData(primaryEmail);
+ addresses->AppendElement(supportsEmail, false);
+ }
+ }
+ }
+ else {
+ rv = card->GetPrimaryEmail(primaryEmail);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!primaryEmail.IsEmpty()) {
+ nsCOMPtr<nsISupportsString> supportsEmail(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ supportsEmail->SetData(primaryEmail);
+ addresses->AppendElement(supportsEmail, false);
+ }
+ }
+ }
+
+ NS_IF_ADDREF(*_retval = addresses);
+
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAbView.h b/mailnews/addrbook/src/nsAbView.h
new file mode 100644
index 000000000..f0a913082
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbView.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsAbView_H_
+#define _nsAbView_H_
+
+#include "nsISupports.h"
+#include "nsStringGlue.h"
+#include "nsIAbView.h"
+#include "nsITreeView.h"
+#include "nsITreeBoxObject.h"
+#include "nsITreeSelection.h"
+#include "nsTArray.h"
+#include "nsIAbDirectory.h"
+#include "nsIAtom.h"
+#include "nsICollation.h"
+#include "nsIAbListener.h"
+#include "nsIObserver.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMemory.h"
+#include "nsIStringBundle.h"
+
+typedef struct AbCard
+{
+ nsIAbCard *card;
+ uint32_t primaryCollationKeyLen;
+ uint32_t secondaryCollationKeyLen;
+ uint8_t *primaryCollationKey;
+ uint8_t *secondaryCollationKey;
+} AbCard;
+
+
+class nsAbView : public nsIAbView, public nsITreeView, public nsIAbListener, public nsIObserver
+{
+public:
+ nsAbView();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABVIEW
+ NS_DECL_NSITREEVIEW
+ NS_DECL_NSIABLISTENER
+ NS_DECL_NSIOBSERVER
+
+ int32_t CompareCollationKeys(uint8_t *key1, uint32_t len1, uint8_t *key2, uint32_t len2);
+
+private:
+ virtual ~nsAbView();
+ nsresult Initialize();
+ int32_t FindIndexForInsert(AbCard *abcard);
+ int32_t FindIndexForCard(nsIAbCard *card);
+ nsresult GenerateCollationKeysForCard(const char16_t *colID, AbCard *abcard);
+ nsresult InvalidateTree(int32_t row);
+ nsresult RemoveCardAt(int32_t row);
+ nsresult AddCard(AbCard *abcard, bool selectCardAfterAdding, int32_t *index);
+ nsresult RemoveCardAndSelectNextCard(nsISupports *item);
+ nsresult EnumerateCards();
+ nsresult SetGeneratedNameFormatFromPrefs();
+ nsresult GetSelectedCards(nsCOMPtr<nsIMutableArray> &aSelectedCards);
+ nsresult ReselectCards(nsIArray *aCards, nsIAbCard *aIndexCard);
+ nsresult GetCardValue(nsIAbCard *card, const char16_t *colID, nsAString &_retval);
+ nsresult RefreshTree();
+
+ nsCOMPtr<nsITreeBoxObject> mTree;
+ nsCOMPtr<nsITreeSelection> mTreeSelection;
+ nsCOMPtr <nsIAbDirectory> mDirectory;
+ nsTArray<AbCard*> mCards;
+ nsString mSortColumn;
+ nsString mSortDirection;
+ nsCOMPtr<nsICollation> mCollationKeyGenerator;
+ nsCOMPtr<nsIAbViewListener> mAbViewListener;
+ nsCOMPtr<nsIStringBundle> mABBundle;
+
+ bool mInitialized;
+ bool mIsAllDirectoryRootView;
+ bool mSuppressSelectionChange;
+ bool mSuppressCountChange;
+ int32_t mGeneratedNameFormat;
+};
+
+#endif /* _nsAbView_H_ */
diff --git a/mailnews/addrbook/src/nsAbWinHelper.cpp b/mailnews/addrbook/src/nsAbWinHelper.cpp
new file mode 100644
index 000000000..c5b9d131f
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbWinHelper.cpp
@@ -0,0 +1,1003 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#define INITGUID
+#define USES_IID_IMAPIProp
+#define USES_IID_IMAPIContainer
+#define USES_IID_IABContainer
+#define USES_IID_IMAPITable
+#define USES_IID_IDistList
+
+#include "nsAbWinHelper.h"
+#include "nsMapiAddressBook.h"
+#include "nsWabAddressBook.h"
+
+#include <mapiguid.h>
+
+#include "mozilla/Logging.h"
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo* gAbWinHelperLog
+ = PR_NewLogModule("nsAbWinHelperLog");
+#endif
+
+#define PRINTF(args) MOZ_LOG(gAbWinHelperLog, mozilla::LogLevel::Debug, args)
+
+// Small utility to ensure release of all MAPI interfaces
+template <class tInterface> struct nsMapiInterfaceWrapper
+{
+ tInterface mInterface ;
+
+ nsMapiInterfaceWrapper(void) : mInterface(NULL) {}
+ ~nsMapiInterfaceWrapper(void) {
+ if (mInterface != NULL) { mInterface->Release() ; }
+ }
+ operator LPUNKNOWN *(void) { return reinterpret_cast<LPUNKNOWN *>(&mInterface) ; }
+ tInterface operator -> (void) const { return mInterface ; }
+ operator tInterface *(void) { return &mInterface ; }
+} ;
+
+static void assignEntryID(LPENTRYID& aTarget, LPENTRYID aSource, ULONG aByteCount)
+{
+ if (aTarget != NULL) {
+ delete [] (reinterpret_cast<LPBYTE>(aTarget)) ;
+ aTarget = NULL ;
+ }
+ if (aSource != NULL) {
+ aTarget = reinterpret_cast<LPENTRYID>(new BYTE [aByteCount]) ;
+ memcpy(aTarget, aSource, aByteCount) ;
+ }
+}
+
+nsMapiEntry::nsMapiEntry(void)
+: mByteCount(0), mEntryId(NULL)
+{
+ MOZ_COUNT_CTOR(nsMapiEntry) ;
+}
+
+nsMapiEntry::nsMapiEntry(ULONG aByteCount, LPENTRYID aEntryId)
+: mByteCount(0), mEntryId(NULL)
+{
+ Assign(aByteCount, aEntryId) ;
+ MOZ_COUNT_CTOR(nsMapiEntry) ;
+}
+
+nsMapiEntry::~nsMapiEntry(void)
+{
+ Assign(0, NULL) ;
+ MOZ_COUNT_DTOR(nsMapiEntry) ;
+}
+
+void nsMapiEntry::Assign(ULONG aByteCount, LPENTRYID aEntryId)
+{
+ assignEntryID(mEntryId, aEntryId, aByteCount) ;
+ mByteCount = aByteCount ;
+}
+
+static char UnsignedToChar(unsigned char aUnsigned)
+{
+ if (aUnsigned < 0xA) { return '0' + aUnsigned ; }
+ return 'A' + aUnsigned - 0xA ;
+}
+
+static char kBase64Encoding [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-." ;
+static const int kARank = 0 ;
+static const int kaRank = 26 ;
+static const int k0Rank = 52 ;
+static const unsigned char kMinusRank = 62 ;
+static const unsigned char kDotRank = 63 ;
+
+static void UnsignedToBase64(unsigned char *& aUnsigned,
+ ULONG aNbUnsigned, nsCString& aString)
+{
+ if (aNbUnsigned > 0) {
+ unsigned char remain0 = (*aUnsigned & 0x03) << 4 ;
+
+ aString.Append(kBase64Encoding [(*aUnsigned >> 2) & 0x3F]) ;
+ ++ aUnsigned ;
+ if (aNbUnsigned > 1) {
+ unsigned char remain1 = (*aUnsigned & 0x0F) << 2 ;
+
+ aString.Append(kBase64Encoding [remain0 | ((*aUnsigned >> 4) & 0x0F)]) ;
+ ++ aUnsigned ;
+ if (aNbUnsigned > 2) {
+ aString.Append(kBase64Encoding [remain1 | ((*aUnsigned >> 6) & 0x03)]) ;
+ aString.Append(kBase64Encoding [*aUnsigned & 0x3F]) ;
+ ++ aUnsigned ;
+ }
+ else {
+ aString.Append(kBase64Encoding [remain1]) ;
+ }
+ }
+ else {
+ aString.Append(kBase64Encoding [remain0]) ;
+ }
+ }
+}
+
+static unsigned char CharToUnsigned(char aChar)
+{
+ if (aChar >= '0' && aChar <= '9') { return static_cast<unsigned char>(aChar) - '0' ; }
+ return static_cast<unsigned char>(aChar) - 'A' + 0xA ;
+}
+
+// This function must return the rank in kBase64Encoding of the
+// character provided.
+static unsigned char Base64To6Bits(char aBase64)
+{
+ if (aBase64 >= 'A' && aBase64 <= 'Z') {
+ return static_cast<unsigned char>(aBase64 - 'A' + kARank) ;
+ }
+ if (aBase64 >= 'a' && aBase64 <= 'z') {
+ return static_cast<unsigned char>(aBase64 - 'a' + kaRank) ;
+ }
+ if (aBase64 >= '0' && aBase64 <= '9') {
+ return static_cast<unsigned char>(aBase64 - '0' + k0Rank) ;
+ }
+ if (aBase64 == '-') { return kMinusRank ; }
+ if (aBase64 == '.') { return kDotRank ; }
+ return 0 ;
+}
+
+static void Base64ToUnsigned(const char *& aBase64, uint32_t aNbBase64,
+ unsigned char *&aUnsigned)
+{
+ // By design of the encoding, we must have at least two characters to use
+ if (aNbBase64 > 1) {
+ unsigned char first6Bits = Base64To6Bits(*aBase64 ++) ;
+ unsigned char second6Bits = Base64To6Bits(*aBase64 ++) ;
+
+ *aUnsigned = first6Bits << 2 ;
+ *aUnsigned |= second6Bits >> 4 ;
+ ++ aUnsigned ;
+ if (aNbBase64 > 2) {
+ unsigned char third6Bits = Base64To6Bits(*aBase64 ++) ;
+
+ *aUnsigned = second6Bits << 4 ;
+ *aUnsigned |= third6Bits >> 2 ;
+ ++ aUnsigned ;
+ if (aNbBase64 > 3) {
+ unsigned char fourth6Bits = Base64To6Bits(*aBase64 ++) ;
+
+ *aUnsigned = third6Bits << 6 ;
+ *aUnsigned |= fourth6Bits ;
+ ++ aUnsigned ;
+ }
+ }
+ }
+}
+
+void nsMapiEntry::Assign(const nsCString& aString)
+{
+ Assign(0, NULL) ;
+ ULONG byteCount = aString.Length() / 4 * 3 ;
+
+ if ((aString.Length() & 0x03) != 0) {
+ byteCount += (aString.Length() & 0x03) - 1 ;
+ }
+ const char *currentSource = aString.get() ;
+ unsigned char *currentTarget = new unsigned char [byteCount] ;
+ uint32_t i = 0 ;
+
+ mByteCount = byteCount ;
+ mEntryId = reinterpret_cast<LPENTRYID>(currentTarget) ;
+ for (i = aString.Length() ; i >= 4 ; i -= 4) {
+ Base64ToUnsigned(currentSource, 4, currentTarget) ;
+ }
+ Base64ToUnsigned(currentSource, i, currentTarget) ;
+}
+
+void nsMapiEntry::ToString(nsCString& aString) const
+{
+ aString.Truncate() ;
+ ULONG i = 0 ;
+ unsigned char *currentSource = reinterpret_cast<unsigned char *>(mEntryId) ;
+
+ for (i = mByteCount ; i >= 3 ; i -= 3) {
+ UnsignedToBase64(currentSource, 3, aString) ;
+ }
+ UnsignedToBase64(currentSource, i, aString) ;
+}
+
+void nsMapiEntry::Dump(void) const
+{
+ PRINTF(("%d\n", mByteCount)) ;
+ for (ULONG i = 0 ; i < mByteCount ; ++ i) {
+ PRINTF(("%02X", (reinterpret_cast<unsigned char *>(mEntryId)) [i])) ;
+ }
+ PRINTF(("\n")) ;
+}
+
+nsMapiEntryArray::nsMapiEntryArray(void)
+: mEntries(NULL), mNbEntries(0)
+{
+ MOZ_COUNT_CTOR(nsMapiEntryArray) ;
+}
+
+nsMapiEntryArray::~nsMapiEntryArray(void)
+{
+ if (mEntries) { delete [] mEntries ; }
+ MOZ_COUNT_DTOR(nsMapiEntryArray) ;
+}
+
+void nsMapiEntryArray::CleanUp(void)
+{
+ if (mEntries != NULL) {
+ delete [] mEntries ;
+ mEntries = NULL ;
+ mNbEntries = 0 ;
+ }
+}
+
+using namespace mozilla;
+
+uint32_t nsAbWinHelper::mEntryCounter = 0;
+nsAutoPtr<mozilla::Mutex> nsAbWinHelper::mMutex;
+uint32_t nsAbWinHelper::mUseCount = 0;
+// There seems to be a deadlock/auto-destruction issue
+// in MAPI when multiple threads perform init/release
+// operations at the same time. So I've put a mutex
+// around both the initialize process and the destruction
+// one. I just hope the rest of the calls don't need the
+// same protection (MAPI is supposed to be thread-safe).
+
+nsAbWinHelper::nsAbWinHelper(void)
+: mAddressBook(NULL), mLastError(S_OK)
+{
+ if (!mUseCount++)
+ mMutex = new mozilla::Mutex("nsAbWinHelper.mMutex");
+
+ MOZ_COUNT_CTOR(nsAbWinHelper);
+}
+
+nsAbWinHelper::~nsAbWinHelper(void)
+{
+ if (!--mUseCount)
+ mMutex = nullptr;
+ MOZ_COUNT_DTOR(nsAbWinHelper) ;
+}
+
+BOOL nsAbWinHelper::GetFolders(nsMapiEntryArray& aFolders)
+{
+ aFolders.CleanUp() ;
+ nsMapiInterfaceWrapper<LPABCONT> rootFolder ;
+ nsMapiInterfaceWrapper<LPMAPITABLE> folders ;
+ ULONG objType = 0 ;
+ ULONG rowCount = 0 ;
+ SRestriction restriction ;
+ SPropTagArray folderColumns ;
+
+ mLastError = mAddressBook->OpenEntry(0, NULL, NULL, 0, &objType,
+ rootFolder) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open root %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ mLastError = rootFolder->GetHierarchyTable(0, folders) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot get hierarchy %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ // We only take into account modifiable containers,
+ // otherwise, we end up with all the directory services...
+ restriction.rt = RES_BITMASK ;
+ restriction.res.resBitMask.ulPropTag = PR_CONTAINER_FLAGS ;
+ restriction.res.resBitMask.relBMR = BMR_NEZ ;
+ restriction.res.resBitMask.ulMask = AB_MODIFIABLE ;
+ mLastError = folders->Restrict(&restriction, 0) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot restrict table %08x.\n", mLastError)) ;
+ }
+ folderColumns.cValues = 1 ;
+ folderColumns.aulPropTag [0] = PR_ENTRYID ;
+ mLastError = folders->SetColumns(&folderColumns, 0) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot set columns %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ mLastError = folders->GetRowCount(0, &rowCount) ;
+ if (HR_SUCCEEDED(mLastError)) {
+ aFolders.mEntries = new nsMapiEntry [rowCount] ;
+ aFolders.mNbEntries = 0 ;
+ do {
+ LPSRowSet rowSet = NULL ;
+
+ rowCount = 0 ;
+ mLastError = folders->QueryRows(1, 0, &rowSet) ;
+ if (HR_SUCCEEDED(mLastError)) {
+ rowCount = rowSet->cRows ;
+ if (rowCount > 0) {
+ nsMapiEntry& current = aFolders.mEntries [aFolders.mNbEntries ++] ;
+ SPropValue& currentValue = rowSet->aRow->lpProps [0] ;
+
+ current.Assign(currentValue.Value.bin.cb,
+ reinterpret_cast<LPENTRYID>(currentValue.Value.bin.lpb)) ;
+ }
+ MyFreeProws(rowSet) ;
+ }
+ else {
+ PRINTF(("Cannot query rows %08x.\n", mLastError)) ;
+ }
+ } while (rowCount > 0) ;
+ }
+ return HR_SUCCEEDED(mLastError) ;
+}
+
+BOOL nsAbWinHelper::GetCards(const nsMapiEntry& aParent, LPSRestriction aRestriction,
+ nsMapiEntryArray& aCards)
+{
+ aCards.CleanUp() ;
+ return GetContents(aParent, aRestriction, &aCards.mEntries, aCards.mNbEntries, 0) ;
+}
+
+BOOL nsAbWinHelper::GetNodes(const nsMapiEntry& aParent, nsMapiEntryArray& aNodes)
+{
+ aNodes.CleanUp() ;
+ return GetContents(aParent, NULL, &aNodes.mEntries, aNodes.mNbEntries, MAPI_DISTLIST) ;
+}
+
+BOOL nsAbWinHelper::GetCardsCount(const nsMapiEntry& aParent, ULONG& aNbCards)
+{
+ aNbCards = 0 ;
+ return GetContents(aParent, NULL, NULL, aNbCards, 0) ;
+}
+
+BOOL nsAbWinHelper::GetPropertyString(const nsMapiEntry& aObject,
+ ULONG aPropertyTag,
+ nsCString& aName)
+{
+ aName.Truncate() ;
+ LPSPropValue values = NULL ;
+ ULONG valueCount = 0 ;
+
+ if (!GetMAPIProperties(aObject, &aPropertyTag, 1, values, valueCount)) { return FALSE ; }
+ if (valueCount == 1 && values != NULL) {
+ if (PROP_TYPE(values->ulPropTag) == PT_STRING8)
+ aName = values->Value.lpszA ;
+ else if (PROP_TYPE(values->ulPropTag) == PT_UNICODE)
+ aName = NS_LossyConvertUTF16toASCII(values->Value.lpszW);
+ }
+ FreeBuffer(values) ;
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::GetPropertyUString(const nsMapiEntry& aObject, ULONG aPropertyTag,
+ nsString& aName)
+{
+ aName.Truncate() ;
+ LPSPropValue values = NULL ;
+ ULONG valueCount = 0 ;
+
+ if (!GetMAPIProperties(aObject, &aPropertyTag, 1, values, valueCount)) { return FALSE ; }
+ if (valueCount == 1 && values != NULL) {
+ if (PROP_TYPE(values->ulPropTag) == PT_UNICODE)
+ aName = values->Value.lpszW ;
+ else if (PROP_TYPE(values->ulPropTag) == PT_STRING8)
+ aName.AssignASCII(values->Value.lpszA);
+ }
+ FreeBuffer(values) ;
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::GetPropertiesUString(const nsMapiEntry& aObject, const ULONG *aPropertyTags,
+ ULONG aNbProperties, nsString *aNames)
+{
+ LPSPropValue values = NULL;
+ ULONG valueCount = 0;
+
+ if (!GetMAPIProperties(aObject, aPropertyTags, aNbProperties, values,
+ valueCount))
+ return FALSE;
+
+ if (valueCount == aNbProperties && values != NULL)
+ {
+ for (ULONG i = 0 ; i < valueCount ; ++ i)
+ {
+ if (PROP_ID(values[i].ulPropTag) == PROP_ID(aPropertyTags[i]))
+ {
+ if (PROP_TYPE(values[i].ulPropTag) == PT_STRING8)
+ aNames[i].AssignASCII(values[i].Value.lpszA);
+ else if (PROP_TYPE(values[i].ulPropTag) == PT_UNICODE)
+ aNames[i] = values[i].Value.lpszW;
+ }
+ }
+ FreeBuffer(values);
+ }
+ return TRUE;
+}
+
+BOOL nsAbWinHelper::GetPropertyDate(const nsMapiEntry& aObject, ULONG aPropertyTag,
+ WORD& aYear, WORD& aMonth, WORD& aDay)
+{
+ aYear = 0 ;
+ aMonth = 0 ;
+ aDay = 0 ;
+ LPSPropValue values = NULL ;
+ ULONG valueCount = 0 ;
+
+ if (!GetMAPIProperties(aObject, &aPropertyTag, 1, values, valueCount)) { return FALSE ; }
+ if (valueCount == 1 && values != NULL && PROP_TYPE(values->ulPropTag) == PT_SYSTIME) {
+ SYSTEMTIME readableTime ;
+
+ if (FileTimeToSystemTime(&values->Value.ft, &readableTime)) {
+ aYear = readableTime.wYear ;
+ aMonth = readableTime.wMonth ;
+ aDay = readableTime.wDay ;
+ }
+ }
+ FreeBuffer(values) ;
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::GetPropertyLong(const nsMapiEntry& aObject,
+ ULONG aPropertyTag,
+ ULONG& aValue)
+{
+ aValue = 0 ;
+ LPSPropValue values = NULL ;
+ ULONG valueCount = 0 ;
+
+ if (!GetMAPIProperties(aObject, &aPropertyTag, 1, values, valueCount)) { return FALSE ; }
+ if (valueCount == 1 && values != NULL && PROP_TYPE(values->ulPropTag) == PT_LONG) {
+ aValue = values->Value.ul ;
+ }
+ FreeBuffer(values) ;
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::GetPropertyBin(const nsMapiEntry& aObject, ULONG aPropertyTag,
+ nsMapiEntry& aValue)
+{
+ aValue.Assign(0, NULL) ;
+ LPSPropValue values = NULL ;
+ ULONG valueCount = 0 ;
+
+ if (!GetMAPIProperties(aObject, &aPropertyTag, 1, values, valueCount)) { return FALSE ; }
+ if (valueCount == 1 && values != NULL && PROP_TYPE(values->ulPropTag) == PT_BINARY) {
+ aValue.Assign(values->Value.bin.cb,
+ reinterpret_cast<LPENTRYID>(values->Value.bin.lpb)) ;
+ }
+ FreeBuffer(values) ;
+ return TRUE ;
+}
+
+// This function, supposedly indicating whether a particular entry was
+// in a particular container, doesn't seem to work very well (has
+// a tendency to return TRUE even if we're talking to different containers...).
+BOOL nsAbWinHelper::TestOpenEntry(const nsMapiEntry& aContainer, const nsMapiEntry& aEntry)
+{
+ nsMapiInterfaceWrapper<LPMAPICONTAINER> container ;
+ nsMapiInterfaceWrapper<LPMAPIPROP> subObject ;
+ ULONG objType = 0 ;
+
+ mLastError = mAddressBook->OpenEntry(aContainer.mByteCount, aContainer.mEntryId,
+ &IID_IMAPIContainer, 0, &objType,
+ container) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open container %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ mLastError = container->OpenEntry(aEntry.mByteCount, aEntry.mEntryId,
+ NULL, 0, &objType, subObject) ;
+ return HR_SUCCEEDED(mLastError) ;
+}
+
+BOOL nsAbWinHelper::DeleteEntry(const nsMapiEntry& aContainer, const nsMapiEntry& aEntry)
+{
+ nsMapiInterfaceWrapper<LPABCONT> container ;
+ ULONG objType = 0 ;
+ SBinary entry ;
+ SBinaryArray entryArray ;
+
+ mLastError = mAddressBook->OpenEntry(aContainer.mByteCount, aContainer.mEntryId,
+ &IID_IABContainer, MAPI_MODIFY, &objType,
+ container) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open container %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ entry.cb = aEntry.mByteCount ;
+ entry.lpb = reinterpret_cast<LPBYTE>(aEntry.mEntryId) ;
+ entryArray.cValues = 1 ;
+ entryArray.lpbin = &entry ;
+ mLastError = container->DeleteEntries(&entryArray, 0) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot delete entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::SetPropertyUString(const nsMapiEntry& aObject, ULONG aPropertyTag,
+ const char16_t *aValue)
+{
+ SPropValue value ;
+ nsAutoCString alternativeValue ;
+
+ value.ulPropTag = aPropertyTag ;
+ if (PROP_TYPE(aPropertyTag) == PT_UNICODE) {
+ value.Value.lpszW = wwc(const_cast<char16_t *>(aValue)) ;
+ }
+ else if (PROP_TYPE(aPropertyTag) == PT_STRING8) {
+ alternativeValue = NS_LossyConvertUTF16toASCII(aValue);
+ value.Value.lpszA = const_cast<char *>(alternativeValue.get()) ;
+ }
+ else {
+ PRINTF(("Property %08x is not a string.\n", aPropertyTag)) ;
+ return TRUE ;
+ }
+ return SetMAPIProperties(aObject, 1, &value) ;
+}
+
+BOOL nsAbWinHelper::SetPropertiesUString(const nsMapiEntry& aObject, const ULONG *aPropertiesTag,
+ ULONG aNbProperties, nsString *aValues)
+{
+ LPSPropValue values = new SPropValue [aNbProperties] ;
+ if (!values)
+ return FALSE ;
+
+ ULONG i = 0 ;
+ ULONG currentValue = 0 ;
+ nsAutoCString alternativeValue ;
+ BOOL retCode = TRUE ;
+
+ for (i = 0 ; i < aNbProperties ; ++ i) {
+ values [currentValue].ulPropTag = aPropertiesTag [i] ;
+ if (PROP_TYPE(aPropertiesTag [i]) == PT_UNICODE) {
+ const wchar_t *value = aValues [i].get() ;
+ values [currentValue ++].Value.lpszW = const_cast<wchar_t *>(value) ;
+ }
+ else if (PROP_TYPE(aPropertiesTag [i]) == PT_STRING8) {
+ LossyCopyUTF16toASCII(aValues [i], alternativeValue);
+ char *av = strdup(alternativeValue.get()) ;
+ if (!av) {
+ retCode = FALSE ;
+ break ;
+ }
+ values [currentValue ++].Value.lpszA = av ;
+ }
+ }
+ if (retCode)
+ retCode = SetMAPIProperties(aObject, currentValue, values) ;
+ for (i = 0 ; i < currentValue ; ++ i) {
+ if (PROP_TYPE(aPropertiesTag [i]) == PT_STRING8) {
+ NS_Free(values [i].Value.lpszA) ;
+ }
+ }
+ delete [] values ;
+ return retCode ;
+}
+
+BOOL nsAbWinHelper::SetPropertyDate(const nsMapiEntry& aObject, ULONG aPropertyTag,
+ WORD aYear, WORD aMonth, WORD aDay)
+{
+ SPropValue value ;
+
+ value.ulPropTag = aPropertyTag ;
+ if (PROP_TYPE(aPropertyTag) == PT_SYSTIME) {
+ SYSTEMTIME readableTime ;
+
+ readableTime.wYear = aYear ;
+ readableTime.wMonth = aMonth ;
+ readableTime.wDay = aDay ;
+ readableTime.wDayOfWeek = 0 ;
+ readableTime.wHour = 0 ;
+ readableTime.wMinute = 0 ;
+ readableTime.wSecond = 0 ;
+ readableTime.wMilliseconds = 0 ;
+ if (SystemTimeToFileTime(&readableTime, &value.Value.ft)) {
+ return SetMAPIProperties(aObject, 1, &value) ;
+ }
+ return TRUE ;
+ }
+ return FALSE ;
+}
+
+BOOL nsAbWinHelper::CreateEntry(const nsMapiEntry& aParent, nsMapiEntry& aNewEntry)
+{
+ nsMapiInterfaceWrapper<LPABCONT> container ;
+ ULONG objType = 0 ;
+
+ mLastError = mAddressBook->OpenEntry(aParent.mByteCount, aParent.mEntryId,
+ &IID_IABContainer, MAPI_MODIFY, &objType,
+ container) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open container %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ SPropTagArray property ;
+ LPSPropValue value = NULL ;
+ ULONG valueCount = 0 ;
+
+ property.cValues = 1 ;
+ property.aulPropTag [0] = PR_DEF_CREATE_MAILUSER ;
+ mLastError = container->GetProps(&property, 0, &valueCount, &value) ;
+ if (HR_FAILED(mLastError) || valueCount != 1) {
+ PRINTF(("Cannot obtain template %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ nsMapiInterfaceWrapper<LPMAPIPROP> newEntry ;
+
+ mLastError = container->CreateEntry(value->Value.bin.cb,
+ reinterpret_cast<LPENTRYID>(value->Value.bin.lpb),
+ CREATE_CHECK_DUP_LOOSE,
+ newEntry) ;
+ FreeBuffer(value) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot create new entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ SPropValue displayName ;
+ LPSPropProblemArray problems = NULL ;
+ nsAutoString tempName ;
+
+ displayName.ulPropTag = PR_DISPLAY_NAME_W ;
+ tempName.AssignLiteral("__MailUser__") ;
+ tempName.AppendInt(mEntryCounter ++) ;
+ const wchar_t *tempNameValue = tempName.get();
+ displayName.Value.lpszW = const_cast<wchar_t *>(tempNameValue) ;
+ mLastError = newEntry->SetProps(1, &displayName, &problems) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot set temporary name %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ mLastError = newEntry->SaveChanges(KEEP_OPEN_READONLY) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot commit new entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ property.aulPropTag [0] = PR_ENTRYID ;
+ mLastError = newEntry->GetProps(&property, 0, &valueCount, &value) ;
+ if (HR_FAILED(mLastError) || valueCount != 1) {
+ PRINTF(("Cannot get entry id %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ aNewEntry.Assign(value->Value.bin.cb, reinterpret_cast<LPENTRYID>(value->Value.bin.lpb)) ;
+ FreeBuffer(value) ;
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::CreateDistList(const nsMapiEntry& aParent, nsMapiEntry& aNewEntry)
+{
+ nsMapiInterfaceWrapper<LPABCONT> container ;
+ ULONG objType = 0 ;
+
+ mLastError = mAddressBook->OpenEntry(aParent.mByteCount, aParent.mEntryId,
+ &IID_IABContainer, MAPI_MODIFY, &objType,
+ container) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open container %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ SPropTagArray property ;
+ LPSPropValue value = NULL ;
+ ULONG valueCount = 0 ;
+
+ property.cValues = 1 ;
+ property.aulPropTag [0] = PR_DEF_CREATE_DL ;
+ mLastError = container->GetProps(&property, 0, &valueCount, &value) ;
+ if (HR_FAILED(mLastError) || valueCount != 1) {
+ PRINTF(("Cannot obtain template %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ nsMapiInterfaceWrapper<LPMAPIPROP> newEntry ;
+
+ mLastError = container->CreateEntry(value->Value.bin.cb,
+ reinterpret_cast<LPENTRYID>(value->Value.bin.lpb),
+ CREATE_CHECK_DUP_LOOSE,
+ newEntry) ;
+ FreeBuffer(value) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot create new entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ SPropValue displayName ;
+ LPSPropProblemArray problems = NULL ;
+ nsAutoString tempName ;
+
+ displayName.ulPropTag = PR_DISPLAY_NAME_W ;
+ tempName.AssignLiteral("__MailList__") ;
+ tempName.AppendInt(mEntryCounter ++) ;
+ const wchar_t *tempNameValue = tempName.get() ;
+ displayName.Value.lpszW = const_cast<wchar_t *>(tempNameValue) ;
+ mLastError = newEntry->SetProps(1, &displayName, &problems) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot set temporary name %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ mLastError = newEntry->SaveChanges(KEEP_OPEN_READONLY) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot commit new entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ property.aulPropTag [0] = PR_ENTRYID ;
+ mLastError = newEntry->GetProps(&property, 0, &valueCount, &value) ;
+ if (HR_FAILED(mLastError) || valueCount != 1) {
+ PRINTF(("Cannot get entry id %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ aNewEntry.Assign(value->Value.bin.cb,
+ reinterpret_cast<LPENTRYID>(value->Value.bin.lpb)) ;
+ FreeBuffer(value) ;
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::CopyEntry(const nsMapiEntry& aContainer, const nsMapiEntry& aSource,
+ nsMapiEntry& aTarget)
+{
+ nsMapiInterfaceWrapper<LPABCONT> container ;
+ ULONG objType = 0 ;
+
+ mLastError = mAddressBook->OpenEntry(aContainer.mByteCount, aContainer.mEntryId,
+ &IID_IABContainer, MAPI_MODIFY, &objType,
+ container) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open container %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ nsMapiInterfaceWrapper<LPMAPIPROP> newEntry ;
+
+ mLastError = container->CreateEntry(aSource.mByteCount, aSource.mEntryId,
+ CREATE_CHECK_DUP_LOOSE, newEntry) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot create new entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ mLastError = newEntry->SaveChanges(KEEP_OPEN_READONLY) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot commit new entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ SPropTagArray property ;
+ LPSPropValue value = NULL ;
+ ULONG valueCount = 0 ;
+
+ property.cValues = 1 ;
+ property.aulPropTag [0] = PR_ENTRYID ;
+ mLastError = newEntry->GetProps(&property, 0, &valueCount, &value) ;
+ if (HR_FAILED(mLastError) || valueCount != 1) {
+ PRINTF(("Cannot get entry id %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ aTarget.Assign(value->Value.bin.cb,
+ reinterpret_cast<LPENTRYID>(value->Value.bin.lpb)) ;
+ FreeBuffer(value) ;
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::GetDefaultContainer(nsMapiEntry& aContainer)
+{
+ LPENTRYID entryId = NULL ;
+ ULONG byteCount = 0 ;
+
+ mLastError = mAddressBook->GetPAB(&byteCount, &entryId) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot get PAB %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ aContainer.Assign(byteCount, entryId) ;
+ FreeBuffer(entryId) ;
+ return TRUE ;
+}
+
+enum
+{
+ ContentsColumnEntryId = 0,
+ ContentsColumnObjectType,
+ ContentsColumnsSize
+} ;
+
+static const SizedSPropTagArray(ContentsColumnsSize, ContentsColumns) =
+{
+ ContentsColumnsSize,
+ {
+ PR_ENTRYID,
+ PR_OBJECT_TYPE
+ }
+} ;
+
+BOOL nsAbWinHelper::GetContents(const nsMapiEntry& aParent, LPSRestriction aRestriction,
+ nsMapiEntry **aList, ULONG& aNbElements, ULONG aMapiType)
+{
+ if (aList != NULL) { *aList = NULL ; }
+ aNbElements = 0 ;
+ nsMapiInterfaceWrapper<LPMAPICONTAINER> parent ;
+ nsMapiInterfaceWrapper<LPMAPITABLE> contents ;
+ ULONG objType = 0 ;
+ ULONG rowCount = 0 ;
+
+ mLastError = mAddressBook->OpenEntry(aParent.mByteCount, aParent.mEntryId,
+ &IID_IMAPIContainer, 0, &objType,
+ parent) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open parent %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ // Here, flags for WAB and MAPI could be different, so this works
+ // only as long as we don't want to use any flag in GetContentsTable
+ mLastError = parent->GetContentsTable(0, contents) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot get contents %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ if (aRestriction != NULL) {
+ mLastError = contents->Restrict(aRestriction, 0) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot set restriction %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ }
+ mLastError = contents->SetColumns((LPSPropTagArray) &ContentsColumns, 0) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot set columns %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ mLastError = contents->GetRowCount(0, &rowCount) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot get result count %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ if (aList != NULL) { *aList = new nsMapiEntry [rowCount] ; }
+ aNbElements = 0 ;
+ do {
+ LPSRowSet rowSet = NULL ;
+
+ rowCount = 0 ;
+ mLastError = contents->QueryRows(1, 0, &rowSet) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot query rows %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ rowCount = rowSet->cRows ;
+ if (rowCount > 0 &&
+ (aMapiType == 0 ||
+ rowSet->aRow->lpProps[ContentsColumnObjectType].Value.ul == aMapiType)) {
+ if (aList != NULL) {
+ nsMapiEntry& current = (*aList) [aNbElements] ;
+ SPropValue& currentValue = rowSet->aRow->lpProps[ContentsColumnEntryId] ;
+
+ current.Assign(currentValue.Value.bin.cb,
+ reinterpret_cast<LPENTRYID>(currentValue.Value.bin.lpb)) ;
+ }
+ ++ aNbElements ;
+ }
+ MyFreeProws(rowSet) ;
+ } while (rowCount > 0) ;
+ return TRUE ;
+}
+
+BOOL nsAbWinHelper::GetMAPIProperties(const nsMapiEntry& aObject, const ULONG *aPropertyTags,
+ ULONG aNbProperties, LPSPropValue& aValue,
+ ULONG& aValueCount)
+{
+ nsMapiInterfaceWrapper<LPMAPIPROP> object ;
+ ULONG objType = 0 ;
+ LPSPropTagArray properties = NULL ;
+ ULONG i = 0 ;
+
+ mLastError = mAddressBook->OpenEntry(aObject.mByteCount, aObject.mEntryId,
+ &IID_IMAPIProp, 0, &objType,
+ object) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ AllocateBuffer(CbNewSPropTagArray(aNbProperties),
+ reinterpret_cast<void **>(&properties)) ;
+ properties->cValues = aNbProperties ;
+ for (i = 0 ; i < aNbProperties ; ++ i) {
+ properties->aulPropTag [i] = aPropertyTags [i] ;
+ }
+ mLastError = object->GetProps(properties, 0, &aValueCount, &aValue) ;
+ FreeBuffer(properties) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot get props %08x.\n", mLastError)) ;
+ }
+ return HR_SUCCEEDED(mLastError) ;
+}
+
+BOOL nsAbWinHelper::SetMAPIProperties(const nsMapiEntry& aObject, ULONG aNbProperties,
+ const LPSPropValue& aValues)
+{
+ nsMapiInterfaceWrapper<LPMAPIPROP> object ;
+ ULONG objType = 0 ;
+ LPSPropProblemArray problems = NULL ;
+
+ mLastError = mAddressBook->OpenEntry(aObject.mByteCount, aObject.mEntryId,
+ &IID_IMAPIProp, MAPI_MODIFY, &objType,
+ object) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot open entry %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ mLastError = object->SetProps(aNbProperties, aValues, &problems) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot update the object %08x.\n", mLastError)) ;
+ return FALSE ;
+ }
+ if (problems != NULL) {
+ for (ULONG i = 0 ; i < problems->cProblem ; ++ i) {
+ PRINTF(("Problem %d: index %d code %08x.\n", i,
+ problems->aProblem [i].ulIndex,
+ problems->aProblem [i].scode)) ;
+ }
+ }
+ mLastError = object->SaveChanges(0) ;
+ if (HR_FAILED(mLastError)) {
+ PRINTF(("Cannot commit changes %08x.\n", mLastError)) ;
+ }
+ return HR_SUCCEEDED(mLastError) ;
+}
+
+void nsAbWinHelper::MyFreeProws(LPSRowSet aRowset)
+{
+ if (aRowset == NULL) { return ; }
+ ULONG i = 0 ;
+
+ for (i = 0 ; i < aRowset->cRows ; ++ i) {
+ FreeBuffer(aRowset->aRow [i].lpProps) ;
+ }
+ FreeBuffer(aRowset) ;
+}
+
+nsAbWinHelperGuard::nsAbWinHelperGuard(uint32_t aType)
+: mHelper(NULL)
+{
+ switch(aType) {
+ case nsAbWinType_Outlook: mHelper = new nsMapiAddressBook ; break ;
+ case nsAbWinType_OutlookExp: mHelper = new nsWabAddressBook ; break ;
+ default: break ;
+ }
+}
+
+nsAbWinHelperGuard::~nsAbWinHelperGuard(void)
+{
+ delete mHelper ;
+}
+
+const char *kOutlookDirectoryScheme = "moz-aboutlookdirectory://" ;
+const int kOutlookDirSchemeLength = 21 ;
+const char *kOutlookStub = "op/" ;
+const int kOutlookStubLength = 3 ;
+const char *kOutlookExpStub = "oe/" ;
+const int kOutlookExpStubLength = 3 ;
+const char *kOutlookCardScheme = "moz-aboutlookcard://" ;
+const int kOutlookCardSchemeLength = 16 ;
+
+nsAbWinType getAbWinType(const char *aScheme, const char *aUri, nsCString& aStub, nsCString& aEntry)
+{
+ aStub.Truncate() ;
+ aEntry.Truncate() ;
+ uint32_t schemeLength = strlen(aScheme) ;
+
+ if (strncmp(aUri, aScheme, schemeLength) == 0) {
+ if (strncmp(aUri + schemeLength, kOutlookStub, kOutlookStubLength) == 0) {
+ aEntry = aUri + schemeLength + kOutlookStubLength ;
+ aStub = kOutlookStub ;
+ return nsAbWinType_Outlook ;
+ }
+ if (strncmp(aUri + schemeLength, kOutlookExpStub, kOutlookExpStubLength) == 0) {
+ aEntry = aUri + schemeLength + kOutlookExpStubLength ;
+ aStub = kOutlookExpStub ;
+ return nsAbWinType_OutlookExp ;
+ }
+ }
+ return nsAbWinType_Unknown ;
+}
+
+void buildAbWinUri(const char *aScheme, uint32_t aType, nsCString& aUri)
+{
+ aUri.Assign(aScheme) ;
+ switch(aType) {
+ case nsAbWinType_Outlook: aUri.Append(kOutlookStub) ; break ;
+ case nsAbWinType_OutlookExp: aUri.Append(kOutlookExpStub) ; break ;
+ default: aUri.Assign("") ;
+ }
+}
+
+
+
+
+
+
diff --git a/mailnews/addrbook/src/nsAbWinHelper.h b/mailnews/addrbook/src/nsAbWinHelper.h
new file mode 100644
index 000000000..fb0ad7e44
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbWinHelper.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsAbWinHelper_h___
+#define nsAbWinHelper_h___
+
+#include <windows.h>
+#include <mapix.h>
+
+#include "nsStringGlue.h"
+#include "mozilla/Mutex.h"
+#include "nsAutoPtr.h"
+
+struct nsMapiEntry
+{
+ ULONG mByteCount ;
+ LPENTRYID mEntryId ;
+
+ nsMapiEntry(void) ;
+ ~nsMapiEntry(void) ;
+ nsMapiEntry(ULONG aByteCount, LPENTRYID aEntryId) ;
+
+ void Assign(ULONG aByteCount, LPENTRYID aEntryId) ;
+ void Assign(const nsCString& aString) ;
+ void ToString(nsCString& aString) const ;
+ void Dump(void) const ;
+} ;
+
+struct nsMapiEntryArray
+{
+ nsMapiEntry *mEntries ;
+ ULONG mNbEntries ;
+
+ nsMapiEntryArray(void) ;
+ ~nsMapiEntryArray(void) ;
+
+ const nsMapiEntry& operator [] (int aIndex) const { return mEntries [aIndex] ; }
+ void CleanUp(void) ;
+} ;
+
+class nsAbWinHelper
+{
+public:
+ nsAbWinHelper(void) ;
+ virtual ~nsAbWinHelper(void) ;
+
+ // Get the top address books
+ BOOL GetFolders(nsMapiEntryArray& aFolders) ;
+ // Get a list of entries for cards/mailing lists in a folder/mailing list
+ BOOL GetCards(const nsMapiEntry& aParent, LPSRestriction aRestriction,
+ nsMapiEntryArray& aCards) ;
+ // Get a list of mailing lists in a folder
+ BOOL GetNodes(const nsMapiEntry& aParent, nsMapiEntryArray& aNodes) ;
+ // Get the number of cards/mailing lists in a folder/mailing list
+ BOOL GetCardsCount(const nsMapiEntry& aParent, ULONG& aNbCards) ;
+ // Access last MAPI error
+ HRESULT LastError(void) const { return mLastError ; }
+ // Get the value of a MAPI property of type string
+ BOOL GetPropertyString(const nsMapiEntry& aObject, ULONG aPropertyTag, nsCString& aValue) ;
+ // Same as previous, but string is returned as unicode
+ BOOL GetPropertyUString(const nsMapiEntry& aObject, ULONG aPropertyTag, nsString& aValue) ;
+ // Get multiple string MAPI properties in one call.
+ BOOL GetPropertiesUString(const nsMapiEntry& aObject, const ULONG *aPropertiesTag,
+ ULONG aNbProperties, nsString *aValues);
+ // Get the value of a MAPI property of type SYSTIME
+ BOOL GetPropertyDate(const nsMapiEntry& aObject, ULONG aPropertyTag,
+ WORD& aYear, WORD& aMonth, WORD& aDay) ;
+ // Get the value of a MAPI property of type LONG
+ BOOL GetPropertyLong(const nsMapiEntry& aObject, ULONG aPropertyTag, ULONG& aValue) ;
+ // Get the value of a MAPI property of type BIN
+ BOOL GetPropertyBin(const nsMapiEntry& aObject, ULONG aPropertyTag, nsMapiEntry& aValue) ;
+ // Tests if a container contains an entry
+ BOOL TestOpenEntry(const nsMapiEntry& aContainer, const nsMapiEntry& aEntry) ;
+ // Delete an entry in the address book
+ BOOL DeleteEntry(const nsMapiEntry& aContainer, const nsMapiEntry& aEntry) ;
+ // Set the value of a MAPI property of type string in unicode
+ BOOL SetPropertyUString (const nsMapiEntry& aObject, ULONG aPropertyTag,
+ const char16_t *aValue) ;
+ // Same as previous, but with a bunch of properties in one call
+ BOOL SetPropertiesUString(const nsMapiEntry& aObject, const ULONG *aPropertiesTag,
+ ULONG aNbProperties, nsString *aValues) ;
+ // Set the value of a MAPI property of type SYSTIME
+ BOOL SetPropertyDate(const nsMapiEntry& aObject, ULONG aPropertyTag,
+ WORD aYear, WORD aMonth, WORD aDay) ;
+ // Create entry in the address book
+ BOOL CreateEntry(const nsMapiEntry& aParent, nsMapiEntry& aNewEntry) ;
+ // Create a distribution list in the address book
+ BOOL CreateDistList(const nsMapiEntry& aParent, nsMapiEntry& aNewEntry) ;
+ // Copy an existing entry in the address book
+ BOOL CopyEntry(const nsMapiEntry& aContainer, const nsMapiEntry& aSource, nsMapiEntry& aTarget) ;
+ // Get a default address book container
+ BOOL GetDefaultContainer(nsMapiEntry& aContainer) ;
+ // Is the helper correctly initialised?
+ BOOL IsOK(void) const { return mAddressBook != NULL ; }
+
+protected:
+ HRESULT mLastError ;
+ LPADRBOOK mAddressBook ;
+ static uint32_t mEntryCounter ;
+ static uint32_t mUseCount ;
+ static nsAutoPtr<mozilla::Mutex> mMutex ;
+
+ // Retrieve the contents of a container, with an optional restriction
+ BOOL GetContents(const nsMapiEntry& aParent, LPSRestriction aRestriction,
+ nsMapiEntry **aList, ULONG &aNbElements, ULONG aMapiType) ;
+ // Retrieve the values of a set of properties on a MAPI object
+ BOOL GetMAPIProperties(const nsMapiEntry& aObject, const ULONG *aPropertyTags,
+ ULONG aNbProperties,
+ LPSPropValue& aValues, ULONG& aValueCount) ;
+ // Set the values of a set of properties on a MAPI object
+ BOOL SetMAPIProperties(const nsMapiEntry& aObject, ULONG aNbProperties,
+ const LPSPropValue& aValues) ;
+ // Clean-up a rowset returned by QueryRows
+ void MyFreeProws(LPSRowSet aSet) ;
+ // Allocation of a buffer for transmission to interfaces
+ virtual void AllocateBuffer(ULONG aByteCount, LPVOID *aBuffer) = 0 ;
+ // Destruction of a buffer provided by the interfaces
+ virtual void FreeBuffer(LPVOID aBuffer) = 0 ;
+
+private:
+} ;
+
+enum nsAbWinType
+{
+ nsAbWinType_Unknown,
+ nsAbWinType_Outlook,
+ nsAbWinType_OutlookExp
+} ;
+
+class nsAbWinHelperGuard
+{
+public :
+ nsAbWinHelperGuard(uint32_t aType) ;
+ ~nsAbWinHelperGuard(void) ;
+
+ nsAbWinHelper *operator ->(void) { return mHelper ; }
+
+private:
+ nsAbWinHelper *mHelper ;
+} ;
+
+extern const char *kOutlookDirectoryScheme ;
+extern const int kOutlookDirSchemeLength ;
+extern const char *kOutlookStub ;
+extern const char *kOutlookExpStub ;
+extern const char *kOutlookCardScheme ;
+
+nsAbWinType getAbWinType(const char *aScheme, const char *aUri,
+ nsCString& aStub, nsCString& aEntry) ;
+void buildAbWinUri(const char *aScheme, uint32_t aType, nsCString& aUri) ;
+
+#endif // nsAbWinHelper_h___
+
+
+
diff --git a/mailnews/addrbook/src/nsAddbookProtocolHandler.cpp b/mailnews/addrbook/src/nsAddbookProtocolHandler.cpp
new file mode 100644
index 000000000..2d1fab36e
--- /dev/null
+++ b/mailnews/addrbook/src/nsAddbookProtocolHandler.cpp
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "msgCore.h" // precompiled header...
+#include "nsStringGlue.h"
+#include "nsIIOService.h"
+
+#include "nsIStreamListener.h"
+#include "nsAddbookProtocolHandler.h"
+
+#include "nsAddbookUrl.h"
+#include "nsAddbookProtocolHandler.h"
+#include "nsCOMPtr.h"
+#include "nsAbBaseCID.h"
+#include "nsNetUtil.h"
+#include "nsStringStream.h"
+#include "nsIAbDirectory.h"
+#include "nsIAbManager.h"
+#include "prmem.h"
+#include "nsIAbView.h"
+#include "nsITreeView.h"
+#include "nsIStringBundle.h"
+#include "nsIServiceManager.h"
+#include "mozilla/Services.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIPipe.h"
+#include "nsIPrincipal.h"
+
+nsAddbookProtocolHandler::nsAddbookProtocolHandler()
+{
+ mAddbookOperation = nsIAddbookUrlOperation::InvalidUrl;
+}
+
+nsAddbookProtocolHandler::~nsAddbookProtocolHandler()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsAddbookProtocolHandler, nsIProtocolHandler)
+
+NS_IMETHODIMP nsAddbookProtocolHandler::GetScheme(nsACString &aScheme)
+{
+ aScheme = "addbook";
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddbookProtocolHandler::GetDefaultPort(int32_t *aDefaultPort)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddbookProtocolHandler::GetProtocolFlags(uint32_t *aUritype)
+{
+ *aUritype = URI_STD | URI_LOADABLE_BY_ANYONE | URI_FORBIDS_COOKIE_ACCESS;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddbookProtocolHandler::NewURI(const nsACString &aSpec,
+ const char *aOriginCharset, // ignored
+ nsIURI *aBaseURI,
+ nsIURI **_retval)
+{
+ nsresult rv;
+ nsCOMPtr <nsIAddbookUrl> addbookUrl = do_CreateInstance(NS_ADDBOOKURL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = addbookUrl->SetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr <nsIURI> uri = do_QueryInterface(addbookUrl, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ NS_ADDREF(*_retval = uri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAddbookProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
+{
+ // don't override anything.
+ *_retval = false;
+ return NS_OK;
+}
+
+nsresult
+nsAddbookProtocolHandler::GenerateXMLOutputChannel( nsString &aOutput,
+ nsIAddbookUrl *addbookUrl,
+ nsIURI *aURI,
+ nsILoadInfo *aLoadInfo,
+ nsIChannel **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIStringInputStream> inStr(do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertUTF16toUTF8 utf8String(aOutput.get());
+
+ rv = inStr->SetData(utf8String.get(), utf8String.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aLoadInfo) {
+ return NS_NewInputStreamChannelInternal(_retval,
+ aURI,
+ inStr,
+ NS_LITERAL_CSTRING("text/xml"),
+ EmptyCString(),
+ aLoadInfo);
+ }
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateInstance of nullprincipalfailed.");
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_NewInputStreamChannel(_retval, aURI, inStr,
+ nullPrincipal, nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER,
+ NS_LITERAL_CSTRING("text/xml"));
+}
+
+NS_IMETHODIMP
+nsAddbookProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval)
+{
+ return NewChannel2(aURI, nullptr, _retval);
+}
+
+NS_IMETHODIMP
+nsAddbookProtocolHandler::NewChannel2(nsIURI *aURI,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel **_retval)
+{
+ nsresult rv;
+ nsCOMPtr <nsIAddbookUrl> addbookUrl = do_QueryInterface(aURI, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = addbookUrl->GetAddbookOperation(&mAddbookOperation);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (mAddbookOperation == nsIAddbookUrlOperation::InvalidUrl) {
+ nsAutoString errorString;
+ errorString.AssignLiteral("Unsupported format/operation requested for ");
+ nsAutoCString spec;
+ rv = aURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ errorString.Append(NS_ConvertUTF8toUTF16(spec));
+ rv = GenerateXMLOutputChannel(errorString, addbookUrl, aURI, aLoadInfo, _retval);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return NS_OK;
+ }
+
+ if (mAddbookOperation == nsIAddbookUrlOperation::AddVCard) {
+ // create an empty pipe for use with the input stream channel.
+ nsCOMPtr<nsIAsyncInputStream> pipeIn;
+ nsCOMPtr<nsIAsyncOutputStream> pipeOut;
+ nsCOMPtr<nsIPipe> pipe = do_CreateInstance("@mozilla.org/pipe;1");
+
+ rv = pipe->Init(false, false, 0, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // These always succeed because the pipe is initialized above.
+ MOZ_ALWAYS_SUCCEEDS(pipe->GetInputStream(getter_AddRefs(pipeIn)));
+ MOZ_ALWAYS_SUCCEEDS(pipe->GetOutputStream(getter_AddRefs(pipeOut)));
+
+ pipeOut->Close();
+ if (aLoadInfo) {
+ return NS_NewInputStreamChannelInternal(_retval,
+ aURI,
+ pipeIn,
+ NS_LITERAL_CSTRING("application/x-addvcard"),
+ EmptyCString(),
+ aLoadInfo);
+ }
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateInstance of nullprincipal failed.");
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_NewInputStreamChannel(_retval, aURI, pipeIn,
+ nullPrincipal, nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER,
+ NS_LITERAL_CSTRING("application/x-addvcard"));
+ }
+
+ nsString output;
+ rv = GeneratePrintOutput(addbookUrl, output);
+ if (NS_FAILED(rv)) {
+ output.AssignLiteral("failed to print. url=");
+ nsAutoCString spec;
+ rv = aURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv,rv);
+ output.Append(NS_ConvertUTF8toUTF16(spec));
+ }
+
+ rv = GenerateXMLOutputChannel(output, addbookUrl, aURI, aLoadInfo, _retval);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return NS_OK;
+}
+
+nsresult
+nsAddbookProtocolHandler::GeneratePrintOutput(nsIAddbookUrl *addbookUrl,
+ nsString &aOutput)
+{
+ NS_ENSURE_ARG_POINTER(addbookUrl);
+
+ nsAutoCString uri;
+ nsresult rv = addbookUrl->GetPath(uri);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ /* turn
+ "//moz-abmdbdirectory/abook.mab?action=print"
+ into "moz-abmdbdirectory://abook.mab"
+ */
+
+ /* step 1:
+ turn "//moz-abmdbdirectory/abook.mab?action=print"
+ into "moz-abmdbdirectory/abook.mab?action=print"
+ */
+ if (uri[0] != '/' && uri[1] != '/')
+ return NS_ERROR_UNEXPECTED;
+
+ uri.Cut(0,2);
+
+ /* step 2:
+ turn "moz-abmdbdirectory/abook.mab?action=print"
+ into "moz-abmdbdirectory/abook.mab"
+ */
+ int32_t pos = uri.Find("?action=print");
+ if (pos == -1)
+ return NS_ERROR_UNEXPECTED;
+
+ uri.SetLength(pos);
+
+ /* step 2:
+ turn "moz-abmdbdirectory/abook.mab"
+ into "moz-abmdbdirectory://abook.mab"
+ */
+ pos = uri.FindChar('/');
+ if (pos == -1)
+ return NS_ERROR_UNEXPECTED;
+
+ uri.Insert('/', pos);
+ uri.Insert(':', pos);
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(uri, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = BuildDirectoryXML(directory, aOutput);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
+nsAddbookProtocolHandler::BuildDirectoryXML(nsIAbDirectory *aDirectory,
+ nsString &aOutput)
+{
+ NS_ENSURE_ARG_POINTER(aDirectory);
+
+ nsresult rv;
+ nsCOMPtr<nsISimpleEnumerator> cardsEnumerator;
+ nsCOMPtr<nsIAbCard> card;
+
+ aOutput.AppendLiteral("<?xml version=\"1.0\"?>\n"
+ "<?xml-stylesheet type=\"text/css\" href=\"chrome://messagebody/content/addressbook/print.css\"?>\n"
+ "<directory>\n");
+
+ // Get Address Book string and set it as title of XML document
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ if (stringBundleService) {
+ rv = stringBundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(bundle));
+ if (NS_SUCCEEDED(rv)) {
+ nsString addrBook;
+ rv = bundle->GetStringFromName(u"addressBook", getter_Copies(addrBook));
+ if (NS_SUCCEEDED(rv)) {
+ aOutput.AppendLiteral("<title xmlns=\"http://www.w3.org/1999/xhtml\">");
+ aOutput.Append(addrBook);
+ aOutput.AppendLiteral("</title>\n");
+ }
+ }
+ }
+
+ // create a view and init it with the generated name sort order. Then, iterate
+ // over the view, getting the card for each row, and printing them.
+ nsString sortColumn;
+ nsCOMPtr <nsIAbView> view = do_CreateInstance("@mozilla.org/addressbook/abview;1", &rv);
+
+ view->SetView(aDirectory, nullptr, NS_LITERAL_STRING("GeneratedName"),
+ NS_LITERAL_STRING("ascending"), sortColumn);
+
+ int32_t numRows;
+ nsCOMPtr <nsITreeView> treeView = do_QueryInterface(view, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ treeView->GetRowCount(&numRows);
+
+ for (int32_t row = 0; row < numRows; row++)
+ {
+
+ nsCOMPtr <nsIAbCard> card;
+ view->GetCardFromRow(row, getter_AddRefs(card));
+ nsCString xmlSubstr;
+
+ rv = card->TranslateTo(NS_LITERAL_CSTRING("xml"), xmlSubstr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ aOutput.AppendLiteral("<separator/>");
+ aOutput.Append(NS_ConvertUTF8toUTF16(xmlSubstr));
+ aOutput.AppendLiteral("<separator/>");
+ }
+
+ aOutput.AppendLiteral("</directory>\n");
+
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAddbookProtocolHandler.h b/mailnews/addrbook/src/nsAddbookProtocolHandler.h
new file mode 100644
index 000000000..1eb07a4ff
--- /dev/null
+++ b/mailnews/addrbook/src/nsAddbookProtocolHandler.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAddbookProtocolHandler_h___
+#define nsAddbookProtocolHandler_h___
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsAddbookProtocolHandler.h"
+#include "nsIProtocolHandler.h"
+#include "nsIAddbookUrl.h"
+#include "nsIAddrDatabase.h"
+
+class nsAddbookProtocolHandler : public nsIProtocolHandler
+{
+public:
+ nsAddbookProtocolHandler();
+
+ NS_DECL_ISUPPORTS
+
+ //////////////////////////////////////////////////////////////////////////
+ // We support the nsIProtocolHandler interface.
+ //////////////////////////////////////////////////////////////////////////
+ NS_DECL_NSIPROTOCOLHANDLER
+
+private:
+ virtual ~nsAddbookProtocolHandler();
+ nsresult GenerateXMLOutputChannel(nsString &aOutput,
+ nsIAddbookUrl *addbookUrl,
+ nsIURI *aURI,
+ nsILoadInfo *aLoadInfo,
+ nsIChannel **_retval);
+
+ nsresult GeneratePrintOutput(nsIAddbookUrl *addbookUrl,
+ nsString &aOutput);
+
+ nsresult BuildDirectoryXML(nsIAbDirectory *aDirectory,
+ nsString &aOutput);
+
+ int32_t mAddbookOperation;
+};
+
+#endif /* nsAddbookProtocolHandler_h___ */
diff --git a/mailnews/addrbook/src/nsAddbookUrl.cpp b/mailnews/addrbook/src/nsAddbookUrl.cpp
new file mode 100644
index 000000000..2084ca467
--- /dev/null
+++ b/mailnews/addrbook/src/nsAddbookUrl.cpp
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIURI.h"
+#include "nsNetCID.h"
+#include "nsAddbookUrl.h"
+#include "nsStringGlue.h"
+#include "nsAbBaseCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsAutoPtr.h"
+
+/////////////////////////////////////////////////////////////////////////////////////
+// addbook url definition
+/////////////////////////////////////////////////////////////////////////////////////
+nsAddbookUrl::nsAddbookUrl()
+{
+ m_baseURL = do_CreateInstance(NS_SIMPLEURI_CONTRACTID);
+
+ mOperationType = nsIAddbookUrlOperation::InvalidUrl;
+}
+
+nsAddbookUrl::~nsAddbookUrl()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsAddbookUrl, nsIAddbookUrl, nsIURI)
+
+NS_IMETHODIMP
+nsAddbookUrl::SetSpec(const nsACString &aSpec)
+{
+ nsresult rv = m_baseURL->SetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ParseUrl();
+}
+
+nsresult nsAddbookUrl::ParseUrl()
+{
+ nsAutoCString pathStr;
+
+ nsresult rv = m_baseURL->GetPath(pathStr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (strstr(pathStr.get(), "?action=print"))
+ mOperationType = nsIAddbookUrlOperation::PrintAddressBook;
+ else if (strstr(pathStr.get(), "?action=add"))
+ mOperationType = nsIAddbookUrlOperation::AddVCard;
+ else
+ mOperationType = nsIAddbookUrlOperation::InvalidUrl;
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// Begin nsIURI support
+////////////////////////////////////////////////////////////////////////////////////
+
+
+NS_IMETHODIMP nsAddbookUrl::GetSpec(nsACString &aSpec)
+{
+ return m_baseURL->GetSpec(aSpec);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetPrePath(nsACString &aPrePath)
+{
+ return m_baseURL->GetPrePath(aPrePath);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetScheme(nsACString &aScheme)
+{
+ return m_baseURL->GetScheme(aScheme);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetScheme(const nsACString &aScheme)
+{
+ return m_baseURL->SetScheme(aScheme);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetUserPass(nsACString &aUserPass)
+{
+ return m_baseURL->GetUserPass(aUserPass);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetUserPass(const nsACString &aUserPass)
+{
+ return m_baseURL->SetUserPass(aUserPass);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetUsername(nsACString &aUsername)
+{
+ return m_baseURL->GetUsername(aUsername);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetUsername(const nsACString &aUsername)
+{
+ return m_baseURL->SetUsername(aUsername);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetPassword(nsACString &aPassword)
+{
+ return m_baseURL->GetPassword(aPassword);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetPassword(const nsACString &aPassword)
+{
+ return m_baseURL->SetPassword(aPassword);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetHostPort(nsACString &aHostPort)
+{
+ return m_baseURL->GetHostPort(aHostPort);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetHostPort(const nsACString &aHostPort)
+{
+ return m_baseURL->SetHostPort(aHostPort);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetHostAndPort(const nsACString &aHostPort)
+{
+ return m_baseURL->SetHostAndPort(aHostPort);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetHost(nsACString &aHost)
+{
+ return m_baseURL->GetHost(aHost);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetHost(const nsACString &aHost)
+{
+ return m_baseURL->SetHost(aHost);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetPort(int32_t *aPort)
+{
+ return m_baseURL->GetPort(aPort);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetPort(int32_t aPort)
+{
+ return m_baseURL->SetPort(aPort);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetPath(nsACString &aPath)
+{
+ return m_baseURL->GetPath(aPath);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SetPath(const nsACString &aPath)
+{
+ m_baseURL->SetPath(aPath);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetAsciiHost(nsACString &aHostA)
+{
+ return m_baseURL->GetAsciiHost(aHostA);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetAsciiHostPort(nsACString &aHostPortA)
+{
+ return m_baseURL->GetAsciiHostPort(aHostPortA);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetAsciiSpec(nsACString &aSpecA)
+{
+ return m_baseURL->GetAsciiSpec(aSpecA);
+}
+
+NS_IMETHODIMP nsAddbookUrl::GetOriginCharset(nsACString &aOriginCharset)
+{
+ return m_baseURL->GetOriginCharset(aOriginCharset);
+}
+
+NS_IMETHODIMP nsAddbookUrl::SchemeIs(const char *aScheme, bool *_retval)
+{
+ return m_baseURL->SchemeIs(aScheme, _retval);
+}
+
+NS_IMETHODIMP nsAddbookUrl::Equals(nsIURI *other, bool *_retval)
+{
+ // The passed-in URI might be an nsMailtoUrl. Pass our inner URL to its
+ // Equals method. The other nsMailtoUrl will then pass its inner URL to
+ // to the Equals method of our inner URL. Other URIs will return false.
+ if (other)
+ return other->Equals(m_baseURL, _retval);
+
+ return m_baseURL->Equals(other, _retval);
+}
+
+nsresult
+nsAddbookUrl::CloneInternal(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef, nsIURI** _retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ RefPtr<nsAddbookUrl> clone = new nsAddbookUrl();
+
+ if (!clone)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+ if (aRefHandlingMode == eHonorRef) {
+ rv = m_baseURL->Clone(getter_AddRefs(clone->m_baseURL));
+ } else if (aRefHandlingMode == eReplaceRef) {
+ rv = m_baseURL->CloneWithNewRef(newRef, getter_AddRefs(clone->m_baseURL));
+ } else {
+ rv = m_baseURL->CloneIgnoringRef(getter_AddRefs(clone->m_baseURL));
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ clone->ParseUrl();
+ clone.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddbookUrl::Clone(nsIURI **_retval)
+{
+ return CloneInternal(eHonorRef, EmptyCString(), _retval);
+}
+
+NS_IMETHODIMP
+nsAddbookUrl::CloneIgnoringRef(nsIURI** _retval)
+{
+ return CloneInternal(eIgnoreRef, EmptyCString(), _retval);
+}
+
+NS_IMETHODIMP
+nsAddbookUrl::CloneWithNewRef(const nsACString& newRef, nsIURI** _retval)
+{
+ return CloneInternal(eReplaceRef, newRef, _retval);
+}
+
+NS_IMETHODIMP nsAddbookUrl::Resolve(const nsACString &relativePath, nsACString &result)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsAddbookUrl::GetRef(nsACString &result)
+{
+ return m_baseURL->GetRef(result);
+}
+
+NS_IMETHODIMP
+nsAddbookUrl::SetRef(const nsACString &aRef)
+{
+ m_baseURL->SetRef(aRef);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsAddbookUrl::EqualsExceptRef(nsIURI *other, bool *_retval)
+{
+ // The passed-in URI might be an nsMailtoUrl. Pass our inner URL to its
+ // Equals method. The other nsMailtoUrl will then pass its inner URL to
+ // to the Equals method of our inner URL. Other URIs will return false.
+ if (other)
+ return other->EqualsExceptRef(m_baseURL, _retval);
+
+ return m_baseURL->EqualsExceptRef(other, _retval);
+}
+
+NS_IMETHODIMP
+nsAddbookUrl::GetSpecIgnoringRef(nsACString &result)
+{
+ return m_baseURL->GetSpecIgnoringRef(result);
+}
+
+NS_IMETHODIMP
+nsAddbookUrl::GetHasRef(bool *result)
+{
+ return m_baseURL->GetHasRef(result);
+}
+
+//
+// Specific nsAddbookUrl operations
+//
+NS_IMETHODIMP
+nsAddbookUrl::GetAddbookOperation(int32_t *_retval)
+{
+ *_retval = mOperationType;
+ return NS_OK;
+}
diff --git a/mailnews/addrbook/src/nsAddbookUrl.h b/mailnews/addrbook/src/nsAddbookUrl.h
new file mode 100644
index 000000000..71a34abe8
--- /dev/null
+++ b/mailnews/addrbook/src/nsAddbookUrl.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAddbookUrl_h__
+#define nsAddbookUrl_h__
+
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+#include "nsIAddbookUrl.h"
+
+class nsAddbookUrl : public nsIAddbookUrl
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIURI
+ NS_DECL_NSIADDBOOKURL
+
+ nsAddbookUrl();
+
+protected:
+ enum RefHandlingEnum {
+ eIgnoreRef,
+ eHonorRef,
+ eReplaceRef
+ };
+ virtual ~nsAddbookUrl();
+ nsresult
+ CloneInternal(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef, nsIURI** _retval);
+
+ nsresult ParseUrl();
+ int32_t mOperationType; // the internal ID for the operation
+
+ nsCOMPtr<nsIURI> m_baseURL; // the base URL for the object
+};
+
+#endif // nsAddbookUrl_h__
diff --git a/mailnews/addrbook/src/nsAddrDatabase.cpp b/mailnews/addrbook/src/nsAddrDatabase.cpp
new file mode 100644
index 000000000..ea29ba8af
--- /dev/null
+++ b/mailnews/addrbook/src/nsAddrDatabase.cpp
@@ -0,0 +1,3335 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// this file implements the nsAddrDatabase interface using the MDB Interface.
+
+#include "nsAddrDatabase.h"
+#include "nsStringGlue.h"
+#include "nsAutoPtr.h"
+#include "nsUnicharUtils.h"
+#include "nsAbBaseCID.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+#include "nsMorkCID.h"
+#include "nsIMdbFactoryFactory.h"
+#include "prprf.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "nsIPromptService.h"
+#include "nsIStringBundle.h"
+#include "nsIFile.h"
+#include "nsEmbedCID.h"
+#include "nsIProperty.h"
+#include "nsIVariant.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIAbManager.h"
+#include "mozilla/Services.h"
+#include <algorithm>
+
+#define ID_PAB_TABLE 1
+#define ID_DELETEDCARDS_TABLE 2
+
+// There's two books by default, although Mac may have one more, so set this
+// to three. Its not going to affect much, but will save us a few reallocations
+// when the cache is allocated.
+const uint32_t kInitialAddrDBCacheSize = 3;
+
+static const char kPabTableKind[] = "ns:addrbk:db:table:kind:pab";
+static const char kDeletedCardsTableKind[] = "ns:addrbk:db:table:kind:deleted"; // this table is used to keep the deleted cards
+
+static const char kCardRowScope[] = "ns:addrbk:db:row:scope:card:all";
+static const char kListRowScope[] = "ns:addrbk:db:row:scope:list:all";
+static const char kDataRowScope[] = "ns:addrbk:db:row:scope:data:all";
+
+#define DATAROW_ROWID 1
+
+#define COLUMN_STR_MAX 16
+
+#define PURGE_CUTOFF_COUNT 50
+
+static const char kRecordKeyColumn[] = "RecordKey";
+static const char kLastRecordKeyColumn[] = "LastRecordKey";
+static const char kRowIDProperty[] = "DbRowID";
+
+static const char kLowerListNameColumn[] = "LowercaseListName";
+
+struct mdbOid gAddressBookTableOID;
+
+static const char kMailListAddressFormat[] = "Address%d";
+
+nsAddrDatabase::nsAddrDatabase()
+ : m_mdbEnv(nullptr), m_mdbStore(nullptr),
+ m_mdbPabTable(nullptr),
+ m_mdbDeletedCardsTable(nullptr),
+ m_mdbTokensInitialized(false),
+ m_PabTableKind(0),
+ m_MailListTableKind(0),
+ m_DeletedCardsTableKind(0),
+ m_CardRowScopeToken(0),
+ m_FirstNameColumnToken(0),
+ m_LastNameColumnToken(0),
+ m_PhoneticFirstNameColumnToken(0),
+ m_PhoneticLastNameColumnToken(0),
+ m_DisplayNameColumnToken(0),
+ m_NickNameColumnToken(0),
+ m_PriEmailColumnToken(0),
+ m_2ndEmailColumnToken(0),
+ m_WorkPhoneColumnToken(0),
+ m_HomePhoneColumnToken(0),
+ m_FaxColumnToken(0),
+ m_PagerColumnToken(0),
+ m_CellularColumnToken(0),
+ m_WorkPhoneTypeColumnToken(0),
+ m_HomePhoneTypeColumnToken(0),
+ m_FaxTypeColumnToken(0),
+ m_PagerTypeColumnToken(0),
+ m_CellularTypeColumnToken(0),
+ m_HomeAddressColumnToken(0),
+ m_HomeAddress2ColumnToken(0),
+ m_HomeCityColumnToken(0),
+ m_HomeStateColumnToken(0),
+ m_HomeZipCodeColumnToken(0),
+ m_HomeCountryColumnToken(0),
+ m_WorkAddressColumnToken(0),
+ m_WorkAddress2ColumnToken(0),
+ m_WorkCityColumnToken(0),
+ m_WorkStateColumnToken(0),
+ m_WorkZipCodeColumnToken(0),
+ m_WorkCountryColumnToken(0),
+ m_CompanyColumnToken(0),
+ m_AimScreenNameColumnToken(0),
+ m_AnniversaryYearColumnToken(0),
+ m_AnniversaryMonthColumnToken(0),
+ m_AnniversaryDayColumnToken(0),
+ m_SpouseNameColumnToken(0),
+ m_FamilyNameColumnToken(0),
+ m_DefaultAddressColumnToken(0),
+ m_CategoryColumnToken(0),
+ m_WebPage1ColumnToken(0),
+ m_WebPage2ColumnToken(0),
+ m_BirthYearColumnToken(0),
+ m_BirthMonthColumnToken(0),
+ m_BirthDayColumnToken(0),
+ m_Custom1ColumnToken(0),
+ m_Custom2ColumnToken(0),
+ m_Custom3ColumnToken(0),
+ m_Custom4ColumnToken(0),
+ m_NotesColumnToken(0),
+ m_LastModDateColumnToken(0),
+ m_MailFormatColumnToken(0),
+ m_PopularityIndexColumnToken(0),
+ m_AddressCharSetColumnToken(0),
+ m_LastRecordKey(0),
+ m_dbDirectory(nullptr)
+{
+}
+
+nsAddrDatabase::~nsAddrDatabase()
+{
+ Close(false); // better have already been closed.
+
+ // better not be any listeners, because we're going away.
+ NS_ASSERTION(m_ChangeListeners.Length() == 0, "shouldn't have any listeners");
+
+ RemoveFromCache(this);
+ // clean up after ourself!
+ if (m_mdbPabTable)
+ m_mdbPabTable->Release();
+ if (m_mdbDeletedCardsTable)
+ m_mdbDeletedCardsTable->Release();
+ NS_IF_RELEASE(m_mdbStore);
+ NS_IF_RELEASE(m_mdbEnv);
+}
+
+NS_IMPL_ISUPPORTS(nsAddrDatabase, nsIAddrDatabase, nsIAddrDBAnnouncer)
+
+NS_IMETHODIMP nsAddrDatabase::AddListener(nsIAddrDBListener *listener)
+{
+ NS_ENSURE_ARG_POINTER(listener);
+ return m_ChangeListeners.AppendElement(listener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDatabase::RemoveListener(nsIAddrDBListener *listener)
+{
+ NS_ENSURE_ARG_POINTER(listener);
+ return m_ChangeListeners.RemoveElement(listener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDatabase::NotifyCardAttribChange(uint32_t abCode)
+{
+ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(m_ChangeListeners, nsIAddrDBListener,
+ OnCardAttribChange, (abCode));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::NotifyCardEntryChange(uint32_t aAbCode, nsIAbCard *aCard, nsIAbDirectory *aParent)
+{
+ int32_t currentDisplayNameVersion = 0;
+
+ //Update "mail.displayname.version" prefernce
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ prefs->GetIntPref("mail.displayname.version",&currentDisplayNameVersion);
+
+ prefs->SetIntPref("mail.displayname.version",++currentDisplayNameVersion);
+
+ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(m_ChangeListeners, nsIAddrDBListener,
+ OnCardEntryChange, (aAbCode, aCard, aParent));
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::NotifyListEntryChange(uint32_t abCode, nsIAbDirectory *dir)
+{
+ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(m_ChangeListeners, nsIAddrDBListener,
+ OnListEntryChange, (abCode, dir));
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsAddrDatabase::NotifyAnnouncerGoingAway(void)
+{
+ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(m_ChangeListeners, nsIAddrDBListener,
+ OnAnnouncerGoingAway, ());
+ return NS_OK;
+}
+
+
+// Apparently its not good for nsTArray to be allocated as static. Don't know
+// why it isn't but its not, so don't think about making it a static variable.
+// Maybe bz knows.
+nsTArray<nsAddrDatabase*>* nsAddrDatabase::m_dbCache = nullptr;
+
+nsTArray<nsAddrDatabase*>*
+nsAddrDatabase::GetDBCache()
+{
+ if (!m_dbCache)
+ m_dbCache = new AutoTArray<nsAddrDatabase*, kInitialAddrDBCacheSize>;
+
+ return m_dbCache;
+}
+
+void
+nsAddrDatabase::CleanupCache()
+{
+ if (m_dbCache)
+ {
+ for (int32_t i = m_dbCache->Length() - 1; i >= 0; --i)
+ {
+ nsAddrDatabase* pAddrDB = m_dbCache->ElementAt(i);
+ if (pAddrDB)
+ pAddrDB->ForceClosed();
+ }
+ // NS_ASSERTION(m_dbCache.Length() == 0, "some msg dbs left open"); // better not be any open db's.
+ delete m_dbCache;
+ m_dbCache = nullptr;
+ }
+}
+
+//----------------------------------------------------------------------
+// FindInCache - this addrefs the db it finds.
+//----------------------------------------------------------------------
+nsAddrDatabase* nsAddrDatabase::FindInCache(nsIFile *dbName)
+{
+ nsTArray<nsAddrDatabase*>* dbCache = GetDBCache();
+ uint32_t length = dbCache->Length();
+ for (uint32_t i = 0; i < length; ++i)
+ {
+ nsAddrDatabase* pAddrDB = dbCache->ElementAt(i);
+ if (pAddrDB->MatchDbName(dbName))
+ {
+ NS_ADDREF(pAddrDB);
+ return pAddrDB;
+ }
+ }
+ return nullptr;
+}
+
+bool nsAddrDatabase::MatchDbName(nsIFile* dbName) // returns true if they match
+{
+ bool dbMatches = false;
+
+ nsresult rv = m_dbName->Equals(dbName, &dbMatches);
+ if (NS_FAILED(rv))
+ return false;
+
+ return dbMatches;
+}
+
+//----------------------------------------------------------------------
+// RemoveFromCache
+//----------------------------------------------------------------------
+void nsAddrDatabase::RemoveFromCache(nsAddrDatabase* pAddrDB)
+{
+ if (m_dbCache)
+ m_dbCache->RemoveElement(pAddrDB);
+}
+
+void nsAddrDatabase::GetMDBFactory(nsIMdbFactory ** aMdbFactory)
+{
+ if (!mMdbFactory)
+ {
+ nsresult rv;
+ nsCOMPtr <nsIMdbFactoryService> mdbFactoryService = do_GetService(NS_MORK_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && mdbFactoryService)
+ rv = mdbFactoryService->GetMdbFactory(getter_AddRefs(mMdbFactory));
+ }
+ NS_IF_ADDREF(*aMdbFactory = mMdbFactory);
+}
+
+/* caller need to delete *aDbPath */
+NS_IMETHODIMP nsAddrDatabase::GetDbPath(nsIFile* *aDbPath)
+{
+ if (!aDbPath)
+ return NS_ERROR_NULL_POINTER;
+
+ return m_dbName->Clone(aDbPath);
+}
+
+NS_IMETHODIMP nsAddrDatabase::SetDbPath(nsIFile* aDbPath)
+{
+ return aDbPath->Clone(getter_AddRefs(m_dbName));
+}
+
+NS_IMETHODIMP nsAddrDatabase::Open
+(nsIFile *aMabFile, bool aCreate, bool upgrading /* unused */, nsIAddrDatabase** pAddrDB)
+{
+ *pAddrDB = nullptr;
+
+ nsAddrDatabase *pAddressBookDB = FindInCache(aMabFile);
+
+ if (pAddressBookDB) {
+ *pAddrDB = pAddressBookDB;
+ return NS_OK;
+ }
+
+ nsresult rv = OpenInternal(aMabFile, aCreate, pAddrDB);
+ if (NS_SUCCEEDED(rv))
+ return NS_OK;
+
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED)
+ {
+ static bool gAlreadyAlerted;
+ // only do this once per session to avoid annoying the user
+ if (!gAlreadyAlerted)
+ {
+ gAlreadyAlerted = true;
+ nsAutoString mabFileName;
+ rv = aMabFile->GetLeafName(mabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ AlertAboutLockedMabFile(mabFileName.get());
+
+ // We just overwrote rv, so return the proper value here.
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ }
+ // try one more time
+ // but first rename corrupt mab file
+ // and prompt the user
+ else if (aCreate)
+ {
+ nsCOMPtr<nsIFile> dummyBackupMabFile;
+ nsCOMPtr<nsIFile> actualBackupMabFile;
+
+ // First create a clone of the corrupt mab file that we'll
+ // use to generate the name for the backup file that we are
+ // going to move it to.
+ rv = aMabFile->Clone(getter_AddRefs(dummyBackupMabFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now create a second clone that we'll use to do the move
+ // (this allows us to leave the original name intact)
+ rv = aMabFile->Clone(getter_AddRefs(actualBackupMabFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now we try and generate a new name for the corrupt mab
+ // file using the dummy backup mab file
+
+ // First append .bak - we have to do this the long way as
+ // AppendNative is to the path, not the LeafName.
+ nsAutoCString dummyBackupMabFileName;
+ rv = dummyBackupMabFile->GetNativeLeafName(dummyBackupMabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ dummyBackupMabFileName.Append(NS_LITERAL_CSTRING(".bak"));
+
+ rv = dummyBackupMabFile->SetNativeLeafName(dummyBackupMabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now see if we can create it unique
+ rv = dummyBackupMabFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now get the new name
+ nsAutoCString backupMabFileName;
+ rv = dummyBackupMabFile->GetNativeLeafName(backupMabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // And the parent directory
+ nsCOMPtr<nsIFile> parentDir;
+ rv = dummyBackupMabFile->GetParent(getter_AddRefs(parentDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now move the corrupt file to its backup location
+ rv = actualBackupMabFile->MoveToNative(parentDir, backupMabFileName);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to rename corrupt mab file");
+
+ if (NS_SUCCEEDED(rv)) {
+ // now we can try to recreate the original mab file
+ rv = OpenInternal(aMabFile, aCreate, pAddrDB);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create .mab file, after rename");
+
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString originalMabFileName;
+ rv = aMabFile->GetLeafName(originalMabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if this fails, we don't care
+ (void)AlertAboutCorruptMabFile(originalMabFileName.get(),
+ NS_ConvertASCIItoUTF16(backupMabFileName).get());
+ }
+ }
+ }
+ return rv;
+}
+
+nsresult nsAddrDatabase::DisplayAlert(const char16_t *titleName, const char16_t *alertStringName, const char16_t **formatStrings, int32_t numFormatStrings)
+{
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString alertMessage;
+ rv = bundle->FormatStringFromName(alertStringName, formatStrings, numFormatStrings,
+ getter_Copies(alertMessage));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString alertTitle;
+ rv = bundle->GetStringFromName(titleName, getter_Copies(alertTitle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPromptService> prompter =
+ do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return prompter->Alert(nullptr /* we don't know the parent window */, alertTitle.get(), alertMessage.get());
+}
+
+nsresult nsAddrDatabase::AlertAboutCorruptMabFile(const char16_t *aOldFileName, const char16_t *aNewFileName)
+{
+ const char16_t *formatStrings[] = { aOldFileName, aOldFileName, aNewFileName };
+ return DisplayAlert(u"corruptMabFileTitle",
+ u"corruptMabFileAlert", formatStrings, 3);
+}
+
+nsresult nsAddrDatabase::AlertAboutLockedMabFile(const char16_t *aFileName)
+{
+ const char16_t *formatStrings[] = { aFileName };
+ return DisplayAlert(u"lockedMabFileTitle",
+ u"lockedMabFileAlert", formatStrings, 1);
+}
+
+nsresult
+nsAddrDatabase::OpenInternal(nsIFile *aMabFile, bool aCreate, nsIAddrDatabase** pAddrDB)
+{
+ nsAddrDatabase *pAddressBookDB = new nsAddrDatabase();
+ if (!pAddressBookDB) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(pAddressBookDB);
+
+ nsresult rv = pAddressBookDB->OpenMDB(aMabFile, aCreate);
+ if (NS_SUCCEEDED(rv))
+ {
+ pAddressBookDB->SetDbPath(aMabFile);
+ GetDBCache()->AppendElement(pAddressBookDB);
+ *pAddrDB = pAddressBookDB;
+ }
+ else
+ {
+ *pAddrDB = nullptr;
+ pAddressBookDB->ForceClosed();
+ NS_IF_RELEASE(pAddressBookDB);
+ pAddressBookDB = nullptr;
+ }
+ return rv;
+}
+
+// Open the MDB database synchronously. If successful, this routine
+// will set up the m_mdbStore and m_mdbEnv of the database object
+// so other database calls can work.
+NS_IMETHODIMP nsAddrDatabase::OpenMDB(nsIFile *dbName, bool create)
+{
+ nsresult ret;
+ nsCOMPtr<nsIMdbFactory> mdbFactory;
+ GetMDBFactory(getter_AddRefs(mdbFactory));
+ NS_ENSURE_TRUE(mdbFactory, NS_ERROR_FAILURE);
+
+ ret = mdbFactory->MakeEnv(NULL, &m_mdbEnv);
+ if (NS_SUCCEEDED(ret))
+ {
+ nsIMdbThumb *thumb = nullptr;
+ nsAutoCString filePath;
+
+ ret = dbName->GetNativePath(filePath);
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ nsIMdbHeap* dbHeap = nullptr;
+
+ if (m_mdbEnv)
+ m_mdbEnv->SetAutoClear(true);
+
+ bool dbNameExists = false;
+ ret = dbName->Exists(&dbNameExists);
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ if (!dbNameExists)
+ ret = NS_ERROR_FILE_NOT_FOUND;
+ else
+ {
+ mdbOpenPolicy inOpenPolicy;
+ mdb_bool canOpen;
+ mdbYarn outFormatVersion;
+ nsIMdbFile* oldFile = nullptr;
+ int64_t fileSize;
+ ret = dbName->GetFileSize(&fileSize);
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ ret = mdbFactory->OpenOldFile(m_mdbEnv, dbHeap, filePath.get(),
+ mdbBool_kFalse, // not readonly, we want modifiable
+ &oldFile);
+ if ( oldFile )
+ {
+ if (NS_SUCCEEDED(ret))
+ {
+ ret = mdbFactory->CanOpenFilePort(m_mdbEnv, oldFile, // the file to investigate
+ &canOpen, &outFormatVersion);
+ if (NS_SUCCEEDED(ret) && canOpen)
+ {
+ inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
+ inOpenPolicy.mOpenPolicy_MinMemory = 0;
+ inOpenPolicy.mOpenPolicy_MaxLazy = 0;
+
+ ret = mdbFactory->OpenFileStore(m_mdbEnv, dbHeap,
+ oldFile, &inOpenPolicy, &thumb);
+ }
+ else if (fileSize != 0)
+ ret = NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ NS_RELEASE(oldFile); // always release our file ref, store has own
+ }
+ if (NS_FAILED(ret))
+ ret = NS_ERROR_FILE_ACCESS_DENIED;
+ }
+
+ if (NS_SUCCEEDED(ret) && thumb)
+ {
+ mdb_count outTotal; // total somethings to do in operation
+ mdb_count outCurrent; // subportion of total completed so far
+ mdb_bool outDone = false; // is operation finished?
+ mdb_bool outBroken; // is operation irreparably dead and broken?
+ do
+ {
+ ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
+ if (NS_FAILED(ret))
+ {
+ outDone = true;
+ break;
+ }
+ }
+ while (NS_SUCCEEDED(ret) && !outBroken && !outDone);
+ if (NS_SUCCEEDED(ret) && outDone)
+ {
+ ret = mdbFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore);
+ if (NS_SUCCEEDED(ret) && m_mdbStore)
+ {
+ ret = InitExistingDB();
+ create = false;
+ }
+ }
+ }
+ else if (create && ret != NS_ERROR_FILE_ACCESS_DENIED)
+ {
+ nsIMdbFile* newFile = 0;
+ ret = mdbFactory->CreateNewFile(m_mdbEnv, dbHeap, filePath.get(), &newFile);
+ if ( newFile )
+ {
+ if (NS_SUCCEEDED(ret))
+ {
+ mdbOpenPolicy inOpenPolicy;
+
+ inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
+ inOpenPolicy.mOpenPolicy_MinMemory = 0;
+ inOpenPolicy.mOpenPolicy_MaxLazy = 0;
+
+ ret = mdbFactory->CreateNewFileStore(m_mdbEnv, dbHeap,
+ newFile, &inOpenPolicy,
+ &m_mdbStore);
+ if (NS_SUCCEEDED(ret))
+ ret = InitNewDB();
+ }
+ NS_RELEASE(newFile); // always release our file ref, store has own
+ }
+ }
+ NS_IF_RELEASE(thumb);
+ }
+ return ret;
+}
+
+NS_IMETHODIMP nsAddrDatabase::CloseMDB(bool commit)
+{
+ if (commit)
+ Commit(nsAddrDBCommitType::kSessionCommit);
+//??? RemoveFromCache(this); // if we've closed it, better not leave it in the cache.
+ return NS_OK;
+}
+
+// force the database to close - this'll flush out anybody holding onto
+// a database without having a listener!
+// This is evil in the com world, but there are times we need to delete the file.
+NS_IMETHODIMP nsAddrDatabase::ForceClosed()
+{
+ nsresult err = NS_OK;
+ nsCOMPtr<nsIAddrDatabase> aDb(do_QueryInterface(this, &err));
+
+ // make sure someone has a reference so object won't get deleted out from under us.
+ AddRef();
+ NotifyAnnouncerGoingAway();
+ // OK, remove from cache first and close the store.
+ RemoveFromCache(this);
+
+ err = CloseMDB(false); // since we're about to delete it, no need to commit.
+ NS_IF_RELEASE(m_mdbStore);
+ Release();
+ return err;
+}
+
+NS_IMETHODIMP nsAddrDatabase::Commit(uint32_t commitType)
+{
+ nsresult err = NS_OK;
+ nsIMdbThumb *commitThumb = nullptr;
+
+ if (commitType == nsAddrDBCommitType::kLargeCommit ||
+ commitType == nsAddrDBCommitType::kSessionCommit)
+ {
+ mdb_percent outActualWaste = 0;
+ mdb_bool outShould;
+ if (m_mdbStore && m_mdbEnv)
+ {
+ // check how much space would be saved by doing a compress commit.
+ // If it's more than 30%, go for it.
+ // N.B. - I'm not sure this calls works in Mork for all cases.
+ err = m_mdbStore->ShouldCompress(m_mdbEnv, 30, &outActualWaste, &outShould);
+ if (NS_SUCCEEDED(err) && outShould)
+ {
+ commitType = nsAddrDBCommitType::kCompressCommit;
+ }
+ }
+ }
+
+ if (m_mdbStore && m_mdbEnv)
+ {
+ switch (commitType)
+ {
+ case nsAddrDBCommitType::kLargeCommit:
+ err = m_mdbStore->LargeCommit(m_mdbEnv, &commitThumb);
+ break;
+ case nsAddrDBCommitType::kSessionCommit:
+ // comment out until persistence works.
+ err = m_mdbStore->SessionCommit(m_mdbEnv, &commitThumb);
+ break;
+ case nsAddrDBCommitType::kCompressCommit:
+ err = m_mdbStore->CompressCommit(m_mdbEnv, &commitThumb);
+ break;
+ }
+ }
+ if (commitThumb && m_mdbEnv)
+ {
+ mdb_count outTotal = 0; // total somethings to do in operation
+ mdb_count outCurrent = 0; // subportion of total completed so far
+ mdb_bool outDone = false; // is operation finished?
+ mdb_bool outBroken = false; // is operation irreparably dead and broken?
+ while (!outDone && !outBroken && NS_SUCCEEDED(err))
+ {
+ err = commitThumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
+ }
+ NS_RELEASE(commitThumb);
+ }
+ // ### do something with error, but clear it now because mork errors out on commits.
+ if (m_mdbEnv)
+ m_mdbEnv->ClearErrors();
+ return err;
+}
+
+NS_IMETHODIMP nsAddrDatabase::Close(bool forceCommit /* = TRUE */)
+{
+ return CloseMDB(forceCommit);
+}
+
+// set up empty tablesetc.
+nsresult nsAddrDatabase::InitNewDB()
+{
+ nsresult err = InitMDBInfo();
+ if (NS_SUCCEEDED(err))
+ {
+ err = InitPabTable();
+ err = InitLastRecorKey();
+ Commit(nsAddrDBCommitType::kLargeCommit);
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::AddRowToDeletedCardsTable(nsIAbCard *card, nsIMdbRow **pCardRow)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+ if (!m_mdbDeletedCardsTable)
+ rv = InitDeletedCardsTable(true);
+
+ if (NS_SUCCEEDED(rv)) {
+ // lets first purge old records if there are more than PURGE_CUTOFF_COUNT records
+ PurgeDeletedCardTable();
+ nsCOMPtr<nsIMdbRow> cardRow;
+ rv = GetNewRow(getter_AddRefs(cardRow));
+ if (NS_SUCCEEDED(rv) && cardRow) {
+ nsresult merror = m_mdbDeletedCardsTable->AddRow(m_mdbEnv, cardRow);
+ NS_ENSURE_SUCCESS(merror, NS_ERROR_FAILURE);
+ nsString unicodeStr;
+ card->GetFirstName(unicodeStr);
+ AddFirstName(cardRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ card->GetLastName(unicodeStr);
+ AddLastName(cardRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ card->GetDisplayName(unicodeStr);
+ AddDisplayName(cardRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ card->GetPrimaryEmail(unicodeStr);
+ if (!unicodeStr.IsEmpty())
+ AddUnicodeToColumn(cardRow, m_PriEmailColumnToken, m_LowerPriEmailColumnToken, unicodeStr.get());
+
+ card->GetPropertyAsAString(k2ndEmailProperty, unicodeStr);
+ if (!unicodeStr.IsEmpty())
+ AddUnicodeToColumn(cardRow, m_2ndEmailColumnToken, m_Lower2ndEmailColumnToken, unicodeStr.get());
+
+ uint32_t nowInSeconds;
+ PRTime now = PR_Now();
+ PRTime2Seconds(now, &nowInSeconds);
+ AddIntColumn(cardRow, m_LastModDateColumnToken, nowInSeconds);
+
+ nsString value;
+ GetCardValue(card, CARD_ATTRIB_PALMID, getter_Copies(value));
+ if (!value.IsEmpty())
+ {
+ nsCOMPtr<nsIAbCard> addedCard;
+ rv = CreateCardFromDeletedCardsTable(cardRow, 0, getter_AddRefs(addedCard));
+ if (NS_SUCCEEDED(rv))
+ SetCardValue(addedCard, CARD_ATTRIB_PALMID, value.get(), false);
+ }
+ NS_IF_ADDREF(*pCardRow = cardRow);
+ }
+ Commit(nsAddrDBCommitType::kLargeCommit);
+ }
+ return rv;
+}
+
+nsresult nsAddrDatabase::DeleteRowFromDeletedCardsTable(nsIMdbRow *pCardRow)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult merror = NS_OK;
+ if (m_mdbDeletedCardsTable) {
+ pCardRow->CutAllColumns(m_mdbEnv);
+ merror = m_mdbDeletedCardsTable->CutRow(m_mdbEnv, pCardRow);
+ }
+ return merror;
+}
+
+
+nsresult nsAddrDatabase::InitDeletedCardsTable(bool aCreate)
+{
+ nsresult mdberr = NS_OK;
+ if (!m_mdbDeletedCardsTable)
+ {
+ struct mdbOid deletedCardsTableOID;
+ deletedCardsTableOID.mOid_Scope = m_CardRowScopeToken;
+ deletedCardsTableOID.mOid_Id = ID_DELETEDCARDS_TABLE;
+ if (m_mdbStore && m_mdbEnv)
+ {
+ m_mdbStore->GetTable(m_mdbEnv, &deletedCardsTableOID, &m_mdbDeletedCardsTable);
+ // if deletedCardsTable does not exist and bCreate is set, create a new one
+ if (!m_mdbDeletedCardsTable && aCreate)
+ {
+ mdberr = (nsresult) m_mdbStore->NewTableWithOid(m_mdbEnv, &deletedCardsTableOID,
+ m_DeletedCardsTableKind,
+ true, (const mdbOid*)nullptr,
+ &m_mdbDeletedCardsTable);
+ }
+ }
+ }
+ return mdberr;
+}
+
+nsresult nsAddrDatabase::InitPabTable()
+{
+ return m_mdbStore && m_mdbEnv ? m_mdbStore->NewTableWithOid(m_mdbEnv,
+ &gAddressBookTableOID,
+ m_PabTableKind,
+ false,
+ (const mdbOid*)nullptr,
+ &m_mdbPabTable)
+ : NS_ERROR_NULL_POINTER;
+}
+
+//save the last record number, store in m_DataRowScopeToken, row 1
+nsresult nsAddrDatabase::InitLastRecorKey()
+{
+ if (!m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIMdbRow *pDataRow = nullptr;
+ mdbOid dataRowOid;
+ dataRowOid.mOid_Scope = m_DataRowScopeToken;
+ dataRowOid.mOid_Id = DATAROW_ROWID;
+ nsresult err = m_mdbStore->NewRowWithOid(m_mdbEnv, &dataRowOid, &pDataRow);
+
+ if (NS_SUCCEEDED(err) && pDataRow)
+ {
+ m_LastRecordKey = 0;
+ err = AddIntColumn(pDataRow, m_LastRecordKeyColumnToken, 0);
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pDataRow);
+ NS_RELEASE(pDataRow);
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::GetDataRow(nsIMdbRow **pDataRow)
+{
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIMdbRow *pRow = nullptr;
+ mdbOid dataRowOid;
+ dataRowOid.mOid_Scope = m_DataRowScopeToken;
+ dataRowOid.mOid_Id = DATAROW_ROWID;
+ m_mdbStore->GetRow(m_mdbEnv, &dataRowOid, &pRow);
+ *pDataRow = pRow;
+
+ return pRow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::GetLastRecordKey()
+{
+ if (!m_mdbPabTable)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr <nsIMdbRow> pDataRow;
+ nsresult err = GetDataRow(getter_AddRefs(pDataRow));
+
+ if (NS_SUCCEEDED(err) && pDataRow)
+ {
+ m_LastRecordKey = 0;
+ err = GetIntColumn(pDataRow, m_LastRecordKeyColumnToken, &m_LastRecordKey, 0);
+ if (NS_FAILED(err))
+ err = NS_ERROR_NOT_AVAILABLE;
+ return NS_OK;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult nsAddrDatabase::UpdateLastRecordKey()
+{
+ if (!m_mdbPabTable || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr <nsIMdbRow> pDataRow;
+ nsresult err = GetDataRow(getter_AddRefs(pDataRow));
+
+ if (NS_SUCCEEDED(err) && pDataRow)
+ {
+ err = AddIntColumn(pDataRow, m_LastRecordKeyColumnToken, m_LastRecordKey);
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pDataRow);
+ return NS_OK;
+ }
+ else if (!pDataRow)
+ err = InitLastRecorKey();
+ else
+ return NS_ERROR_NOT_AVAILABLE;
+ return err;
+}
+
+nsresult nsAddrDatabase::InitExistingDB()
+{
+ nsresult err = InitMDBInfo();
+ if (NS_SUCCEEDED(err))
+ {
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ err = m_mdbStore->GetTable(m_mdbEnv, &gAddressBookTableOID, &m_mdbPabTable);
+ if (NS_SUCCEEDED(err) && m_mdbPabTable)
+ {
+ err = GetLastRecordKey();
+ if (err == NS_ERROR_NOT_AVAILABLE)
+ CheckAndUpdateRecordKey();
+ UpdateLowercaseEmailListName();
+ }
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::CheckAndUpdateRecordKey()
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ nsIMdbTableRowCursor* rowCursor = nullptr;
+ nsIMdbRow* findRow = nullptr;
+ mdb_pos rowPos = 0;
+
+ nsresult merror = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
+
+ NS_ENSURE_TRUE(NS_SUCCEEDED(merror) && rowCursor, NS_ERROR_FAILURE);
+
+ nsCOMPtr <nsIMdbRow> pDataRow;
+ err = GetDataRow(getter_AddRefs(pDataRow));
+ if (NS_FAILED(err))
+ InitLastRecorKey();
+
+ do
+ { //add key to each card and mailing list row
+ merror = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos);
+ if (NS_SUCCEEDED(merror) && findRow)
+ {
+ mdbOid rowOid;
+
+ if (NS_SUCCEEDED(findRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ if (!IsDataRowScopeToken(rowOid.mOid_Scope))
+ {
+ m_LastRecordKey++;
+ err = AddIntColumn(findRow, m_RecordKeyColumnToken, m_LastRecordKey);
+ }
+ }
+ }
+ } while (findRow);
+
+ UpdateLastRecordKey();
+ Commit(nsAddrDBCommitType::kLargeCommit);
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::UpdateLowercaseEmailListName()
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ nsIMdbTableRowCursor* rowCursor = nullptr;
+ nsIMdbRow* findRow = nullptr;
+ mdb_pos rowPos = 0;
+ bool commitRequired = false;
+
+ nsresult merror = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
+
+ NS_ENSURE_TRUE(NS_SUCCEEDED(merror) && rowCursor, NS_ERROR_FAILURE);
+
+ do
+ { // Add lowercase primary+secondary email to each card and mailing list row.
+ merror = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos);
+ if (NS_SUCCEEDED(merror) && findRow)
+ {
+ mdbOid rowOid;
+
+ if (NS_SUCCEEDED(findRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ nsAutoString tempString;
+ if (IsCardRowScopeToken(rowOid.mOid_Scope))
+ {
+ err = GetStringColumn(findRow, m_LowerPriEmailColumnToken, tempString);
+ if (NS_FAILED(err)) // not set yet
+ {
+ err = ConvertAndAddLowercaseColumn(findRow, m_PriEmailColumnToken,
+ m_LowerPriEmailColumnToken);
+ commitRequired = commitRequired || NS_SUCCEEDED(err);
+ }
+
+ err = GetStringColumn(findRow, m_Lower2ndEmailColumnToken, tempString);
+ if (NS_FAILED(err)) // not set yet
+ {
+ err = ConvertAndAddLowercaseColumn(findRow, m_2ndEmailColumnToken,
+ m_Lower2ndEmailColumnToken);
+ commitRequired = commitRequired || NS_SUCCEEDED(err);
+ }
+ }
+ else if (IsListRowScopeToken(rowOid.mOid_Scope))
+ {
+ err = GetStringColumn(findRow, m_LowerListNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err)) // already set up
+ continue;
+
+ err = ConvertAndAddLowercaseColumn(findRow, m_ListNameColumnToken,
+ m_LowerListNameColumnToken);
+ commitRequired = commitRequired || NS_SUCCEEDED(err);
+ }
+ }
+ findRow->Release();
+ }
+ } while (findRow);
+
+ if (findRow)
+ findRow->Release();
+ rowCursor->Release();
+ if (commitRequired)
+ Commit(nsAddrDBCommitType::kLargeCommit);
+ return NS_OK;
+}
+
+/*
+We store UTF8 strings in the database. We need to convert the UTF8
+string into unicode string, then convert to lower case. Before storing
+back into the database, we need to convert the lowercase unicode string
+into UTF8 string.
+*/
+nsresult nsAddrDatabase::ConvertAndAddLowercaseColumn
+(nsIMdbRow * row, mdb_token fromCol, mdb_token toCol)
+{
+ nsAutoString colString;
+
+ nsresult rv = GetStringColumn(row, fromCol, colString);
+ if (!colString.IsEmpty())
+ {
+ rv = AddLowercaseColumn(row, toCol, NS_ConvertUTF16toUTF8(colString).get());
+ }
+ return rv;
+}
+
+// Change the unicode string to lowercase, then convert to UTF8 string to store in db
+nsresult nsAddrDatabase::AddUnicodeToColumn(nsIMdbRow * row, mdb_token aColToken, mdb_token aLowerCaseColToken, const char16_t* aUnicodeStr)
+{
+ nsresult rv = AddCharStringColumn(row, aColToken, NS_ConvertUTF16toUTF8(aUnicodeStr).get());
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddLowercaseColumn(row, aLowerCaseColToken, NS_ConvertUTF16toUTF8(aUnicodeStr).get());
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+// initialize the various tokens and tables in our db's env
+nsresult nsAddrDatabase::InitMDBInfo()
+{
+ nsresult err = NS_OK;
+
+ if (!m_mdbTokensInitialized && m_mdbStore && m_mdbEnv)
+ {
+ m_mdbTokensInitialized = true;
+ err = m_mdbStore->StringToToken(m_mdbEnv, kCardRowScope, &m_CardRowScopeToken);
+ err = m_mdbStore->StringToToken(m_mdbEnv, kListRowScope, &m_ListRowScopeToken);
+ err = m_mdbStore->StringToToken(m_mdbEnv, kDataRowScope, &m_DataRowScopeToken);
+ gAddressBookTableOID.mOid_Scope = m_CardRowScopeToken;
+ gAddressBookTableOID.mOid_Id = ID_PAB_TABLE;
+ if (NS_SUCCEEDED(err))
+ {
+ m_mdbStore->StringToToken(m_mdbEnv, kFirstNameProperty, &m_FirstNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastNameProperty, &m_LastNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPhoneticFirstNameProperty, &m_PhoneticFirstNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPhoneticLastNameProperty, &m_PhoneticLastNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDisplayNameProperty, &m_DisplayNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kNicknameProperty, &m_NickNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPriEmailProperty, &m_PriEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLowerPriEmailColumn, &m_LowerPriEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, k2ndEmailProperty, &m_2ndEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLower2ndEmailColumn, &m_Lower2ndEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPreferMailFormatProperty, &m_MailFormatColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPopularityIndexProperty, &m_PopularityIndexColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkPhoneProperty, &m_WorkPhoneColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomePhoneProperty, &m_HomePhoneColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFaxProperty, &m_FaxColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPagerProperty, &m_PagerColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCellularProperty, &m_CellularColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkPhoneTypeProperty, &m_WorkPhoneTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomePhoneTypeProperty, &m_HomePhoneTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFaxTypeProperty, &m_FaxTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPagerTypeProperty, &m_PagerTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCellularTypeProperty, &m_CellularTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeAddressProperty, &m_HomeAddressColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeAddress2Property, &m_HomeAddress2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeCityProperty, &m_HomeCityColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeStateProperty, &m_HomeStateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeZipCodeProperty, &m_HomeZipCodeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeCountryProperty, &m_HomeCountryColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkAddressProperty, &m_WorkAddressColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkAddress2Property, &m_WorkAddress2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkCityProperty, &m_WorkCityColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkStateProperty, &m_WorkStateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkZipCodeProperty, &m_WorkZipCodeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkCountryProperty, &m_WorkCountryColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kJobTitleProperty, &m_JobTitleColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDepartmentProperty, &m_DepartmentColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCompanyProperty, &m_CompanyColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kScreenNameProperty, &m_AimScreenNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryYearProperty, &m_AnniversaryYearColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryMonthProperty, &m_AnniversaryMonthColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryDayProperty, &m_AnniversaryDayColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kSpouseNameProperty, &m_SpouseNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFamilyNameProperty, &m_FamilyNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkWebPageProperty, &m_WebPage1ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeWebPageProperty, &m_WebPage2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthYearProperty, &m_BirthYearColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthMonthProperty, &m_BirthMonthColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthDayProperty, &m_BirthDayColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom1Property, &m_Custom1ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom2Property, &m_Custom2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom3Property, &m_Custom3ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom4Property, &m_Custom4ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kNotesProperty, &m_NotesColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastModifiedDateProperty, &m_LastModDateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kRecordKeyColumn, &m_RecordKeyColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAddressCharSetColumn, &m_AddressCharSetColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastRecordKeyColumn, &m_LastRecordKeyColumnToken);
+
+ err = m_mdbStore->StringToToken(m_mdbEnv, kPabTableKind, &m_PabTableKind);
+
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListName, &m_ListNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListNickName, &m_ListNickNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListDescription, &m_ListDescriptionColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListTotalAddresses, &m_ListTotalColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLowerListNameColumn, &m_LowerListNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDeletedCardsTableKind, &m_DeletedCardsTableKind);
+ }
+ }
+ return err;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult nsAddrDatabase::AddRecordKeyColumnToRow(nsIMdbRow *pRow)
+{
+ if (pRow && m_mdbEnv)
+ {
+ m_LastRecordKey++;
+ nsresult err = AddIntColumn(pRow, m_RecordKeyColumnToken, m_LastRecordKey);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pRow);
+ UpdateLastRecordKey();
+ return err;
+ }
+ return NS_ERROR_NULL_POINTER;
+}
+
+nsresult nsAddrDatabase::AddAttributeColumnsToRow(nsIAbCard *card, nsIMdbRow *cardRow)
+{
+ nsresult rv = NS_OK;
+
+ if ((!card && !cardRow) || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid;
+ cardRow->GetOid(m_mdbEnv, &rowOid);
+
+ card->SetPropertyAsUint32(kRowIDProperty, rowOid.mOid_Id);
+
+ // add the row to the singleton table.
+ if (card && cardRow)
+ {
+ nsCOMPtr<nsISimpleEnumerator> properties;
+ rv = card->GetProperties(getter_AddRefs(properties));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(properties->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> next;
+ rv = properties->GetNext(getter_AddRefs(next));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIProperty> prop = do_QueryInterface(next);
+ nsAutoString name;
+ prop->GetName(name);
+
+ nsCOMPtr<nsIVariant> variant;
+ prop->GetValue(getter_AddRefs(variant));
+
+ // We can't get as a char * because that messes up UTF8 stuff
+ nsAutoCString value;
+ variant->GetAsAUTF8String(value);
+
+ mdb_token token;
+ rv = m_mdbStore->StringToToken(m_mdbEnv, NS_ConvertUTF16toUTF8(name).get(), &token);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = AddCharStringColumn(cardRow, token, value.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Primary email is special: it is stored lowercase as well as in its
+ // original format.
+ nsAutoString primaryEmail;
+ card->GetPrimaryEmail(primaryEmail);
+ AddPrimaryEmail(cardRow, NS_ConvertUTF16toUTF8(primaryEmail).get());
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::CreateNewCardAndAddToDB(nsIAbCard *aNewCard, bool aNotify /* = FALSE */, nsIAbDirectory *aParent)
+{
+ nsCOMPtr <nsIMdbRow> cardRow;
+
+ if (!aNewCard || !m_mdbPabTable || !m_mdbEnv || !m_mdbStore)
+ return NS_ERROR_NULL_POINTER;
+
+ // Per the UUID requirements, we want to try to reuse the local id if at all
+ // possible. nsACString::ToInteger probably won't fail if the local id looks
+ // like "23bozo" (returning 23 instead), but it's okay since we aren't going
+ // to overwrite anything with 23 if it already exists and the id for the row
+ // doesn't matter otherwise.
+ nsresult rv;
+
+ nsAutoCString id;
+ aNewCard->GetLocalId(id);
+
+ mdbOid rowId;
+ rowId.mOid_Scope = m_CardRowScopeToken;
+ rowId.mOid_Id = id.ToInteger(&rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ // Mork is being very naughty here. If the table does not have the oid, we
+ // should be able to reuse it. To be on the safe side, however, we're going
+ // to reference the store's reference count.
+ mdb_count rowCount = 1;
+ m_mdbStore->GetRowRefCount(m_mdbEnv, &rowId, &rowCount);
+ if (rowCount == 0)
+ {
+ // So apparently, the row can have a count of 0 yet still exist (probably
+ // meaning we haven't flushed it out of memory). In this case, we need to
+ // get the row and cut its cells.
+ rv = m_mdbStore->GetRow(m_mdbEnv, &rowId, getter_AddRefs(cardRow));
+ if (NS_SUCCEEDED(rv) && cardRow)
+ cardRow->CutAllColumns(m_mdbEnv);
+ else
+ rv = m_mdbStore->NewRowWithOid(m_mdbEnv, &rowId, getter_AddRefs(cardRow));
+ }
+ }
+
+ // If we don't have a cardRow yet, just get one with any ol' id.
+ if (!cardRow)
+ rv = GetNewRow(getter_AddRefs(cardRow));
+
+ if (NS_SUCCEEDED(rv) && cardRow)
+ {
+ AddAttributeColumnsToRow(aNewCard, cardRow);
+ AddRecordKeyColumnToRow(cardRow);
+
+ // we need to do this for dnd
+ uint32_t key = 0;
+ rv = GetIntColumn(cardRow, m_RecordKeyColumnToken, &key, 0);
+ if (NS_SUCCEEDED(rv))
+ aNewCard->SetPropertyAsUint32(kRecordKeyColumn, key);
+
+ aNewCard->GetPropertyAsAUTF8String(kRowIDProperty, id);
+ aNewCard->SetLocalId(id);
+
+ nsCOMPtr<nsIAbDirectory> abDir = do_QueryReferent(m_dbDirectory);
+ if (abDir)
+ abDir->GetUuid(id);
+
+ aNewCard->SetDirectoryId(id);
+
+ nsresult merror = m_mdbPabTable->AddRow(m_mdbEnv, cardRow);
+ NS_ENSURE_SUCCESS(merror, NS_ERROR_FAILURE);
+ }
+ else
+ return rv;
+
+ // do notification
+ if (aNotify)
+ {
+ NotifyCardEntryChange(AB_NotifyInserted, aNewCard, aParent);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::CreateNewListCardAndAddToDB(nsIAbDirectory *aList, uint32_t listRowID, nsIAbCard *newCard, bool notify /* = FALSE */)
+{
+ if (!newCard || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIMdbRow* pListRow = nullptr;
+ mdbOid listRowOid;
+ listRowOid.mOid_Scope = m_ListRowScopeToken;
+ listRowOid.mOid_Id = listRowID;
+ nsresult rv = m_mdbStore->GetRow(m_mdbEnv, &listRowOid, &pListRow);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!pListRow)
+ return NS_OK;
+
+ nsCOMPtr<nsIMutableArray> addressList;
+ rv = aList->GetAddressLists(getter_AddRefs(addressList));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ uint32_t count;
+ addressList->GetLength(&count);
+
+ nsAutoString newEmail;
+ rv = newCard->GetPrimaryEmail(newEmail);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ uint32_t i;
+ for (i = 0; i < count; i++) {
+ nsCOMPtr<nsIAbCard> currentCard = do_QueryElementAt(addressList, i, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool equals;
+ rv = newCard->Equals(currentCard, &equals);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (equals) {
+ // card is already in list, bail out.
+ // this can happen when dropping a card on a mailing list from the directory that contains the mailing list
+ return NS_OK;
+ }
+
+ nsAutoString currentEmail;
+ rv = currentCard->GetPrimaryEmail(currentEmail);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (newEmail.Equals(currentEmail)) {
+ // card is already in list, bail out
+ // this can happen when dropping a card on a mailing list from another directory (not the one that contains the mailing list
+ // or if you have multiple cards on a directory, with the same primary email address.
+ return NS_OK;
+ }
+ }
+
+ // start from 1
+ uint32_t totalAddress = GetListAddressTotal(pListRow) + 1;
+ SetListAddressTotal(pListRow, totalAddress);
+ nsCOMPtr<nsIAbCard> pNewCard;
+ rv = AddListCardColumnsToRow(newCard, pListRow, totalAddress, getter_AddRefs(pNewCard), true /* aInMailingList */, aList, nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ addressList->AppendElement(newCard, false);
+
+ if (notify)
+ NotifyCardEntryChange(AB_NotifyInserted, newCard, aList);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::AddListCardColumnsToRow
+(nsIAbCard *aPCard, nsIMdbRow *aPListRow, uint32_t aPos, nsIAbCard** aPNewCard, bool aInMailingList, nsIAbDirectory *aParent, nsIAbDirectory *aRoot)
+{
+ if (!aPCard || !aPListRow || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ nsString email;
+ aPCard->GetPrimaryEmail(email);
+ if (!email.IsEmpty())
+ {
+ nsIMdbRow *pCardRow = nullptr;
+ // Please DO NOT change the 3rd param of GetRowFromAttribute() call to
+ // true (ie, case insensitive) without reading bugs #128535 and #121478.
+ err = GetRowFromAttribute(kPriEmailProperty, NS_ConvertUTF16toUTF8(email),
+ false /* retain case */, &pCardRow, nullptr);
+ bool cardWasAdded = false;
+ if (NS_FAILED(err) || !pCardRow)
+ {
+ //New Email, then add a new row with this email
+ err = GetNewRow(&pCardRow);
+
+ if (NS_SUCCEEDED(err) && pCardRow)
+ {
+ AddPrimaryEmail(pCardRow, NS_ConvertUTF16toUTF8(email).get());
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pCardRow);
+ // Create a key for this row as well.
+ if (NS_SUCCEEDED(err))
+ AddRecordKeyColumnToRow(pCardRow);
+ }
+
+ cardWasAdded = true;
+ }
+
+ NS_ENSURE_TRUE(pCardRow, NS_ERROR_NULL_POINTER);
+
+ nsString name;
+ aPCard->GetDisplayName(name);
+ if (!name.IsEmpty()) {
+ AddDisplayName(pCardRow, NS_ConvertUTF16toUTF8(name).get());
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pCardRow);
+ }
+
+ nsCOMPtr<nsIAbCard> newCard;
+ CreateABCard(pCardRow, 0, getter_AddRefs(newCard));
+ NS_IF_ADDREF(*aPNewCard = newCard);
+
+ if (cardWasAdded) {
+ NotifyCardEntryChange(AB_NotifyInserted, newCard, aParent);
+ if (aRoot)
+ NotifyCardEntryChange(AB_NotifyInserted, newCard, aRoot);
+ }
+ else if (!aInMailingList) {
+ nsresult rv;
+ nsCOMPtr<nsIAddrDBListener> parentListener(do_QueryInterface(aParent, &rv));
+
+ // Ensure the parent is in the listener list (and hence wants to be notified)
+ if (NS_SUCCEEDED(rv) && m_ChangeListeners.Contains(parentListener))
+ parentListener->OnCardEntryChange(AB_NotifyInserted, aPCard, aParent);
+ }
+ else {
+ NotifyCardEntryChange(AB_NotifyPropertyChanged, aPCard, aParent);
+ }
+
+ //add a column with address row id to the list row
+ mdb_token listAddressColumnToken;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, aPos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ mdbOid outOid;
+
+ if (NS_SUCCEEDED(pCardRow->GetOid(m_mdbEnv, &outOid)))
+ {
+ //save address row ID to the list row
+ err = AddIntColumn(aPListRow, listAddressColumnToken, outOid.mOid_Id);
+ }
+ NS_RELEASE(pCardRow);
+
+ }
+
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::AddListAttributeColumnsToRow(nsIAbDirectory *list, nsIMdbRow *listRow, nsIAbDirectory *aParent)
+{
+ nsresult err = NS_OK;
+
+ if ((!list && !listRow) || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid, tableOid;
+ m_mdbPabTable->GetOid(m_mdbEnv, &tableOid);
+ listRow->GetOid(m_mdbEnv, &rowOid);
+
+ nsCOMPtr<nsIAbMDBDirectory> dblist(do_QueryInterface(list,&err));
+ if (NS_SUCCEEDED(err))
+ dblist->SetDbRowID(rowOid.mOid_Id);
+
+ // add the row to the singleton table.
+ if (NS_SUCCEEDED(err) && listRow)
+ {
+ nsString unicodeStr;
+
+ list->GetDirName(unicodeStr);
+ if (!unicodeStr.IsEmpty())
+ AddUnicodeToColumn(listRow, m_ListNameColumnToken, m_LowerListNameColumnToken, unicodeStr.get());
+
+ list->GetListNickName(unicodeStr);
+ AddListNickName(listRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ list->GetDescription(unicodeStr);
+ AddListDescription(listRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ // XXX todo, this code has problems if you manually enter duplicate emails.
+ nsCOMPtr<nsIMutableArray> pAddressLists;
+ list->GetAddressLists(getter_AddRefs(pAddressLists));
+
+ uint32_t count;
+ pAddressLists->GetLength(&count);
+
+ nsAutoString email;
+ uint32_t i, total;
+ total = 0;
+ for (i = 0; i < count; i++)
+ {
+ nsCOMPtr<nsIAbCard> pCard(do_QueryElementAt(pAddressLists, i, &err));
+
+ if (NS_FAILED(err))
+ continue;
+
+ pCard->GetPrimaryEmail(email);
+ if (!email.IsEmpty())
+ total++;
+ }
+ SetListAddressTotal(listRow, total);
+
+ uint32_t pos;
+ for (i = 0; i < count; i++)
+ {
+ nsCOMPtr<nsIAbCard> pCard(do_QueryElementAt(pAddressLists, i, &err));
+
+ if (NS_FAILED(err))
+ continue;
+
+ bool listHasCard = false;
+ err = list->HasCard(pCard, &listHasCard);
+
+ // start from 1
+ pos = i + 1;
+ pCard->GetPrimaryEmail(email);
+ if (!email.IsEmpty())
+ {
+ nsCOMPtr<nsIAbCard> pNewCard;
+ err = AddListCardColumnsToRow(pCard, listRow, pos, getter_AddRefs(pNewCard), listHasCard, list, aParent);
+ if (pNewCard)
+ pAddressLists->ReplaceElementAt(pNewCard, i, false);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+uint32_t nsAddrDatabase::GetListAddressTotal(nsIMdbRow* listRow)
+{
+ uint32_t count = 0;
+ GetIntColumn(listRow, m_ListTotalColumnToken, &count, 0);
+ return count;
+}
+
+NS_IMETHODIMP nsAddrDatabase::SetListAddressTotal(nsIMdbRow* aListRow, uint32_t aTotal)
+{
+ return AddIntColumn(aListRow, m_ListTotalColumnToken, aTotal);
+}
+
+NS_IMETHODIMP nsAddrDatabase::FindRowByCard(nsIAbCard * aCard,nsIMdbRow **aRow)
+{
+ nsString primaryEmail;
+ aCard->GetPrimaryEmail(primaryEmail);
+ return GetRowForCharColumn(primaryEmail.get(), m_PriEmailColumnToken,
+ true, true, aRow, nullptr);
+}
+
+nsresult nsAddrDatabase::GetAddressRowByPos(nsIMdbRow* listRow, uint16_t pos, nsIMdbRow** cardRow)
+{
+ if (!m_mdbStore || !listRow || !cardRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdb_token listAddressColumnToken;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ nsAutoString tempString;
+ mdb_id rowID;
+ nsresult err = GetIntColumn(listRow, listAddressColumnToken, (uint32_t*)&rowID, 0);
+ NS_ENSURE_SUCCESS(err, err);
+
+ return GetCardRowByRowID(rowID, cardRow);
+}
+
+NS_IMETHODIMP nsAddrDatabase::CreateMailListAndAddToDB(nsIAbDirectory *aNewList, bool aNotify /* = FALSE */, nsIAbDirectory *aParent)
+{
+ if (!aNewList || !m_mdbPabTable || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIMdbRow *listRow;
+ nsresult err = GetNewListRow(&listRow);
+
+ if (NS_SUCCEEDED(err) && listRow)
+ {
+ AddListAttributeColumnsToRow(aNewList, listRow, aParent);
+ AddRecordKeyColumnToRow(listRow);
+ nsresult merror = m_mdbPabTable->AddRow(m_mdbEnv, listRow);
+ NS_ENSURE_SUCCESS(merror, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIAbCard> listCard;
+ CreateABListCard(listRow, getter_AddRefs(listCard));
+ NotifyCardEntryChange(AB_NotifyInserted, listCard, aParent);
+
+ NS_RELEASE(listRow);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+void nsAddrDatabase::DeleteCardFromAllMailLists(mdb_id cardRowID)
+{
+ if (!m_mdbEnv)
+ return;
+
+ nsCOMPtr <nsIMdbTableRowCursor> rowCursor;
+ m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
+
+ if (rowCursor)
+ {
+ nsCOMPtr <nsIMdbRow> pListRow;
+ mdb_pos rowPos;
+ do
+ {
+ nsresult err = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(pListRow), &rowPos);
+
+ if (NS_SUCCEEDED(err) && pListRow)
+ {
+ mdbOid rowOid;
+
+ if (NS_SUCCEEDED(pListRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ if (IsListRowScopeToken(rowOid.mOid_Scope))
+ DeleteCardFromListRow(pListRow, cardRowID);
+ }
+ }
+ } while (pListRow);
+ }
+}
+
+NS_IMETHODIMP nsAddrDatabase::DeleteCard(nsIAbCard *aCard, bool aNotify, nsIAbDirectory *aParent)
+{
+ if (!aCard || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ bool bIsMailList = false;
+ aCard->GetIsMailList(&bIsMailList);
+
+ // get the right row
+ nsIMdbRow* pCardRow = nullptr;
+ mdbOid rowOid;
+
+ rowOid.mOid_Scope = bIsMailList ? m_ListRowScopeToken : m_CardRowScopeToken;
+
+ err = aCard->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, &pCardRow);
+ NS_ENSURE_SUCCESS(err,err);
+ if (!pCardRow)
+ return NS_OK;
+
+ // Reset the directory id
+ aCard->SetDirectoryId(EmptyCString());
+
+ // Add the deleted card to the deletedcards table
+ nsCOMPtr <nsIMdbRow> cardRow;
+ AddRowToDeletedCardsTable(aCard, getter_AddRefs(cardRow));
+ err = DeleteRow(m_mdbPabTable, pCardRow);
+
+ //delete the person card from all mailing list
+ if (!bIsMailList)
+ DeleteCardFromAllMailLists(rowOid.mOid_Id);
+
+ if (NS_SUCCEEDED(err)) {
+ if (aNotify)
+ NotifyCardEntryChange(AB_NotifyDeleted, aCard, aParent);
+ }
+ else
+ DeleteRowFromDeletedCardsTable(cardRow);
+
+ NS_RELEASE(pCardRow);
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::DeleteCardFromListRow(nsIMdbRow* pListRow, mdb_id cardRowID)
+{
+ NS_ENSURE_ARG_POINTER(pListRow);
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ uint32_t totalAddress = GetListAddressTotal(pListRow);
+
+ uint32_t pos;
+ for (pos = 1; pos <= totalAddress; pos++)
+ {
+ mdb_token listAddressColumnToken;
+ mdb_id rowID;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ err = GetIntColumn(pListRow, listAddressColumnToken, (uint32_t*)&rowID, 0);
+
+ if (cardRowID == rowID)
+ {
+ if (pos == totalAddress)
+ err = pListRow->CutColumn(m_mdbEnv, listAddressColumnToken);
+ else
+ {
+ //replace the deleted one with the last one and delete the last one
+ mdb_id lastRowID;
+ mdb_token lastAddressColumnToken;
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, totalAddress);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &lastAddressColumnToken);
+
+ err = GetIntColumn(pListRow, lastAddressColumnToken, (uint32_t*)&lastRowID, 0);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = AddIntColumn(pListRow, listAddressColumnToken, lastRowID);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = pListRow->CutColumn(m_mdbEnv, lastAddressColumnToken);
+ NS_ENSURE_SUCCESS(err, err);
+ }
+
+ // Reset total count after the card has been deleted.
+ SetListAddressTotal(pListRow, totalAddress-1);
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::DeleteCardFromMailList(nsIAbDirectory *mailList, nsIAbCard *card, bool aNotify)
+{
+ if (!card || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ // get the right row
+ nsIMdbRow* pListRow = nullptr;
+ mdbOid listRowOid;
+ listRowOid.mOid_Scope = m_ListRowScopeToken;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList,&err));
+ NS_ENSURE_SUCCESS(err, err);
+
+ dbmailList->GetDbRowID((uint32_t*)&listRowOid.mOid_Id);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &listRowOid, &pListRow);
+ NS_ENSURE_SUCCESS(err,err);
+ if (!pListRow)
+ return NS_OK;
+
+ uint32_t cardRowID;
+
+ err = card->GetPropertyAsUint32(kRowIDProperty, &cardRowID);
+ if (NS_FAILED(err))
+ return NS_ERROR_NULL_POINTER;
+
+ err = DeleteCardFromListRow(pListRow, cardRowID);
+ if (NS_SUCCEEDED(err) && aNotify) {
+ NotifyCardEntryChange(AB_NotifyDeleted, card, mailList);
+ }
+ NS_RELEASE(pListRow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::SetCardValue(nsIAbCard *card, const char *name, const char16_t *value, bool notify)
+{
+ NS_ENSURE_ARG_POINTER(card);
+ NS_ENSURE_ARG_POINTER(name);
+ NS_ENSURE_ARG_POINTER(value);
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr <nsIMdbRow> cardRow;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+
+ // it might be that the caller always has a nsAbMDBCard
+ rv = card->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!cardRow)
+ return NS_OK;
+
+ mdb_token token;
+ rv = m_mdbStore->StringToToken(m_mdbEnv, name, &token);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return AddCharStringColumn(cardRow, token, NS_ConvertUTF16toUTF8(value).get());
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetCardValue(nsIAbCard *card, const char *name, char16_t **value)
+{
+ if (!m_mdbStore || !card || !name || !value || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr <nsIMdbRow> cardRow;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+
+ // it might be that the caller always has a nsAbMDBCard
+ rv = card->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!cardRow) {
+ *value = nullptr;
+ // this can happen when adding cards when editing a mailing list
+ return NS_OK;
+ }
+
+ mdb_token token;
+ m_mdbStore->StringToToken(m_mdbEnv, name, &token);
+
+ // XXX fix me
+ // avoid extra copying and allocations (did dmb already do this on the trunk?)
+ nsAutoString tempString;
+ rv = GetStringColumn(cardRow, token, tempString);
+ if (NS_FAILED(rv)) {
+ // not all cards are going this column
+ *value = nullptr;
+ return NS_OK;
+ }
+
+ *value = NS_strdup(tempString.get());
+ if (!*value)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetDeletedCardList(nsIArray **aResult)
+{
+ if (!m_mdbEnv || !aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ *aResult = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> resultCardArray =
+ do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // make sure the member is set properly
+ InitDeletedCardsTable(false);
+ if (m_mdbDeletedCardsTable)
+ {
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ mdb_pos rowPos;
+ bool done = false;
+ nsCOMPtr<nsIMdbRow> currentRow;
+
+ m_mdbDeletedCardsTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
+ if (!rowCursor)
+ return NS_ERROR_FAILURE;
+ while (!done)
+ {
+ rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
+ if (currentRow && NS_SUCCEEDED(rv))
+ {
+ mdbOid rowOid;
+ if (NS_SUCCEEDED(currentRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ nsCOMPtr<nsIAbCard> card;
+ rv = CreateCardFromDeletedCardsTable(currentRow, 0, getter_AddRefs(card));
+ if (NS_SUCCEEDED(rv)) {
+ resultCardArray->AppendElement(card, false);
+ }
+ }
+ }
+ else
+ done = true;
+ }
+ }
+
+ NS_IF_ADDREF(*aResult = resultCardArray);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetDeletedCardCount(uint32_t *aCount)
+{
+ // initialize count first
+ *aCount = 0;
+ InitDeletedCardsTable(false);
+ if (m_mdbDeletedCardsTable)
+ return m_mdbDeletedCardsTable->GetCount(m_mdbEnv, aCount);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::PurgeDeletedCardTable()
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ if (m_mdbDeletedCardsTable) {
+ mdb_count cardCount=0;
+ // if not too many cards let it be
+ m_mdbDeletedCardsTable->GetCount(m_mdbEnv, &cardCount);
+ if(cardCount < PURGE_CUTOFF_COUNT)
+ return NS_OK;
+ uint32_t purgeTimeInSec;
+ PRTime2Seconds(PR_Now(), &purgeTimeInSec);
+ purgeTimeInSec -= (182*24*60*60); // six months in seconds
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ nsresult rv = m_mdbDeletedCardsTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
+ while(NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIMdbRow> currentRow;
+ mdb_pos rowPos;
+ rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
+ if(currentRow) {
+ uint32_t deletedTimeStamp = 0;
+ GetIntColumn(currentRow, m_LastModDateColumnToken, &deletedTimeStamp, 0);
+ // if record was deleted more than six months earlier, purge it
+ if(deletedTimeStamp && (deletedTimeStamp < purgeTimeInSec)) {
+ if(NS_SUCCEEDED(currentRow->CutAllColumns(m_mdbEnv)))
+ m_mdbDeletedCardsTable->CutRow(m_mdbEnv, currentRow);
+ }
+ else
+ // since the ordering in Mork is maintained and thus
+ // the cards added later appear on the top when retrieved
+ break;
+ }
+ else
+ break; // no more row
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::EditCard(nsIAbCard *aCard, bool aNotify, nsIAbDirectory *aParent)
+{
+ // XXX make sure this isn't getting called when we're just editing one or two well known fields
+ if (!aCard || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ nsCOMPtr <nsIMdbRow> cardRow;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+
+ uint32_t nowInSeconds;
+ PRTime now = PR_Now();
+ PRTime2Seconds(now, &nowInSeconds);
+ aCard->SetPropertyAsUint32(kLastModifiedDateProperty, nowInSeconds);
+ err = aCard->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (!cardRow)
+ return NS_OK;
+
+ err = AddAttributeColumnsToRow(aCard, cardRow);
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (aNotify)
+ NotifyCardEntryChange(AB_NotifyPropertyChanged, aCard, aParent);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::ContainsCard(nsIAbCard *card, bool *hasCard)
+{
+ if (!card || !m_mdbPabTable || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ mdb_bool hasOid;
+ mdbOid rowOid;
+ bool bIsMailList;
+
+ card->GetIsMailList(&bIsMailList);
+
+ if (bIsMailList)
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+ else
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+
+ err = card->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = m_mdbPabTable->HasOid(m_mdbEnv, &rowOid, &hasOid);
+ if (NS_SUCCEEDED(err))
+ {
+ *hasCard = hasOid;
+ }
+
+ return err;
+}
+
+NS_IMETHODIMP nsAddrDatabase::DeleteMailList(nsIAbDirectory *aMailList,
+ nsIAbDirectory *aParent)
+{
+ if (!aMailList || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ // get the row
+ nsCOMPtr<nsIMdbRow> pListRow;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(aMailList, &err));
+ NS_ENSURE_SUCCESS(err, err);
+ dbmailList->GetDbRowID((uint32_t*)&rowOid.mOid_Id);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(pListRow));
+ NS_ENSURE_SUCCESS(err,err);
+
+ if (!pListRow)
+ return NS_OK;
+
+ nsCOMPtr<nsIAbCard> card;
+ err = CreateABListCard(pListRow, getter_AddRefs(card));
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = DeleteRow(m_mdbPabTable, pListRow);
+
+ if (NS_SUCCEEDED(err) && aParent)
+ NotifyCardEntryChange(AB_NotifyDeleted, card, aParent);
+
+ return err;
+}
+
+NS_IMETHODIMP nsAddrDatabase::EditMailList(nsIAbDirectory *mailList, nsIAbCard *listCard, bool notify)
+{
+ if (!mailList || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ nsIMdbRow* pListRow = nullptr;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList, &err));
+ NS_ENSURE_SUCCESS(err, err);
+ dbmailList->GetDbRowID((uint32_t*)&rowOid.mOid_Id);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, &pListRow);
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (!pListRow)
+ return NS_OK;
+
+ err = AddListAttributeColumnsToRow(mailList, pListRow, mailList);
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (notify)
+ {
+ NotifyListEntryChange(AB_NotifyPropertyChanged, mailList);
+
+ if (listCard)
+ {
+ NotifyCardEntryChange(AB_NotifyPropertyChanged, listCard, mailList);
+ }
+ }
+
+ NS_RELEASE(pListRow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::ContainsMailList(nsIAbDirectory *mailList, bool *hasList)
+{
+ if (!mailList || !m_mdbPabTable || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ mdb_bool hasOid;
+ mdbOid rowOid;
+
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList,&err));
+ NS_ENSURE_SUCCESS(err, err);
+ dbmailList->GetDbRowID((uint32_t*)&rowOid.mOid_Id);
+
+ err = m_mdbPabTable->HasOid(m_mdbEnv, &rowOid, &hasOid);
+ if (NS_SUCCEEDED(err))
+ *hasList = hasOid;
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetNewRow(nsIMdbRow * *newRow)
+{
+ if (!m_mdbStore || !newRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ return m_mdbStore->NewRow(m_mdbEnv, m_CardRowScopeToken, newRow);
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetNewListRow(nsIMdbRow * *newRow)
+{
+ if (!m_mdbStore || !newRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ return m_mdbStore->NewRow(m_mdbEnv, m_ListRowScopeToken, newRow);
+}
+
+NS_IMETHODIMP nsAddrDatabase::AddCardRowToDB(nsIMdbRow *newRow)
+{
+ if (m_mdbPabTable && m_mdbEnv)
+ {
+ if (NS_SUCCEEDED(m_mdbPabTable->AddRow(m_mdbEnv, newRow)))
+ {
+ AddRecordKeyColumnToRow(newRow);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDatabase::AddLdifListMember(nsIMdbRow* listRow, const char* value)
+{
+ if (!m_mdbStore || !listRow || !value || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ uint32_t total = GetListAddressTotal(listRow);
+ //add member
+ nsAutoCString valueString(value);
+ nsAutoCString email;
+ int32_t emailPos = valueString.Find("mail=");
+ emailPos += strlen("mail=");
+ email = Substring(valueString, emailPos);
+ nsCOMPtr <nsIMdbRow> cardRow;
+ // Please DO NOT change the 3rd param of GetRowFromAttribute() call to
+ // true (ie, case insensitive) without reading bugs #128535 and #121478.
+ nsresult rv = GetRowFromAttribute(kPriEmailProperty, email, false /* retain case */,
+ getter_AddRefs(cardRow), nullptr);
+ if (NS_SUCCEEDED(rv) && cardRow)
+ {
+ mdbOid outOid;
+ mdb_id rowID = 0;
+ if (NS_SUCCEEDED(cardRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ // start from 1
+ total += 1;
+ mdb_token listAddressColumnToken;
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, total);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ rv = AddIntColumn(listRow, listAddressColumnToken, rowID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetListAddressTotal(listRow, total);
+ }
+ return NS_OK;
+}
+
+
+void nsAddrDatabase::GetCharStringYarn(char* str, struct mdbYarn* strYarn)
+{
+ strYarn->mYarn_Grow = nullptr;
+ strYarn->mYarn_Buf = str;
+ strYarn->mYarn_Size = PL_strlen((const char *) strYarn->mYarn_Buf) + 1;
+ strYarn->mYarn_Fill = strYarn->mYarn_Size - 1;
+ strYarn->mYarn_Form = 0;
+}
+
+void nsAddrDatabase::GetStringYarn(const nsAString & aStr, struct mdbYarn* strYarn)
+{
+ strYarn->mYarn_Buf = ToNewUTF8String(aStr);
+ strYarn->mYarn_Size = PL_strlen((const char *) strYarn->mYarn_Buf) + 1;
+ strYarn->mYarn_Fill = strYarn->mYarn_Size - 1;
+ strYarn->mYarn_Form = 0;
+}
+
+void nsAddrDatabase::GetIntYarn(uint32_t nValue, struct mdbYarn* intYarn)
+{
+ intYarn->mYarn_Fill = intYarn->mYarn_Size;
+ intYarn->mYarn_Form = 0;
+ intYarn->mYarn_Grow = nullptr;
+
+ PR_snprintf((char*)intYarn->mYarn_Buf, intYarn->mYarn_Size, "%lx", nValue);
+ intYarn->mYarn_Fill = PL_strlen((const char *) intYarn->mYarn_Buf);
+}
+
+nsresult nsAddrDatabase::AddCharStringColumn(nsIMdbRow* cardRow, mdb_column inColumn, const char* str)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ struct mdbYarn yarn;
+
+ GetCharStringYarn((char *) str, &yarn);
+ nsresult err = cardRow->AddColumn(m_mdbEnv, inColumn, &yarn);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::AddStringColumn(nsIMdbRow* aCardRow, mdb_column aInColumn, const nsAString & aStr)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ struct mdbYarn yarn;
+
+ GetStringYarn(aStr, &yarn);
+ nsresult err = aCardRow->AddColumn(m_mdbEnv, aInColumn, &yarn);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::AddIntColumn(nsIMdbRow* cardRow, mdb_column inColumn, uint32_t nValue)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ struct mdbYarn yarn;
+ char yarnBuf[100];
+
+ yarn.mYarn_Buf = (void *) yarnBuf;
+ yarn.mYarn_Size = sizeof(yarnBuf);
+ GetIntYarn(nValue, &yarn);
+ nsresult err = cardRow->AddColumn(m_mdbEnv, inColumn, &yarn);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::AddBoolColumn(nsIMdbRow* cardRow, mdb_column inColumn, bool bValue)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ struct mdbYarn yarn;
+ char yarnBuf[100];
+
+ yarn.mYarn_Buf = (void *) yarnBuf;
+ yarn.mYarn_Size = sizeof(yarnBuf);
+
+ GetIntYarn(bValue ? 1 : 0, &yarn);
+
+ nsresult err = cardRow->AddColumn(m_mdbEnv, inColumn, &yarn);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::GetStringColumn(nsIMdbRow *cardRow, mdb_token outToken, nsString& str)
+{
+ nsresult err = NS_ERROR_NULL_POINTER;
+ nsIMdbCell *cardCell;
+
+ if (cardRow && m_mdbEnv)
+ {
+ err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
+ if (NS_SUCCEEDED(err) && cardCell)
+ {
+ struct mdbYarn yarn;
+ cardCell->AliasYarn(m_mdbEnv, &yarn);
+ NS_ConvertUTF8toUTF16 uniStr((const char*) yarn.mYarn_Buf, yarn.mYarn_Fill);
+ if (!uniStr.IsEmpty())
+ str.Assign(uniStr);
+ else
+ err = NS_ERROR_FAILURE;
+ cardCell->Release(); // always release ref
+ }
+ else
+ err = NS_ERROR_FAILURE;
+ }
+ return err;
+}
+
+void nsAddrDatabase::YarnToUInt32(struct mdbYarn *yarn, uint32_t *pResult)
+{
+ uint8_t numChars = std::min<mdb_fill>(8, yarn->mYarn_Fill);
+ *pResult = MsgUnhex((char *) yarn->mYarn_Buf, numChars);
+}
+
+nsresult nsAddrDatabase::GetIntColumn
+(nsIMdbRow *cardRow, mdb_token outToken, uint32_t* pValue, uint32_t defaultValue)
+{
+ nsresult err = NS_ERROR_NULL_POINTER;
+ nsIMdbCell *cardCell;
+
+ if (pValue)
+ *pValue = defaultValue;
+ if (cardRow && m_mdbEnv)
+ {
+ err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
+ if (NS_SUCCEEDED(err) && cardCell)
+ {
+ struct mdbYarn yarn;
+ cardCell->AliasYarn(m_mdbEnv, &yarn);
+ YarnToUInt32(&yarn, pValue);
+ cardCell->Release();
+ }
+ else
+ err = NS_ERROR_FAILURE;
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::GetBoolColumn(nsIMdbRow *cardRow, mdb_token outToken, bool* pValue)
+{
+ NS_ENSURE_ARG_POINTER(pValue);
+
+ nsresult err = NS_ERROR_NULL_POINTER;
+ nsIMdbCell *cardCell;
+ uint32_t nValue = 0;
+
+ if (cardRow && m_mdbEnv)
+ {
+ err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
+ if (NS_SUCCEEDED(err) && cardCell)
+ {
+ struct mdbYarn yarn;
+ cardCell->AliasYarn(m_mdbEnv, &yarn);
+ YarnToUInt32(&yarn, &nValue);
+ cardCell->Release();
+ }
+ else
+ err = NS_ERROR_FAILURE;
+ }
+
+ *pValue = nValue ? true : false;
+ return err;
+}
+
+/* value is UTF8 string */
+NS_IMETHODIMP nsAddrDatabase::AddPrimaryEmail(nsIMdbRow *aRow, const char *aValue)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ nsresult rv = AddCharStringColumn(aRow, m_PriEmailColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddLowercaseColumn(aRow, m_LowerPriEmailColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+/* value is UTF8 string */
+NS_IMETHODIMP nsAddrDatabase::Add2ndEmail(nsIMdbRow *aRow, const char *aValue)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ nsresult rv = AddCharStringColumn(aRow, m_2ndEmailColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddLowercaseColumn(aRow, m_Lower2ndEmailColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+/* value is UTF8 string */
+NS_IMETHODIMP nsAddrDatabase::AddListName(nsIMdbRow *aRow, const char *aValue)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ nsresult rv = AddCharStringColumn(aRow, m_ListNameColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddLowercaseColumn(aRow, m_LowerListNameColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+/*
+columnValue is UTF8 string, need to convert back to lowercase unicode then
+back to UTF8 string
+*/
+nsresult nsAddrDatabase::AddLowercaseColumn
+(nsIMdbRow * row, mdb_token columnToken, const char* columnValue)
+{
+ nsresult rv = NS_OK;
+ if (columnValue)
+ {
+ NS_ConvertUTF8toUTF16 newUnicodeString(columnValue);
+ ToLowerCase(newUnicodeString);
+ rv = AddCharStringColumn(row, columnToken, NS_ConvertUTF16toUTF8(newUnicodeString).get());
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::InitCardFromRow(nsIAbCard *newCard, nsIMdbRow* cardRow)
+{
+ nsresult rv = NS_OK;
+ if (!newCard || !cardRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr<nsIMdbRowCellCursor> cursor;
+ nsCOMPtr<nsIMdbCell> cell;
+
+ rv = cardRow->GetRowCellCursor(m_mdbEnv, -1, getter_AddRefs(cursor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mdb_column columnNumber;
+ char columnName[100];
+ struct mdbYarn colYarn = {columnName, 0, sizeof(columnName), 0, 0, nullptr};
+ struct mdbYarn cellYarn;
+
+ do
+ {
+ rv = cursor->NextCell(m_mdbEnv, getter_AddRefs(cell), &columnNumber, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!cell)
+ break;
+
+ // Get the value of the cell
+ cell->AliasYarn(m_mdbEnv, &cellYarn);
+ NS_ConvertUTF8toUTF16 value(static_cast<const char*>(cellYarn.mYarn_Buf),
+ cellYarn.mYarn_Fill);
+
+ if (!value.IsEmpty())
+ {
+ // Get the column of the cell
+ // Mork makes this so hard...
+ rv = m_mdbStore->TokenToString(m_mdbEnv, columnNumber, &colYarn);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char *name = PL_strndup(static_cast<char *>(colYarn.mYarn_Buf),
+ colYarn.mYarn_Fill);
+ newCard->SetPropertyAsAString(name, value);
+ PL_strfree(name);
+ }
+ } while (true);
+
+ uint32_t key = 0;
+ rv = GetIntColumn(cardRow, m_RecordKeyColumnToken, &key, 0);
+ if (NS_SUCCEEDED(rv))
+ newCard->SetPropertyAsUint32(kRecordKeyColumn, key);
+
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::GetListCardFromDB(nsIAbCard *listCard, nsIMdbRow* listRow)
+{
+ nsresult err = NS_OK;
+ if (!listCard || !listRow)
+ return NS_ERROR_NULL_POINTER;
+
+ nsAutoString tempString;
+
+ err = GetStringColumn(listRow, m_ListNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ listCard->SetDisplayName(tempString);
+ listCard->SetLastName(tempString);
+ }
+ err = GetStringColumn(listRow, m_ListNickNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ listCard->SetPropertyAsAString(kNicknameProperty, tempString);
+ }
+ err = GetStringColumn(listRow, m_ListDescriptionColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ listCard->SetPropertyAsAString(kNotesProperty, tempString);
+ }
+ uint32_t key = 0;
+ err = GetIntColumn(listRow, m_RecordKeyColumnToken, &key, 0);
+ if (NS_SUCCEEDED(err))
+ listCard->SetPropertyAsUint32(kRecordKeyColumn, key);
+ return err;
+}
+
+nsresult nsAddrDatabase::GetListFromDB(nsIAbDirectory *newList, nsIMdbRow* listRow)
+{
+ nsresult err = NS_OK;
+ if (!newList || !listRow || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsAutoString tempString;
+
+ err = GetStringColumn(listRow, m_ListNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ newList->SetDirName(tempString);
+ }
+ err = GetStringColumn(listRow, m_ListNickNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ newList->SetListNickName(tempString);
+ }
+ err = GetStringColumn(listRow, m_ListDescriptionColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ newList->SetDescription(tempString);
+ }
+
+ nsCOMPtr<nsIAbMDBDirectory> dbnewList(do_QueryInterface(newList, &err));
+ NS_ENSURE_SUCCESS(err, err);
+
+ uint32_t totalAddress = GetListAddressTotal(listRow);
+ uint32_t pos;
+ for (pos = 1; pos <= totalAddress; ++pos)
+ {
+ mdb_token listAddressColumnToken;
+ mdb_id rowID;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ nsCOMPtr <nsIMdbRow> cardRow;
+ err = GetIntColumn(listRow, listAddressColumnToken, (uint32_t*)&rowID, 0);
+ NS_ENSURE_SUCCESS(err, err);
+ err = GetCardRowByRowID(rowID, getter_AddRefs(cardRow));
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (cardRow)
+ {
+ nsCOMPtr<nsIAbCard> card;
+ err = CreateABCard(cardRow, 0, getter_AddRefs(card));
+
+ if(NS_SUCCEEDED(err))
+ dbnewList->AddAddressToList(card);
+ }
+// NS_IF_ADDREF(card);
+ }
+
+ return err;
+}
+
+class nsAddrDBEnumerator : public nsISimpleEnumerator, public nsIAddrDBListener
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator methods:
+ NS_DECL_NSISIMPLEENUMERATOR
+ NS_DECL_NSIADDRDBLISTENER
+ // nsAddrDBEnumerator methods:
+
+ nsAddrDBEnumerator(nsAddrDatabase* aDb);
+ void Clear();
+protected:
+ virtual ~nsAddrDBEnumerator();
+ RefPtr<nsAddrDatabase> mDb;
+ nsIMdbTable *mDbTable;
+ nsCOMPtr<nsIMdbTableRowCursor> mRowCursor;
+ nsCOMPtr<nsIMdbRow> mCurrentRow;
+ mdb_pos mRowPos;
+};
+
+nsAddrDBEnumerator::nsAddrDBEnumerator(nsAddrDatabase* aDb)
+ : mDb(aDb),
+ mDbTable(aDb->GetPabTable()),
+ mRowPos(-1)
+{
+ if (aDb)
+ aDb->AddListener(this);
+}
+
+nsAddrDBEnumerator::~nsAddrDBEnumerator()
+{
+ Clear();
+}
+
+void nsAddrDBEnumerator::Clear()
+{
+ mRowCursor = nullptr;
+ mCurrentRow = nullptr;
+ mDbTable = nullptr;
+ if (mDb)
+ mDb->RemoveListener(this);
+}
+
+NS_IMPL_ISUPPORTS(nsAddrDBEnumerator, nsISimpleEnumerator, nsIAddrDBListener)
+
+NS_IMETHODIMP
+nsAddrDBEnumerator::HasMoreElements(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+
+ if (!mDbTable || !mDb->GetEnv())
+ {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ mDbTable->GetTableRowCursor(mDb->GetEnv(), mRowPos,
+ getter_AddRefs(rowCursor));
+ NS_ENSURE_TRUE(rowCursor, NS_ERROR_FAILURE);
+
+ mdbOid rowOid;
+ rowCursor->NextRowOid(mDb->GetEnv(), &rowOid, nullptr);
+ while (rowOid.mOid_Id != (mdb_id)-1)
+ {
+ if (mDb->IsListRowScopeToken(rowOid.mOid_Scope) ||
+ mDb->IsCardRowScopeToken(rowOid.mOid_Scope))
+ {
+ *aResult = true;
+
+ return NS_OK;
+ }
+
+ if (!mDb->IsDataRowScopeToken(rowOid.mOid_Scope))
+ {
+ return NS_ERROR_FAILURE;
+ }
+
+ rowCursor->NextRowOid(mDb->GetEnv(), &rowOid, nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAddrDBEnumerator::GetNext(nsISupports **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (!mDbTable || !mDb->GetEnv())
+ {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mRowCursor)
+ {
+ mDbTable->GetTableRowCursor(mDb->GetEnv(), -1,
+ getter_AddRefs(mRowCursor));
+ NS_ENSURE_TRUE(mRowCursor, NS_ERROR_FAILURE);
+ }
+
+ nsCOMPtr<nsIAbCard> resultCard;
+ mRowCursor->NextRow(mDb->GetEnv(), getter_AddRefs(mCurrentRow), &mRowPos);
+ while (mCurrentRow)
+ {
+ mdbOid rowOid;
+ if (NS_SUCCEEDED(mCurrentRow->GetOid(mDb->GetEnv(), &rowOid)))
+ {
+ nsresult rv;
+ if (mDb->IsListRowScopeToken(rowOid.mOid_Scope))
+ {
+ rv = mDb->CreateABListCard(mCurrentRow,
+ getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else if (mDb->IsCardRowScopeToken(rowOid.mOid_Scope))
+ {
+ rv = mDb->CreateABCard(mCurrentRow, 0,
+ getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else if (!mDb->IsDataRowScopeToken(rowOid.mOid_Scope))
+ {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (resultCard)
+ {
+ return CallQueryInterface(resultCard, aResult);
+ }
+ }
+
+ mRowCursor->NextRow(mDb->GetEnv(), getter_AddRefs(mCurrentRow),
+ &mRowPos);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDBEnumerator::OnCardAttribChange(uint32_t abCode)
+{
+ return NS_OK;
+}
+
+/* void onCardEntryChange (in unsigned long aAbCode, in nsIAbCard aCard, in nsIAbDirectory aParent); */
+NS_IMETHODIMP nsAddrDBEnumerator::OnCardEntryChange(uint32_t aAbCode, nsIAbCard *aCard, nsIAbDirectory *aParent)
+{
+ return NS_OK;
+}
+
+/* void onListEntryChange (in unsigned long abCode, in nsIAbDirectory list); */
+NS_IMETHODIMP nsAddrDBEnumerator::OnListEntryChange(uint32_t abCode, nsIAbDirectory *list)
+{
+ return NS_OK;
+}
+
+/* void onAnnouncerGoingAway (); */
+NS_IMETHODIMP nsAddrDBEnumerator::OnAnnouncerGoingAway()
+{
+ Clear();
+ return NS_OK;
+}
+
+class nsListAddressEnumerator final : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator methods:
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsListAddressEnumerator methods:
+
+ nsListAddressEnumerator(nsAddrDatabase* aDb, mdb_id aRowID);
+
+protected:
+ ~nsListAddressEnumerator() {}
+ RefPtr<nsAddrDatabase> mDb;
+ nsIMdbTable *mDbTable;
+ nsCOMPtr<nsIMdbRow> mListRow;
+ mdb_id mListRowID;
+ uint32_t mAddressTotal;
+ uint16_t mAddressPos;
+};
+
+nsListAddressEnumerator::nsListAddressEnumerator(nsAddrDatabase* aDb,
+ mdb_id aRowID)
+ : mDb(aDb),
+ mDbTable(aDb->GetPabTable()),
+ mListRowID(aRowID),
+ mAddressPos(0)
+{
+ mDb->GetListRowByRowID(mListRowID, getter_AddRefs(mListRow));
+ mAddressTotal = aDb->GetListAddressTotal(mListRow);
+}
+
+NS_IMPL_ISUPPORTS(nsListAddressEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsListAddressEnumerator::HasMoreElements(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = false;
+
+ if (!mDbTable || !mDb->GetEnv())
+ {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // In some cases it is possible that GetAddressRowByPos returns success,
+ // but currentRow is null. This is typically due to the fact that a card
+ // has been deleted from the parent and not the list. Whilst we have fixed
+ // that there are still a few dbs around there that we need to support
+ // correctly. Therefore, whilst processing lists ensure that we don't return
+ // false if the only thing stopping us is a blank row, just skip it and try
+ // the next one.
+ while (mAddressPos < mAddressTotal)
+ {
+ nsCOMPtr<nsIMdbRow> currentRow;
+ nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos + 1,
+ getter_AddRefs(currentRow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (currentRow)
+ {
+ *aResult = true;
+ break;
+ }
+
+ ++mAddressPos;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsListAddressEnumerator::GetNext(nsISupports **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (!mDbTable || !mDb->GetEnv())
+ {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (++mAddressPos <= mAddressTotal)
+ {
+ nsCOMPtr<nsIMdbRow> currentRow;
+ nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos,
+ getter_AddRefs(currentRow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> resultCard;
+ rv = mDb->CreateABCard(currentRow, mListRowID,
+ getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(resultCard, aResult);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP nsAddrDatabase::EnumerateCards(nsIAbDirectory *directory, nsISimpleEnumerator **result)
+{
+ nsAddrDBEnumerator* e = new nsAddrDBEnumerator(this);
+ m_dbDirectory = do_GetWeakReference(directory);
+ if (!e)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(e);
+ *result = e;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetMailingListsFromDB(nsIAbDirectory *parentDir)
+{
+ nsCOMPtr<nsIAbDirectory> resultList;
+ nsIMdbTableRowCursor* rowCursor = nullptr;
+ nsCOMPtr<nsIMdbRow> currentRow;
+ mdb_pos rowPos;
+ bool done = false;
+
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ m_dbDirectory = do_GetWeakReference(parentDir);
+
+ nsIMdbTable* dbTable = GetPabTable();
+
+ if (!dbTable)
+ return NS_ERROR_FAILURE;
+
+ dbTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
+ if (!rowCursor)
+ return NS_ERROR_FAILURE;
+
+ while (!done)
+ {
+ nsresult rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
+ if (currentRow && NS_SUCCEEDED(rv))
+ {
+ mdbOid rowOid;
+
+ if (NS_SUCCEEDED(currentRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ if (IsListRowScopeToken(rowOid.mOid_Scope))
+ rv = CreateABList(currentRow, getter_AddRefs(resultList));
+ }
+ }
+ else
+ done = true;
+ }
+ NS_IF_RELEASE(rowCursor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::EnumerateListAddresses(nsIAbDirectory *directory, nsISimpleEnumerator **result)
+{
+ nsresult rv = NS_OK;
+ mdb_id rowID;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbdirectory(do_QueryInterface(directory,&rv));
+
+ if(NS_SUCCEEDED(rv))
+ {
+ dbdirectory->GetDbRowID((uint32_t*)&rowID);
+
+ nsListAddressEnumerator* e = new nsListAddressEnumerator(this, rowID);
+ m_dbDirectory = do_GetWeakReference(directory);
+ if (!e)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(e);
+ *result = e;
+ }
+ return rv;
+}
+
+nsresult nsAddrDatabase::CreateCardFromDeletedCardsTable(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
+{
+ if (!cardRow || !m_mdbEnv || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(cardRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ if(NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIAbCard> personCard;
+ personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ InitCardFromRow(personCard, cardRow);
+ personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
+
+ NS_IF_ADDREF(*result = personCard);
+ }
+
+ return rv;
+}
+
+nsresult nsAddrDatabase::CreateCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
+{
+ if (!cardRow || !m_mdbEnv || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(cardRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ if(NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIAbCard> personCard;
+ personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ InitCardFromRow(personCard, cardRow);
+ personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
+
+ nsAutoCString id;
+ id.AppendInt(rowID);
+ personCard->SetLocalId(id);
+
+ nsCOMPtr<nsIAbDirectory> abDir(do_QueryReferent(m_dbDirectory));
+ if (abDir)
+ abDir->GetUuid(id);
+
+ personCard->SetDirectoryId(id);
+
+ NS_IF_ADDREF(*result = personCard);
+ }
+
+ return rv;
+}
+
+nsresult nsAddrDatabase::CreateABCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
+{
+ return CreateCard(cardRow, listRowID, result);
+}
+
+/* create a card for mailing list in the address book */
+nsresult nsAddrDatabase::CreateABListCard(nsIMdbRow* listRow, nsIAbCard **result)
+{
+ if (!listRow || !m_mdbEnv || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(listRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ char* listURI = nullptr;
+
+ nsAutoString fileName;
+ rv = m_dbName->GetLeafName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ listURI = PR_smprintf("%s%s/MailList%ld", kMDBDirectoryRoot, NS_ConvertUTF16toUTF8(fileName).get(), rowID);
+
+ nsCOMPtr<nsIAbCard> personCard;
+ nsCOMPtr<nsIAbMDBDirectory> dbm_dbDirectory(do_QueryReferent(m_dbDirectory,
+ &rv));
+ if (NS_SUCCEEDED(rv) && dbm_dbDirectory)
+ {
+ personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (personCard)
+ {
+ GetListCardFromDB(personCard, listRow);
+
+ personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
+ personCard->SetIsMailList(true);
+ personCard->SetMailListURI(listURI);
+
+ nsAutoCString id;
+ id.AppendInt(rowID);
+ personCard->SetLocalId(id);
+
+ nsCOMPtr<nsIAbDirectory> abDir(do_QueryReferent(m_dbDirectory));
+ if (abDir)
+ abDir->GetUuid(id);
+ personCard->SetDirectoryId(id);
+ }
+
+ NS_IF_ADDREF(*result = personCard);
+ }
+ if (listURI)
+ PR_smprintf_free(listURI);
+
+ return rv;
+}
+
+/* create a sub directory for mailing list in the address book left pane */
+nsresult nsAddrDatabase::CreateABList(nsIMdbRow* listRow, nsIAbDirectory **result)
+{
+ nsresult rv = NS_OK;
+
+ if (!listRow || !m_mdbEnv || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(listRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ char* listURI = nullptr;
+
+ nsAutoString fileName;
+ m_dbName->GetLeafName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ listURI = PR_smprintf("%s%s/MailList%ld", kMDBDirectoryRoot, NS_ConvertUTF16toUTF8(fileName).get(), rowID);
+
+ nsCOMPtr<nsIAbDirectory> mailList;
+ nsCOMPtr<nsIAbMDBDirectory> dbm_dbDirectory(do_QueryReferent(m_dbDirectory,
+ &rv));
+ if (NS_SUCCEEDED(rv) && dbm_dbDirectory)
+ {
+ rv = dbm_dbDirectory->AddDirectory(listURI, getter_AddRefs(mailList));
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList (do_QueryInterface(mailList, &rv));
+
+ if (mailList)
+ {
+ // if we are using turbo, and we "exit" and restart with the same profile
+ // the current mailing list will still be in memory, so when we do
+ // GetResource() and QI, we'll get it again.
+ // in that scenario, the mailList that we pass in will already be
+ // be a mailing list, with a valid row and all the entries
+ // in that scenario, we can skip GetListFromDB(), which would have
+ // have added all the cards to the list again.
+ // see bug #134743
+ mdb_id existingID;
+ dbmailList->GetDbRowID(&existingID);
+ if (existingID != rowID) {
+ // Ensure IsMailList is set up first.
+ mailList->SetIsMailList(true);
+ GetListFromDB(mailList, listRow);
+ dbmailList->SetDbRowID(rowID);
+ }
+
+ dbm_dbDirectory->AddMailListToDirectory(mailList);
+ NS_IF_ADDREF(*result = mailList);
+ }
+ }
+
+ if (listURI)
+ PR_smprintf_free(listURI);
+
+ return rv;
+}
+
+nsresult nsAddrDatabase::GetCardRowByRowID(mdb_id rowID, nsIMdbRow **dbRow)
+{
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+ rowOid.mOid_Id = rowID;
+
+ return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
+}
+
+nsresult nsAddrDatabase::GetListRowByRowID(mdb_id rowID, nsIMdbRow **dbRow)
+{
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+ rowOid.mOid_Id = rowID;
+
+ return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
+}
+
+nsresult nsAddrDatabase::GetRowFromAttribute(const char *aName,
+ const nsACString &aUTF8Value,
+ bool aCaseInsensitive,
+ nsIMdbRow **aCardRow,
+ mdb_pos *aRowPos)
+{
+ NS_ENSURE_ARG_POINTER(aName);
+ NS_ENSURE_ARG_POINTER(aCardRow);
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdb_token token;
+ m_mdbStore->StringToToken(m_mdbEnv, aName, &token);
+ NS_ConvertUTF8toUTF16 newUnicodeString(aUTF8Value);
+
+ return GetRowForCharColumn(newUnicodeString.get(), token, true,
+ aCaseInsensitive, aCardRow, aRowPos);
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetCardFromAttribute(nsIAbDirectory *aDirectory,
+ const char *aName,
+ const nsACString &aUTF8Value,
+ bool aCaseInsensitive,
+ nsIAbCard **aCardResult)
+{
+ NS_ENSURE_ARG_POINTER(aCardResult);
+
+ m_dbDirectory = do_GetWeakReference(aDirectory);
+ nsCOMPtr<nsIMdbRow> cardRow;
+ if (NS_SUCCEEDED(GetRowFromAttribute(aName, aUTF8Value, aCaseInsensitive,
+ getter_AddRefs(cardRow), nullptr)) && cardRow)
+ return CreateABCard(cardRow, 0, aCardResult);
+
+ *aCardResult = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetCardsFromAttribute(nsIAbDirectory *aDirectory,
+ const char *aName,
+ const nsACString & aUTF8Value,
+ bool aCaseInsensitive,
+ nsISimpleEnumerator **cards)
+{
+ NS_ENSURE_ARG_POINTER(cards);
+
+ m_dbDirectory = do_GetWeakReference(aDirectory);
+ nsCOMPtr<nsIMdbRow> row;
+ bool done = false;
+ nsCOMArray<nsIAbCard> list;
+ nsCOMPtr<nsIAbCard> card;
+ mdb_pos rowPos = -1;
+
+ do
+ {
+ if (NS_SUCCEEDED(GetRowFromAttribute(aName, aUTF8Value, aCaseInsensitive,
+ getter_AddRefs(row), &rowPos)) && row)
+ {
+ if (NS_FAILED(CreateABCard(row, 0, getter_AddRefs(card))))
+ continue;
+ list.AppendObject(card);
+ }
+ else
+ done = true;
+ } while (!done);
+
+ return NS_NewArrayEnumerator(cards, list);
+}
+
+NS_IMETHODIMP nsAddrDatabase::AddListDirNode(nsIMdbRow * listRow)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoString parentURI;
+ rv = m_dbName->GetLeafName(parentURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ parentURI.Replace(0, 0, NS_LITERAL_STRING(kMDBDirectoryRoot));
+
+ nsCOMPtr<nsIAbDirectory> parentDir;
+ rv = abManager->GetDirectory(NS_ConvertUTF16toUTF8(parentURI),
+ getter_AddRefs(parentDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (parentDir)
+ {
+ m_dbDirectory = do_GetWeakReference(parentDir);
+ nsCOMPtr<nsIAbDirectory> mailList;
+ rv = CreateABList(listRow, getter_AddRefs(mailList));
+ if (mailList)
+ {
+ nsCOMPtr<nsIAbMDBDirectory> dbparentDir(do_QueryInterface(parentDir, &rv));
+ if(NS_SUCCEEDED(rv))
+ dbparentDir->NotifyDirItemAdded(mailList);
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::FindMailListbyUnicodeName(const char16_t *listName, bool *exist)
+{
+ nsAutoString unicodeString(listName);
+ ToLowerCase(unicodeString);
+
+ nsCOMPtr <nsIMdbRow> listRow;
+ nsresult rv = GetRowForCharColumn(unicodeString.get(),
+ m_LowerListNameColumnToken, false,
+ false, getter_AddRefs(listRow), nullptr);
+ *exist = (NS_SUCCEEDED(rv) && listRow);
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetCardCount(uint32_t *count)
+{
+ nsresult rv;
+ mdb_count c;
+ rv = m_mdbPabTable->GetCount(m_mdbEnv, &c);
+ if (NS_SUCCEEDED(rv))
+ *count = c - 1; // Don't count LastRecordKey
+
+ return rv;
+}
+
+bool
+nsAddrDatabase::HasRowButDeletedForCharColumn(const char16_t *unicodeStr, mdb_column findColumn, bool aIsCard, nsIMdbRow **aFindRow)
+{
+ if (!m_mdbStore || !aFindRow || !m_mdbEnv)
+ return false;
+
+ mdbYarn sourceYarn;
+
+ NS_ConvertUTF16toUTF8 UTF8String(unicodeStr);
+ sourceYarn.mYarn_Buf = (void *) UTF8String.get();
+ sourceYarn.mYarn_Fill = UTF8String.Length();
+ sourceYarn.mYarn_Form = 0;
+ sourceYarn.mYarn_Size = sourceYarn.mYarn_Fill;
+
+ mdbOid outRowId;
+ nsresult rv;
+
+ if (aIsCard)
+ {
+ rv = m_mdbStore->FindRow(m_mdbEnv, m_CardRowScopeToken,
+ findColumn, &sourceYarn, &outRowId, aFindRow);
+
+ // no such card, so bail out early
+ if (NS_FAILED(rv) || !*aFindRow)
+ return false;
+
+ // we might not have loaded the "delete cards" table yet
+ // so do that (but don't create it, if we don't have one),
+ // so we can see if the row is really a delete card.
+ if (!m_mdbDeletedCardsTable)
+ rv = InitDeletedCardsTable(false);
+
+ // if still no deleted cards table, there are no deleted cards
+ if (!m_mdbDeletedCardsTable)
+ return false;
+
+ mdb_bool hasRow = false;
+ rv = m_mdbDeletedCardsTable->HasRow(m_mdbEnv, *aFindRow, &hasRow);
+ return (NS_SUCCEEDED(rv) && hasRow);
+ }
+
+ rv = m_mdbStore->FindRow(m_mdbEnv, m_ListRowScopeToken,
+ findColumn, &sourceYarn, &outRowId, aFindRow);
+ return (NS_SUCCEEDED(rv) && *aFindRow);
+}
+
+/* @param aRowPos Contains the row position for multiple calls. Should be
+ * instantiated to -1 on the first call. Or can be null
+ * if you are not making multiple calls.
+ */
+nsresult
+nsAddrDatabase::GetRowForCharColumn(const char16_t *unicodeStr,
+ mdb_column findColumn, bool aIsCard,
+ bool aCaseInsensitive,
+ nsIMdbRow **aFindRow,
+ mdb_pos *aRowPos)
+{
+ NS_ENSURE_ARG_POINTER(unicodeStr);
+ NS_ENSURE_ARG_POINTER(aFindRow);
+ NS_ENSURE_TRUE(m_mdbEnv && m_mdbPabTable, NS_ERROR_NULL_POINTER);
+
+ *aFindRow = nullptr;
+
+ // see bug #198303
+ // the addition of the m_mdbDeletedCardsTable table has complicated life in the addressbook
+ // (it was added for palm sync). until we fix the underlying problem, we have to jump through hoops
+ // in order to know if we have a row (think card) for a given column value (think email=foo@bar.com)
+ // there are 4 scenarios:
+ // 1) no cards with a match
+ // 2) at least one deleted card with a match, but no non-deleted cards
+ // 3) at least one non-deleted card with a match, but no deleted cards
+ // 4) at least one deleted card, and one non-deleted card with a match.
+ //
+ // if we have no cards that match (FindRow() returns nothing), we can bail early
+ // but if FindRow() returns something, we have to check if it is in the deleted table
+ // if not in the deleted table we can return the row (we found a non-deleted card)
+ // but if so, we have to search through the table of non-deleted cards
+ // for a match. If we find one, we return it. but if not, we report that there are no
+ // non-deleted cards. This is the expensive part. The worse case scenario is to have
+ // deleted lots of cards, and then have a lot of non-deleted cards.
+ // we'd have to call FindRow(), HasRow(), and then search the list of non-deleted cards
+ // each time we call GetRowForCharColumn().
+ if (!aRowPos && !HasRowButDeletedForCharColumn(unicodeStr, findColumn, aIsCard, aFindRow))
+ {
+ // If we have a row, it's the row for the non-delete card, so return NS_OK.
+ // If we don't have a row, there are two possible conditions: either the
+ // card does not exist, or we are doing case-insensitive searching and the
+ // value isn't lowercase.
+
+ // Valid result, return.
+ if (*aFindRow)
+ return NS_OK;
+
+ // We definitely don't have anything at this point if case-sensitive.
+ if (!aCaseInsensitive)
+ return NS_ERROR_FAILURE;
+ }
+
+ // check if there is a non-deleted card
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ mdb_pos rowPos = -1;
+ bool done = false;
+ nsCOMPtr<nsIMdbRow> currentRow;
+ nsAutoString columnValue;
+
+ if (aRowPos)
+ rowPos = *aRowPos;
+
+ mdb_scope targetScope = aIsCard ? m_CardRowScopeToken : m_ListRowScopeToken;
+
+ m_mdbPabTable->GetTableRowCursor(m_mdbEnv, rowPos, getter_AddRefs(rowCursor));
+ if (!rowCursor)
+ return NS_ERROR_FAILURE;
+
+ while (!done)
+ {
+ nsresult rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
+ if (currentRow && NS_SUCCEEDED(rv))
+ {
+ mdbOid rowOid;
+ if (NS_SUCCEEDED(currentRow->GetOid(m_mdbEnv, &rowOid)) && (rowOid.mOid_Scope == targetScope))
+ {
+ rv = GetStringColumn(currentRow, findColumn, columnValue);
+
+ bool equals = aCaseInsensitive ?
+ columnValue.Equals(unicodeStr, nsCaseInsensitiveStringComparator()) :
+ columnValue.Equals(unicodeStr);
+
+ if (NS_SUCCEEDED(rv) && equals)
+ {
+ NS_IF_ADDREF(*aFindRow = currentRow);
+ if (aRowPos)
+ *aRowPos = rowPos;
+ return NS_OK;
+ }
+ }
+ }
+ else
+ done = true;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::DeleteRow(nsIMdbTable* dbTable, nsIMdbRow* dbRow)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = dbRow->CutAllColumns(m_mdbEnv);
+ err = dbTable->CutRow(m_mdbEnv, dbRow);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
diff --git a/mailnews/addrbook/src/nsAddrDatabase.h b/mailnews/addrbook/src/nsAddrDatabase.h
new file mode 100644
index 000000000..3b4e4eee6
--- /dev/null
+++ b/mailnews/addrbook/src/nsAddrDatabase.h
@@ -0,0 +1,439 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsAddrDatabase_H_
+#define _nsAddrDatabase_H_
+
+#include "mozilla/Attributes.h"
+#include "nsIAddrDatabase.h"
+#include "mdb.h"
+#include "nsStringGlue.h"
+#include "nsIAddrDBListener.h"
+#include "nsCOMPtr.h"
+#include "nsTObserverArray.h"
+#include "nsWeakPtr.h"
+#include "nsIWeakReferenceUtils.h"
+
+typedef enum
+{
+ AB_NotifyInserted,
+ AB_NotifyDeleted,
+ AB_NotifyPropertyChanged
+} AB_NOTIFY_CODE;
+
+class nsAddrDatabase : public nsIAddrDatabase
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIADDRDBANNOUNCER
+ //////////////////////////////////////////////////////////////////////////////
+ // nsIAddrDatabase methods:
+
+ NS_IMETHOD GetDbPath(nsIFile * *aDbPath) override;
+ NS_IMETHOD SetDbPath(nsIFile * aDbPath) override;
+ NS_IMETHOD Open(nsIFile *aMabFile, bool aCreate, bool upgrading, nsIAddrDatabase **pCardDB) override;
+ NS_IMETHOD Close(bool forceCommit) override;
+ NS_IMETHOD OpenMDB(nsIFile *dbName, bool create) override;
+ NS_IMETHOD CloseMDB(bool commit) override;
+ NS_IMETHOD Commit(uint32_t commitType) override;
+ NS_IMETHOD ForceClosed() override;
+
+ NS_IMETHOD CreateNewCardAndAddToDB(nsIAbCard *newCard, bool notify, nsIAbDirectory *parent) override;
+ NS_IMETHOD CreateNewListCardAndAddToDB(nsIAbDirectory *list, uint32_t listRowID, nsIAbCard *newCard, bool notify) override;
+ NS_IMETHOD CreateMailListAndAddToDB(nsIAbDirectory *newList, bool notify, nsIAbDirectory *parent) override;
+ NS_IMETHOD EnumerateCards(nsIAbDirectory *directory, nsISimpleEnumerator **result) override;
+ NS_IMETHOD GetMailingListsFromDB(nsIAbDirectory *parentDir) override;
+ NS_IMETHOD EnumerateListAddresses(nsIAbDirectory *directory, nsISimpleEnumerator **result) override;
+ NS_IMETHOD DeleteCard(nsIAbCard *newCard, bool notify, nsIAbDirectory *parent) override;
+ NS_IMETHOD EditCard(nsIAbCard *card, bool notify, nsIAbDirectory *parent) override;
+ NS_IMETHOD ContainsCard(nsIAbCard *card, bool *hasCard) override;
+ NS_IMETHOD DeleteMailList(nsIAbDirectory *aMailList, nsIAbDirectory *aParent) override;
+ NS_IMETHOD EditMailList(nsIAbDirectory *mailList, nsIAbCard *listCard, bool notify) override;
+ NS_IMETHOD ContainsMailList(nsIAbDirectory *mailList, bool *hasCard) override;
+ NS_IMETHOD DeleteCardFromMailList(nsIAbDirectory *mailList, nsIAbCard *card, bool aNotify) override;
+ NS_IMETHOD GetCardFromAttribute(nsIAbDirectory *aDirectory, const char *aName,
+ const nsACString &aValue,
+ bool aCaseInsensitive, nsIAbCard **card) override;
+ NS_IMETHOD GetCardsFromAttribute(nsIAbDirectory *aDirectory,
+ const char *aName,
+ const nsACString & uUTF8Value,
+ bool aCaseInsensitive,
+ nsISimpleEnumerator **cards) override;
+ NS_IMETHOD GetNewRow(nsIMdbRow * *newRow) override;
+ NS_IMETHOD GetNewListRow(nsIMdbRow * *newRow) override;
+ NS_IMETHOD AddCardRowToDB(nsIMdbRow *newRow) override;
+ NS_IMETHOD AddLdifListMember(nsIMdbRow* row, const char * value) override;
+
+ NS_IMETHOD GetDeletedCardList(nsIArray **aResult) override;
+ NS_IMETHOD GetDeletedCardCount(uint32_t *aCount) override;
+ NS_IMETHOD PurgeDeletedCardTable();
+
+ NS_IMETHOD AddFirstName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_FirstNameColumnToken, value); }
+
+ NS_IMETHOD AddLastName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_LastNameColumnToken, value); }
+
+ NS_IMETHOD AddPhoneticFirstName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_PhoneticFirstNameColumnToken, value); }
+
+ NS_IMETHOD AddPhoneticLastName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_PhoneticLastNameColumnToken, value); }
+
+ NS_IMETHOD AddDisplayName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_DisplayNameColumnToken, value); }
+
+ NS_IMETHOD AddNickName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_NickNameColumnToken, value); }
+
+ NS_IMETHOD AddPrimaryEmail(nsIMdbRow * row, const char * value) override;
+
+ NS_IMETHOD Add2ndEmail(nsIMdbRow * row, const char * value) override;
+
+ NS_IMETHOD AddPreferMailFormat(nsIMdbRow * row, uint32_t value) override
+ { return AddIntColumn(row, m_MailFormatColumnToken, value); }
+
+ NS_IMETHOD AddPopularityIndex(nsIMdbRow * row, uint32_t value) override
+ { return AddIntColumn(row, m_PopularityIndexColumnToken, value); }
+
+ NS_IMETHOD AddWorkPhone(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WorkPhoneColumnToken, value); }
+
+ NS_IMETHOD AddHomePhone(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_HomePhoneColumnToken, value); }
+
+ NS_IMETHOD AddFaxNumber(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_FaxColumnToken, value); }
+
+ NS_IMETHOD AddPagerNumber(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_PagerColumnToken, value); }
+
+ NS_IMETHOD AddCellularNumber(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_CellularColumnToken, value); }
+
+ NS_IMETHOD AddWorkPhoneType(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WorkPhoneTypeColumnToken, value); }
+
+ NS_IMETHOD AddHomePhoneType(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_HomePhoneTypeColumnToken, value); }
+
+ NS_IMETHOD AddFaxNumberType(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_FaxTypeColumnToken, value); }
+
+ NS_IMETHOD AddPagerNumberType(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_PagerTypeColumnToken, value); }
+
+ NS_IMETHOD AddCellularNumberType(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_CellularTypeColumnToken, value); }
+
+ NS_IMETHOD AddHomeAddress(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_HomeAddressColumnToken, value); }
+
+ NS_IMETHOD AddHomeAddress2(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_HomeAddress2ColumnToken, value); }
+
+ NS_IMETHOD AddHomeCity(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_HomeCityColumnToken, value); }
+
+ NS_IMETHOD AddHomeState(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_HomeStateColumnToken, value); }
+
+ NS_IMETHOD AddHomeZipCode(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_HomeZipCodeColumnToken, value); }
+
+ NS_IMETHOD AddHomeCountry(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_HomeCountryColumnToken, value); }
+
+ NS_IMETHOD AddWorkAddress(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WorkAddressColumnToken, value); }
+
+ NS_IMETHOD AddWorkAddress2(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WorkAddress2ColumnToken, value); }
+
+ NS_IMETHOD AddWorkCity(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WorkCityColumnToken, value); }
+
+ NS_IMETHOD AddWorkState(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WorkStateColumnToken, value); }
+
+ NS_IMETHOD AddWorkZipCode(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WorkZipCodeColumnToken, value); }
+
+ NS_IMETHOD AddWorkCountry(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WorkCountryColumnToken, value); }
+
+ NS_IMETHOD AddJobTitle(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_JobTitleColumnToken, value); }
+
+ NS_IMETHOD AddDepartment(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_DepartmentColumnToken, value); }
+
+ NS_IMETHOD AddCompany(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_CompanyColumnToken, value); }
+
+ NS_IMETHOD AddAimScreenName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_AimScreenNameColumnToken, value); }
+
+ NS_IMETHOD AddAnniversaryYear(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_AnniversaryYearColumnToken, value); }
+
+ NS_IMETHOD AddAnniversaryMonth(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_AnniversaryMonthColumnToken, value); }
+
+ NS_IMETHOD AddAnniversaryDay(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_AnniversaryDayColumnToken, value); }
+
+ NS_IMETHOD AddSpouseName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_SpouseNameColumnToken, value); }
+
+ NS_IMETHOD AddFamilyName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_FamilyNameColumnToken, value); }
+
+ NS_IMETHOD AddDefaultAddress(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_DefaultAddressColumnToken, value); }
+
+ NS_IMETHOD AddCategory(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_CategoryColumnToken, value); }
+
+ NS_IMETHOD AddWebPage1(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WebPage1ColumnToken, value); }
+
+ NS_IMETHOD AddWebPage2(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_WebPage2ColumnToken, value); }
+
+ NS_IMETHOD AddBirthYear(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_BirthYearColumnToken, value); }
+
+ NS_IMETHOD AddBirthMonth(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_BirthMonthColumnToken, value); }
+
+ NS_IMETHOD AddBirthDay(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_BirthDayColumnToken, value); }
+
+ NS_IMETHOD AddCustom1(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_Custom1ColumnToken, value); }
+
+ NS_IMETHOD AddCustom2(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_Custom2ColumnToken, value); }
+
+ NS_IMETHOD AddCustom3(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_Custom3ColumnToken, value); }
+
+ NS_IMETHOD AddCustom4(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_Custom4ColumnToken, value); }
+
+ NS_IMETHOD AddNotes(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_NotesColumnToken, value); }
+
+ NS_IMETHOD AddListName(nsIMdbRow * row, const char * value) override;
+
+ NS_IMETHOD AddListNickName(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_ListNickNameColumnToken, value); }
+
+ NS_IMETHOD AddListDescription(nsIMdbRow * row, const char * value) override
+ { return AddCharStringColumn(row, m_ListDescriptionColumnToken, value); }
+
+
+ NS_IMETHOD AddListDirNode(nsIMdbRow * listRow) override;
+
+ NS_IMETHOD FindMailListbyUnicodeName(const char16_t *listName, bool *exist) override;
+
+ NS_IMETHOD GetCardCount(uint32_t *count) override;
+
+ NS_IMETHOD SetCardValue(nsIAbCard *card, const char *name, const char16_t *value, bool notify) override;
+ NS_IMETHOD GetCardValue(nsIAbCard *card, const char *name, char16_t **value) override;
+ // nsAddrDatabase methods:
+
+ nsAddrDatabase();
+
+ void GetMDBFactory(nsIMdbFactory ** aMdbFactory);
+ nsIMdbEnv *GetEnv() {return m_mdbEnv;}
+ uint32_t GetCurVersion();
+ nsIMdbTableRowCursor *GetTableRowCursor();
+ nsIMdbTable *GetPabTable() {return m_mdbPabTable;}
+
+ static nsAddrDatabase* FindInCache(nsIFile *dbName);
+
+ static void CleanupCache();
+
+ nsresult CreateABCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result);
+ nsresult CreateABListCard(nsIMdbRow* listRow, nsIAbCard **result);
+ nsresult CreateABList(nsIMdbRow* listRow, nsIAbDirectory **result);
+
+ bool IsListRowScopeToken(mdb_scope scope) { return (scope == m_ListRowScopeToken) ? true: false; }
+ bool IsCardRowScopeToken(mdb_scope scope) { return (scope == m_CardRowScopeToken) ? true: false; }
+ bool IsDataRowScopeToken(mdb_scope scope) { return (scope == m_DataRowScopeToken) ? true: false; }
+ nsresult GetCardRowByRowID(mdb_id rowID, nsIMdbRow **dbRow);
+ nsresult GetListRowByRowID(mdb_id rowID, nsIMdbRow **dbRow);
+
+ uint32_t GetListAddressTotal(nsIMdbRow* listRow);
+ nsresult GetAddressRowByPos(nsIMdbRow* listRow, uint16_t pos, nsIMdbRow** cardRow);
+
+ NS_IMETHOD AddListCardColumnsToRow(nsIAbCard *aPCard, nsIMdbRow *aPListRow, uint32_t aPos, nsIAbCard** aPNewCard, bool aInMailingList, nsIAbDirectory *aParent, nsIAbDirectory *aRoot) override;
+ NS_IMETHOD InitCardFromRow(nsIAbCard *aNewCard, nsIMdbRow* aCardRow) override;
+ NS_IMETHOD SetListAddressTotal(nsIMdbRow* aListRow, uint32_t aTotal) override;
+ NS_IMETHOD FindRowByCard(nsIAbCard * card,nsIMdbRow **aRow) override;
+
+protected:
+ virtual ~nsAddrDatabase();
+
+ static void RemoveFromCache(nsAddrDatabase* pAddrDB);
+ bool MatchDbName(nsIFile *dbName); // returns TRUE if they match
+
+ void YarnToUInt32(struct mdbYarn *yarn, uint32_t *pResult);
+ void GetCharStringYarn(char* str, struct mdbYarn* strYarn);
+ void GetStringYarn(const nsAString & aStr, struct mdbYarn* strYarn);
+ void GetIntYarn(uint32_t nValue, struct mdbYarn* intYarn);
+ nsresult AddCharStringColumn(nsIMdbRow* cardRow, mdb_column inColumn, const char* str);
+ nsresult AddStringColumn(nsIMdbRow* aCardRow, mdb_column aInColumn, const nsAString & aStr);
+ nsresult AddIntColumn(nsIMdbRow* cardRow, mdb_column inColumn, uint32_t nValue);
+ nsresult AddBoolColumn(nsIMdbRow* cardRow, mdb_column inColumn, bool bValue);
+ nsresult GetStringColumn(nsIMdbRow *cardRow, mdb_token outToken, nsString& str);
+ nsresult GetIntColumn(nsIMdbRow *cardRow, mdb_token outToken,
+ uint32_t* pValue, uint32_t defaultValue);
+ nsresult GetBoolColumn(nsIMdbRow *cardRow, mdb_token outToken, bool* pValue);
+ nsresult GetListCardFromDB(nsIAbCard *listCard, nsIMdbRow* listRow);
+ nsresult GetListFromDB(nsIAbDirectory *newCard, nsIMdbRow* listRow);
+ nsresult AddRecordKeyColumnToRow(nsIMdbRow *pRow);
+ nsresult AddAttributeColumnsToRow(nsIAbCard *card, nsIMdbRow *cardRow);
+ nsresult AddListAttributeColumnsToRow(nsIAbDirectory *list, nsIMdbRow *listRow, nsIAbDirectory *parent);
+ nsresult CreateCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result);
+ nsresult CreateCardFromDeletedCardsTable(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result);
+ nsresult DeleteCardFromListRow(nsIMdbRow* pListRow, mdb_id cardRowID);
+ void DeleteCardFromAllMailLists(mdb_id cardRowID);
+ nsresult NotifyListEntryChange(uint32_t abCode, nsIAbDirectory *dir);
+
+ nsresult AddLowercaseColumn(nsIMdbRow * row, mdb_token columnToken, const char* utf8String);
+ nsresult GetRowFromAttribute(const char *aName, const nsACString &aUTF8Value,
+ bool aCaseInsensitive, nsIMdbRow **aCardRow,
+ mdb_pos *aRowPos);
+
+ static nsTArray<nsAddrDatabase*>* m_dbCache;
+ static nsTArray<nsAddrDatabase*>* GetDBCache();
+
+ // mdb bookkeeping stuff
+ nsresult InitExistingDB();
+ nsresult InitNewDB();
+ nsresult InitMDBInfo();
+ nsresult InitPabTable();
+ nsresult InitDeletedCardsTable(bool aCreate);
+ nsresult AddRowToDeletedCardsTable(nsIAbCard *card, nsIMdbRow **pCardRow);
+ nsresult DeleteRowFromDeletedCardsTable(nsIMdbRow *pCardRow);
+
+ nsresult InitLastRecorKey();
+ nsresult GetDataRow(nsIMdbRow **pDataRow);
+ nsresult GetLastRecordKey();
+ nsresult UpdateLastRecordKey();
+ nsresult CheckAndUpdateRecordKey();
+ nsresult UpdateLowercaseEmailListName();
+ nsresult ConvertAndAddLowercaseColumn(nsIMdbRow * row, mdb_token fromCol, mdb_token toCol);
+ nsresult AddUnicodeToColumn(nsIMdbRow * row, mdb_token colToken, mdb_token lowerCaseColToken, const char16_t* pUnicodeStr);
+
+ nsresult DeleteRow(nsIMdbTable* dbTable, nsIMdbRow* dbRow);
+
+ nsIMdbEnv *m_mdbEnv; // to be used in all the db calls.
+ nsIMdbStore *m_mdbStore;
+ nsIMdbTable *m_mdbPabTable;
+ nsIMdbTable *m_mdbDeletedCardsTable;
+ nsCOMPtr<nsIFile> m_dbName;
+ bool m_mdbTokensInitialized;
+ nsTObserverArray<nsIAddrDBListener*> m_ChangeListeners;
+
+ mdb_kind m_PabTableKind;
+ mdb_kind m_MailListTableKind;
+ mdb_kind m_DeletedCardsTableKind;
+
+ mdb_scope m_CardRowScopeToken;
+ mdb_scope m_ListRowScopeToken;
+ mdb_scope m_DataRowScopeToken;
+
+ mdb_token m_FirstNameColumnToken;
+ mdb_token m_LastNameColumnToken;
+ mdb_token m_PhoneticFirstNameColumnToken;
+ mdb_token m_PhoneticLastNameColumnToken;
+ mdb_token m_DisplayNameColumnToken;
+ mdb_token m_NickNameColumnToken;
+ mdb_token m_PriEmailColumnToken;
+ mdb_token m_2ndEmailColumnToken;
+ mdb_token m_DefaultEmailColumnToken;
+ mdb_token m_CardTypeColumnToken;
+ mdb_token m_WorkPhoneColumnToken;
+ mdb_token m_HomePhoneColumnToken;
+ mdb_token m_FaxColumnToken;
+ mdb_token m_PagerColumnToken;
+ mdb_token m_CellularColumnToken;
+ mdb_token m_WorkPhoneTypeColumnToken;
+ mdb_token m_HomePhoneTypeColumnToken;
+ mdb_token m_FaxTypeColumnToken;
+ mdb_token m_PagerTypeColumnToken;
+ mdb_token m_CellularTypeColumnToken;
+ mdb_token m_HomeAddressColumnToken;
+ mdb_token m_HomeAddress2ColumnToken;
+ mdb_token m_HomeCityColumnToken;
+ mdb_token m_HomeStateColumnToken;
+ mdb_token m_HomeZipCodeColumnToken;
+ mdb_token m_HomeCountryColumnToken;
+ mdb_token m_WorkAddressColumnToken;
+ mdb_token m_WorkAddress2ColumnToken;
+ mdb_token m_WorkCityColumnToken;
+ mdb_token m_WorkStateColumnToken;
+ mdb_token m_WorkZipCodeColumnToken;
+ mdb_token m_WorkCountryColumnToken;
+ mdb_token m_JobTitleColumnToken;
+ mdb_token m_DepartmentColumnToken;
+ mdb_token m_CompanyColumnToken;
+ mdb_token m_AimScreenNameColumnToken;
+ mdb_token m_AnniversaryYearColumnToken;
+ mdb_token m_AnniversaryMonthColumnToken;
+ mdb_token m_AnniversaryDayColumnToken;
+ mdb_token m_SpouseNameColumnToken;
+ mdb_token m_FamilyNameColumnToken;
+ mdb_token m_DefaultAddressColumnToken;
+ mdb_token m_CategoryColumnToken;
+ mdb_token m_WebPage1ColumnToken;
+ mdb_token m_WebPage2ColumnToken;
+ mdb_token m_BirthYearColumnToken;
+ mdb_token m_BirthMonthColumnToken;
+ mdb_token m_BirthDayColumnToken;
+ mdb_token m_Custom1ColumnToken;
+ mdb_token m_Custom2ColumnToken;
+ mdb_token m_Custom3ColumnToken;
+ mdb_token m_Custom4ColumnToken;
+ mdb_token m_NotesColumnToken;
+ mdb_token m_LastModDateColumnToken;
+ mdb_token m_RecordKeyColumnToken;
+ mdb_token m_LowerPriEmailColumnToken;
+ mdb_token m_Lower2ndEmailColumnToken;
+
+ mdb_token m_MailFormatColumnToken;
+ mdb_token m_PopularityIndexColumnToken;
+
+ mdb_token m_AddressCharSetColumnToken;
+ mdb_token m_LastRecordKeyColumnToken;
+
+ mdb_token m_ListNameColumnToken;
+ mdb_token m_ListNickNameColumnToken;
+ mdb_token m_ListDescriptionColumnToken;
+ mdb_token m_ListTotalColumnToken;
+ mdb_token m_LowerListNameColumnToken;
+
+ uint32_t m_LastRecordKey;
+ nsWeakPtr m_dbDirectory;
+ nsCOMPtr<nsIMdbFactory> mMdbFactory;
+
+private:
+ nsresult GetRowForCharColumn(const char16_t *unicodeStr,
+ mdb_column findColumn, bool bIsCard,
+ bool aCaseInsensitive, nsIMdbRow **findRow,
+ mdb_pos *aRowPos);
+ bool HasRowButDeletedForCharColumn(const char16_t *unicodeStr, mdb_column findColumn, bool aIsCard, nsIMdbRow **aFindRow);
+ nsresult OpenInternal(nsIFile *aMabFile, bool aCreate, nsIAddrDatabase **pCardDB);
+ nsresult AlertAboutCorruptMabFile(const char16_t *aOldFileName, const char16_t *aNewFileName);
+ nsresult AlertAboutLockedMabFile(const char16_t *aFileName);
+ nsresult DisplayAlert(const char16_t *titleName, const char16_t *alertStringName,
+ const char16_t **formatStrings, int32_t numFormatStrings);
+};
+
+#endif
diff --git a/mailnews/addrbook/src/nsAddrbook.manifest b/mailnews/addrbook/src/nsAddrbook.manifest
new file mode 100644
index 000000000..56efc3bda
--- /dev/null
+++ b/mailnews/addrbook/src/nsAddrbook.manifest
@@ -0,0 +1,12 @@
+component {5b259db2-e451-4de9-8a6f-cfba91402973} nsAbAutoCompleteMyDomain.js
+contract @mozilla.org/autocomplete/search;1?name=mydomain {5b259db2-e451-4de9-8a6f-cfba91402973}
+component {2f946df9-114c-41fe-8899-81f10daf4f0c} nsAbAutoCompleteSearch.js
+contract @mozilla.org/autocomplete/search;1?name=addrbook {2f946df9-114c-41fe-8899-81f10daf4f0c}
+component {127b341a-bdda-4270-85e1-edff569a9b85} nsAbLDAPAttributeMap.js
+contract @mozilla.org/addressbook/ldap-attribute-map;1 {127b341a-bdda-4270-85e1-edff569a9b85}
+component {4ed7d5e1-8800-40da-9e78-c4f509d7ac5e} nsAbLDAPAttributeMap.js
+contract @mozilla.org/addressbook/ldap-attribute-map-service;1 {4ed7d5e1-8800-40da-9e78-c4f509d7ac5e}
+#ifdef MOZ_LDAP_XPCOM
+component {227e6482-fe9f-441f-9b7d-7b60375e7449} nsAbLDAPAutoCompleteSearch.js
+contract @mozilla.org/autocomplete/search;1?name=ldap {227e6482-fe9f-441f-9b7d-7b60375e7449}
+#endif \ No newline at end of file
diff --git a/mailnews/addrbook/src/nsDirPrefs.cpp b/mailnews/addrbook/src/nsDirPrefs.cpp
new file mode 100644
index 000000000..5e4c046b2
--- /dev/null
+++ b/mailnews/addrbook/src/nsDirPrefs.cpp
@@ -0,0 +1,1452 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* directory server preferences (used to be dirprefs.c in 4.x) */
+
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsDirPrefs.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsIObserver.h"
+#include "nsTArray.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMemory.h"
+#include "nsIAddrDatabase.h"
+#include "nsAbBaseCID.h"
+#include "nsIAbManager.h"
+#include "nsIFile.h"
+#include "nsWeakReference.h"
+#include "nsIAbMDBDirectory.h"
+#if defined(MOZ_LDAP_XPCOM)
+#include "nsIAbLDAPDirectory.h"
+#endif
+#include "prmem.h"
+#include "prprf.h"
+#include "plstr.h"
+#include "nsQuickSort.h"
+#include "nsComponentManagerUtils.h"
+#include "msgCore.h"
+#include "nsStringGlue.h"
+
+#include <ctype.h>
+
+/*****************************************************************************
+ * Private definitions
+ */
+
+/* Default settings for site-configurable prefs */
+#define kDefaultPosition 1
+static bool dir_IsServerDeleted(DIR_Server * server);
+
+static char *DIR_GetStringPref(const char *prefRoot, const char *prefLeaf, const char *defaultValue);
+static int32_t DIR_GetIntPref(const char *prefRoot, const char *prefLeaf, int32_t defaultValue);
+static char *DIR_GetLocalizedStringPref(const char *prefRoot, const char *prefLeaf);
+
+static char * dir_ConvertDescriptionToPrefName(DIR_Server * server);
+
+void DIR_SetFileName(char** filename, const char* leafName);
+static void DIR_SetIntPref(const char *prefRoot, const char *prefLeaf, int32_t value, int32_t defaultValue);
+static DIR_Server *dir_MatchServerPrefToServer(nsTArray<DIR_Server*> *wholeList, const char *pref);
+static bool dir_ValidateAndAddNewServer(nsTArray<DIR_Server*> *wholeList, const char *fullprefname);
+static void DIR_DeleteServerList(nsTArray<DIR_Server*> *wholeList);
+
+static char *dir_CreateServerPrefName(DIR_Server *server);
+static void DIR_GetPrefsForOneServer(DIR_Server *server);
+
+static void DIR_InitServer(DIR_Server *server, DirectoryType dirType = (DirectoryType)0);
+static DIR_PrefId DIR_AtomizePrefName(const char *prefname);
+
+const int32_t DIR_POS_APPEND = -1;
+const int32_t DIR_POS_DELETE = -2;
+static bool DIR_SetServerPosition(nsTArray<DIR_Server*> *wholeList, DIR_Server *server, int32_t position);
+
+/* These two routines should be called to initialize and save
+ * directory preferences from the XP Java Script preferences
+ */
+static nsresult DIR_GetServerPreferences(nsTArray<DIR_Server*>** list);
+static void DIR_SaveServerPreferences(nsTArray<DIR_Server*> *wholeList);
+
+static int32_t dir_UserId = 0;
+nsTArray<DIR_Server*> *dir_ServerList = nullptr;
+
+/*****************************************************************************
+ * Functions for creating the new back end managed DIR_Server list.
+ */
+class DirPrefObserver final : public nsSupportsWeakReference,
+ public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ ~DirPrefObserver() {}
+};
+
+NS_IMPL_ISUPPORTS(DirPrefObserver, nsISupportsWeakReference, nsIObserver)
+
+NS_IMETHODIMP DirPrefObserver::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
+{
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_QueryInterface(aSubject));
+ nsCString strPrefName;
+ strPrefName.Assign(NS_ConvertUTF16toUTF8(aData));
+ const char *prefname = strPrefName.get();
+
+ DIR_PrefId id = DIR_AtomizePrefName(prefname);
+
+ // Just get out if we get nothing here - we don't need to do anything
+ if (id == idNone)
+ return NS_OK;
+
+ /* Check to see if the server is in the unified server list.
+ */
+ DIR_Server *server = dir_MatchServerPrefToServer(dir_ServerList, prefname);
+ if (server)
+ {
+ /* If the server is in the process of being saved, just ignore this
+ * change. The DIR_Server structure is not really changing.
+ */
+ if (server->savingServer)
+ return NS_OK;
+
+ /* If the pref that changed is the position, read it in. If the new
+ * position is zero, remove the server from the list.
+ */
+ if (id == idPosition)
+ {
+ int32_t position;
+
+ /* We must not do anything if the new position is the same as the
+ * position in the DIR_Server. This avoids recursion in cases
+ * where we are deleting the server.
+ */
+ prefBranch->GetIntPref(prefname, &position);
+ if (position != server->position)
+ {
+ server->position = position;
+ if (dir_IsServerDeleted(server))
+ DIR_SetServerPosition(dir_ServerList, server, DIR_POS_DELETE);
+ }
+ }
+
+ if (id == idDescription) {
+ // Ensure the local copy of the description is kept up to date.
+ PR_FREEIF(server->description);
+ server->description = DIR_GetLocalizedStringPref(prefname, nullptr);
+ }
+ }
+ /* If the server is not in the unified list, we may need to add it. Servers
+ * are only added when the position, serverName and description are valid.
+ */
+ else if (id == idPosition || id == idType || id == idDescription)
+ {
+ dir_ValidateAndAddNewServer(dir_ServerList, prefname);
+ }
+
+ return NS_OK;
+}
+
+// A pointer to the pref observer
+static DirPrefObserver *prefObserver = nullptr;
+
+static nsresult DIR_GetDirServers()
+{
+ nsresult rv = NS_OK;
+
+ if (!dir_ServerList)
+ {
+ /* we need to build the DIR_Server list */
+ rv = DIR_GetServerPreferences(&dir_ServerList);
+
+ /* Register the preference call back if necessary. */
+ if (NS_SUCCEEDED(rv) && !prefObserver)
+ {
+ nsCOMPtr<nsIPrefBranch> pbi(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ prefObserver = new DirPrefObserver();
+
+ if (!prefObserver)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(prefObserver);
+
+ pbi->AddObserver(PREF_LDAP_SERVER_TREE_NAME, prefObserver, true);
+ }
+ }
+ return rv;
+}
+
+nsTArray<DIR_Server*>* DIR_GetDirectories()
+{
+ if (!dir_ServerList)
+ DIR_GetDirServers();
+ return dir_ServerList;
+}
+
+DIR_Server* DIR_GetServerFromList(const char* prefName)
+{
+ DIR_Server* result = nullptr;
+
+ if (!dir_ServerList)
+ DIR_GetDirServers();
+
+ if (dir_ServerList)
+ {
+ int32_t count = dir_ServerList->Length();
+ int32_t i;
+ for (i = 0; i < count; ++i)
+ {
+ DIR_Server *server = dir_ServerList->ElementAt(i);
+
+ if (server && strcmp(server->prefName, prefName) == 0)
+ {
+ result = server;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+static nsresult SavePrefsFile()
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefService> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ return pPref->SavePrefFile(nullptr);
+}
+
+nsresult DIR_ShutDown() /* FEs should call this when the app is shutting down. It frees all DIR_Servers regardless of ref count values! */
+{
+ nsresult rv = SavePrefsFile();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ DIR_DeleteServerList(dir_ServerList);
+ dir_ServerList = nullptr;
+
+ /* unregister the preference call back, if necessary.
+ * we need to do this as DIR_Shutdown() is called when switching profiles
+ * when using turbo. (see nsAbDirectoryDataSource::Observe())
+ * When switching profiles, prefs get unloaded and then re-loaded
+ * we don't want our callback to get called for all that.
+ * We'll reset our callback the first time DIR_GetDirServers() is called
+ * after we've switched profiles.
+ */
+ NS_IF_RELEASE(prefObserver);
+
+ return NS_OK;
+}
+
+nsresult DIR_ContainsServer(DIR_Server* pServer, bool *hasDir)
+{
+ if (dir_ServerList)
+ {
+ int32_t count = dir_ServerList->Length();
+ int32_t i;
+ for (i = 0; i < count; i++)
+ {
+ DIR_Server* server = dir_ServerList->ElementAt(i);
+ if (server == pServer)
+ {
+ *hasDir = true;
+ return NS_OK;
+ }
+ }
+ }
+ *hasDir = false;
+ return NS_OK;
+}
+
+nsresult DIR_AddNewAddressBook(const nsAString &dirName,
+ const nsACString &fileName,
+ const nsACString &uri,
+ DirectoryType dirType,
+ const nsACString &prefName,
+ DIR_Server** pServer)
+{
+ DIR_Server * server = (DIR_Server *) PR_Malloc(sizeof(DIR_Server));
+ if (!server)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ DIR_InitServer(server, dirType);
+ if (!dir_ServerList)
+ DIR_GetDirServers();
+ if (dir_ServerList)
+ {
+ server->description = ToNewCString(NS_ConvertUTF16toUTF8(dirName));
+ server->position = kDefaultPosition; // don't set position so alphabetic sort will happen.
+
+ if (!fileName.IsEmpty())
+ server->fileName = ToNewCString(fileName);
+ else if (dirType == PABDirectory)
+ DIR_SetFileName(&server->fileName, kPersonalAddressbook);
+ else if (dirType == LDAPDirectory)
+ DIR_SetFileName(&server->fileName, kMainLdapAddressBook);
+
+ if (dirType != PABDirectory) {
+ if (!uri.IsEmpty())
+ server->uri = ToNewCString(uri);
+ }
+
+ if (!prefName.IsEmpty())
+ server->prefName = ToNewCString(prefName);
+
+ dir_ServerList->AppendElement(server);
+
+ DIR_SavePrefsForOneServer(server);
+
+ *pServer = server;
+
+ // save new address book into pref file
+ return SavePrefsFile();
+ }
+ return NS_ERROR_FAILURE;
+}
+
+/*****************************************************************************
+ * Functions for creating DIR_Servers
+ */
+static void DIR_InitServer(DIR_Server *server, DirectoryType dirType)
+{
+ if (!server) {
+ NS_WARNING("DIR_InitServer: server parameter not initialized");
+ return;
+ }
+
+ memset(server, 0, sizeof(DIR_Server));
+ server->position = kDefaultPosition;
+ server->uri = nullptr;
+ server->savingServer = false;
+ server->dirType = dirType;
+}
+
+/* Function for setting the position of a server. Can be used to append,
+ * delete, or move a server in a server list.
+ *
+ * The third parameter specifies the new position the server is to occupy.
+ * The resulting position may differ depending on the lock state of the
+ * given server and other servers in the list. The following special values
+ * are supported:
+ * DIR_POS_APPEND - Appends the server to the end of the list. If the server
+ * is already in the list, does nothing.
+ * DIR_POS_DELETE - Deletes the given server from the list. Note that this
+ * does not cause the server structure to be freed.
+ *
+ * Returns true if the server list was re-sorted.
+ */
+static bool DIR_SetServerPosition(nsTArray<DIR_Server*> *wholeList, DIR_Server *server, int32_t position)
+ {
+ NS_ENSURE_TRUE(wholeList, false);
+
+ int32_t i, count, num;
+ bool resort = false;
+ DIR_Server *s=nullptr;
+
+ switch (position) {
+ case DIR_POS_APPEND:
+ /* Do nothing if the request is to append a server that is already
+ * in the list.
+ */
+ count = wholeList->Length();
+ for (i= 0; i < count; i++)
+ {
+ if ((s = wholeList->ElementAt(i)) != nullptr)
+ if (s == server)
+ return false;
+ }
+ /* In general, if there are any servers already in the list, set the
+ * position to the position of the last server plus one. If there
+ * are none, set it to position 1.
+ */
+ if (count > 0)
+ {
+ s = wholeList->ElementAt(count - 1);
+ server->position = s->position + 1;
+ }
+ else
+ server->position = 1;
+
+ wholeList->AppendElement(server);
+ break;
+
+ case DIR_POS_DELETE:
+ /* Remove the prefs corresponding to the given server. If the prefName
+ * value is nullptr, the server has never been saved and there are no
+ * prefs to remove.
+ */
+ if (server->prefName)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return false;
+
+ pPref->DeleteBranch(server->prefName);
+
+ // mark the server as deleted by setting its position to 0
+ DIR_SetIntPref(server->prefName, "position", 0, -1);
+ }
+
+ /* If the server is in the server list, remove it.
+ */
+ num = wholeList->IndexOf(server);
+ if (num >= 0)
+ {
+ /* The list does not need to be re-sorted if the server is the
+ * last one in the list.
+ */
+ count = wholeList->Length();
+ if (num == count - 1)
+ {
+ wholeList->RemoveElementAt(num);
+ }
+ else
+ {
+ resort = true;
+ wholeList->RemoveElement(server);
+ }
+ }
+ break;
+
+ default:
+ /* See if the server is already in the list.
+ */
+ count = wholeList->Length();
+ for (i= 0; i < count; i++)
+ {
+ if ((s = wholeList->ElementAt(i)) != nullptr)
+ if (s == server)
+ break;
+ }
+
+ /* If the server is not in the list, add it to the beginning and re-sort.
+ */
+ if (s == nullptr)
+ {
+ server->position = position;
+ wholeList->AppendElement(server);
+ resort = true;
+ }
+
+ /* Don't re-sort if the server is already in the requested position.
+ */
+ else if (server->position != position)
+ {
+ server->position = position;
+ wholeList->RemoveElement(server);
+ wholeList->AppendElement(server);
+ resort = true;
+ }
+ break;
+ }
+
+ /* Make sure our position changes get saved back to prefs
+ */
+ DIR_SaveServerPreferences(wholeList);
+
+ return resort;
+}
+
+/*****************************************************************************
+ * DIR_Server Callback Notification Functions
+ */
+
+/* dir_matchServerPrefToServer
+ *
+ * This function finds the DIR_Server in the unified DIR_Server list to which
+ * the given preference string belongs.
+ */
+static DIR_Server *dir_MatchServerPrefToServer(nsTArray<DIR_Server*> *wholeList, const char *pref)
+{
+ DIR_Server *server;
+
+ int32_t count = wholeList->Length();
+ int32_t i;
+ for (i = 0; i < count; i++)
+ {
+ if ((server = wholeList->ElementAt(i)) != nullptr)
+ {
+ if (server->prefName && PL_strstr(pref, server->prefName) == pref)
+ {
+ char c = pref[PL_strlen(server->prefName)];
+ if (c == 0 || c == '.')
+ return server;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/* dir_ValidateAndAddNewServer
+ *
+ * This function verifies that the position, serverName and description values
+ * are set for the given prefName. If they are then it adds the server to the
+ * unified server list.
+ */
+static bool dir_ValidateAndAddNewServer(nsTArray<DIR_Server*> *wholeList, const char *fullprefname)
+{
+ bool rc = false;
+
+ const char *endname = PL_strchr(&fullprefname[PL_strlen(PREF_LDAP_SERVER_TREE_NAME) + 1], '.');
+ if (endname)
+ {
+ char *prefname = (char *)PR_Malloc(endname - fullprefname + 1);
+ if (prefname)
+ {
+ int32_t dirType;
+ char *t1 = nullptr, *t2 = nullptr;
+
+ PL_strncpyz(prefname, fullprefname, endname - fullprefname + 1);
+
+ dirType = DIR_GetIntPref(prefname, "dirType", -1);
+ if (dirType != -1 &&
+ DIR_GetIntPref(prefname, "position", 0) != 0 &&
+ (t1 = DIR_GetLocalizedStringPref(prefname, "description")) != nullptr)
+ {
+ if (dirType == PABDirectory ||
+ (t2 = DIR_GetStringPref(prefname, "serverName", nullptr)) != nullptr)
+ {
+ DIR_Server *server = (DIR_Server *)PR_Malloc(sizeof(DIR_Server));
+ if (server)
+ {
+ DIR_InitServer(server, (DirectoryType)dirType);
+ server->prefName = prefname;
+ DIR_GetPrefsForOneServer(server);
+ DIR_SetServerPosition(wholeList, server, server->position);
+ rc = true;
+ }
+ PR_FREEIF(t2);
+ }
+ PR_Free(t1);
+ }
+ else
+ PR_Free(prefname);
+ }
+ }
+
+ return rc;
+}
+
+static DIR_PrefId DIR_AtomizePrefName(const char *prefname)
+{
+ if (!prefname)
+ return idNone;
+
+ DIR_PrefId rc = idNone;
+
+ /* Skip the "ldap_2.servers.<server-name>." portion of the string.
+ */
+ if (PL_strstr(prefname, PREF_LDAP_SERVER_TREE_NAME) == prefname)
+ {
+ prefname = PL_strchr(&prefname[PL_strlen(PREF_LDAP_SERVER_TREE_NAME) + 1], '.');
+ if (!prefname)
+ return idNone;
+ else
+ prefname = prefname + 1;
+ }
+
+ switch (prefname[0]) {
+ case 'd':
+ switch (prefname[1]) {
+ case 'e': /* description */
+ rc = idDescription;
+ break;
+ case 'i': /* dirType */
+ rc = idType;
+ break;
+ }
+ break;
+
+ case 'f':
+ rc = idFileName;
+ break;
+
+ case 'p':
+ switch (prefname[1]) {
+ case 'o':
+ switch (prefname[2]) {
+ case 's': /* position */
+ rc = idPosition;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 'u': /* uri */
+ rc = idUri;
+ break;
+ }
+
+ return rc;
+}
+
+/*****************************************************************************
+ * Functions for destroying DIR_Servers
+ */
+
+/* this function determines if the passed in server is no longer part of the of
+ the global server list. */
+static bool dir_IsServerDeleted(DIR_Server * server)
+{
+ return (server && server->position == 0);
+}
+
+/* when the back end manages the server list, deleting a server just decrements its ref count,
+ in the old world, we actually delete the server */
+static void DIR_DeleteServer(DIR_Server *server)
+{
+ if (server)
+ {
+ /* when destroying the server check its clear flag to see if things need cleared */
+#ifdef XP_FileRemove
+ if (DIR_TestFlag(server, DIR_CLEAR_SERVER))
+ {
+ if (server->fileName)
+ XP_FileRemove (server->fileName, xpAddrBookNew);
+ }
+#endif /* XP_FileRemove */
+ PR_Free(server->prefName);
+ PR_Free(server->description);
+ PR_Free(server->fileName);
+ PR_Free(server->uri);
+ PR_Free(server);
+ }
+}
+
+nsresult DIR_DeleteServerFromList(DIR_Server *server)
+{
+ if (!server)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIFile> dbPath;
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = abManager->GetUserProfileDirectory(getter_AddRefs(dbPath));
+
+ if (NS_SUCCEEDED(rv))
+ {
+ // close the database, as long as it isn't the special ones
+ // (personal addressbook and collected addressbook)
+ // which can never be deleted. There was a bug where we would slap in
+ // "abook.mab" as the file name for LDAP directories, which would cause a crash
+ // on delete of LDAP directories. this is just extra protection.
+ if (server->fileName &&
+ strcmp(server->fileName, kPersonalAddressbook) &&
+ strcmp(server->fileName, kCollectedAddressbook))
+ {
+ nsCOMPtr<nsIAddrDatabase> database;
+
+ rv = dbPath->AppendNative(nsDependentCString(server->fileName));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // close file before delete it
+ nsCOMPtr<nsIAddrDatabase> addrDBFactory =
+ do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv) && addrDBFactory)
+ rv = addrDBFactory->Open(dbPath, false, true, getter_AddRefs(database));
+ if (database) /* database exists */
+ {
+ database->ForceClosed();
+ rv = dbPath->Remove(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ nsTArray<DIR_Server*> *dirList = DIR_GetDirectories();
+ DIR_SetServerPosition(dirList, server, DIR_POS_DELETE);
+ DIR_DeleteServer(server);
+
+ return SavePrefsFile();
+ }
+
+ return NS_ERROR_NULL_POINTER;
+}
+
+static void DIR_DeleteServerList(nsTArray<DIR_Server*> *wholeList)
+{
+ if (wholeList)
+ {
+ DIR_Server *server = nullptr;
+
+ /* TBD: Send notifications? */
+ int32_t count = wholeList->Length();
+ int32_t i;
+ for (i = count - 1; i >=0; i--)
+ {
+ server = wholeList->ElementAt(i);
+ if (server != nullptr)
+ DIR_DeleteServer(server);
+ }
+ delete wholeList;
+ }
+}
+
+/*****************************************************************************
+ * Functions for managing JavaScript prefs for the DIR_Servers
+ */
+
+static int
+comparePrefArrayMembers(const void* aElement1, const void* aElement2, void* aData)
+{
+ const char* element1 = *static_cast<const char* const *>(aElement1);
+ const char* element2 = *static_cast<const char* const *>(aElement2);
+ const uint32_t offset = *((const uint32_t*)aData);
+
+ // begin the comparison at |offset| chars into the string -
+ // this avoids comparing the "ldap_2.servers." portion of every element,
+ // which will always remain the same.
+ return strcmp(element1 + offset, element2 + offset);
+}
+
+static nsresult dir_GetChildList(const nsCString &aBranch,
+ uint32_t *aCount, char ***aChildList)
+{
+ uint32_t branchLen = aBranch.Length();
+
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (!prefBranch) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = prefBranch->GetChildList(aBranch.get(), aCount, aChildList);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // traverse the list, and truncate all the descendant strings to just
+ // one branch level below the root branch.
+ for (uint32_t i = *aCount; i--; ) {
+ // The prefname we passed to GetChildList was of the form
+ // "ldap_2.servers." and we are returned the descendants
+ // in the form of "ldap_2.servers.servername.foo"
+ // But we want the prefbranch of the servername, so
+ // write a NUL character in to terminate the string early.
+ char *endToken = strchr((*aChildList)[i] + branchLen, '.');
+ if (endToken)
+ *endToken = '\0';
+ }
+
+ if (*aCount > 1) {
+ // sort the list, in preparation for duplicate entry removal
+ NS_QuickSort(*aChildList, *aCount, sizeof(char*), comparePrefArrayMembers, &branchLen);
+
+ // traverse the list and remove duplicate entries.
+ // we use two positions in the list; the current entry and the next
+ // entry; and perform a bunch of in-place ptr moves. so |cur| points
+ // to the last unique entry, and |next| points to some (possibly much
+ // later) entry to test, at any given point. we know we have >= 2
+ // elements in the list here, so we just init the two counters sensibly
+ // to begin with.
+ uint32_t cur = 0;
+ for (uint32_t next = 1; next < *aCount; ++next) {
+ // check if the elements are equal or unique
+ if (!comparePrefArrayMembers(&((*aChildList)[cur]), &((*aChildList)[next]), &branchLen)) {
+ // equal - just free & increment the next element ptr
+
+ free((*aChildList)[next]);
+ } else {
+ // cur & next are unique, so we need to shift the element.
+ // ++cur will point to the next free location in the
+ // reduced array (it's okay if that's == next)
+ (*aChildList)[++cur] = (*aChildList)[next];
+ }
+ }
+
+ // update the unique element count
+ *aCount = cur + 1;
+ }
+
+ return NS_OK;
+}
+
+static char *DIR_GetStringPref(const char *prefRoot, const char *prefLeaf, const char *defaultValue)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsCString value;
+ nsAutoCString prefLocation(prefRoot);
+
+ prefLocation.Append('.');
+ prefLocation.Append(prefLeaf);
+
+ if (NS_SUCCEEDED(pPref->GetCharPref(prefLocation.get(), getter_Copies(value))))
+ {
+ /* unfortunately, there may be some prefs out there which look like this */
+ if (value.EqualsLiteral("(null)"))
+ {
+ if (defaultValue)
+ value = defaultValue;
+ else
+ value.Truncate();
+ }
+
+ if (value.IsEmpty())
+ {
+ rv = pPref->GetCharPref(prefLocation.get(), getter_Copies(value));
+ }
+ }
+ else
+ value = defaultValue;
+
+ return ToNewCString(value);
+}
+
+/**
+ * Get localized unicode string pref from properties file, convert into an UTF8 string
+ * since address book prefs store as UTF8 strings. So far there are 2 default
+ * prefs stored in addressbook.properties.
+ * "ldap_2.servers.pab.description"
+ * "ldap_2.servers.history.description"
+ */
+static char *DIR_GetLocalizedStringPref(const char *prefRoot, const char *prefLeaf)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsAutoCString prefLocation(prefRoot);
+ if (prefLeaf) {
+ prefLocation.Append('.');
+ prefLocation.Append(prefLeaf);
+ }
+
+ nsString wvalue;
+ nsCOMPtr<nsIPrefLocalizedString> locStr;
+
+ rv = pPref->GetComplexValue(prefLocation.get(), NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(locStr));
+ if (NS_SUCCEEDED(rv))
+ rv = locStr->ToString(getter_Copies(wvalue));
+
+ char *value = nullptr;
+ if (!wvalue.IsEmpty())
+ {
+ value = ToNewCString(NS_ConvertUTF16toUTF8(wvalue));
+ }
+ else
+ {
+ // In TB 2 only some prefs had chrome:// URIs. We had code in place that would
+ // only get the localized string pref for the particular address books that
+ // were built-in.
+ // Additionally, nsIPrefBranch::getComplexValue will only get a non-user-set,
+ // non-locked pref value if it is a chrome:// URI and will get the string
+ // value at that chrome URI. This breaks extensions/autoconfig that want to
+ // set default pref values and allow users to change directory names.
+ //
+ // Now we have to support this, and so if for whatever reason we fail to get
+ // the localized version, then we try and get the non-localized version
+ // instead. If the string value is empty, then we'll just get the empty value
+ // back here.
+ rv = pPref->GetCharPref(prefLocation.get(), &value);
+ if (NS_FAILED(rv))
+ value = nullptr;
+ }
+
+ return value;
+}
+
+static int32_t DIR_GetIntPref(const char *prefRoot, const char *prefLeaf, int32_t defaultValue)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+
+ if (NS_FAILED(rv))
+ return defaultValue;
+
+ int32_t value;
+ nsAutoCString prefLocation(prefRoot);
+
+ prefLocation.Append('.');
+ prefLocation.Append(prefLeaf);
+
+ if (NS_FAILED(pPref->GetIntPref(prefLocation.get(), &value)))
+ value = defaultValue;
+
+ return value;
+}
+
+/* This will convert from the old preference that was a path and filename */
+/* to a just a filename */
+static void DIR_ConvertServerFileName(DIR_Server* pServer)
+{
+ char* leafName = pServer->fileName;
+ char* newLeafName = nullptr;
+#if defined(XP_WIN)
+ /* jefft -- bug 73349 This is to allow users share same address book.
+ * It only works if the user specify a full path filename.
+ */
+#ifdef XP_FileIsFullPath
+ if (! XP_FileIsFullPath(leafName))
+ newLeafName = XP_STRRCHR (leafName, '\\');
+#endif /* XP_FileIsFullPath */
+#else
+ newLeafName = strrchr(leafName, '/');
+#endif
+ pServer->fileName = newLeafName ? strdup(newLeafName + 1) : strdup(leafName);
+ if (leafName) PR_Free(leafName);
+}
+
+/* This will generate a correct filename and then remove the path.
+ * Note: we are assuming that the default name is in the native
+ * filesystem charset. The filename will be returned as a UTF8
+ * string.
+ */
+void DIR_SetFileName(char** fileName, const char* defaultName)
+{
+ if (!fileName)
+ return;
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIFile> dbPath;
+
+ *fileName = nullptr;
+
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = abManager->GetUserProfileDirectory(getter_AddRefs(dbPath));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = dbPath->AppendNative(nsDependentCString(defaultName));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = dbPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0664);
+
+ nsAutoString realFileName;
+ rv = dbPath->GetLeafName(realFileName);
+
+ if (NS_SUCCEEDED(rv))
+ *fileName = ToNewUTF8String(realFileName);
+ }
+ }
+}
+
+/****************************************************************
+Helper function used to generate a file name from the description
+of a directory. Caller must free returned string.
+An extension is not applied
+*****************************************************************/
+
+static char * dir_ConvertDescriptionToPrefName(DIR_Server * server)
+{
+#define MAX_PREF_NAME_SIZE 25
+ char * fileName = nullptr;
+ char fileNameBuf[MAX_PREF_NAME_SIZE];
+ int32_t srcIndex = 0;
+ int32_t destIndex = 0;
+ int32_t numSrcBytes = 0;
+ const char * descr = nullptr;
+ if (server && server->description)
+ {
+ descr = server->description;
+ numSrcBytes = PL_strlen(descr);
+ while (srcIndex < numSrcBytes && destIndex < MAX_PREF_NAME_SIZE-1)
+ {
+ if (IS_DIGIT(descr[srcIndex]) || IS_ALPHA(descr[srcIndex]))
+ {
+ fileNameBuf[destIndex] = descr[srcIndex];
+ destIndex++;
+ }
+
+ srcIndex++;
+ }
+
+ fileNameBuf[destIndex] = '\0'; /* zero out the last character */
+ }
+
+ if (destIndex) /* have at least one character in the file name? */
+ fileName = strdup(fileNameBuf);
+
+ return fileName;
+}
+
+
+void DIR_SetServerFileName(DIR_Server *server)
+{
+ char * tempName = nullptr;
+ const char * prefName = nullptr;
+ uint32_t numHeaderBytes = 0;
+
+ if (server && (!server->fileName || !(*server->fileName)) )
+ {
+ PR_FREEIF(server->fileName); // might be one byte empty string.
+ /* make sure we have a pref name...*/
+ if (!server->prefName || !*server->prefName)
+ server->prefName = dir_CreateServerPrefName(server);
+
+ /* set default personal address book file name*/
+ if ((server->position == 1) && (server->dirType == PABDirectory))
+ server->fileName = strdup(kPersonalAddressbook);
+ else
+ {
+ /* now use the pref name as the file name since we know the pref name
+ will be unique */
+ prefName = server->prefName;
+ if (prefName && *prefName)
+ {
+ /* extract just the pref name part and not the ldap tree name portion from the string */
+ numHeaderBytes = PL_strlen(PREF_LDAP_SERVER_TREE_NAME) + 1; /* + 1 for the '.' b4 the name */
+ if (PL_strlen(prefName) > numHeaderBytes)
+ tempName = strdup(prefName + numHeaderBytes);
+
+ if (tempName)
+ {
+ server->fileName = PR_smprintf("%s%s", tempName, kABFileName_CurrentSuffix);
+ PR_Free(tempName);
+ }
+ }
+ }
+
+ if (!server->fileName || !*server->fileName) /* when all else has failed, generate a default name */
+ {
+ if (server->dirType == LDAPDirectory)
+ DIR_SetFileName(&(server->fileName), kMainLdapAddressBook); /* generates file name with an ldap prefix */
+ else
+ DIR_SetFileName(&(server->fileName), kPersonalAddressbook);
+ }
+ }
+}
+
+static char *dir_CreateServerPrefName (DIR_Server *server)
+{
+ /* we are going to try to be smart in how we generate our server
+ pref name. We'll try to convert the description into a pref name
+ and then verify that it is unique. If it is unique then use it... */
+ char * leafName = dir_ConvertDescriptionToPrefName(server);
+ char * prefName = nullptr;
+ bool isUnique = false;
+
+ if (!leafName || !*leafName)
+ {
+ // we need to handle this in case the description has no alphanumeric chars
+ // it's very common for cjk users
+ leafName = strdup("_nonascii");
+ }
+
+ if (leafName)
+ {
+ int32_t uniqueIDCnt = 0;
+ char **children = nullptr;
+ /* we need to verify that this pref string name is unique */
+ prefName = PR_smprintf(PREF_LDAP_SERVER_TREE_NAME".%s", leafName);
+ isUnique = false;
+ uint32_t prefCount;
+ nsresult rv = dir_GetChildList(NS_LITERAL_CSTRING(PREF_LDAP_SERVER_TREE_NAME "."),
+ &prefCount, &children);
+ if (NS_SUCCEEDED(rv))
+ {
+ while (!isUnique && prefName)
+ {
+ isUnique = true; /* now flip the logic and assume we are unique until we find a match */
+ for (uint32_t i = 0; i < prefCount && isUnique; ++i)
+ {
+ if (!PL_strcasecmp(children[i], prefName)) /* are they the same branch? */
+ isUnique = false;
+ }
+ if (!isUnique) /* then try generating a new pref name and try again */
+ {
+ PR_smprintf_free(prefName);
+ prefName = PR_smprintf(PREF_LDAP_SERVER_TREE_NAME".%s_%d", leafName, ++uniqueIDCnt);
+ }
+ } /* if we have a list of pref Names */
+
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, children);
+ } /* while we don't have a unique name */
+
+ // fallback to "user_directory_N" form if we failed to verify
+ if (!isUnique && prefName)
+ {
+ PR_smprintf_free(prefName);
+ prefName = nullptr;
+ }
+
+ PR_Free(leafName);
+
+ } /* if leafName */
+
+ if (!prefName) /* last resort if we still don't have a pref name is to use user_directory string */
+ return PR_smprintf(PREF_LDAP_SERVER_TREE_NAME".user_directory_%d", ++dir_UserId);
+ else
+ return prefName;
+}
+
+static void DIR_GetPrefsForOneServer(DIR_Server *server)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+
+ char *prefstring = server->prefName;
+
+ // this call fills in tempstring with the position pref, and
+ // we then check to see if it's locked.
+ server->position = DIR_GetIntPref (prefstring, "position", kDefaultPosition);
+
+ // For default address books, this will get the name from the chrome
+ // file referenced, for other address books it'll just retrieve it from prefs
+ // as normal.
+ server->description = DIR_GetLocalizedStringPref(prefstring, "description");
+
+ server->dirType = (DirectoryType)DIR_GetIntPref (prefstring, "dirType", LDAPDirectory);
+
+ server->fileName = DIR_GetStringPref (prefstring, "filename", "");
+ // if we don't have a file name try and get one
+ if (!server->fileName || !*(server->fileName))
+ DIR_SetServerFileName (server);
+ if (server->fileName && *server->fileName)
+ DIR_ConvertServerFileName(server);
+
+ // the string "s" is the default uri ( <scheme> + "://" + <filename> )
+ nsCString s((server->dirType == PABDirectory || server->dirType == MAPIDirectory) ?
+#if defined(MOZ_LDAP_XPCOM)
+ kMDBDirectoryRoot : kLDAPDirectoryRoot);
+#else
+ // Fallback to the all directory root in the non-ldap enabled case.
+ kMDBDirectoryRoot : kAllDirectoryRoot);
+#endif
+ s.Append (server->fileName);
+ server->uri = DIR_GetStringPref (prefstring, "uri", s.get ());
+}
+
+static nsresult dir_GetPrefs(nsTArray<DIR_Server*> **list)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ (*list) = new nsTArray<DIR_Server*>();
+ if (!(*list))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ char **children;
+ uint32_t prefCount;
+
+ rv = dir_GetChildList(NS_LITERAL_CSTRING(PREF_LDAP_SERVER_TREE_NAME "."),
+ &prefCount, &children);
+ if (NS_FAILED(rv))
+ return rv;
+
+ /* TBD: Temporary code to read broken "ldap" preferences tree.
+ * Remove line with if statement after M10.
+ */
+ if (dir_UserId == 0)
+ pPref->GetIntPref(PREF_LDAP_GLOBAL_TREE_NAME".user_id", &dir_UserId);
+
+ for (uint32_t i = 0; i < prefCount; ++i)
+ {
+ DIR_Server *server;
+
+ server = (DIR_Server *)PR_Calloc(1, sizeof(DIR_Server));
+ if (server)
+ {
+ DIR_InitServer(server);
+ server->prefName = strdup(children[i]);
+ DIR_GetPrefsForOneServer(server);
+ if (server->description && server->description[0] &&
+ ((server->dirType == PABDirectory ||
+ server->dirType == MAPIDirectory ||
+ server->dirType == FixedQueryLDAPDirectory || // this one might go away
+ server->dirType == LDAPDirectory)))
+ {
+ if (!dir_IsServerDeleted(server))
+ {
+ (*list)->AppendElement(server);
+ }
+ else
+ DIR_DeleteServer(server);
+ }
+ else
+ {
+ DIR_DeleteServer(server);
+ }
+ }
+ }
+
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, children);
+
+ return NS_OK;
+}
+
+// I don't think we care about locked positions, etc.
+void DIR_SortServersByPosition(nsTArray<DIR_Server*> *serverList)
+{
+ int i, j;
+ DIR_Server *server;
+
+ int count = serverList->Length();
+ for (i = 0; i < count - 1; i++)
+ {
+ for (j = i + 1; j < count; j++)
+ {
+ if (serverList->ElementAt(j)->position < serverList->ElementAt(i)->position)
+ {
+ server = serverList->ElementAt(i);
+ serverList->ReplaceElementAt(i, serverList->ElementAt(j));
+ serverList->ReplaceElementAt(j, server);
+ }
+ }
+ }
+}
+
+static nsresult DIR_GetServerPreferences(nsTArray<DIR_Server*>** list)
+{
+ nsresult err;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &err));
+ if (NS_FAILED(err))
+ return err;
+
+ int32_t version = -1;
+ nsTArray<DIR_Server*> *newList = nullptr;
+
+ /* Update the ldap list version and see if there are old prefs to migrate. */
+ err = pPref->GetIntPref(PREF_LDAP_VERSION_NAME, &version);
+ NS_ENSURE_SUCCESS(err, err);
+
+ /* Find the new-style "ldap_2.servers" tree in prefs */
+ err = dir_GetPrefs(&newList);
+
+ if (version < kCurrentListVersion)
+ {
+ pPref->SetIntPref(PREF_LDAP_VERSION_NAME, kCurrentListVersion);
+ }
+
+ DIR_SortServersByPosition(newList);
+
+ *list = newList;
+
+ return err;
+}
+
+static void DIR_SetStringPref(const char *prefRoot, const char *prefLeaf, const char *value, const char *defaultValue)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+
+ nsCString defaultPref;
+ nsAutoCString prefLocation(prefRoot);
+
+ prefLocation.Append('.');
+ prefLocation.Append(prefLeaf);
+
+ if (NS_SUCCEEDED(pPref->GetCharPref(prefLocation.get(), getter_Copies(defaultPref))))
+ {
+ /* If there's a default pref, just set ours in and let libpref worry
+ * about potential defaults in all.js
+ */
+ if (value) /* added this check to make sure we have a value before we try to set it..*/
+ rv = pPref->SetCharPref (prefLocation.get(), value);
+ else
+ rv = pPref->ClearUserPref(prefLocation.get());
+ }
+ else
+ {
+ /* If there's no default pref, look for a user pref, and only set our value in
+ * if the user pref is different than one of them.
+ */
+ nsCString userPref;
+ if (NS_SUCCEEDED(pPref->GetCharPref (prefLocation.get(), getter_Copies(userPref))))
+ {
+ if (value && (defaultValue ? PL_strcasecmp(value, defaultValue) : value != defaultValue))
+ rv = pPref->SetCharPref (prefLocation.get(), value);
+ else
+ rv = pPref->ClearUserPref(prefLocation.get());
+ }
+ else
+ {
+ if (value && (defaultValue ? PL_strcasecmp(value, defaultValue) : value != defaultValue))
+ rv = pPref->SetCharPref (prefLocation.get(), value);
+ }
+ }
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Could not set pref in DIR_SetStringPref");
+}
+
+static void DIR_SetLocalizedStringPref
+(const char *prefRoot, const char *prefLeaf, const char *value)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefService> prefSvc(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+
+ if (NS_FAILED(rv))
+ return;
+
+ nsAutoCString prefLocation(prefRoot);
+ prefLocation.Append('.');
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ rv = prefSvc->GetBranch(prefLocation.get(), getter_AddRefs(prefBranch));
+ if (NS_FAILED(rv))
+ return;
+
+ nsString wvalue;
+ nsCOMPtr<nsIPrefLocalizedString> newStr(
+ do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ {
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Could not createInstance in DIR_SetLocalizedStringPref");
+ return;
+ }
+
+ NS_ConvertUTF8toUTF16 newValue(value);
+
+ rv = newStr->SetData(newValue.get());
+ if (NS_FAILED(rv))
+ {
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Could not set pref data in DIR_SetLocalizedStringPref");
+ return;
+ }
+ nsCOMPtr<nsIPrefLocalizedString> locStr;
+ if (NS_SUCCEEDED(prefBranch->GetComplexValue(prefLeaf,
+ NS_GET_IID(nsIPrefLocalizedString),
+ getter_AddRefs(locStr))))
+ {
+ nsString data;
+ locStr->GetData(getter_Copies(data));
+
+ // Only set the pref if the data values aren't the same (i.e. don't change
+ // unnecessarily, but also, don't change in the case that its a chrome
+ // string pointing to the value we want to set the pref to).
+ if (newValue != data)
+ rv = prefBranch->SetComplexValue(prefLeaf,
+ NS_GET_IID(nsIPrefLocalizedString),
+ newStr);
+ }
+ else {
+ // No value set, but check the default pref branch (i.e. user may have
+ // cleared the pref)
+ nsCOMPtr<nsIPrefBranch> dPB;
+ rv = prefSvc->GetDefaultBranch(prefLocation.get(),
+ getter_AddRefs(dPB));
+
+ if (NS_SUCCEEDED(dPB->GetComplexValue(prefLeaf,
+ NS_GET_IID(nsIPrefLocalizedString),
+ getter_AddRefs(locStr))))
+ {
+ // Default branch has a value
+ nsString data;
+ locStr->GetData(getter_Copies(data));
+
+ if (newValue != data)
+ // If the vales aren't the same, set the data on the main pref branch
+ rv = prefBranch->SetComplexValue(prefLeaf,
+ NS_GET_IID(nsIPrefLocalizedString),
+ newStr);
+ else
+ // Else if they are, kill the user pref
+ rv = prefBranch->ClearUserPref(prefLeaf);
+ }
+ else
+ // No values set anywhere, so just set the pref
+ rv = prefBranch->SetComplexValue(prefLeaf,
+ NS_GET_IID(nsIPrefLocalizedString),
+ newStr);
+ }
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Could not set pref in DIR_SetLocalizedStringPref");
+}
+
+
+static void DIR_SetIntPref(const char *prefRoot, const char *prefLeaf, int32_t value, int32_t defaultValue)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+
+ int32_t defaultPref;
+ nsAutoCString prefLocation(prefRoot);
+
+ prefLocation.Append('.');
+ prefLocation.Append(prefLeaf);
+
+ if (NS_SUCCEEDED(pPref->GetIntPref(prefLocation.get(), &defaultPref)))
+ {
+ /* solve the problem where reordering user prefs must override default prefs */
+ rv = pPref->SetIntPref(prefLocation.get(), value);
+ }
+ else
+ {
+ int32_t userPref;
+ if (NS_SUCCEEDED(pPref->GetIntPref(prefLocation.get(), &userPref)))
+ {
+ if (value != defaultValue)
+ rv = pPref->SetIntPref(prefLocation.get(), value);
+ else
+ rv = pPref->ClearUserPref(prefLocation.get());
+ }
+ else
+ {
+ if (value != defaultValue)
+ rv = pPref->SetIntPref(prefLocation.get(), value);
+ }
+ }
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Could not set pref in DIR_SetIntPref");
+}
+
+void DIR_SavePrefsForOneServer(DIR_Server *server)
+{
+ if (!server)
+ return;
+
+ char *prefstring;
+
+ if (server->prefName == nullptr)
+ server->prefName = dir_CreateServerPrefName(server);
+ prefstring = server->prefName;
+
+ server->savingServer = true;
+
+ DIR_SetIntPref (prefstring, "position", server->position, kDefaultPosition);
+
+ // Only save the non-default address book name
+ DIR_SetLocalizedStringPref(prefstring, "description", server->description);
+
+ DIR_SetStringPref(prefstring, "filename", server->fileName, "");
+ DIR_SetIntPref(prefstring, "dirType", server->dirType, LDAPDirectory);
+
+ if (server->dirType != PABDirectory)
+ DIR_SetStringPref(prefstring, "uri", server->uri, "");
+
+ server->savingServer = false;
+}
+
+static void DIR_SaveServerPreferences(nsTArray<DIR_Server*> *wholeList)
+{
+ if (wholeList)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("DIR_SaveServerPreferences: Failed to get the pref service\n");
+ return;
+ }
+
+ int32_t i;
+ int32_t count = wholeList->Length();
+ DIR_Server *server;
+
+ for (i = 0; i < count; i++)
+ {
+ server = wholeList->ElementAt(i);
+ if (server)
+ DIR_SavePrefsForOneServer(server);
+ }
+ pPref->SetIntPref(PREF_LDAP_GLOBAL_TREE_NAME".user_id", dir_UserId);
+ }
+}
diff --git a/mailnews/addrbook/src/nsDirPrefs.h b/mailnews/addrbook/src/nsDirPrefs.h
new file mode 100644
index 000000000..d27a4fcb5
--- /dev/null
+++ b/mailnews/addrbook/src/nsDirPrefs.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _NSDIRPREFS_H_
+#define _NSDIRPREFS_H_
+
+#include "nsTArray.h"
+
+//
+// XXX nsDirPrefs is being greatly reduced if not removed altogether. Directory
+// Prefs etc. should be handled via their appropriate nsAb*Directory classes.
+//
+
+#define kPreviousListVersion 2
+#define kCurrentListVersion 3
+#define PREF_LDAP_GLOBAL_TREE_NAME "ldap_2"
+#define PREF_LDAP_VERSION_NAME "ldap_2.version"
+#define PREF_LDAP_SERVER_TREE_NAME "ldap_2.servers"
+
+#define kMainLdapAddressBook "ldap.mab" /* v3 main ldap address book file */
+
+/* DIR_Server.dirType */
+typedef enum
+{
+ LDAPDirectory,
+ HTMLDirectory,
+ PABDirectory,
+ MAPIDirectory,
+ FixedQueryLDAPDirectory = 777
+} DirectoryType;
+
+typedef enum
+{
+ idNone = 0, /* Special value */
+ idPrefName,
+ idPosition,
+ idDescription,
+ idFileName,
+ idUri,
+ idType
+} DIR_PrefId;
+
+#define DIR_Server_typedef 1 /* this quiets a redeclare warning in libaddr */
+
+typedef struct DIR_Server
+{
+ /* Housekeeping fields */
+ char *prefName; /* preference name, this server's subtree */
+ int32_t position; /* relative position in server list */
+
+ /* General purpose fields */
+ char *description; /* human readable name */
+ char *fileName; /* XP path name of local DB */
+ DirectoryType dirType;
+ char *uri; // URI of the address book
+
+ // Set whilst saving the server to avoid updating it again
+ bool savingServer;
+} DIR_Server;
+
+/* We are developing a new model for managing DIR_Servers. In the 4.0x world, the FEs managed each list.
+ Calls to FE_GetDirServer caused the FEs to manage and return the DIR_Server list. In our new view of the
+ world, the back end does most of the list management so we are going to have the back end create and
+ manage the list. Replace calls to FE_GetDirServers() with DIR_GetDirServers(). */
+
+nsTArray<DIR_Server*>* DIR_GetDirectories();
+DIR_Server* DIR_GetServerFromList(const char* prefName);
+nsresult DIR_ShutDown(void); /* FEs should call this when the app is shutting down. It frees all DIR_Servers regardless of ref count values! */
+
+nsresult DIR_AddNewAddressBook(const nsAString &dirName,
+ const nsACString &fileName,
+ const nsACString &uri,
+ DirectoryType dirType,
+ const nsACString &prefName,
+ DIR_Server** pServer);
+nsresult DIR_ContainsServer(DIR_Server* pServer, bool *hasDir);
+
+nsresult DIR_DeleteServerFromList (DIR_Server *);
+
+void DIR_SavePrefsForOneServer(DIR_Server *server);
+
+void DIR_SetServerFileName(DIR_Server* pServer);
+
+#endif /* dirprefs.h */
diff --git a/mailnews/addrbook/src/nsMapiAddressBook.cpp b/mailnews/addrbook/src/nsMapiAddressBook.cpp
new file mode 100644
index 000000000..80d737fb2
--- /dev/null
+++ b/mailnews/addrbook/src/nsMapiAddressBook.cpp
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsMapiAddressBook.h"
+
+#include "mozilla/Logging.h"
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo* gMapiAddressBookLog
+ = PR_NewLogModule("nsMapiAddressBookLog");
+#endif
+
+#define PRINTF(args) MOZ_LOG(gMapiAddressBookLog, mozilla::LogLevel::Debug, args)
+
+using namespace mozilla;
+
+HMODULE nsMapiAddressBook::mLibrary = NULL ;
+int32_t nsMapiAddressBook::mLibUsage = 0 ;
+LPMAPIINITIALIZE nsMapiAddressBook::mMAPIInitialize = NULL ;
+LPMAPIUNINITIALIZE nsMapiAddressBook::mMAPIUninitialize = NULL ;
+LPMAPIALLOCATEBUFFER nsMapiAddressBook::mMAPIAllocateBuffer = NULL ;
+LPMAPIFREEBUFFER nsMapiAddressBook::mMAPIFreeBuffer = NULL ;
+LPMAPILOGONEX nsMapiAddressBook::mMAPILogonEx = NULL ;
+
+BOOL nsMapiAddressBook::mInitialized = FALSE ;
+BOOL nsMapiAddressBook::mLogonDone = FALSE ;
+LPMAPISESSION nsMapiAddressBook::mRootSession = NULL ;
+LPADRBOOK nsMapiAddressBook::mRootBook = NULL ;
+
+BOOL nsMapiAddressBook::LoadMapiLibrary(void)
+{
+ if (mLibrary) { ++ mLibUsage ; return TRUE ; }
+ HMODULE libraryHandle = LoadLibrary("MAPI32.DLL") ;
+
+ if (!libraryHandle) { return FALSE ; }
+ FARPROC entryPoint = GetProcAddress(libraryHandle, "MAPIGetNetscapeVersion") ;
+
+ if (entryPoint) {
+ FreeLibrary(libraryHandle) ;
+ libraryHandle = LoadLibrary("MAPI32BAK.DLL") ;
+ if (!libraryHandle) { return FALSE ; }
+ }
+ mLibrary = libraryHandle ;
+ ++ mLibUsage ;
+ mMAPIInitialize = reinterpret_cast<LPMAPIINITIALIZE>(GetProcAddress(mLibrary, "MAPIInitialize")) ;
+ if (!mMAPIInitialize) { return FALSE ; }
+ mMAPIUninitialize = reinterpret_cast<LPMAPIUNINITIALIZE>(GetProcAddress(mLibrary, "MAPIUninitialize")) ;
+ if (!mMAPIUninitialize) { return FALSE ; }
+ mMAPIAllocateBuffer = reinterpret_cast<LPMAPIALLOCATEBUFFER>(GetProcAddress(mLibrary, "MAPIAllocateBuffer")) ;
+ if (!mMAPIAllocateBuffer) { return FALSE ; }
+ mMAPIFreeBuffer = reinterpret_cast<LPMAPIFREEBUFFER>(GetProcAddress(mLibrary, "MAPIFreeBuffer")) ;
+ if (!mMAPIFreeBuffer) { return FALSE ; }
+ mMAPILogonEx = reinterpret_cast<LPMAPILOGONEX>(GetProcAddress(mLibrary, "MAPILogonEx")) ;
+ if (!mMAPILogonEx) { return FALSE ; }
+ MAPIINIT_0 mapiInit = { MAPI_INIT_VERSION, MAPI_MULTITHREAD_NOTIFICATIONS } ;
+ HRESULT retCode = mMAPIInitialize(&mapiInit) ;
+
+ if (HR_FAILED(retCode)) {
+ PRINTF(("Cannot initialize MAPI %08x.\n", retCode)) ; return FALSE ;
+ }
+ mInitialized = TRUE ;
+ retCode = mMAPILogonEx(0, NULL, NULL,
+ MAPI_NO_MAIL |
+ MAPI_USE_DEFAULT |
+ MAPI_EXTENDED |
+ MAPI_NEW_SESSION,
+ &mRootSession) ;
+ if (HR_FAILED(retCode)) {
+ PRINTF(("Cannot logon to MAPI %08x.\n", retCode)) ; return FALSE ;
+ }
+ mLogonDone = TRUE ;
+ retCode = mRootSession->OpenAddressBook(0, NULL, 0, &mRootBook) ;
+ if (HR_FAILED(retCode)) {
+ PRINTF(("Cannot open MAPI address book %08x.\n", retCode)) ;
+ }
+ return HR_SUCCEEDED(retCode) ;
+}
+
+void nsMapiAddressBook::FreeMapiLibrary(void)
+{
+ if (mLibrary) {
+ if (-- mLibUsage == 0) {
+ {
+ if (mRootBook) { mRootBook->Release() ; }
+ if (mRootSession) {
+ if (mLogonDone) {
+ mRootSession->Logoff(NULL, 0, 0) ;
+ mLogonDone = FALSE ;
+ }
+ mRootSession->Release() ;
+ }
+ if (mInitialized) {
+ mMAPIUninitialize() ;
+ mInitialized = FALSE ;
+ }
+ }
+ FreeLibrary(mLibrary) ;
+ mLibrary = NULL ;
+ }
+ }
+}
+
+nsMapiAddressBook::nsMapiAddressBook(void)
+: nsAbWinHelper()
+{
+ BOOL result = Initialize() ;
+
+ NS_ASSERTION(result == TRUE, "Couldn't initialize Mapi Helper") ;
+ MOZ_COUNT_CTOR(nsMapiAddressBook) ;
+}
+
+nsMapiAddressBook::~nsMapiAddressBook(void)
+{
+ MutexAutoLock guard(*mMutex) ;
+
+ FreeMapiLibrary() ;
+ MOZ_COUNT_DTOR(nsMapiAddressBook) ;
+}
+
+BOOL nsMapiAddressBook::Initialize(void)
+{
+ if (mAddressBook) { return TRUE ; }
+ MutexAutoLock guard(*mMutex) ;
+
+ if (!LoadMapiLibrary()) {
+ PRINTF(("Cannot load library.\n")) ;
+ return FALSE ;
+ }
+ mAddressBook = mRootBook ;
+ return TRUE ;
+}
+
+void nsMapiAddressBook::AllocateBuffer(ULONG aByteCount, LPVOID *aBuffer)
+{
+ mMAPIAllocateBuffer(aByteCount, aBuffer) ;
+}
+
+void nsMapiAddressBook::FreeBuffer(LPVOID aBuffer)
+{
+ mMAPIFreeBuffer(aBuffer) ;
+}
+
+
+
+
+
diff --git a/mailnews/addrbook/src/nsMapiAddressBook.h b/mailnews/addrbook/src/nsMapiAddressBook.h
new file mode 100644
index 000000000..ed19b01be
--- /dev/null
+++ b/mailnews/addrbook/src/nsMapiAddressBook.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsMapiAddressBook_h___
+#define nsMapiAddressBook_h___
+
+#include "mozilla/Attributes.h"
+#include "nsAbWinHelper.h"
+
+class nsMapiAddressBook : public nsAbWinHelper
+{
+public :
+ nsMapiAddressBook(void) ;
+ virtual ~nsMapiAddressBook(void) ;
+
+protected :
+ // Class members to handle the library/entry points
+ static HMODULE mLibrary ;
+ static int32_t mLibUsage ;
+ static LPMAPIINITIALIZE mMAPIInitialize ;
+ static LPMAPIUNINITIALIZE mMAPIUninitialize ;
+ static LPMAPIALLOCATEBUFFER mMAPIAllocateBuffer ;
+ static LPMAPIFREEBUFFER mMAPIFreeBuffer ;
+ static LPMAPILOGONEX mMAPILogonEx ;
+ // Shared session and address book used by all instances.
+ // For reasons best left unknown, MAPI doesn't seem to like
+ // having different threads playing with supposedly different
+ // sessions and address books. They ll end up fighting over
+ // the same resources, with hangups and GPF resulting. Not nice.
+ // So it seems that if everybody (as long as some client is
+ // still alive) is using the same sessions and address books,
+ // MAPI feels better. And who are we to get in the way of MAPI
+ // happiness? Thus the following class members:
+ static BOOL mInitialized ;
+ static BOOL mLogonDone ;
+ static LPMAPISESSION mRootSession ;
+ static LPADRBOOK mRootBook ;
+
+ // Load the MAPI environment
+ BOOL Initialize(void) ;
+ // Allocation of a buffer for transmission to interfaces
+ virtual void AllocateBuffer(ULONG aByteCount, LPVOID *aBuffer) override;
+ // Destruction of a buffer provided by the interfaces
+ virtual void FreeBuffer(LPVOID aBuffer) override;
+ // Library management
+ static BOOL LoadMapiLibrary(void) ;
+ static void FreeMapiLibrary(void) ;
+
+private :
+} ;
+
+#endif // nsMapiAddressBook_h___
+
diff --git a/mailnews/addrbook/src/nsMsgVCardService.cpp b/mailnews/addrbook/src/nsMsgVCardService.cpp
new file mode 100644
index 000000000..91f6f522e
--- /dev/null
+++ b/mailnews/addrbook/src/nsMsgVCardService.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgVCardService.h"
+#include "nsVCard.h"
+#include "prmem.h"
+#include "plstr.h"
+
+NS_IMPL_ISUPPORTS(nsMsgVCardService, nsIMsgVCardService)
+
+nsMsgVCardService::nsMsgVCardService()
+{
+}
+
+nsMsgVCardService::~nsMsgVCardService()
+{
+}
+
+NS_IMETHODIMP_(void) nsMsgVCardService::CleanVObject(VObject * o)
+{
+ cleanVObject(o);
+}
+
+NS_IMETHODIMP_(VObject *) nsMsgVCardService::NextVObjectInList(VObject * o)
+{
+ return nextVObjectInList(o);
+}
+
+NS_IMETHODIMP_(VObject *) nsMsgVCardService::Parse_MIME(const char *input, uint32_t len)
+{
+ return parse_MIME(input, (unsigned long)len);
+}
+
+NS_IMETHODIMP_(char *) nsMsgVCardService::FakeCString(VObject * o)
+{
+ return fakeCString(vObjectUStringZValue(o));
+}
+
+NS_IMETHODIMP_(VObject *) nsMsgVCardService::IsAPropertyOf(VObject * o, const char *id)
+{
+ return isAPropertyOf(o,id);
+}
+
+NS_IMETHODIMP_(char *) nsMsgVCardService::WriteMemoryVObjects(const char *s, int32_t *len, VObject * list, bool expandSpaces)
+{
+ return writeMemoryVObjects((char *)s, len, list, expandSpaces);
+}
+
+NS_IMETHODIMP_(VObject *) nsMsgVCardService::NextVObject(VObjectIterator * i)
+{
+ return nextVObject(i);
+}
+
+NS_IMETHODIMP_(void) nsMsgVCardService::InitPropIterator(VObjectIterator * i, VObject * o)
+{
+ initPropIterator(i,o);
+}
+
+NS_IMETHODIMP_(int32_t) nsMsgVCardService::MoreIteration(VObjectIterator * i)
+{
+ return ((int32_t)moreIteration(i));
+}
+
+NS_IMETHODIMP_(const char *) nsMsgVCardService::VObjectName(VObject * o)
+{
+ return vObjectName(o);
+}
+
+NS_IMETHODIMP_(char *) nsMsgVCardService::VObjectAnyValue(VObject * o)
+{
+ char *retval = (char *)PR_MALLOC(strlen((char *)vObjectAnyValue(o)) + 1);
+ if (retval)
+ PL_strcpy(retval, (char *) vObjectAnyValue(o));
+ return retval;
+}
diff --git a/mailnews/addrbook/src/nsMsgVCardService.h b/mailnews/addrbook/src/nsMsgVCardService.h
new file mode 100644
index 000000000..7e7da8766
--- /dev/null
+++ b/mailnews/addrbook/src/nsMsgVCardService.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMsgVCardService_h___
+#define nsMsgVCardService_h___
+
+#include "nsIMsgVCardService.h"
+#include "nsISupports.h"
+
+class nsMsgVCardService : public nsIMsgVCardService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGVCARDSERVICE
+
+ nsMsgVCardService();
+
+private:
+ virtual ~nsMsgVCardService();
+};
+
+#endif /* nsMsgVCardService_h___ */
diff --git a/mailnews/addrbook/src/nsVCard.cpp b/mailnews/addrbook/src/nsVCard.cpp
new file mode 100644
index 000000000..b8c2455b2
--- /dev/null
+++ b/mailnews/addrbook/src/nsVCard.cpp
@@ -0,0 +1,1571 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+ * src: vcc.c
+ * doc: Parser for vCard and vCalendar. Note that this code is
+ * generated by a yacc parser generator. Generally it should not
+ * be edited by hand. The real source is vcc.y. The #line directives
+ * can be commented out here to make it easier to trace through
+ * in a debugger. However, if a bug is found it should
+ *
+ * the vcc.y that _this_ vcc.c comes from is lost.
+ * I couldn't find it in the 4.x tree
+ * I bet we took it from IMC's original SDK, but the SDK has been taken down.
+ * see http://www.imc.org/imc-vcard/mail-archive/msg00460.html
+ *
+ * for what it's worth, see
+ * http://softwarestudio.org/libical/
+ * http://lxr.mozilla.org/mozilla/source/other-licenses/libical/src/libicalvcal/vcc.y
+ * http://lxr.mozilla.org/mozilla/source/other-licenses/libical/src/libicalvcal/vcc.c
+ */
+#include "nsVCard.h"
+#include "nsVCardObj.h"
+#include "prprf.h"
+#include "nscore.h"
+#include <string.h>
+#include <ctype.h>
+
+#ifndef lint
+char yysccsid[] = "@(#)yaccpar 1.4 (Berkeley) 02/25/90";
+#endif
+/*#line 2 "vcc.y" */
+
+/* debugging utilities */
+#define DBG_(x)
+
+#ifndef _NO_LINE_FOLDING
+#define _SUPPORT_LINE_FOLDING
+#endif
+
+/**** External Functions ****/
+
+/* assign local name to parser variables and functions so that
+ we can use more than one yacc based parser.
+*/
+
+#define yyparse mime_parse
+#define yylex mime_lex
+#define yyerror mime_error
+#define yychar mime_char
+/* #define p_yyval p_mime_val */
+#undef yyval
+#define yyval mime_yyval
+/* #define p_yylval p_mime_lval */
+#undef yylval
+#define yylval mime_yylval
+#define yydebug mime_debug
+#define yynerrs mime_nerrs
+#define yyerrflag mime_errflag
+#define yyss mime_ss
+#define yyssp mime_ssp
+#define yyvs mime_vs
+#define yyvsp mime_vsp
+#define yylhs mime_lhs
+#define yylen mime_len
+#define yydefred mime_defred
+#define yydgoto mime_dgoto
+#define yysindex mime_sindex
+#define yyrindex mime_rindex
+#define yygindex mime_gindex
+#define yytable mime_table
+#define yycheck mime_check
+#define yyname mime_name
+#define yyrule mime_rule
+#define YYPREFIX "mime_"
+
+#include "prmem.h"
+#include "plstr.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/**** Types, Constants ****/
+
+#define YYDEBUG 0 /* 1 to compile in some debugging code */
+#define PR_MAXTOKEN 256 /* maximum token (line) length */
+#define YYSTACKSIZE 50 /* ~unref ?*/
+#define PR_MAXLEVEL 10 /* max # of nested objects parseable */
+ /* (includes outermost) */
+
+
+/**** Global Variables ****/
+int mime_lineNum, mime_numErrors; /* yyerror() can use these */
+static VObject* vObjList;
+static VObject *curProp;
+static VObject *curObj;
+static VObject* ObjStack[PR_MAXLEVEL];
+static int ObjStackTop;
+
+
+/* A helpful utility for the rest of the app. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern void yyerror(const char *s);
+ extern char** fieldedProp;
+
+#ifdef __cplusplus
+ }
+#endif
+
+int yyparse();
+
+enum LexMode {
+ L_NORMAL,
+ L_VCARD,
+ L_VCAL,
+ L_VEVENT,
+ L_VTODO,
+ L_VALUES,
+ L_BASE64,
+ L_QUOTED_PRINTABLE
+ };
+
+/**** Private Forward Declarations ****/
+static int pushVObject(const char *prop);
+static VObject* popVObject();
+static int lexGeta();
+static int lexGetc_();
+static int lexGetc();
+static void lexSkipLookahead();
+static int lexLookahead();
+static void lexSkipWhite();
+static void lexClearToken();
+static char * lexStr();
+static char * lexGetDataFromBase64();
+static char * lexGetQuotedPrintable();
+static char * lexGet1Value();
+static char * lexGetWord();
+static void finiLex();
+
+static VObject* parse_MIMEHelper();
+
+/*static char* lexDataFromBase64();*/
+static void lexPopMode(int top);
+static int lexWithinMode(enum LexMode mode);
+static void lexPushMode(enum LexMode mode);
+static void enterProps(const char *s);
+static void enterAttr(const char *s1, const char *s2);
+static void enterValues(const char *value);
+
+/*#line 250 "vcc.y" */
+typedef union {
+ char *str;
+ VObject *vobj;
+ } YYSTYPE;
+/*#line 253 "y_tab.c"*/
+#define EQ 257
+#define COLON 258
+#define DOT 259
+#define SEMICOLON 260
+#define SPACE 261
+#define HTAB 262
+#define LINESEP 263
+#define NEWLINE 264
+#define BEGIN_VCARD 265
+#define END_VCARD 266
+#define BEGIN_VCAL 267
+#define END_VCAL 268
+#define BEGIN_VEVENT 269
+#define END_VEVENT 270
+#define BEGIN_VTODO 271
+#define END_VTODO 272
+#define ID 273
+#define STRING 274
+#define YYERRCODE 256
+short yylhs[] = { -1,
+ 0, 7, 6, 6, 5, 5, 9, 3, 10, 3,
+ 8, 8, 14, 11, 11, 16, 12, 12, 15, 15,
+ 17, 18, 18, 1, 19, 13, 13, 2, 2, 21,
+ 4, 22, 4, 20, 20, 23, 23, 23, 26, 24,
+ 27, 24, 28, 25, 29, 25,
+};
+short yylen[] = { 2,
+ 1, 0, 3, 1, 1, 1, 0, 4, 0, 3,
+ 2, 1, 0, 5, 1, 0, 3, 1, 2, 1,
+ 2, 1, 3, 1, 0, 4, 1, 1, 0, 0,
+ 4, 0, 3, 2, 1, 1, 1, 1, 0, 4,
+ 0, 3, 0, 4, 0, 3,
+};
+short yydefred[] = { 0,
+ 0, 0, 0, 5, 6, 0, 1, 0, 0, 0,
+ 0, 0, 15, 24, 0, 0, 0, 0, 10, 0,
+ 0, 38, 0, 0, 36, 37, 33, 3, 0, 8,
+ 11, 13, 0, 0, 0, 0, 31, 34, 0, 17,
+ 0, 0, 0, 42, 0, 46, 0, 21, 19, 28,
+ 0, 0, 40, 44, 0, 25, 14, 23, 0, 26,
+};
+short yydgoto[] = { 3,
+ 15, 51, 4, 5, 6, 7, 12, 22, 8, 9,
+ 17, 18, 52, 42, 40, 29, 41, 48, 59, 23,
+ 10, 11, 24, 25, 26, 33, 34, 35, 36,
+};
+short yysindex[] = { -227,
+ 0, 0, 0, 0, 0, 0, 0, -249, -262, -253,
+ -258, -227, 0, 0, 0, -234, -249, -215, 0, 0,
+ 0, 0, -223, -253, 0, 0, 0, 0, -247, 0,
+ 0, 0, -249, -222, -249, -225, 0, 0, -224, 0,
+ -247, -221, -220, 0, -218, 0, -206, 0, 0, 0,
+ -208, -207, 0, 0, -224, 0, 0, 0, -221, 0,
+};
+short yyrindex[] = { 0,
+ -245, -254, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, -219, 0, -235, 0, 0, -244,
+ -250, 0, 0, -213, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -201, -255, 0, 0, 0, 0, -216, 0, 0, 0,
+ -205, 0, 0, 0, 0, 0, 0, 0, -255, 0,
+};
+short yygindex[] = { 0,
+ -9, 0, 0, 0, 0, 47, 0, -8, 0, 0,
+ 0, 0, 2, 0, 19, 0, 0, 0, 0, 38,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#define YYTABLESIZE 268
+short yytable[] = { 16,
+ 4, 30, 13, 19, 29, 43, 13, 29, 31, 27,
+ 7, 39, 39, 32, 30, 20, 30, 21, 30, 14,
+ 9, 45, 43, 14, 43, 41, 45, 7, 39, 47,
+ 12, 30, 12, 12, 12, 12, 12, 1, 18, 2,
+ 16, 22, 32, 22, 37, 58, 46, 44, 14, 53,
+ 55, 56, 50, 54, 35, 57, 20, 27, 28, 49,
+ 60, 38, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 0, 2,
+};
+short yycheck[] = { 8,
+ 0, 256, 256, 266, 260, 256, 256, 263, 17, 268,
+ 256, 256, 260, 268, 269, 269, 271, 271, 273, 273,
+ 266, 272, 273, 273, 33, 270, 35, 273, 273, 39,
+ 266, 266, 268, 269, 270, 271, 272, 265, 258, 267,
+ 260, 258, 258, 260, 268, 55, 272, 270, 273, 270,
+ 257, 260, 274, 272, 268, 263, 258, 263, 12, 41,
+ 59, 24, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 265, -1, 267,
+};
+#define YYFINAL 3
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYPR_MAXTOKEN 274
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"EQ","COLON","DOT","SEMICOLON",
+"SPACE","HTAB","LINESEP","NEWLINE","BEGIN_VCARD","END_VCARD","BEGIN_VCAL",
+"END_VCAL","BEGIN_VEVENT","END_VEVENT","BEGIN_VTODO","END_VTODO","ID","STRING",
+};
+char *yyrule[] = {
+"$accept : mime",
+"mime : vobjects",
+"$$1 :",
+"vobjects : vobject $$1 vobjects",
+"vobjects : vobject",
+"vobject : vcard",
+"vobject : vcal",
+"$$2 :",
+"vcard : BEGIN_VCARD $$2 items END_VCARD",
+"$$3 :",
+"vcard : BEGIN_VCARD $$3 END_VCARD",
+"items : item items",
+"items : item",
+"$$4 :",
+"item : prop COLON $$4 values LINESEP",
+"item : error",
+"$$5 :",
+"prop : name $$5 attr_params",
+"prop : name",
+"attr_params : attr_param attr_params",
+"attr_params : attr_param",
+"attr_param : SEMICOLON attr",
+"attr : name",
+"attr : name EQ name",
+"name : ID",
+"$$6 :",
+"values : value SEMICOLON $$6 values",
+"values : value",
+"value : STRING",
+"value :",
+"$$7 :",
+"vcal : BEGIN_VCAL $$7 calitems END_VCAL",
+"$$8 :",
+"vcal : BEGIN_VCAL $$8 END_VCAL",
+"calitems : calitem calitems",
+"calitems : calitem",
+"calitem : eventitem",
+"calitem : todoitem",
+"calitem : items",
+"$$9 :",
+"eventitem : BEGIN_VEVENT $$9 items END_VEVENT",
+"$$10 :",
+"eventitem : BEGIN_VEVENT $$10 END_VEVENT",
+"$$11 :",
+"todoitem : BEGIN_VTODO $$11 items END_VTODO",
+"$$12 :",
+"todoitem : BEGIN_VTODO $$12 END_VTODO",
+};
+#endif
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#ifndef YYSTACKSIZE
+#ifdef YYPR_MAXDEPTH
+#define YYSTACKSIZE YYPR_MAXDEPTH
+#else
+#define YYSTACKSIZE 300
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+#define yystacksize YYSTACKSIZE
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+/*#line 444 "vcc.y"*/
+/******************************************************************************/
+static int pushVObject(const char *prop)
+ {
+ VObject *newObj;
+ if (ObjStackTop == PR_MAXLEVEL)
+ return FALSE;
+
+ ObjStack[++ObjStackTop] = curObj;
+
+ if (curObj) {
+ newObj = addProp(curObj,prop);
+ curObj = newObj;
+ }
+ else
+ curObj = newVObject(prop);
+
+ return TRUE;
+ }
+
+
+/******************************************************************************/
+/* This pops the recently built vCard off the stack and returns it. */
+static VObject* popVObject()
+ {
+ VObject *oldObj;
+ if (ObjStackTop < 0) {
+ yyerror("pop on empty Object Stack\n");
+ return 0;
+ }
+ oldObj = curObj;
+ curObj = ObjStack[ObjStackTop--];
+
+ return oldObj;
+ }
+
+extern "C" void deleteString(char *p);
+
+static void enterValues(const char *value)
+ {
+ if (fieldedProp && *fieldedProp) {
+ if (value) {
+ addPropValue(curProp,*fieldedProp,value);
+ }
+ /* else this field is empty, advance to next field */
+ fieldedProp++;
+ }
+ else {
+ if (value) {
+ setVObjectUStringZValue_(curProp,fakeUnicode(value,0));
+ }
+ }
+ deleteString((char *)value);
+ }
+
+static void enterProps(const char *s)
+ {
+ curProp = addGroup(curObj,s);
+ deleteString((char *)s);
+ }
+
+static void enterAttr(const char *s1, const char *s2)
+{
+ const char *p1, *p2 = nullptr;
+ p1 = lookupProp_(s1);
+ if (s2) {
+ VObject *a;
+ p2 = lookupProp_(s2);
+ a = addProp(curProp,p1);
+ setVObjectStringZValue(a,p2);
+ }
+ else
+ addProp(curProp,p1);
+ if (PL_strcasecmp(p1,VCBase64Prop) == 0 || (s2 && PL_strcasecmp(p2,VCBase64Prop)==0))
+ lexPushMode(L_BASE64);
+ else if (PL_strcasecmp(p1,VCQuotedPrintableProp) == 0
+ || (s2 && PL_strcasecmp(p2,VCQuotedPrintableProp)==0))
+ lexPushMode(L_QUOTED_PRINTABLE);
+ deleteString((char *)s1); deleteString((char *)s2);
+}
+
+
+#define PR_MAX_LEX_LOOKAHEAD_0 32
+#define PR_MAX_LEX_LOOKAHEAD 64
+#define PR_MAX_LEX_MODE_STACK_SIZE 10
+#define LEXMODE() (lexBuf.lexModeStack[lexBuf.lexModeStackTop])
+
+struct LexBuf {
+ /* input */
+ char *inputString;
+ unsigned long curPos;
+ unsigned long inputLen;
+ /* lookahead buffer */
+ /* -- lookahead buffer is short instead of char so that EOF
+ / can be represented correctly.
+ */
+ unsigned long len;
+ short buf[PR_MAX_LEX_LOOKAHEAD];
+ unsigned long getPtr;
+ /* context stack */
+ unsigned long lexModeStackTop;
+ enum LexMode lexModeStack[PR_MAX_LEX_MODE_STACK_SIZE];
+ /* token buffer */
+ unsigned long maxToken;
+ char *strs;
+ unsigned long strsLen;
+ } lexBuf;
+
+static void lexPushMode(enum LexMode mode)
+ {
+ if (lexBuf.lexModeStackTop == (PR_MAX_LEX_MODE_STACK_SIZE-1))
+ yyerror("lexical context stack overflow");
+ else {
+ lexBuf.lexModeStack[++lexBuf.lexModeStackTop] = mode;
+ }
+ }
+
+static void lexPopMode(int top)
+ {
+ /* special case of pop for ease of error recovery -- this
+ version will never underflow */
+ if (top)
+ lexBuf.lexModeStackTop = 0;
+ else
+ if (lexBuf.lexModeStackTop > 0) lexBuf.lexModeStackTop--;
+ }
+
+static int lexWithinMode(enum LexMode mode) {
+ unsigned long i;
+ for (i=0;i<lexBuf.lexModeStackTop;i++)
+ if (mode == lexBuf.lexModeStack[i]) return 1;
+ return 0;
+ }
+
+static int lexGetc_()
+{
+ /* get next char from input, no buffering. */
+ if (lexBuf.curPos == lexBuf.inputLen)
+ return EOF;
+ else if (lexBuf.inputString)
+ return *(lexBuf.inputString + lexBuf.curPos++);
+
+ return -1;
+}
+
+static int lexGeta()
+ {
+ ++lexBuf.len;
+ return (lexBuf.buf[lexBuf.getPtr] = lexGetc_());
+ }
+
+static int lexGeta_(int i)
+ {
+ ++lexBuf.len;
+ return (lexBuf.buf[(lexBuf.getPtr+i)%PR_MAX_LEX_LOOKAHEAD] = lexGetc_());
+ }
+
+static void lexSkipLookahead() {
+ if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) {
+ /* don't skip EOF. */
+ lexBuf.getPtr = (lexBuf.getPtr + 1) % PR_MAX_LEX_LOOKAHEAD;
+ lexBuf.len--;
+ }
+ }
+
+static int lexLookahead() {
+ int c = (lexBuf.len)?
+ lexBuf.buf[lexBuf.getPtr]:
+ lexGeta();
+ /* do the \r\n -> \n or \r -> \n translation here */
+ if (c == '\r') {
+ int a = (lexBuf.len>1)?
+ lexBuf.buf[(lexBuf.getPtr+1)%PR_MAX_LEX_LOOKAHEAD]:
+ lexGeta_(1);
+ if (a == '\n') {
+ lexSkipLookahead();
+ }
+ lexBuf.buf[lexBuf.getPtr] = c = '\n';
+ }
+ else if (c == '\n') {
+ int a = (lexBuf.len>1)?
+ lexBuf.buf[lexBuf.getPtr+1]:
+ lexGeta_(1);
+ if (a == '\r') {
+ lexSkipLookahead();
+ }
+ lexBuf.buf[lexBuf.getPtr] = '\n';
+ }
+ return c;
+ }
+
+static int lexGetc() {
+ int c = lexLookahead();
+ if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) {
+ /* EOF will remain in lookahead buffer */
+ lexBuf.getPtr = (lexBuf.getPtr + 1) % PR_MAX_LEX_LOOKAHEAD;
+ lexBuf.len--;
+ }
+ return c;
+ }
+
+static void lexSkipLookaheadWord() {
+ if (lexBuf.strsLen <= lexBuf.len) {
+ lexBuf.len -= lexBuf.strsLen;
+ lexBuf.getPtr = (lexBuf.getPtr + lexBuf.strsLen) % PR_MAX_LEX_LOOKAHEAD;
+ }
+ }
+
+static void lexClearToken()
+ {
+ lexBuf.strsLen = 0;
+ }
+
+static void lexAppendc(int c)
+ {
+ lexBuf.strs[lexBuf.strsLen] = c;
+ /* append up to zero termination */
+ if (c == 0) return;
+ lexBuf.strsLen++;
+ if (lexBuf.strsLen >= lexBuf.maxToken) {
+ /* double the token string size */
+ lexBuf.maxToken <<= 1;
+ lexBuf.strs = (char*) PR_Realloc(lexBuf.strs,lexBuf.maxToken);
+ }
+ }
+
+static char* lexStr() {
+ return dupStr(lexBuf.strs,lexBuf.strsLen+1);
+ }
+
+static void lexSkipWhite() {
+ int c = lexLookahead();
+ while (c == ' ' || c == '\t') {
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ }
+
+static char* lexGetWord() {
+ int c;
+ lexSkipWhite();
+ lexClearToken();
+ c = lexLookahead();
+ while (c != EOF && !PL_strchr("\t\n ;:=",(char)c)) {
+ lexAppendc(c);
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ return lexStr();
+ }
+
+#if 0
+static void lexPushLookahead(char *s, int len) {
+ int putptr;
+ if (len == 0) len = PL_strlen(s);
+ putptr = lexBuf.getPtr - len;
+ /* this function assumes that length of word to push back
+ / is not greater than PR_MAX_LEX_LOOKAHEAD.
+ */
+ if (putptr < 0) putptr += PR_MAX_LEX_LOOKAHEAD;
+ lexBuf.getPtr = putptr;
+ while (*s) {
+ lexBuf.buf[putptr] = *s++;
+ putptr = (putptr + 1) % PR_MAX_LEX_LOOKAHEAD;
+ }
+ lexBuf.len += len;
+ }
+#endif
+
+static void lexPushLookaheadc(int c) {
+ int putptr;
+ /* can't putback EOF, because it never leaves lookahead buffer */
+ if (c == EOF) return;
+ putptr = (int) lexBuf.getPtr - 1;
+ if (putptr < 0) putptr += PR_MAX_LEX_LOOKAHEAD;
+ lexBuf.getPtr = putptr;
+ lexBuf.buf[putptr] = c;
+ lexBuf.len += 1;
+ }
+
+static char* lexLookaheadWord() {
+ /* this function can lookahead word with max size of PR_MAX_LEX_LOOKAHEAD_0
+ / and thing bigger than that will stop the lookahead and return 0;
+ / leading white spaces are not recoverable.
+ */
+ int c;
+ int len = 0;
+ int curgetptr = 0;
+ lexSkipWhite();
+ lexClearToken();
+ curgetptr = (int) lexBuf.getPtr; /* remember! */
+ while (len < (PR_MAX_LEX_LOOKAHEAD_0)) {
+ c = lexGetc();
+ len++;
+ if (c == EOF || PL_strchr("\t\n ;:=", (char)c)) {
+ lexAppendc(0);
+ /* restore lookahead buf. */
+ lexBuf.len += len;
+ lexBuf.getPtr = curgetptr;
+ return lexStr();
+ }
+ else
+ lexAppendc(c);
+ }
+ lexBuf.len += len; /* char that has been moved to lookahead buffer */
+ lexBuf.getPtr = curgetptr;
+ return 0;
+ }
+
+#ifdef _SUPPORT_LINE_FOLDING
+static void handleMoreRFC822LineBreak(int c) {
+ /* support RFC 822 line break in cases like
+ * ADR: foo;
+ * morefoo;
+ * more foo;
+ */
+ if (c == ';') {
+ int a;
+ lexSkipLookahead();
+ /* skip white spaces */
+ a = lexLookahead();
+ while (a == ' ' || a == '\t') {
+ lexSkipLookahead();
+ a = lexLookahead();
+ }
+ if (a == '\n') {
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == ' ' || a == '\t') {
+ /* continuation, throw away all the \n and spaces read so
+ * far
+ */
+ lexSkipWhite();
+ lexPushLookaheadc(';');
+ }
+ else {
+ lexPushLookaheadc('\n');
+ lexPushLookaheadc(';');
+ }
+ }
+ else {
+ lexPushLookaheadc(';');
+ }
+ }
+ }
+
+static char* lexGet1Value() {
+/* int size = 0; */
+ int c;
+ lexSkipWhite();
+ c = lexLookahead();
+ lexClearToken();
+ while (c != EOF && c != ';') {
+ if (c == '\n') {
+ int a;
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == ' ' || a == '\t') {
+ lexAppendc(' ');
+ lexSkipLookahead();
+ }
+ else {
+ lexPushLookaheadc('\n');
+ break;
+ }
+ }
+ else if (c == '\\') {
+ int a;
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == '\\' || a == ',' || a == ';' || a == ':') {
+ lexAppendc(a);
+ }
+ else if (a == 'n' || a == 'N') {
+ lexAppendc('\n');
+ }
+ else {
+ lexAppendc(c);
+ lexAppendc(a);
+ }
+ lexSkipLookahead();
+ }
+ else {
+ lexAppendc(c);
+ lexSkipLookahead();
+ }
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ handleMoreRFC822LineBreak(c);
+ return c==EOF?0:lexStr();
+ }
+#endif
+
+
+#ifndef _SUPPORT_LINE_FOLDING
+static char* lexGetStrUntil(char *termset) {
+ int c = lexLookahead();
+ lexClearToken();
+ while (c != EOF && !PL_strchr(termset,c)) {
+ lexAppendc(c);
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ return c==EOF?0:lexStr();
+ }
+#endif /* ! _SUPPORT_LINE_FOLDING */
+
+static int match_begin_name(int end) {
+ char *n = lexLookaheadWord();
+ int token = ID;
+ if (n) {
+ if (!PL_strcasecmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD;
+ else if (!PL_strcasecmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL;
+ else if (!PL_strcasecmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT;
+ else if (!PL_strcasecmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO;
+ deleteString(n);
+ return token;
+ }
+ return 0;
+ }
+
+void initLex(const char *inputstring, unsigned long inputlen)
+ {
+ /* initialize lex mode stack */
+ lexBuf.lexModeStack[lexBuf.lexModeStackTop=0] = L_NORMAL;
+
+ /* iniatialize lex buffer. */
+ lexBuf.inputString = (char*) inputstring;
+ lexBuf.inputLen = inputlen;
+ lexBuf.curPos = 0;
+
+ lexBuf.len = 0;
+ lexBuf.getPtr = 0;
+
+ lexBuf.maxToken = PR_MAXTOKEN;
+ lexBuf.strs = (char*)PR_CALLOC(PR_MAXTOKEN);
+ lexBuf.strsLen = 0;
+
+ }
+
+static void finiLex() {
+ PR_FREEIF(lexBuf.strs);
+ }
+
+
+/******************************************************************************/
+/* This parses and converts the base64 format for binary encoding into
+ * a decoded buffer (allocated with new). See RFC 1521.
+ */
+static char * lexGetDataFromBase64()
+ {
+ unsigned long bytesLen = 0, bytesMax = 0;
+ int quadIx = 0, pad = 0;
+ unsigned long trip = 0;
+ unsigned char b;
+ int c;
+ unsigned char *bytes = nullptr;
+ unsigned char *oldBytes = nullptr;
+
+ DBG_(("db: lexGetDataFromBase64\n"));
+ while (1) {
+ c = lexGetc();
+ if (c == '\n') {
+ ++mime_lineNum;
+ if (lexLookahead() == '\n') {
+ /* a '\n' character by itself means end of data */
+ break;
+ }
+ else continue; /* ignore '\n' */
+ }
+ else {
+ if ((c >= 'A') && (c <= 'Z'))
+ b = (unsigned char)(c - 'A');
+ else if ((c >= 'a') && (c <= 'z'))
+ b = (unsigned char)(c - 'a') + 26;
+ else if ((c >= '0') && (c <= '9'))
+ b = (unsigned char)(c - '0') + 52;
+ else if (c == '+')
+ b = 62;
+ else if (c == '/')
+ b = 63;
+ else if (c == '=' && (quadIx == 2 || quadIx == 3)) {
+ b = 0;
+ pad++;
+ } else if ((c == ' ') || (c == '\t')) {
+ continue;
+ } else { /* error condition */
+ if (bytes)
+ PR_Free (bytes);
+ else if (oldBytes)
+ PR_Free (oldBytes);
+ /* error recovery: skip until 2 adjacent newlines. */
+ DBG_(("db: invalid character 0x%x '%c'\n", c,c));
+ if (c != EOF) {
+ c = lexGetc();
+ while (c != EOF) {
+ if (c == '\n' && lexLookahead() == '\n') {
+ ++mime_lineNum;
+ break;
+ }
+ c = lexGetc();
+ }
+ }
+ return NULL;
+ }
+ trip = (trip << 6) | b;
+ if (++quadIx == 4) {
+ unsigned char outBytes[3];
+ int numOut;
+ int i;
+ for (i = 0; i < 3; i++) {
+ outBytes[2-i] = (unsigned char)(trip & 0xFF);
+ trip >>= 8;
+ }
+ numOut = 3 - pad;
+ if (bytesLen + numOut > bytesMax) {
+ if (!bytes) {
+ bytesMax = 1024;
+ } else {
+ bytesMax <<= 2;
+ oldBytes = bytes;
+ }
+ bytes = (unsigned char*) PR_Realloc(oldBytes, bytesMax);
+ if (!bytes) {
+ mime_error("out of memory while processing BASE64 data\n");
+ break;
+ }
+ }
+ if (bytes) {
+ memcpy(bytes + bytesLen, outBytes, numOut);
+ bytesLen += numOut;
+ }
+ trip = 0;
+ quadIx = 0;
+ pad = 0;
+ }
+ }
+ } /* while */
+ DBG_(("db: bytesLen = %d\n", bytesLen));
+ /* kludge: all this won't be necessary if we have tree form
+ representation */
+ if (bytes) {
+ setValueWithSize(curProp,bytes,(unsigned int)bytesLen);
+ PR_FREEIF(bytes);
+ }
+ else if (oldBytes) {
+ setValueWithSize(curProp,oldBytes,(unsigned int)bytesLen);
+ PR_FREEIF(oldBytes);
+ }
+ return 0;
+ }
+
+static int match_begin_end_name(int end) {
+ int token;
+ lexSkipWhite();
+ if (lexLookahead() != ':') return ID;
+ lexSkipLookahead();
+ lexSkipWhite();
+ token = match_begin_name(end);
+ if (token == ID) {
+ lexPushLookaheadc(':');
+ DBG_(("db: ID '%s'\n", yylval.str));
+ return ID;
+ }
+ else if (token != 0) {
+ lexSkipLookaheadWord();
+ deleteString(yylval.str);
+ DBG_(("db: begin/end %d\n", token));
+ return token;
+ }
+ return 0;
+ }
+
+static char* lexGetQuotedPrintable()
+ {
+ char cur;
+/* unsigned long len = 0; */
+
+ lexClearToken();
+ do {
+ cur = lexGetc();
+ switch (cur) {
+ case '=': {
+ int c = 0;
+ int next[2];
+ int tab [1];
+ int i;
+ for (i = 0; i < 2; i++) {
+ next[i] = lexGetc();
+ if (next[i] >= '0' && next[i] <= '9')
+ c = c * 16 + next[i] - '0';
+ else if (next[i] >= 'A' && next[i] <= 'F')
+ c = c * 16 + next[i] - 'A' + 10;
+ else
+ break;
+ }
+ if (i == 0) {
+ /* single '=' follow by LINESEP is continuation sign? */
+ if (next[0] == '\n') {
+ tab[0] = lexGetc();
+ if (tab[0] == '\t')
+ lexSkipWhite();
+ ++mime_lineNum;
+ }
+ else {
+ lexAppendc(cur);
+ /* lexPushLookaheadc('=');
+ goto EndString; */
+ }
+ }
+ else if (i == 1) {
+ lexPushLookaheadc(next[1]);
+ lexPushLookaheadc(next[0]);
+ lexAppendc('=');
+ } else {
+ lexAppendc(c);
+ }
+ break;
+ } /* '=' */
+ case '\n': {
+ lexPushLookaheadc('\n');
+ goto EndString;
+ }
+ case ';': {
+ lexPushLookaheadc(';');
+ goto EndString;
+ }
+ case (char)EOF:
+ break;
+ default:
+ lexAppendc(cur);
+ break;
+ } /* switch */
+ } while (cur != (char)EOF);
+
+EndString:
+ lexAppendc(0);
+ return lexStr();
+ } /* LexQuotedPrintable */
+
+static int yylex() {
+/* int token = 0; */
+
+ int lexmode = LEXMODE();
+ if (lexmode == L_VALUES) {
+ int c = lexGetc();
+ if (c == ';') {
+ DBG_(("db: SEMICOLON\n"));
+#ifdef _SUPPORT_LINE_FOLDING
+ lexPushLookaheadc(c);
+ handleMoreRFC822LineBreak(c);
+ lexSkipLookahead();
+#endif
+ return SEMICOLON;
+ }
+ else if (PL_strchr("\n",(char)c)) {
+ ++mime_lineNum;
+ /* consume all line separator(s) adjacent to each other */
+ c = lexLookahead();
+ while (PL_strchr("\n",(char)c)) {
+ lexSkipLookahead();
+ c = lexLookahead();
+ ++mime_lineNum;
+ }
+ DBG_(("db: LINESEP\n"));
+ return LINESEP;
+ }
+ else {
+ char *p = 0;
+ lexPushLookaheadc(c);
+ if (lexWithinMode(L_BASE64)) {
+ /* get each char and convert to bin on the fly... */
+ p = lexGetDataFromBase64();
+ yylval.str = p;
+ return !p && lexLookahead() == EOF ? 0 : STRING;
+ }
+ else if (lexWithinMode(L_QUOTED_PRINTABLE)) {
+ p = lexGetQuotedPrintable();
+ }
+ else {
+#ifdef _SUPPORT_LINE_FOLDING
+ p = lexGet1Value();
+#else
+ p = lexGetStrUntil(";\n");
+#endif
+ }
+ if (p && (*p || lexLookahead() != EOF)) {
+ DBG_(("db: STRING: '%s'\n", p));
+ yylval.str = p;
+ return STRING;
+ }
+ else return 0;
+ }
+ }
+ else {
+ /* normal mode */
+ while (1) {
+ int c = lexGetc();
+ switch(c) {
+ case ':': {
+ /* consume all line separator(s) adjacent to each other */
+ /* ignoring linesep immediately after colon. */
+ c = lexLookahead();
+ while (PL_strchr("\n",(char)c)) {
+ lexSkipLookahead();
+ c = lexLookahead();
+ ++mime_lineNum;
+ }
+ DBG_(("db: COLON\n"));
+ return COLON;
+ }
+ case ';':
+ DBG_(("db: SEMICOLON\n"));
+ return SEMICOLON;
+ case '=':
+ DBG_(("db: EQ\n"));
+ return EQ;
+ /* ignore whitespace in this mode */
+ case '\t':
+ case ' ': continue;
+ case '\n': {
+ ++mime_lineNum;
+ continue;
+ }
+ case EOF: return 0;
+ break;
+ default: {
+ lexPushLookaheadc(c);
+ if (isalpha(c)) {
+ char *t = lexGetWord();
+ yylval.str = t;
+ if (!PL_strcasecmp(t, "BEGIN")) {
+ return match_begin_end_name(0);
+ }
+ else if (!PL_strcasecmp(t,"END")) {
+ return match_begin_end_name(1);
+ }
+ else {
+ DBG_(("db: ID '%s'\n", t));
+ return ID;
+ }
+ }
+ else {
+ /* unknown token */
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+
+/***************************************************************************/
+/*** Public Functions ****/
+/***************************************************************************/
+
+static VObject* parse_MIMEHelper()
+ {
+ ObjStackTop = -1;
+ mime_numErrors = 0;
+ mime_lineNum = 1;
+ vObjList = 0;
+ curObj = 0;
+
+ if (yyparse() != 0)
+ return 0;
+
+ finiLex();
+ return vObjList;
+ }
+
+/******************************************************************************/
+VObject* parse_MIME(const char *input, unsigned long len)
+ {
+ initLex(input, len);
+ return parse_MIMEHelper();
+ }
+
+static MimeErrorHandler mimeErrorHandler;
+
+void registerMimeErrorHandler(MimeErrorHandler me)
+ {
+ mimeErrorHandler = me;
+ }
+
+void mime_error(const char *s)
+{
+ char msg[256];
+ if (mimeErrorHandler) {
+ PR_snprintf(msg, sizeof(msg), "%s at line %d", s, mime_lineNum);
+ mimeErrorHandler(msg);
+ }
+}
+
+/*#line 1221 "y_tab.c"*/
+#define YYABORT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ int yym, yyn, yystate;
+#if YYDEBUG
+ char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if ((yyn = yydefred[yystate])) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYPR_MAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, reading %d (%s)\n", yystate,
+ yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, shifting to state %d\n",
+ yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+/*yynewerror: */
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, error recovery shifting\
+ to state %d\n", *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: error recovery discarding state %d\n",
+ *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYPR_MAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, error recovery discards token %d (%s)\n",
+ yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, reducing by rule %d (%s)\n",
+ yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 2:
+/*#line 282 "vcc.y"*/
+{ addList(&vObjList, yyvsp[0].vobj ); curObj = 0; }
+break;
+case 4:
+/*#line 285 "vcc.y"*/
+{ addList(&vObjList, yyvsp[0].vobj ); curObj = 0; }
+break;
+case 7:
+/*#line 294 "vcc.y"*/
+{
+ lexPushMode(L_VCARD);
+ if (!pushVObject(VCCardProp)) YYERROR;
+ }
+break;
+case 8:
+/*#line 299 "vcc.y"*/
+{
+ lexPopMode(0);
+ yyval.vobj = popVObject();
+ }
+break;
+case 9:
+/*#line 304 "vcc.y"*/
+{
+ lexPushMode(L_VCARD);
+ if (!pushVObject(VCCardProp)) YYERROR;
+ }
+break;
+case 10:
+/*#line 309 "vcc.y"*/
+{
+ lexPopMode(0);
+ yyval.vobj = popVObject();
+ }
+break;
+case 13:
+/*#line 320 "vcc.y"*/
+{
+ lexPushMode(L_VALUES);
+ }
+break;
+case 14:
+/*#line 324 "vcc.y"*/
+{
+ if (lexWithinMode(L_BASE64) || lexWithinMode(L_QUOTED_PRINTABLE))
+ lexPopMode(0);
+ lexPopMode(0);
+ }
+break;
+case 16:
+/*#line 332 "vcc.y"*/
+{
+ enterProps(yyvsp[0].str );
+ }
+break;
+case 18:
+/*#line 337 "vcc.y"*/
+{
+ enterProps(yyvsp[0].str );
+ }
+break;
+case 22:
+/*#line 350 "vcc.y"*/
+{
+ enterAttr(yyvsp[0].str ,0);
+ }
+break;
+case 23:
+/*#line 354 "vcc.y"*/
+{
+ enterAttr(yyvsp[-2].str ,yyvsp[0].str );
+
+ }
+break;
+case 25:
+/*#line 363 "vcc.y"*/
+{ enterValues(yyvsp[-1].str ); }
+break;
+case 27:
+/*#line 365 "vcc.y"*/
+{ enterValues(yyvsp[0].str ); }
+break;
+case 29:
+/*#line 370 "vcc.y"*/
+{ yyval.str = 0; }
+break;
+case 30:
+/*#line 375 "vcc.y"*/
+{ if (!pushVObject(VCCalProp)) YYERROR; }
+break;
+case 31:
+/*#line 378 "vcc.y"*/
+{ yyval.vobj = popVObject(); }
+break;
+case 32:
+/*#line 380 "vcc.y"*/
+{ if (!pushVObject(VCCalProp)) YYERROR; }
+break;
+case 33:
+/*#line 382 "vcc.y"*/
+{ yyval.vobj = popVObject(); }
+break;
+case 39:
+/*#line 397 "vcc.y"*/
+{
+ lexPushMode(L_VEVENT);
+ if (!pushVObject(VCEventProp)) YYERROR;
+ }
+break;
+case 40:
+/*#line 403 "vcc.y"*/
+{
+ lexPopMode(0);
+ popVObject();
+ }
+break;
+case 41:
+/*#line 408 "vcc.y"*/
+{
+ lexPushMode(L_VEVENT);
+ if (!pushVObject(VCEventProp)) YYERROR;
+ }
+break;
+case 42:
+/*#line 413 "vcc.y"*/
+{
+ lexPopMode(0);
+ popVObject();
+ }
+break;
+case 43:
+/*#line 421 "vcc.y"*/
+{
+ lexPushMode(L_VTODO);
+ if (!pushVObject(VCTodoProp)) YYERROR;
+ }
+break;
+case 44:
+/*#line 427 "vcc.y"*/
+{
+ lexPopMode(0);
+ popVObject();
+ }
+break;
+case 45:
+/*#line 432 "vcc.y"*/
+{
+ lexPushMode(L_VTODO);
+ if (!pushVObject(VCTodoProp)) YYERROR;
+ }
+break;
+case 46:
+/*#line 437 "vcc.y"*/
+{
+ lexPopMode(0);
+ popVObject();
+ }
+break;
+/*#line 1520 "y_tab.c"*/
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: after reduction, shifting from state 0 to\
+ state %d\n", YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYPR_MAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, reading %d (%s)\n",
+ YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: after reduction, shifting from state %d \
+to state %d\n", *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/mailnews/addrbook/src/nsVCard.h b/mailnews/addrbook/src/nsVCard.h
new file mode 100644
index 000000000..041cf0042
--- /dev/null
+++ b/mailnews/addrbook/src/nsVCard.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+#ifndef __VCC_H__
+#define __VCC_H__ 1
+
+#include "nsVCardObj.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+VObject* parse_MIME(const char *input, unsigned long len);
+
+typedef void (*MimeErrorHandler)(char *);
+
+void registerMimeErrorHandler(MimeErrorHandler);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VCC_H__ */
diff --git a/mailnews/addrbook/src/nsVCardObj.cpp b/mailnews/addrbook/src/nsVCardObj.cpp
new file mode 100644
index 000000000..bf8ceb2fb
--- /dev/null
+++ b/mailnews/addrbook/src/nsVCardObj.cpp
@@ -0,0 +1,1330 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+ * doc: vobject and APIs to construct vobject, APIs pretty print
+ * vobject, and convert a vobject into its textual representation.
+ */
+
+#include "prlog.h"
+#include "nsVCard.h"
+#include "nsVCardObj.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "prprf.h"
+#include "nsStringGlue.h"
+
+/* debugging utilities */
+#define DBG_(x)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ char **fieldedProp;
+
+#ifdef __cplusplus
+ }
+#endif
+
+
+
+static VObject* newVObject_(const char *id);
+#if 0
+static int vObjectValueType(VObject *o);
+static void initVObjectIterator(VObjectIterator *i, VObject *o);
+#endif
+
+/*----------------------------------------------------------------------
+ The following functions involve with memory allocation:
+ newVObject
+ deleteVObject
+ dupStr
+ deleteString
+ newStrItem
+ deleteStrItem
+ ----------------------------------------------------------------------*/
+
+static bool needsQuotedPrintable (const char *s)
+{
+ const unsigned char *p = (const unsigned char *)s;
+
+ while (*p) {
+ if (*p & 0x80 || *p == '\015' || *p == '\012')
+ return true;
+ p++;
+ }
+
+ return false;
+}
+
+VObject* newVObject_(const char *id)
+{
+ VObject *p = (VObject*) new(VObject);
+ p->next = 0;
+ p->id = id;
+ p->prop = 0;
+ VALUE_TYPE(p) = 0;
+ ANY_VALUE_OF(p) = 0;
+ return p;
+}
+
+VObject* newVObject(const char *id)
+{
+ return newVObject_(lookupStr(id));
+}
+
+void deleteVObject(VObject *p)
+{
+ unUseStr(p->id);
+ delete (p);
+}
+
+char* dupStr(const char *s, unsigned int size)
+{
+ char *t;
+ if (size == 0) {
+ size = PL_strlen(s);
+ }
+ t = (char*)PR_CALLOC(size+1);
+ if (t) {
+ memcpy(t,s,size);
+ t[size] = 0;
+ return t;
+ }
+ else {
+ return (char*)0;
+ }
+}
+
+static StrItem* newStrItem(const char *s, StrItem *next)
+{
+ StrItem *p = (StrItem*)PR_CALLOC(sizeof(StrItem));
+ p->next = next;
+ p->s = s;
+ p->refCnt = 1;
+ return p;
+}
+
+extern "C"
+void deleteString(char *p)
+{
+ if (p)
+ PR_Free ((void*)p);
+}
+
+extern "C"
+void deleteStrItem(StrItem *p)
+{
+ if (p)
+ PR_FREEIF (p);
+}
+
+
+
+/*----------------------------------------------------------------------
+ The following function provide accesses to VObject's value.
+ ----------------------------------------------------------------------*/
+
+const char* vObjectName(VObject *o)
+{
+ return NAME_OF(o);
+}
+
+void setVObjectName(VObject *o, const char* id)
+{
+ NAME_OF(o) = id;
+}
+
+const char* vObjectStringZValue(VObject *o)
+{
+ return STRINGZ_VALUE_OF(o);
+}
+
+void setVObjectStringZValue(VObject *o, const char *s)
+{
+ STRINGZ_VALUE_OF(o) = dupStr(s,0);
+ VALUE_TYPE(o) = VCVT_STRINGZ;
+}
+
+void setVObjectStringZValue_(VObject *o, const char *s)
+{
+ STRINGZ_VALUE_OF(o) = s;
+ VALUE_TYPE(o) = VCVT_STRINGZ;
+}
+
+const vwchar_t* vObjectUStringZValue(VObject *o)
+{
+ return USTRINGZ_VALUE_OF(o);
+}
+
+void setVObjectUStringZValue(VObject *o, const vwchar_t *s)
+{
+ USTRINGZ_VALUE_OF(o) = (vwchar_t*) dupStr((char*)s,(uStrLen(s)+1)*2);
+ VALUE_TYPE(o) = VCVT_USTRINGZ;
+}
+
+void setVObjectUStringZValue_(VObject *o, const vwchar_t *s)
+{
+ USTRINGZ_VALUE_OF(o) = s;
+ VALUE_TYPE(o) = VCVT_USTRINGZ;
+}
+
+unsigned int vObjectIntegerValue(VObject *o)
+{
+ return INTEGER_VALUE_OF(o);
+}
+
+void setVObjectIntegerValue(VObject *o, unsigned int i)
+{
+ INTEGER_VALUE_OF(o) = i;
+ VALUE_TYPE(o) = VCVT_UINT;
+}
+
+unsigned long vObjectLongValue(VObject *o)
+{
+ return LONG_VALUE_OF(o);
+}
+
+void setVObjectLongValue(VObject *o, unsigned long l)
+{
+ LONG_VALUE_OF(o) = l;
+ VALUE_TYPE(o) = VCVT_ULONG;
+}
+
+void* vObjectAnyValue(VObject *o)
+{
+ return ANY_VALUE_OF(o);
+}
+
+void setVObjectAnyValue(VObject *o, void *t)
+{
+ ANY_VALUE_OF(o) = t;
+ VALUE_TYPE(o) = VCVT_RAW;
+}
+
+VObject* vObjectVObjectValue(VObject *o)
+{
+ return VOBJECT_VALUE_OF(o);
+}
+
+void setVObjectVObjectValue(VObject *o, VObject *p)
+{
+ VOBJECT_VALUE_OF(o) = p;
+ VALUE_TYPE(o) = VCVT_VOBJECT;
+}
+
+#if 0
+int vObjectValueType(VObject *o)
+{
+ return VALUE_TYPE(o);
+}
+#endif
+
+
+/*----------------------------------------------------------------------
+ The following functions can be used to build VObject.
+ ----------------------------------------------------------------------*/
+
+VObject* addVObjectProp(VObject *o, VObject *p)
+{
+ /* circular link list pointed to tail */
+ /*
+ o {next,id,prop,val}
+ V
+ pn {next,id,prop,val}
+ V
+ ...
+ p1 {next,id,prop,val}
+ V
+ pn
+ -->
+ o {next,id,prop,val}
+ V
+ pn {next,id,prop,val}
+ V
+ p {next,id,prop,val}
+ ...
+ p1 {next,id,prop,val}
+ V
+ pn
+ */
+
+ VObject *tail = o->prop;
+ if (tail) {
+ p->next = tail->next;
+ o->prop = tail->next = p;
+ }
+ else {
+ o->prop = p->next = p;
+ }
+ return p;
+}
+
+VObject* addProp(VObject *o, const char *id)
+{
+ return addVObjectProp(o,newVObject(id));
+}
+
+VObject* addProp_(VObject *o, const char *id)
+{
+ return addVObjectProp(o,newVObject_(id));
+}
+
+void addList(VObject **o, VObject *p)
+{
+ p->next = 0;
+ if (*o == 0) {
+ *o = p;
+ }
+ else {
+ VObject *t = *o;
+ while (t->next) {
+ t = t->next;
+ }
+ t->next = p;
+ }
+}
+
+VObject* nextVObjectInList(VObject *o)
+{
+ return o->next;
+}
+
+VObject* setValueWithSize_(VObject *prop, void *val, unsigned int size)
+{
+ VObject *sizeProp;
+ setVObjectAnyValue(prop, val);
+ sizeProp = addProp(prop,VCDataSizeProp);
+ setVObjectLongValue(sizeProp, size);
+ return prop;
+}
+
+VObject* setValueWithSize(VObject *prop, void *val, unsigned int size)
+{
+ void *p = dupStr((const char *)val,size);
+ return setValueWithSize_(prop,p,p?size:0);
+}
+
+void initPropIterator(VObjectIterator *i, VObject *o)
+{
+ i->start = o->prop;
+ i->next = 0;
+}
+
+#if 0
+void initVObjectIterator(VObjectIterator *i, VObject *o)
+{
+ i->start = o->next;
+ i->next = 0;
+}
+#endif
+
+int moreIteration(VObjectIterator *i)
+{
+ return (i->start && (i->next==0 || i->next!=i->start));
+}
+
+VObject* nextVObject(VObjectIterator *i)
+{
+ if (i->start && i->next != i->start) {
+ if (i->next == 0) {
+ i->next = i->start->next;
+ return i->next;
+ }
+ else {
+ i->next = i->next->next;
+ return i->next;
+ }
+ }
+ else return (VObject*)0;
+}
+
+VObject* isAPropertyOf(VObject *o, const char *id)
+{
+ VObjectIterator i;
+ initPropIterator(&i,o);
+ while (moreIteration(&i)) {
+ VObject *each = nextVObject(&i);
+ if (!PL_strcasecmp(id,each->id))
+ return each;
+ }
+ return (VObject*)0;
+}
+
+VObject* addGroup(VObject *o, const char *g)
+{
+ /*
+ a.b.c
+ -->
+ prop(c)
+ prop(VCGrouping=b)
+ prop(VCGrouping=a)
+ */
+ char *dot = PL_strrchr(g,'.');
+ if (dot) {
+ VObject *p, *t;
+ char *gs, *n = dot+1;
+ gs = dupStr(g,0); /* so we can write to it. */
+ t = p = addProp_(o,lookupProp(n));
+ dot = PL_strrchr(gs,'.');
+ *dot = 0;
+ do {
+ dot = PL_strrchr(gs,'.');
+ if (dot) {
+ n = dot+1;
+ *dot=0;
+ }
+ else
+ n = gs;
+ /* property(VCGroupingProp=n);
+ * and the value may have VCGrouping property
+ */
+ t = addProp(t,VCGroupingProp);
+ setVObjectStringZValue(t,lookupProp_(n));
+ } while (n != gs);
+ deleteString(gs);
+ return p;
+ }
+ else
+ return addProp_(o,lookupProp(g));
+}
+
+VObject* addPropValue(VObject *o, const char *p, const char *v)
+{
+ VObject *prop;
+ prop = addProp(o,p);
+ if (v) {
+ setVObjectUStringZValue_(prop, fakeUnicode(v,0));
+ if (needsQuotedPrintable (v)) {
+ if (PL_strcasecmp (VCCardProp, vObjectName(o)) == 0)
+ addProp (prop, VCQuotedPrintableProp);
+ else
+ addProp (o, VCQuotedPrintableProp);
+ }
+ }
+ else
+ setVObjectUStringZValue_(prop, fakeUnicode("",0));
+
+ return prop;
+}
+
+VObject* addPropSizedValue_(VObject *o, const char *p, const char *v,
+ unsigned int size)
+{
+ VObject *prop;
+ prop = addProp(o,p);
+ setValueWithSize_(prop, (void*)v, size);
+ return prop;
+}
+
+VObject* addPropSizedValue(VObject *o, const char *p, const char *v,
+ unsigned int size)
+{
+ return addPropSizedValue_(o,p,dupStr(v,size),size);
+}
+
+void cleanVObject(VObject *o)
+{
+ if (o == 0) return;
+ if (o->prop) {
+ /* destroy time: cannot use the iterator here.
+ Have to break the cycle in the circular link
+ list and turns it into regular NULL-terminated
+ list -- since at some point of destruction,
+ the reference entry for the iterator to work
+ will not longer be valid.
+ */
+ VObject *p;
+ p = o->prop->next;
+ o->prop->next = 0;
+ do {
+ VObject *t = p->next;
+ cleanVObject(p);
+ p = t;
+ } while (p);
+ }
+ switch (VALUE_TYPE(o)) {
+ case VCVT_USTRINGZ:
+ case VCVT_STRINGZ:
+ case VCVT_RAW:
+ /* assume they are all allocated by malloc. */
+ if ((char*) STRINGZ_VALUE_OF(o))
+ PR_Free ((char*)STRINGZ_VALUE_OF(o));
+ break;
+ case VCVT_VOBJECT:
+ cleanVObject(VOBJECT_VALUE_OF(o));
+ break;
+ }
+ deleteVObject(o);
+}
+
+void cleanVObjects(VObject *list)
+{
+ while (list) {
+ VObject *t = list;
+ list = nextVObjectInList(list);
+ cleanVObject(t);
+ }
+}
+
+/*----------------------------------------------------------------------
+ The following is a String Table Facilities.
+ ----------------------------------------------------------------------*/
+
+#define STRTBLSIZE 255
+
+static StrItem *strTbl[STRTBLSIZE];
+
+static unsigned int hashStr(const char *s)
+{
+ unsigned int h = 0;
+ int i;
+ for (i=0;s[i];i++) {
+ h += s[i]*i;
+ }
+ return h % STRTBLSIZE;
+}
+
+void unUseStr(const char *s)
+{
+ StrItem *t, *p;
+ unsigned int h = hashStr(s);
+ if ((t = strTbl[h]) != 0) {
+ p = t;
+ do {
+ if (PL_strcasecmp(t->s,s) == 0) {
+ t->refCnt--;
+ if (t->refCnt == 0) {
+ if (t == strTbl[h]) {
+ strTbl[h] = t->next;
+ }
+ else {
+ p->next = t->next;
+ }
+ deleteString((char *)t->s);
+ deleteStrItem(t);
+ return;
+ }
+ }
+ p = t;
+ t = t->next;
+ } while (t);
+ }
+}
+
+struct PreDefProp {
+ const char *name;
+ const char *alias;
+ const char** fields;
+ unsigned int flags;
+ };
+
+/* flags in PreDefProp */
+#define PD_BEGIN 0x1
+#define PD_INTERNAL 0x2
+
+static const char *adrFields[] = {
+ VCPostalBoxProp,
+ VCExtAddressProp,
+ VCStreetAddressProp,
+ VCCityProp,
+ VCRegionProp,
+ VCPostalCodeProp,
+ VCCountryNameProp,
+ 0
+};
+
+static const char *nameFields[] = {
+ VCFamilyNameProp,
+ VCGivenNameProp,
+ VCAdditionalNamesProp,
+ VCNamePrefixesProp,
+ VCNameSuffixesProp,
+ NULL
+ };
+
+static const char *orgFields[] = {
+ VCOrgNameProp,
+ VCOrgUnitProp,
+ VCOrgUnit2Prop,
+ VCOrgUnit3Prop,
+ VCOrgUnit4Prop,
+ NULL
+ };
+
+static const char *AAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCAudioContentProp,
+ 0
+ };
+
+static const char *coolTalkFields[] = {
+ VCCooltalkAddress,
+ VCUseServer,
+ 0
+ };
+
+/* ExDate -- has unamed fields */
+/* RDate -- has unamed fields */
+
+static const char *DAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCDisplayStringProp,
+ 0
+ };
+
+static const char *MAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCEmailAddressProp,
+ VCNoteProp,
+ 0
+ };
+
+static const char *PAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCProcedureNameProp,
+ 0
+ };
+
+static struct PreDefProp propNames[] = {
+ { VC7bitProp, 0, 0, 0 },
+ { VC8bitProp, 0, 0, 0 },
+ { VCAAlarmProp, 0, AAlarmFields, 0 },
+ { VCAdditionalNamesProp, 0, 0, 0 },
+ { VCAdrProp, 0, adrFields, 0 },
+ { VCAgentProp, 0, 0, 0 },
+ { VCAIFFProp, 0, 0, 0 },
+ { VCAOLProp, 0, 0, 0 },
+ { VCAppleLinkProp, 0, 0, 0 },
+ { VCAttachProp, 0, 0, 0 },
+ { VCAttendeeProp, 0, 0, 0 },
+ { VCATTMailProp, 0, 0, 0 },
+ { VCAudioContentProp, 0, 0, 0 },
+ { VCAVIProp, 0, 0, 0 },
+ { VCBase64Prop, 0, 0, 0 },
+ { VCBBSProp, 0, 0, 0 },
+ { VCBirthDateProp, 0, 0, 0 },
+ { VCBMPProp, 0, 0, 0 },
+ { VCBodyProp, 0, 0, 0 },
+ { VCBusinessRoleProp, 0, 0, 0 },
+ { VCCalProp, 0, 0, PD_BEGIN },
+ { VCCaptionProp, 0, 0, 0 },
+ { VCCardProp, 0, 0, PD_BEGIN },
+ { VCCarProp, 0, 0, 0 },
+ { VCCategoriesProp, 0, 0, 0 },
+ { VCCellularProp, 0, 0, 0 },
+ { VCCGMProp, 0, 0, 0 },
+ { VCCharSetProp, 0, 0, 0 },
+ { VCCIDProp, VCContentIDProp, 0, 0 },
+ { VCCISProp, 0, 0, 0 },
+ { VCCityProp, 0, 0, 0 },
+ { VCClassProp, 0, 0, 0 },
+ { VCCommentProp, 0, 0, 0 },
+ { VCCompletedProp, 0, 0, 0 },
+ { VCContentIDProp, 0, 0, 0 },
+ { VCCountryNameProp, 0, 0, 0 },
+ { VCDAlarmProp, 0, DAlarmFields, 0 },
+ { VCDataSizeProp, 0, 0, PD_INTERNAL },
+ { VCDayLightProp, 0, 0 ,0 },
+ { VCDCreatedProp, 0, 0, 0 },
+ { VCDeliveryLabelProp, 0, 0, 0 },
+ { VCDescriptionProp, 0, 0, 0 },
+ { VCDIBProp, 0, 0, 0 },
+ { VCDisplayStringProp, 0, 0, 0 },
+ { VCDomesticProp, 0, 0, 0 },
+ { VCDTendProp, 0, 0, 0 },
+ { VCDTstartProp, 0, 0, 0 },
+ { VCDueProp, 0, 0, 0 },
+ { VCEmailAddressProp, 0, 0, 0 },
+ { VCEncodingProp, 0, 0, 0 },
+ { VCEndProp, 0, 0, 0 },
+ { VCEventProp, 0, 0, PD_BEGIN },
+ { VCEWorldProp, 0, 0, 0 },
+ { VCExNumProp, 0, 0, 0 },
+ { VCExpDateProp, 0, 0, 0 },
+ { VCExpectProp, 0, 0, 0 },
+ { VCExtAddressProp, 0, 0, 0 },
+ { VCFamilyNameProp, 0, 0, 0 },
+ { VCFaxProp, 0, 0, 0 },
+ { VCFullNameProp, 0, 0, 0 },
+ { VCGeoLocationProp, 0, 0, 0 },
+ { VCGeoProp, 0, 0, 0 },
+ { VCGIFProp, 0, 0, 0 },
+ { VCGivenNameProp, 0, 0, 0 },
+ { VCGroupingProp, 0, 0, 0 },
+ { VCHomeProp, 0, 0, 0 },
+ { VCIBMMailProp, 0, 0, 0 },
+ { VCInlineProp, 0, 0, 0 },
+ { VCInternationalProp, 0, 0, 0 },
+ { VCInternetProp, 0, 0, 0 },
+ { VCISDNProp, 0, 0, 0 },
+ { VCJPEGProp, 0, 0, 0 },
+ { VCLanguageProp, 0, 0, 0 },
+ { VCLastModifiedProp, 0, 0, 0 },
+ { VCLastRevisedProp, 0, 0, 0 },
+ { VCLocationProp, 0, 0, 0 },
+ { VCLogoProp, 0, 0, 0 },
+ { VCMailerProp, 0, 0, 0 },
+ { VCMAlarmProp, 0, MAlarmFields, 0 },
+ { VCMCIMailProp, 0, 0, 0 },
+ { VCMessageProp, 0, 0, 0 },
+ { VCMETProp, 0, 0, 0 },
+ { VCModemProp, 0, 0, 0 },
+ { VCMPEG2Prop, 0, 0, 0 },
+ { VCMPEGProp, 0, 0, 0 },
+ { VCMSNProp, 0, 0, 0 },
+ { VCNamePrefixesProp, 0, 0, 0 },
+ { VCNameProp, 0, nameFields, 0 },
+ { VCNameSuffixesProp, 0, 0, 0 },
+ { VCNoteProp, 0, 0, 0 },
+ { VCOrgNameProp, 0, 0, 0 },
+ { VCOrgProp, 0, orgFields, 0 },
+ { VCOrgUnit2Prop, 0, 0, 0 },
+ { VCOrgUnit3Prop, 0, 0, 0 },
+ { VCOrgUnit4Prop, 0, 0, 0 },
+ { VCOrgUnitProp, 0, 0, 0 },
+ { VCPagerProp, 0, 0, 0 },
+ { VCPAlarmProp, 0, PAlarmFields, 0 },
+ { VCParcelProp, 0, 0, 0 },
+ { VCPartProp, 0, 0, 0 },
+ { VCPCMProp, 0, 0, 0 },
+ { VCPDFProp, 0, 0, 0 },
+ { VCPGPProp, 0, 0, 0 },
+ { VCPhotoProp, 0, 0, 0 },
+ { VCPICTProp, 0, 0, 0 },
+ { VCPMBProp, 0, 0, 0 },
+ { VCPostalBoxProp, 0, 0, 0 },
+ { VCPostalCodeProp, 0, 0, 0 },
+ { VCPostalProp, 0, 0, 0 },
+ { VCPowerShareProp, 0, 0, 0 },
+ { VCPreferredProp, 0, 0, 0 },
+ { VCPriorityProp, 0, 0, 0 },
+ { VCProcedureNameProp, 0, 0, 0 },
+ { VCProdIdProp, 0, 0, 0 },
+ { VCProdigyProp, 0, 0, 0 },
+ { VCPronunciationProp, 0, 0, 0 },
+ { VCPSProp, 0, 0, 0 },
+ { VCPublicKeyProp, 0, 0, 0 },
+ { VCQPProp, VCQuotedPrintableProp, 0, 0 },
+ { VCQuickTimeProp, 0, 0, 0 },
+ { VCQuotedPrintableProp, 0, 0, 0 },
+ { VCRDateProp, 0, 0, 0 },
+ { VCRegionProp, 0, 0, 0 },
+ { VCRelatedToProp, 0, 0, 0 },
+ { VCRepeatCountProp, 0, 0, 0 },
+ { VCResourcesProp, 0, 0, 0 },
+ { VCRNumProp, 0, 0, 0 },
+ { VCRoleProp, 0, 0, 0 },
+ { VCRRuleProp, 0, 0, 0 },
+ { VCRSVPProp, 0, 0, 0 },
+ { VCRunTimeProp, 0, 0, 0 },
+ { VCSequenceProp, 0, 0, 0 },
+ { VCSnoozeTimeProp, 0, 0, 0 },
+ { VCStartProp, 0, 0, 0 },
+ { VCStatusProp, 0, 0, 0 },
+ { VCStreetAddressProp, 0, 0, 0 },
+ { VCSubTypeProp, 0, 0, 0 },
+ { VCSummaryProp, 0, 0, 0 },
+ { VCTelephoneProp, 0, 0, 0 },
+ { VCTIFFProp, 0, 0, 0 },
+ { VCTimeZoneProp, 0, 0, 0 },
+ { VCTitleProp, 0, 0, 0 },
+ { VCTLXProp, 0, 0, 0 },
+ { VCTodoProp, 0, 0, PD_BEGIN },
+ { VCTranspProp, 0, 0, 0 },
+ { VCUniqueStringProp, 0, 0, 0 },
+ { VCURLProp, 0, 0, 0 },
+ { VCURLValueProp, 0, 0, 0 },
+ { VCValueProp, 0, 0, 0 },
+ { VCVersionProp, 0, 0, 0 },
+ { VCVideoProp, 0, 0, 0 },
+ { VCVoiceProp, 0, 0, 0 },
+ { VCWAVEProp, 0, 0, 0 },
+ { VCWMFProp, 0, 0, 0 },
+ { VCWorkProp, 0, 0, 0 },
+ { VCX400Prop, 0, 0, 0 },
+ { VCX509Prop, 0, 0, 0 },
+ { VCXRuleProp, 0, 0, 0 },
+ { VCCooltalk, 0, coolTalkFields, 0 },
+ { VCCooltalkAddress, 0, 0, 0 },
+ { VCUseServer, 0, 0, 0 },
+ { VCUseHTML, 0, 0, 0 },
+ { 0,0,0,0 }
+ };
+
+
+static struct PreDefProp* lookupPropInfo(const char* str)
+{
+ /* brute force for now, could use a hash table here. */
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (PL_strcasecmp(str, propNames[i].name) == 0) {
+ return &propNames[i];
+ }
+
+ return 0;
+}
+
+
+const char* lookupProp_(const char* str)
+{
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (PL_strcasecmp(str, propNames[i].name) == 0) {
+ const char* s;
+ s = propNames[i].alias?propNames[i].alias:propNames[i].name;
+ return lookupStr(s);
+ }
+ return lookupStr(str);
+}
+
+
+const char* lookupProp(const char* str)
+{
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (PL_strcasecmp(str, propNames[i].name) == 0) {
+ const char *s;
+ fieldedProp = (char **)propNames[i].fields;
+ s = propNames[i].alias?propNames[i].alias:propNames[i].name;
+ return lookupStr(s);
+ }
+ fieldedProp = 0;
+ return lookupStr(str);
+}
+
+
+/*----------------------------------------------------------------------
+ APIs to Output text form.
+ ----------------------------------------------------------------------*/
+#define OFILE_REALLOC_SIZE 256
+
+static void appendcOFile_(OFile *fp, char c)
+{
+ if (fp->fail)
+ return;
+stuff:
+ if (fp->len+1 < fp->limit) {
+ fp->s[fp->len] = c;
+ fp->len++;
+ return;
+ }
+ else if (fp->alloc) {
+ fp->limit = fp->limit + OFILE_REALLOC_SIZE;
+ char* newBuf = (char *) PR_Realloc(fp->s, fp->limit);
+ if (newBuf) {
+ fp->s = newBuf;
+ goto stuff;
+ }
+ }
+ if (fp->alloc)
+ PR_FREEIF(fp->s);
+ fp->s = 0;
+ fp->fail = 1;
+}
+
+static void appendcOFile(OFile *fp, char c)
+{
+/* int i = 0; */
+ if (c == '\n') {
+ /* write out as <CR><LF> */
+ /* for (i = 0; i < LINEBREAK_LEN; i++)
+ appendcOFile_(fp,LINEBREAK [ i ]); */
+ appendcOFile_(fp,0xd);
+ appendcOFile_(fp,0xa);
+ }
+ else
+ appendcOFile_(fp,c);
+}
+
+static void appendsOFile(OFile *fp, const char *s)
+{
+ int i, slen;
+ slen = PL_strlen (s);
+ for (i=0; i<slen; i++) {
+ appendcOFile(fp,s[i]);
+ }
+}
+
+static void initMemOFile(OFile *fp, char *s, int len)
+{
+ fp->s = s;
+ fp->len = 0;
+ fp->limit = s?len:0;
+ fp->alloc = s?0:1;
+ fp->fail = 0;
+}
+
+
+static int writeBase64(OFile *fp, unsigned char *s, long len)
+{
+ long cur = 0;
+ int i, numQuads = 0;
+ unsigned long trip;
+ unsigned char b;
+ char quad[5];
+#define PR_MAXQUADS 16
+
+ quad[4] = 0;
+
+ while (cur < len) {
+ /* collect the triplet of bytes into 'trip' */
+ trip = 0;
+ for (i = 0; i < 3; i++) {
+ b = (cur < len) ? *(s + cur) : 0;
+ cur++;
+ trip = trip << 8 | b;
+ }
+ /* fill in 'quad' with the appropriate four characters */
+ for (i = 3; i >= 0; i--) {
+ b = (unsigned char)(trip & 0x3F);
+ trip = trip >> 6;
+ if ((3 - i) < (cur - len))
+ quad[i] = '='; /* pad char */
+ else if (b < 26) quad[i] = (char)b + 'A';
+ else if (b < 52) quad[i] = (char)(b - 26) + 'a';
+ else if (b < 62) quad[i] = (char)(b - 52) + '0';
+ else if (b == 62) quad[i] = '+';
+ else quad[i] = '/';
+ }
+ /* now output 'quad' with appropriate whitespace and line ending */
+ appendsOFile(fp, (numQuads == 0 ? " " : ""));
+ appendsOFile(fp, quad);
+ appendsOFile(fp, ((cur >= len)?"\n" :(numQuads==PR_MAXQUADS-1?"\n" : "")));
+ numQuads = (numQuads + 1) % PR_MAXQUADS;
+ }
+ appendcOFile(fp,'\n');
+
+ return 1;
+}
+
+static void writeQPString(OFile *fp, const char *s)
+{
+ const unsigned char *p = (const unsigned char *)s;
+ int current_column = 0;
+ static const char hexdigits[] = "0123456789ABCDEF";
+ bool white = false;
+ bool contWhite = false;
+ bool mb_p = false;
+
+ if (needsQuotedPrintable (s))
+ {
+ while (*p) {
+ if (*p == '\r' || *p == '\n')
+ {
+ /* Whitespace cannot be allowed to occur at the end of the line.
+ So we encode " \n" as " =\n\n", that is, the whitespace, a
+ soft line break, and then a hard line break.
+ */
+
+ if (white)
+ {
+ appendcOFile(fp,'=');
+ appendcOFile(fp,'\n');
+ appendcOFile(fp,'\t');
+ appendsOFile(fp,"=0D");
+ appendsOFile(fp,"=0A");
+ appendcOFile(fp,'=');
+ appendcOFile(fp,'\n');
+ appendcOFile(fp,'\t');
+ }
+ else
+ {
+ appendsOFile(fp,"=0D");
+ appendsOFile(fp,"=0A");
+ appendcOFile(fp,'=');
+ appendcOFile(fp,'\n');
+ appendcOFile(fp,'\t');
+ contWhite = false;
+ }
+
+ /* If its CRLF, swallow two chars instead of one. */
+ if (*p == '\r' && *(p+1) == '\n')
+ p++;
+ white = false;
+ current_column = 0;
+ }
+ else
+ {
+ if ((*p >= 33 && *p <= 60) || /* safe printing chars */
+ (*p >= 62 && *p <= 126) ||
+ (mb_p && (*p == 61 || *p == 127 || *p == 0x1B)))
+ {
+ appendcOFile(fp,*p);
+ current_column++;
+ white = false;
+ contWhite = false;
+ }
+ else if (*p == ' ' || *p == '\t') /* whitespace */
+ {
+ if (contWhite)
+ {
+ appendcOFile(fp,'=');
+ appendcOFile(fp,hexdigits[*p >> 4]);
+ appendcOFile(fp,hexdigits[*p & 0xF]);
+ current_column += 3;
+ contWhite = false;
+ }
+ else
+ {
+ appendcOFile(fp,*p);
+ current_column++;
+ }
+ white = true;
+ }
+ else /* print as =FF */
+ {
+ appendcOFile(fp,'=');
+ appendcOFile(fp,hexdigits[*p >> 4]);
+ appendcOFile(fp,hexdigits[*p & 0xF]);
+ current_column += 3;
+ white = false;
+ contWhite = false;
+ }
+
+ NS_ASSERTION(current_column <= 76, "1.10 <rhp@netscape.com> 06 Jan 2000 08:01"); /* Hard limit required by spec */
+
+ if (current_column >= 73 || ((*(p+1) == ' ') && (current_column + 3 >= 73))) /* soft line break: "=\r\n" */
+ {
+ appendcOFile(fp,'=');
+ appendcOFile(fp,'\n');
+ appendcOFile(fp,'\t');
+ current_column = 0;
+ if (white)
+ contWhite = true;
+ else
+ contWhite = false;
+ white = false;
+ }
+ }
+ p++;
+ } /* while */
+ } /* if */
+ else
+ {
+ while (*p) {
+ appendcOFile(fp,*p);
+ p++;
+ }
+ }
+}
+
+
+static void writeValue(OFile *fp, VObject *o, unsigned long size)
+{
+ if (o == 0) return;
+ switch (VALUE_TYPE(o)) {
+ case VCVT_USTRINGZ: {
+ char *s = fakeCString(USTRINGZ_VALUE_OF(o));
+ writeQPString(fp, s);
+ deleteString(s);
+ break;
+ }
+ case VCVT_STRINGZ: {
+ writeQPString(fp, STRINGZ_VALUE_OF(o));
+ break;
+ }
+ case VCVT_UINT: {
+ char buf[11];
+ sprintf(buf,"%u", INTEGER_VALUE_OF(o));
+ appendsOFile(fp, buf);
+ break;
+ }
+ case VCVT_ULONG: {
+ char buf[21];
+ sprintf(buf,"%lu", LONG_VALUE_OF(o));
+ appendsOFile(fp, buf);
+ break;
+ }
+ case VCVT_RAW: {
+ appendcOFile(fp,'\n');
+ writeBase64(fp,(unsigned char*)(ANY_VALUE_OF(o)),size);
+ break;
+ }
+ case VCVT_VOBJECT:
+ appendcOFile(fp,'\n');
+ writeVObject_(fp,VOBJECT_VALUE_OF(o));
+ break;
+ }
+}
+
+static void writeAttrValue(OFile *fp, VObject *o, int* length)
+{
+ int ilen = 0;
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ pi = lookupPropInfo(NAME_OF(o));
+ if (pi && ((pi->flags & PD_INTERNAL) != 0)) return;
+ appendcOFile(fp,';');
+ if (*length != -1)
+ (*length)++;
+ appendsOFile(fp,NAME_OF(o));
+ if (*length != -1)
+ (*length) += PL_strlen (NAME_OF(o));
+ }
+ else {
+ appendcOFile(fp,';');
+ (*length)++;
+ }
+ if (VALUE_TYPE(o)) {
+ appendcOFile(fp,'=');
+ if (*length != -1) {
+ (*length)++;
+ for (ilen = 0; ilen < MAXMOZPROPNAMESIZE - (*length); ilen++)
+ appendcOFile(fp,' ');
+ }
+ writeValue(fp,o,0);
+ }
+}
+
+static void writeGroup(OFile *fp, VObject *o)
+{
+ nsAutoCString buf(NAME_OF(o));
+
+ while ((o=isAPropertyOf(o,VCGroupingProp)) != 0) {
+ buf.Insert(NS_LITERAL_CSTRING("."), 0);
+ buf.Insert(STRINGZ_VALUE_OF(o), 0);
+ }
+ appendsOFile(fp, buf.get());
+}
+
+static int inList(const char **list, const char *s)
+{
+ if (list == 0) return 0;
+ while (*list) {
+ if (PL_strcasecmp(*list,s) == 0) return 1;
+ list++;
+ }
+ return 0;
+}
+
+static void writeProp(OFile *fp, VObject *o)
+{
+ int length = -1;
+ //int ilen = 0;
+
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ VObjectIterator t;
+ const char **fields_ = 0;
+ pi = lookupPropInfo(NAME_OF(o));
+ if (pi && ((pi->flags & PD_BEGIN) != 0)) {
+ writeVObject_(fp,o);
+ return;
+ }
+ if (isAPropertyOf(o,VCGroupingProp))
+ writeGroup(fp,o);
+ else
+ appendsOFile(fp,NAME_OF(o));
+ if (pi) fields_ = pi->fields;
+ initPropIterator(&t,o);
+ while (moreIteration(&t)) {
+ const char *s;
+ VObject *eachProp = nextVObject(&t);
+ s = NAME_OF(eachProp);
+ if (PL_strcasecmp(VCGroupingProp,s) && !inList(fields_,s))
+ writeAttrValue(fp,eachProp, &length);
+ }
+ if (fields_) {
+ int i = 0, n = 0;
+ const char** fields = fields_;
+ /* output prop as fields */
+ appendcOFile(fp,':');
+ while (*fields) {
+ VObject *tt = isAPropertyOf(o,*fields);
+ i++;
+ if (tt) n = i;
+ fields++;
+ }
+ fields = fields_;
+ for (i=0;i<n;i++) {
+ writeValue(fp,isAPropertyOf(o,*fields),0);
+ fields++;
+ if (i<(n-1)) appendcOFile(fp,';');
+ }
+ }
+ }
+
+ if (VALUE_TYPE(o)) {
+ unsigned long size = 0;
+ VObject *p = isAPropertyOf(o,VCDataSizeProp);
+ if (p) size = LONG_VALUE_OF(p);
+ appendcOFile(fp,':');
+ writeValue(fp,o,size);
+ }
+ appendcOFile(fp,'\n');
+}
+
+void writeVObject_(OFile *fp, VObject *o)
+{
+ //int ilen = 0;
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ pi = lookupPropInfo(NAME_OF(o));
+
+ if (pi && ((pi->flags & PD_BEGIN) != 0)) {
+ VObjectIterator t;
+ const char *begin = NAME_OF(o);
+ appendsOFile(fp,"begin:");
+ appendsOFile(fp,begin);
+ appendcOFile(fp,'\n');
+ initPropIterator(&t,o);
+ while (moreIteration(&t)) {
+ VObject *eachProp = nextVObject(&t);
+ writeProp(fp, eachProp);
+ }
+ appendsOFile(fp,"end:");
+ appendsOFile(fp,begin);
+ appendsOFile(fp,"\n\n");
+ }
+ }
+}
+
+char* writeMemVObject(char *s, int *len, VObject *o)
+{
+ OFile ofp;
+ initMemOFile(&ofp,s,len?*len:0);
+ writeVObject_(&ofp,o);
+ if (len) *len = ofp.len;
+ appendcOFile(&ofp,0);
+ return ofp.s;
+}
+
+extern "C"
+char * writeMemoryVObjects(char *s, int *len, VObject *list, bool expandSpaces)
+{
+ OFile ofp;
+ initMemOFile(&ofp,s,len?*len:0);
+ while (list) {
+ writeVObject_(&ofp,list);
+ list = nextVObjectInList(list);
+ }
+ if (len) *len = ofp.len;
+ appendcOFile(&ofp,0);
+ return ofp.s;
+}
+
+/*----------------------------------------------------------------------
+ APIs to do fake Unicode stuff.
+ ----------------------------------------------------------------------*/
+vwchar_t* fakeUnicode(const char *ps, int *bytes)
+{
+ vwchar_t *r, *pw;
+ int len = strlen(ps)+1;
+
+ pw = r = (vwchar_t*)PR_CALLOC(sizeof(vwchar_t)*len);
+ if (bytes)
+ *bytes = len * sizeof(vwchar_t);
+
+ while (*ps) {
+ if (*ps == '\n')
+ *pw = (vwchar_t)0x2028;
+ else if (*ps == '\r')
+ *pw = (vwchar_t)0x2029;
+ else
+ *pw = (vwchar_t)(unsigned char)*ps;
+ ps++; pw++;
+ }
+ *pw = (vwchar_t)0;
+
+ return r;
+}
+
+int uStrLen(const vwchar_t *u)
+{
+ if (!u)
+ return 0;
+ int i = 0;
+ while (*u != (vwchar_t)0) { u++; i++; }
+ return i;
+}
+
+char* fakeCString(const vwchar_t *u)
+{
+ char *s, *t;
+ int len = uStrLen(u) + 1;
+ t = s = (char*)PR_CALLOC(len);
+ if (u) {
+ while (*u) {
+ if (*u == (vwchar_t)0x2028)
+ *t = '\n';
+ else if (*u == (vwchar_t)0x2029)
+ *t = '\r';
+ else
+ *t = (char)*u;
+ u++; t++;
+ }
+ }
+ *t = 0;
+ return s;
+}
+
+const char* lookupStr(const char *s)
+{
+ StrItem *t;
+ unsigned int h = hashStr(s);
+ if ((t = strTbl[h]) != 0) {
+ do {
+ if (PL_strcasecmp(t->s,s) == 0) {
+ t->refCnt++;
+ return t->s;
+ }
+ t = t->next;
+ } while (t);
+ }
+ s = dupStr(s,0);
+ strTbl[h] = newStrItem(s,strTbl[h]);
+ return s;
+}
diff --git a/mailnews/addrbook/src/nsVCardObj.h b/mailnews/addrbook/src/nsVCardObj.h
new file mode 100644
index 000000000..b7ca36997
--- /dev/null
+++ b/mailnews/addrbook/src/nsVCardObj.h
@@ -0,0 +1,396 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+
+The vCard/vCalendar C interface is implemented in the set
+of files as follows:
+
+vcc.y, yacc source, and vcc.c, the yacc output you will use
+implements the core parser
+
+vobject.c implements an API that insulates the caller from
+the parser and changes in the vCard/vCalendar BNF
+
+port.h defines compilation environment dependent stuff
+
+vcc.h and vobject.h are header files for their .c counterparts
+
+vcaltmp.h and vcaltmp.c implement vCalendar "macro" functions
+which you may find useful.
+
+test.c is a standalone test driver that exercises some of
+the features of the APIs provided. Invoke test.exe on a
+VCARD/VCALENDAR input text file and you will see the pretty
+print output of the internal representation (this pretty print
+output should give you a good idea of how the internal
+representation looks like -- there is one such output in the
+following too). Also, a file with the .out suffix is generated
+to show that the internal representation can be written back
+in the original text format.
+
+For more information on this API see the readme.txt file
+which accompanied this distribution.
+
+ Also visit:
+
+ http://www.versit.com
+ http://www.ralden.com
+
+*/
+
+
+#ifndef __VOBJECT_H__
+#define __VOBJECT_H__ 1
+
+/*
+Unfortunately, on the Mac (and possibly other platforms) with our current, out-dated
+libraries (Plauger), |wchar_t| is defined incorrectly, which breaks vcards.
+
+We can't fix Plauger because it doesn't come with source. Later, when we
+upgrade to MSL, we can make this evil hack go away. In the mean time,
+vcards are not allowed to use the (incorrectly defined) |wchar_t| type. Instead,
+they will use an appropriately defined local type |vwchar_t|.
+*/
+
+typedef wchar_t vwchar_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VC7bitProp "7bit"
+#define VC8bitProp "8bit"
+#define VCAAlarmProp "aalarm"
+#define VCAdditionalNamesProp "addn"
+#define VCAdrProp "adr"
+#define VCAgentProp "agent"
+#define VCAIFFProp "aiff"
+#define VCAOLProp "aol"
+#define VCAppleLinkProp "applelink"
+#define VCAttachProp "attach"
+#define VCAttendeeProp "attendee"
+#define VCATTMailProp "attmail"
+#define VCAudioContentProp "audiocontent"
+#define VCAVIProp "avi"
+#define VCBase64Prop "base64"
+#define VCBBSProp "bbs"
+#define VCBirthDateProp "bday"
+#define VCBMPProp "bmp"
+#define VCBodyProp "body"
+#define VCBusinessRoleProp "role"
+#define VCCalProp "vcalendar"
+#define VCCaptionProp "cap"
+#define VCCardProp "vcard"
+#define VCCarProp "car"
+#define VCCategoriesProp "categories"
+#define VCCellularProp "cell"
+#define VCCGMProp "cgm"
+#define VCCharSetProp "cs"
+#define VCCIDProp "cid"
+#define VCCISProp "cis"
+#define VCCityProp "l"
+#define VCClassProp "class"
+#define VCCommentProp "note"
+#define VCCompletedProp "completed"
+#define VCContentIDProp "content-id"
+#define VCCountryNameProp "c"
+#define VCDAlarmProp "dalarm"
+#define VCDataSizeProp "datasize"
+#define VCDayLightProp "daylight"
+#define VCDCreatedProp "dcreated"
+#define VCDeliveryLabelProp "label"
+#define VCDescriptionProp "description"
+#define VCDIBProp "dib"
+#define VCDisplayStringProp "displaystring"
+#define VCDomesticProp "dom"
+#define VCDTendProp "dtend"
+#define VCDTstartProp "dtstart"
+#define VCDueProp "due"
+#define VCEmailAddressProp "email"
+#define VCEncodingProp "encoding"
+#define VCEndProp "end"
+#define VCEventProp "vevent"
+#define VCEWorldProp "eworld"
+#define VCExNumProp "exnum"
+#define VCExpDateProp "exdate"
+#define VCExpectProp "expect"
+#define VCExtAddressProp "ext add"
+#define VCFamilyNameProp "f"
+#define VCFaxProp "fax"
+#define VCFullNameProp "fn"
+#define VCGeoProp "geo"
+#define VCGeoLocationProp "geo"
+#define VCGIFProp "gif"
+#define VCGivenNameProp "g"
+#define VCGroupingProp "grouping"
+#define VCHomeProp "home"
+#define VCIBMMailProp "ibmmail"
+#define VCInlineProp "inline"
+#define VCInternationalProp "intl"
+#define VCInternetProp "internet"
+#define VCISDNProp "isdn"
+#define VCJPEGProp "jpeg"
+#define VCLanguageProp "lang"
+#define VCLastModifiedProp "last-modified"
+#define VCLastRevisedProp "rev"
+#define VCLocationProp "location"
+#define VCLogoProp "logo"
+#define VCMailerProp "mailer"
+#define VCMAlarmProp "malarm"
+#define VCMCIMailProp "mcimail"
+#define VCMessageProp "msg"
+#define VCMETProp "met"
+#define VCModemProp "modem"
+#define VCMPEG2Prop "mpeg2"
+#define VCMPEGProp "mpeg"
+#define VCMSNProp "msn"
+#define VCNamePrefixesProp "npre"
+#define VCNameProp "n"
+#define VCNameSuffixesProp "nsuf"
+#define VCNoteProp "note"
+#define VCOrgNameProp "orgname"
+#define VCOrgProp "org"
+#define VCOrgUnit2Prop "oun2"
+#define VCOrgUnit3Prop "oun3"
+#define VCOrgUnit4Prop "oun4"
+#define VCOrgUnitProp "oun"
+#define VCPagerProp "pager"
+#define VCPAlarmProp "palarm"
+#define VCParcelProp "parcel"
+#define VCPartProp "part"
+#define VCPCMProp "pcm"
+#define VCPDFProp "pdf"
+#define VCPGPProp "pgp"
+#define VCPhotoProp "photo"
+#define VCPICTProp "pict"
+#define VCPMBProp "pmb"
+#define VCPostalBoxProp "box"
+#define VCPostalCodeProp "pc"
+#define VCPostalProp "postal"
+#define VCPowerShareProp "powershare"
+#define VCPreferredProp "pref"
+#define VCPriorityProp "priority"
+#define VCProcedureNameProp "procedurename"
+#define VCProdIdProp "prodid"
+#define VCProdigyProp "prodigy"
+#define VCPronunciationProp "sound"
+#define VCPSProp "ps"
+#define VCPublicKeyProp "key"
+#define VCQPProp "qp"
+#define VCQuickTimeProp "qtime"
+#define VCQuotedPrintableProp "quoted-printable"
+#define VCRDateProp "rdate"
+#define VCRegionProp "r"
+#define VCRelatedToProp "related-to"
+#define VCRepeatCountProp "repeatcount"
+#define VCResourcesProp "resources"
+#define VCRNumProp "rnum"
+#define VCRoleProp "role"
+#define VCRRuleProp "rrule"
+#define VCRSVPProp "rsvp"
+#define VCRunTimeProp "runtime"
+#define VCSequenceProp "sequence"
+#define VCSnoozeTimeProp "snoozetime"
+#define VCStartProp "start"
+#define VCStatusProp "status"
+#define VCStreetAddressProp "street"
+#define VCSubTypeProp "subtype"
+#define VCSummaryProp "summary"
+#define VCTelephoneProp "tel"
+#define VCTIFFProp "tiff"
+#define VCTimeZoneProp "tz"
+#define VCTitleProp "title"
+#define VCTLXProp "tlx"
+#define VCTodoProp "vtodo"
+#define VCTranspProp "transp"
+#define VCUniqueStringProp "uid"
+#define VCURLProp "url"
+#define VCURLValueProp "urlval"
+#define VCValueProp "value"
+#define VCVersionProp "version"
+#define VCVideoProp "video"
+#define VCVoiceProp "voice"
+#define VCWAVEProp "wave"
+#define VCWMFProp "wmf"
+#define VCWorkProp "work"
+#define VCX400Prop "x400"
+#define VCX509Prop "x509"
+#define VCXRuleProp "xrule"
+#define VCCooltalk "x-mozilla-cpt"
+#define VCCooltalkAddress "x-moxilla-cpadr"
+#define VCUseServer "x-mozilla-cpsrv"
+#define VCUseHTML "x-mozilla-html"
+
+/* return type of vObjectValueType: */
+#define VCVT_NOVALUE 0
+ /* if the VObject has no value associated with it. */
+#define VCVT_STRINGZ 1
+ /* if the VObject has value set by setVObjectStringZValue. */
+#define VCVT_USTRINGZ 2
+ /* if the VObject has value set by setVObjectUStringZValue. */
+#define VCVT_UINT 3
+ /* if the VObject has value set by setVObjectIntegerValue. */
+#define VCVT_ULONG 4
+ /* if the VObject has value set by setVObjectLongValue. */
+#define VCVT_RAW 5
+ /* if the VObject has value set by setVObjectAnyValue. */
+#define VCVT_VOBJECT 6
+ /* if the VObject has value set by setVObjectVObjectValue. */
+
+#define NAME_OF(o) o->id
+#define VALUE_TYPE(o) o->valType
+#define STRINGZ_VALUE_OF(o) o->val.strs
+#define USTRINGZ_VALUE_OF(o) o->val.ustrs
+#define INTEGER_VALUE_OF(o) o->val.i
+#define LONG_VALUE_OF(o) o->val.l
+#define ANY_VALUE_OF(o) o->val.any
+#define VOBJECT_VALUE_OF(o) o->val.vobj
+
+typedef struct VObject VObject;
+
+typedef union ValueItem {
+ const char *strs;
+ const vwchar_t *ustrs;
+ unsigned int i;
+ unsigned long l;
+ void *any;
+ VObject *vobj;
+ } ValueItem;
+
+struct VObject {
+ VObject *next;
+ const char *id;
+ VObject *prop;
+ unsigned short valType;
+ ValueItem val;
+ };
+
+typedef struct StrItem StrItem;
+
+struct StrItem {
+ StrItem *next;
+ const char *s;
+ unsigned int refCnt;
+ };
+
+typedef struct OFile {
+ char *s;
+ int len;
+ int limit;
+ int alloc:1;
+ int fail:1;
+ } OFile;
+
+typedef struct VObjectIterator {
+ VObject* start;
+ VObject* next;
+ } VObjectIterator;
+
+VObject* newVObject(const char *id);
+void deleteVObject(VObject *p);
+char* dupStr(const char *s, unsigned int size);
+extern "C" void deleteString(char *p);
+void unUseStr(const char *s);
+
+void setVObjectName(VObject *o, const char* id);
+void setVObjectStringZValue(VObject *o, const char *s);
+void setVObjectStringZValue_(VObject *o, const char *s);
+void setVObjectUStringZValue(VObject *o, const vwchar_t *s);
+void setVObjectUStringZValue_(VObject *o, const vwchar_t *s);
+void setVObjectIntegerValue(VObject *o, unsigned int i);
+void setVObjectLongValue(VObject *o, unsigned long l);
+void setVObjectAnyValue(VObject *o, void *t);
+VObject* setValueWithSize(VObject *prop, void *val, unsigned int size);
+VObject* setValueWithSize_(VObject *prop, void *val, unsigned int size);
+
+const char* vObjectName(VObject *o);
+const char* vObjectStringZValue(VObject *o);
+const vwchar_t* vObjectUStringZValue(VObject *o);
+unsigned int vObjectIntegerValue(VObject *o);
+unsigned long vObjectLongValue(VObject *o);
+void* vObjectAnyValue(VObject *o);
+VObject* vObjectVObjectValue(VObject *o);
+void setVObjectVObjectValue(VObject *o, VObject *p);
+
+VObject* addVObjectProp(VObject *o, VObject *p);
+VObject* addProp(VObject *o, const char *id);
+VObject* addProp_(VObject *o, const char *id);
+VObject* addPropValue(VObject *o, const char *p, const char *v);
+VObject* addPropSizedValue_(VObject *o, const char *p, const char *v, unsigned int size);
+VObject* addPropSizedValue(VObject *o, const char *p, const char *v, unsigned int size);
+VObject* addGroup(VObject *o, const char *g);
+void addList(VObject **o, VObject *p);
+
+VObject* isAPropertyOf(VObject *o, const char *id);
+
+VObject* nextVObjectInList(VObject *o);
+void initPropIterator(VObjectIterator *i, VObject *o);
+int moreIteration(VObjectIterator *i);
+VObject* nextVObject(VObjectIterator *i);
+
+void writeVObject_(OFile *fp, VObject *o);
+char* writeMemVObject(char *s, int *len, VObject *o);
+extern "C" char* writeMemoryVObjects(char *s, int *len, VObject *list, bool expandSpaces);
+
+const char* lookupStr(const char *s);
+
+void cleanVObject(VObject *o);
+void cleanVObjects(VObject *list);
+
+const char* lookupProp(const char* str);
+const char* lookupProp_(const char* str);
+
+vwchar_t* fakeUnicode(const char *ps, int *bytes);
+int uStrLen(const vwchar_t *u);
+char* fakeCString(const vwchar_t *u);
+
+#define MAXPROPNAMESIZE 256
+#define MAXMOZPROPNAMESIZE 16
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VOBJECT_H__ */
+
+
diff --git a/mailnews/addrbook/src/nsWabAddressBook.cpp b/mailnews/addrbook/src/nsWabAddressBook.cpp
new file mode 100644
index 000000000..9c991ddf5
--- /dev/null
+++ b/mailnews/addrbook/src/nsWabAddressBook.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <tchar.h>
+#include "nsWabAddressBook.h"
+#include "mozilla/Logging.h"
+#include <algorithm>
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo* gWabAddressBookLog
+ = PR_NewLogModule("nsWabAddressBookLog");
+#endif
+
+#define PRINTF(args) MOZ_LOG(gWabAddressBookLog, mozilla::LogLevel::Debug, args)
+
+using namespace mozilla;
+
+HMODULE nsWabAddressBook::mLibrary = NULL ;
+int32_t nsWabAddressBook::mLibUsage = 0 ;
+LPWABOPEN nsWabAddressBook::mWABOpen = NULL ;
+LPWABOBJECT nsWabAddressBook::mRootSession = NULL ;
+LPADRBOOK nsWabAddressBook::mRootBook = NULL ;
+
+BOOL nsWabAddressBook::LoadWabLibrary(void)
+{
+ if (mLibrary) { ++ mLibUsage ; return TRUE ; }
+ // We try to fetch the location of the WAB DLL from the registry
+ TCHAR wabDLLPath [MAX_PATH] ;
+ DWORD keyType = 0 ;
+ ULONG byteCount = sizeof(wabDLLPath) ;
+ HKEY keyHandle = NULL ;
+ wabDLLPath [MAX_PATH - 1] = 0 ;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WAB_DLL_PATH_KEY, 0, KEY_READ, &keyHandle) == ERROR_SUCCESS) {
+ RegQueryValueEx(keyHandle, "", NULL, &keyType, (LPBYTE) wabDLLPath, &byteCount) ;
+ if (keyType == REG_EXPAND_SZ) {
+ // Expand the environment variables
+ DWORD bufferSize = ExpandEnvironmentStrings(wabDLLPath, NULL, 0);
+ if (bufferSize && bufferSize < MAX_PATH) {
+ TCHAR tmp[MAX_PATH];
+ ExpandEnvironmentStrings(wabDLLPath, tmp, bufferSize);
+ _tcscpy(wabDLLPath, tmp);
+ }
+ else {
+ return FALSE;
+ }
+ }
+ }
+ else {
+ if (GetSystemDirectory(wabDLLPath, MAX_PATH)) {
+ _tcsncat(wabDLLPath, WAB_DLL_NAME,
+ std::min(_tcslen(WAB_DLL_NAME), MAX_PATH - _tcslen(wabDLLPath) - 1));
+ }
+ else {
+ return FALSE;
+ }
+ }
+ if (keyHandle) { RegCloseKey(keyHandle) ; }
+ mLibrary = LoadLibrary( (lstrlen(wabDLLPath)) ? wabDLLPath : WAB_DLL_NAME );
+ if (!mLibrary) { return FALSE ; }
+ ++ mLibUsage ;
+ mWABOpen = reinterpret_cast<LPWABOPEN>(GetProcAddress(mLibrary, "WABOpen")) ;
+ if (!mWABOpen) { return FALSE ; }
+ HRESULT retCode = mWABOpen(&mRootBook, &mRootSession, NULL, 0) ;
+
+ if (HR_FAILED(retCode)) {
+ PRINTF(("Cannot initialize WAB %08x.\n", retCode)) ; return FALSE ;
+ }
+ return TRUE ;
+}
+
+void nsWabAddressBook::FreeWabLibrary(void)
+{
+ if (mLibrary) {
+ if (-- mLibUsage == 0) {
+ if (mRootBook) { mRootBook->Release() ; }
+ if (mRootSession) { mRootSession->Release() ; }
+ FreeLibrary(mLibrary) ;
+ mLibrary = NULL ;
+ }
+ }
+}
+
+nsWabAddressBook::nsWabAddressBook(void)
+: nsAbWinHelper()
+{
+ BOOL result = Initialize() ;
+
+ NS_ASSERTION(result == TRUE, "Couldn't initialize Wab Helper") ;
+ MOZ_COUNT_CTOR(nsWabAddressBook) ;
+}
+
+nsWabAddressBook::~nsWabAddressBook(void)
+{
+ MutexAutoLock guard(*mMutex) ;
+ FreeWabLibrary() ;
+ MOZ_COUNT_DTOR(nsWabAddressBook) ;
+}
+
+BOOL nsWabAddressBook::Initialize(void)
+{
+ if (mAddressBook) { return TRUE ; }
+ MutexAutoLock guard(*mMutex) ;
+
+ if (!LoadWabLibrary()) {
+ PRINTF(("Cannot load library.\n")) ;
+ return FALSE ;
+ }
+ mAddressBook = mRootBook ;
+ return TRUE ;
+}
+
+void nsWabAddressBook::AllocateBuffer(ULONG aByteCount, LPVOID *aBuffer)
+{
+ mRootSession->AllocateBuffer(aByteCount, aBuffer) ;
+}
+
+void nsWabAddressBook::FreeBuffer(LPVOID aBuffer)
+{
+ mRootSession->FreeBuffer(aBuffer) ;
+}
+
+
+
+
+
+
diff --git a/mailnews/addrbook/src/nsWabAddressBook.h b/mailnews/addrbook/src/nsWabAddressBook.h
new file mode 100644
index 000000000..35b76b213
--- /dev/null
+++ b/mailnews/addrbook/src/nsWabAddressBook.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsWabAddressBook_h___
+#define nsWabAddressBook_h___
+
+#include "mozilla/Attributes.h"
+#include "nsAbWinHelper.h"
+#include <wab.h>
+
+class nsWabAddressBook : public nsAbWinHelper
+{
+public :
+ nsWabAddressBook(void) ;
+ virtual ~nsWabAddressBook(void) ;
+
+protected :
+ // Session and address book that will be shared by all instances
+ // (see nsMapiAddressBook.h for details)
+ static LPWABOBJECT mRootSession ;
+ static LPADRBOOK mRootBook ;
+ // Class members to handle library loading/entry points
+ static int32_t mLibUsage ;
+ static HMODULE mLibrary ;
+ static LPWABOPEN mWABOpen ;
+
+ // Load the WAB environment
+ BOOL Initialize(void) ;
+ // Allocation of a buffer for transmission to interfaces
+ virtual void AllocateBuffer(ULONG aByteCount, LPVOID *aBuffer) override;
+ // Destruction of a buffer provided by the interfaces
+ virtual void FreeBuffer(LPVOID aBuffer) override;
+ // Manage the library
+ static BOOL LoadWabLibrary(void) ;
+ static void FreeWabLibrary(void) ;
+
+private :
+} ;
+
+// Additional definitions for WAB stuff. These properties are
+// only defined with regards to the default character sizes,
+// and not in two _A and _W versions...
+#define PR_BUSINESS_ADDRESS_CITY_A PR_LOCALITY_A
+#define PR_BUSINESS_ADDRESS_COUNTRY_A PR_COUNTRY_A
+#define PR_BUSINESS_ADDRESS_POSTAL_CODE_A PR_POSTAL_CODE_A
+#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A PR_STATE_OR_PROVINCE_A
+#define PR_BUSINESS_ADDRESS_STREET_A PR_STREET_ADDRESS_A
+
+#define PR_BUSINESS_ADDRESS_CITY_W PR_LOCALITY_W
+#define PR_BUSINESS_ADDRESS_COUNTRY_W PR_COUNTRY_W
+#define PR_BUSINESS_ADDRESS_POSTAL_CODE_W PR_POSTAL_CODE_W
+#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_W PR_STATE_OR_PROVINCE_W
+#define PR_BUSINESS_ADDRESS_STREET_W PR_STREET_ADDRESS_W
+
+#endif // nsWABAddressBook_h___
+