diff options
Diffstat (limited to 'comm/mailnews/addrbook/modules/LDAPReplicationService.jsm')
-rw-r--r-- | comm/mailnews/addrbook/modules/LDAPReplicationService.jsm | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/comm/mailnews/addrbook/modules/LDAPReplicationService.jsm b/comm/mailnews/addrbook/modules/LDAPReplicationService.jsm new file mode 100644 index 0000000000..2a11d15eee --- /dev/null +++ b/comm/mailnews/addrbook/modules/LDAPReplicationService.jsm @@ -0,0 +1,233 @@ +/* -*- Mode: JavaScript; 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/. */ + +const EXPORTED_SYMBOLS = ["LDAPReplicationService"]; + +const { LDAPListenerBase } = ChromeUtils.import( + "resource:///modules/LDAPListenerBase.jsm" +); +var { SQLiteDirectory } = ChromeUtils.import( + "resource:///modules/SQLiteDirectory.jsm" +); + +/** + * A service to replicate a LDAP directory to a local SQLite db. + * + * @implements {nsIAbLDAPReplicationService} + * @implements {nsILDAPMessageListener} + */ +class LDAPReplicationService extends LDAPListenerBase { + QueryInterface = ChromeUtils.generateQI([ + "nsIAbLDAPReplicationService", + "nsILDAPMessageListener", + ]); + + /** + * @see nsIAbLDAPReplicationService + */ + startReplication(directory, progressListener) { + this._directory = directory; + this._listener = progressListener; + this._attrMap = directory.attributeMap; + this._count = 0; + this._cards = []; + this._connection = Cc[ + "@mozilla.org/network/ldap-connection;1" + ].createInstance(Ci.nsILDAPConnection); + this._operation = Cc[ + "@mozilla.org/network/ldap-operation;1" + ].createInstance(Ci.nsILDAPOperation); + + this._connection.init( + directory.lDAPURL, + directory.authDn, + this, + null, + directory.protocolVersion + ); + } + + /** + * @see nsIAbLDAPReplicationService + */ + cancelReplication(directory) { + this._operation.abandonExt(); + this.done(false); + } + + /** + * @see nsIAbLDAPReplicationService + */ + done(success) { + this._done(success); + } + + /** + * @see nsILDAPMessageListener + */ + onLDAPMessage(msg) { + switch (msg.type) { + case Ci.nsILDAPMessage.RES_BIND: + this._onLDAPBind(msg); + break; + case Ci.nsILDAPMessage.RES_SEARCH_ENTRY: + this._onLDAPSearchEntry(msg); + break; + case Ci.nsILDAPMessage.RES_SEARCH_RESULT: + this._onLDAPSearchResult(msg); + break; + default: + break; + } + } + + /** + * @see nsILDAPMessageListener + */ + onLDAPError(status, secInfo, location) { + this.done(false); + } + + /** + * @see LDAPListenerBase + */ + _actionOnBindSuccess() { + this._openABForReplicationDir(); + let ldapUrl = this._directory.lDAPURL; + this._operation.init(this._connection, this, null); + this._listener.onStateChange( + null, + null, + Ci.nsIWebProgressListener.STATE_START, + Cr.NS_OK + ); + this._operation.searchExt( + ldapUrl.dn, + ldapUrl.scope, + ldapUrl.filter, + ldapUrl.attributes, + 0, + 0 + ); + } + + /** + * @see LDAPListenerBase + */ + _actionOnBindFailure() { + this._done(false); + } + + /** + * Handler of nsILDAPMessage.RES_SEARCH_ENTRY message. + * + * @param {nsILDAPMessage} msg - The received LDAP message. + */ + async _onLDAPSearchEntry(msg) { + let newCard = Cc["@mozilla.org/addressbook/cardproperty;1"].createInstance( + Ci.nsIAbCard + ); + this._attrMap.setCardPropertiesFromLDAPMessage(msg, newCard); + this._cards.push(newCard); + this._count++; + if (this._count % 10 == 0) { + // inform the listener every 10 entries + this._listener.onProgressChange( + null, + null, + this._count, + -1, + this._count, + -1 + ); + } + if (this._count % 100 == 0 && !this._writePromise) { + // Write to the db to release some memory. + this._writePromise = this._replicationDB.bulkAddCards(this._cards); + this._cards = []; + await this._writePromise; + this._writePromise = null; + } + } + + /** + * Handler of nsILDAPMessage.RES_SEARCH_RESULT message. + * + * @param {nsILDAPMessage} msg - The received LDAP message. + */ + async _onLDAPSearchResult(msg) { + if ( + msg.errorCode == Ci.nsILDAPErrors.SUCCESS || + msg.errorCode == Ci.nsILDAPErrors.SIZELIMIT_EXCEEDED + ) { + if (this._writePromise) { + await this._writePromise; + } + await this._replicationDB.bulkAddCards(this._cards); + this.done(true); + return; + } + this.done(false); + } + + /** + * Init a jsaddrbook from the replicationFileName of the current LDAP directory. + */ + _openABForReplicationDir() { + this._oldReplicationFileName = this._directory.replicationFileName; + this._replicationFile = this._directory.replicationFile; + if (this._replicationFile.exists()) { + // If the database file already exists, create a new one here, and replace + // the old file in _done when success. + this._replicationFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); + // What we need is the unique file name, _replicationDB will create an + // empty database file. + this._replicationFile.remove(false); + // Set replicationFileName to the new db file name, so that _replicationDB + // works correctly. + this._directory.replicationFileName = this._replicationFile.leafName; + } + + this._replicationDB = new SQLiteDirectory(); + this._replicationDB.init(`jsaddrbook://${this._replicationFile.leafName}`); + } + + /** + * Clean up depending on whether replication succeeded or failed, emit + * STATE_STOP event. + * + * @param {bool} success - Replication succeeded or failed. + */ + async _done(success) { + this._cards = []; + if (this._replicationDB) { + // Close the db. + await this._replicationDB.cleanUp(); + } + if (success) { + // Replace the old db file with new db file. + this._replicationFile.moveTo(null, this._oldReplicationFileName); + } else if ( + this._replicationFile && + this._replicationFile.path != this._oldReplicationFileName + ) { + this._replicationFile.remove(false); + } + if (this._oldReplicationFileName) { + // Reset replicationFileName to the old db file name. + this._directory.replicationFileName = this._oldReplicationFileName; + } + this._listener.onStateChange( + null, + null, + Ci.nsIWebProgressListener.STATE_STOP, + success ? Cr.NS_OK : Cr.NS_ERROR_FAILURE + ); + } +} + +LDAPReplicationService.prototype.classID = Components.ID( + "{dbe204e8-ae09-11eb-b4c8-a7e4b3e6e82e}" +); |