diff options
Diffstat (limited to 'ldap/xpcom/src/nsLDAPConnection.cpp')
-rw-r--r-- | ldap/xpcom/src/nsLDAPConnection.cpp | 737 |
1 files changed, 737 insertions, 0 deletions
diff --git a/ldap/xpcom/src/nsLDAPConnection.cpp b/ldap/xpcom/src/nsLDAPConnection.cpp new file mode 100644 index 000000000..834e803b4 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPConnection.cpp @@ -0,0 +1,737 @@ +/* -*- 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 "nsLDAPInternal.h" +#include "nsIServiceManager.h" +#include "nsStringGlue.h" +#include "nsIComponentManager.h" +#include "nsIDNSRecord.h" +#include "nsLDAPConnection.h" +#include "nsLDAPMessage.h" +#include "nsThreadUtils.h" +#include "nsIConsoleService.h" +#include "nsIDNSService.h" +#include "nsINetAddr.h" +#include "nsIRequestObserver.h" +#include "nsError.h" +#include "nsLDAPOperation.h" +#include "nsILDAPErrors.h" +#include "nsIClassInfoImpl.h" +#include "nsILDAPURL.h" +#include "nsIObserverService.h" +#include "mozilla/Services.h" +#include "nsMemory.h" +#include "nsLDAPUtils.h" +#include "nsProxyRelease.h" +#include "mozilla/Logging.h" +#include "mozilla/Attributes.h" + +using namespace mozilla; + +const char kDNSServiceContractId[] = "@mozilla.org/network/dns-service;1"; + +// constructor +// +nsLDAPConnection::nsLDAPConnection() + : mConnectionHandle(nullptr), + mPendingOperationsMutex("nsLDAPConnection.mPendingOperationsMutex"), + mPendingOperations(10), + mSSL(false), + mVersion(nsILDAPConnection::VERSION3), + mDNSRequest(nullptr) +{ +} + +// destructor +// +nsLDAPConnection::~nsLDAPConnection() +{ + nsCOMPtr<nsIObserverService> obsServ = + do_GetService(NS_OBSERVERSERVICE_CONTRACTID); + if (obsServ) + obsServ->RemoveObserver(this, "profile-change-net-teardown"); + Close(); +} + +NS_IMPL_ADDREF(nsLDAPConnection) +NS_IMPL_RELEASE(nsLDAPConnection) +NS_IMPL_CLASSINFO(nsLDAPConnection, NULL, nsIClassInfo::THREADSAFE, + NS_LDAPCONNECTION_CID) + +NS_INTERFACE_MAP_BEGIN(nsLDAPConnection) + NS_INTERFACE_MAP_ENTRY(nsILDAPConnection) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIDNSListener) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPConnection) + NS_IMPL_QUERY_CLASSINFO(nsLDAPConnection) +NS_INTERFACE_MAP_END_THREADSAFE +NS_IMPL_CI_INTERFACE_GETTER(nsLDAPConnection, nsILDAPConnection, + nsISupportsWeakReference, nsIDNSListener, + nsIObserver) + +NS_IMETHODIMP +nsLDAPConnection::Init(nsILDAPURL *aUrl, const nsACString &aBindName, + nsILDAPMessageListener *aMessageListener, + nsISupports *aClosure, uint32_t aVersion) +{ + NS_ENSURE_ARG_POINTER(aUrl); + NS_ENSURE_ARG_POINTER(aMessageListener); + + nsresult rv; + nsCOMPtr<nsIObserverService> obsServ = + do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + // We have to abort all LDAP pending operation before shutdown. + obsServ->AddObserver(this, "profile-change-net-teardown", true); + + // Save various items that we'll use later + mBindName.Assign(aBindName); + mClosure = aClosure; + mInitListener = aMessageListener; + + // Make sure we haven't called Init earlier, i.e. there's a DNS + // request pending. + NS_ASSERTION(!mDNSRequest, "nsLDAPConnection::Init() " + "Connection was already initialized\n"); + + // Check and save the version number + if (aVersion != nsILDAPConnection::VERSION2 && + aVersion != nsILDAPConnection::VERSION3) { + NS_ERROR("nsLDAPConnection::Init(): illegal version"); + return NS_ERROR_ILLEGAL_VALUE; + } + mVersion = aVersion; + + // Get the port number, SSL flag for use later, once the DNS server(s) + // has resolved the host part. + rv = aUrl->GetPort(&mPort); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t options; + rv = aUrl->GetOptions(&options); + NS_ENSURE_SUCCESS(rv, rv); + + mSSL = options & nsILDAPURL::OPT_SECURE; + + nsCOMPtr<nsIThread> curThread = do_GetCurrentThread(); + if (!curThread) { + NS_ERROR("nsLDAPConnection::Init(): couldn't get current thread"); + return NS_ERROR_FAILURE; + } + + // Do the pre-resolve of the hostname, using the DNS service. This + // will also initialize the LDAP connection properly, once we have + // the IPs resolved for the hostname. This includes creating the + // new thread for this connection. + // + // XXX - What return codes can we expect from the DNS service? + // + nsCOMPtr<nsIDNSService> + pDNSService(do_GetService(kDNSServiceContractId, &rv)); + + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPConnection::Init(): couldn't create the DNS Service object"); + return NS_ERROR_FAILURE; + } + + rv = aUrl->GetAsciiHost(mDNSHost); + NS_ENSURE_SUCCESS(rv, rv); + + // if the caller has passed in a space-delimited set of hosts, as the + // ldap c-sdk allows, strip off the trailing hosts for now. + // Soon, we'd like to make multiple hosts work, but now make + // at least the first one work. + LdapCompressWhitespace(mDNSHost); + + int32_t spacePos = mDNSHost.FindChar(' '); + // trim off trailing host(s) + if (spacePos != kNotFound) + mDNSHost.SetLength(spacePos); + + rv = pDNSService->AsyncResolve(mDNSHost, 0, this, curThread, + getter_AddRefs(mDNSRequest)); + + if (NS_FAILED(rv)) { + switch (rv) { + case NS_ERROR_OUT_OF_MEMORY: + case NS_ERROR_UNKNOWN_HOST: + case NS_ERROR_FAILURE: + case NS_ERROR_OFFLINE: + break; + + default: + rv = NS_ERROR_UNEXPECTED; + } + mDNSHost.Truncate(); + } + return rv; +} + +// this might get exposed to clients, so we've broken it +// out of the destructor. +void +nsLDAPConnection::Close() +{ + int rc; + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("unbinding\n")); + + if (mConnectionHandle) { + // note that the ldap_unbind() call in the 5.0 version of the LDAP C SDK + // appears to be exactly identical to ldap_unbind_s(), so it may in fact + // still be synchronous + // + rc = ldap_unbind(mConnectionHandle); +#ifdef PR_LOGGING + if (rc != LDAP_SUCCESS) { + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, + ("nsLDAPConnection::Close(): %s\n", + ldap_err2string(rc))); + } +#endif + mConnectionHandle = nullptr; + } + + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("unbound\n")); + + NS_ASSERTION(!mThread || NS_SUCCEEDED(mThread->Shutdown()), + "Failed to shutdown thread cleanly"); + + // Cancel the DNS lookup if needed, and also drop the reference to the + // Init listener (if still there). + // + if (mDNSRequest) { + mDNSRequest->Cancel(NS_ERROR_ABORT); + mDNSRequest = nullptr; + } + mInitListener = nullptr; + +} + +NS_IMETHODIMP +nsLDAPConnection::Observe(nsISupports *aSubject, const char *aTopic, + const char16_t *aData) +{ + if (!strcmp(aTopic, "profile-change-net-teardown")) { + // Abort all ldap requests. + /* We cannot use enumerate function to abort operations because + * nsILDAPOperation::AbandonExt() is modifying list of operations + * and this leads to starvation. + * We have to do a copy of pending operations. + */ + nsTArray<nsILDAPOperation*> pending_operations; + { + MutexAutoLock lock(mPendingOperationsMutex); + for (auto iter = mPendingOperations.Iter(); !iter.Done(); iter.Next()) { + pending_operations.AppendElement(iter.UserData()); + } + } + for (uint32_t i = 0; i < pending_operations.Length(); i++) { + pending_operations[i]->AbandonExt(); + } + Close(); + } else { + NS_NOTREACHED("unexpected topic"); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPConnection::GetClosure(nsISupports **_retval) +{ + if (!_retval) { + return NS_ERROR_ILLEGAL_VALUE; + } + NS_IF_ADDREF(*_retval = mClosure); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPConnection::SetClosure(nsISupports *aClosure) +{ + mClosure = aClosure; + return NS_OK; +} + +// who we're binding as +// +// readonly attribute AUTF8String bindName +// +NS_IMETHODIMP +nsLDAPConnection::GetBindName(nsACString& _retval) +{ + _retval.Assign(mBindName); + return NS_OK; +} + +// wrapper for ldap_get_lderrno +// XXX should copy before returning +// +NS_IMETHODIMP +nsLDAPConnection::GetLdErrno(nsACString& matched, nsACString& errString, + int32_t *_retval) +{ + char *match, *err; + + NS_ENSURE_ARG_POINTER(_retval); + + *_retval = ldap_get_lderrno(mConnectionHandle, &match, &err); + matched.Assign(match); + errString.Assign(err); + return NS_OK; +} + +// return the error string corresponding to GetLdErrno. +// +// XXX - deal with optional params +// XXX - how does ldap_perror know to look at the global errno? +// +NS_IMETHODIMP +nsLDAPConnection::GetErrorString(char16_t **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + // get the error string + // + char *rv = ldap_err2string(ldap_get_lderrno(mConnectionHandle, 0, 0)); + if (!rv) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // make a copy using the XPCOM shared allocator + // + *_retval = ToNewUnicode(NS_ConvertUTF8toUTF16(rv)); + if (!*_retval) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +/** + * Add an nsILDAPOperation to the list of operations pending on + * this connection. This is also mainly intended for use by the + * nsLDAPOperation code. + */ +nsresult +nsLDAPConnection::AddPendingOperation(uint32_t aOperationID, nsILDAPOperation *aOperation) +{ + NS_ENSURE_ARG_POINTER(aOperation); + + nsIRunnable* runnable = new nsLDAPConnectionRunnable(aOperationID, aOperation, + this); + { + MutexAutoLock lock(mPendingOperationsMutex); + mPendingOperations.Put((uint32_t)aOperationID, aOperation); + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("pending operation added; total pending operations now = %d\n", + mPendingOperations.Count())); + } + + nsresult rv; + if (!mThread) + { + rv = NS_NewThread(getter_AddRefs(mThread), runnable); + NS_ENSURE_SUCCESS(rv, rv); + } + else + { + rv = mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +/** + * Remove an nsILDAPOperation from the list of operations pending on this + * connection. Mainly intended for use by the nsLDAPOperation code. + * + * @param aOperation operation to add + * @exception NS_ERROR_INVALID_POINTER aOperation was NULL + * @exception NS_ERROR_OUT_OF_MEMORY out of memory + * @exception NS_ERROR_FAILURE could not delete the operation + * + * void removePendingOperation(in nsILDAPOperation aOperation); + */ +nsresult +nsLDAPConnection::RemovePendingOperation(uint32_t aOperationID) +{ + NS_ENSURE_TRUE(aOperationID > 0, NS_ERROR_UNEXPECTED); + + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPConnection::RemovePendingOperation(): operation removed\n")); + + { + MutexAutoLock lock(mPendingOperationsMutex); + mPendingOperations.Remove(aOperationID); + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPConnection::RemovePendingOperation(): operation " + "removed; total pending operations now = %d\n", + mPendingOperations.Count())); + } + + return NS_OK; +} + +class nsOnLDAPMessageRunnable : public Runnable +{ +public: + nsOnLDAPMessageRunnable(nsLDAPMessage *aMsg, bool aClear) + : m_msg(aMsg) + , m_clear(aClear) + {} + NS_DECL_NSIRUNNABLE +private: + RefPtr<nsLDAPMessage> m_msg; + bool m_clear; +}; + +NS_IMETHODIMP nsOnLDAPMessageRunnable::Run() +{ + // get the message listener object. + nsLDAPOperation *nsoperation = static_cast<nsLDAPOperation *>(m_msg->mOperation.get()); + nsCOMPtr<nsILDAPMessageListener> listener; + nsresult rv = nsoperation->GetMessageListener(getter_AddRefs(listener)); + + if (m_clear) + { + // try to break cycles + nsoperation->Clear(); + } + + if (!listener) + { + NS_ERROR("nsLDAPConnection::InvokeMessageCallback(): probable " + "memory corruption: GetMessageListener() returned nullptr"); + return rv; + } + + return listener->OnLDAPMessage(m_msg); +} + +nsresult +nsLDAPConnection::InvokeMessageCallback(LDAPMessage *aMsgHandle, + nsILDAPMessage *aMsg, + int32_t aOperation, + bool aRemoveOpFromConnQ) +{ +#if defined(DEBUG) + // We only want this being logged for debug builds so as not to affect performance too much. + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("InvokeMessageCallback entered\n")); +#endif + + // Get the operation. + nsCOMPtr<nsILDAPOperation> operation; + { + MutexAutoLock lock(mPendingOperationsMutex); + mPendingOperations.Get((uint32_t)aOperation, getter_AddRefs(operation)); + } + + NS_ENSURE_TRUE(operation, NS_ERROR_NULL_POINTER); + + nsLDAPMessage *msg = static_cast<nsLDAPMessage *>(aMsg); + msg->mOperation = operation; + + // proxy the listener callback to the ui thread. + RefPtr<nsOnLDAPMessageRunnable> runnable = + new nsOnLDAPMessageRunnable(msg, aRemoveOpFromConnQ); + // invoke the callback + NS_DispatchToMainThread(runnable); + + // if requested (ie the operation is done), remove the operation + // from the connection queue. + if (aRemoveOpFromConnQ) + { + MutexAutoLock lock(mPendingOperationsMutex); + mPendingOperations.Remove(aOperation); + + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("pending operation removed; total pending operations now =" + " %d\n", mPendingOperations.Count())); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPConnection::OnLookupComplete(nsICancelable *aRequest, + nsIDNSRecord *aRecord, + nsresult aStatus) +{ + nsresult rv = NS_OK; + + if (aRecord) { + // Build mResolvedIP list + // + mResolvedIP.Truncate(); + + int32_t index = 0; + nsCString addrbuf; + nsCOMPtr<nsINetAddr> addr; + + while (NS_SUCCEEDED(aRecord->GetScriptableNextAddr(0, getter_AddRefs(addr)))) { + // We can only use v4 addresses + // + uint16_t family = 0; + bool v4mapped = false; + addr->GetFamily(&family); + addr->GetIsV4Mapped(&v4mapped); + if (family == nsINetAddr::FAMILY_INET || v4mapped) { + // If there are more IPs in the list, we separate them with + // a space, as supported/used by the LDAP C-SDK. + // + if (index++) + mResolvedIP.Append(' '); + + // Convert the IPv4 address to a string, and append it to our + // list of IPs. Strip leading '::FFFF:' (the IPv4-mapped-IPv6 + // indicator) if present. + // + addr->GetAddress(addrbuf); + if (addrbuf[0] == ':' && addrbuf.Length() > 7) + mResolvedIP.Append(Substring(addrbuf, 7)); + else + mResolvedIP.Append(addrbuf); + } + } + } + + if (NS_FAILED(aStatus)) { + // The DNS service failed, lets pass something reasonable + // back to the listener. + // + switch (aStatus) { + case NS_ERROR_OUT_OF_MEMORY: + case NS_ERROR_UNKNOWN_HOST: + case NS_ERROR_FAILURE: + case NS_ERROR_OFFLINE: + rv = aStatus; + break; + + default: + rv = NS_ERROR_UNEXPECTED; + break; + } + } else if (!mResolvedIP.Length()) { + // We have no host resolved, that is very bad, and should most + // likely have been caught earlier. + // + NS_ERROR("nsLDAPConnection::OnStopLookup(): the resolved IP " + "string is empty.\n"); + + rv = NS_ERROR_UNKNOWN_HOST; + } else { + // We've got the IP(s) for the hostname, now lets setup the + // LDAP connection using this information. Note that if the + // LDAP server returns a referral, the C-SDK will perform a + // new, synchronous DNS lookup, which might hang (but hopefully + // if we've come this far, DNS is working properly). + // + mConnectionHandle = ldap_init(mResolvedIP.get(), + mPort == -1 ? + (mSSL ? LDAPS_PORT : LDAP_PORT) : mPort); + // Check that we got a proper connection, and if so, setup the + // threading functions for this connection. + // + if ( !mConnectionHandle ) { + rv = NS_ERROR_FAILURE; // LDAP C SDK API gives no useful error + } else { +#if defined(DEBUG_dmose) || defined(DEBUG_bienvenu) + const int lDebug = 0; + ldap_set_option(mConnectionHandle, LDAP_OPT_DEBUG_LEVEL, &lDebug); +#endif + + // the C SDK currently defaults to v2. if we're to use v3, + // tell it so. + // + int version; + switch (mVersion) { + case 2: + break; + case 3: + version = LDAP_VERSION3; + ldap_set_option(mConnectionHandle, LDAP_OPT_PROTOCOL_VERSION, + &version); + break; + default: + NS_ERROR("nsLDAPConnection::OnLookupComplete(): mVersion" + " invalid"); + } + + // This code sets up the current connection to use PSM for SSL + // functionality. Making this use libssldap instead for + // non-browser user shouldn't be hard. + + extern nsresult nsLDAPInstallSSL(LDAP *ld, const char *aHostName); + + if (mSSL) { + if (ldap_set_option(mConnectionHandle, LDAP_OPT_SSL, + LDAP_OPT_ON) != LDAP_SUCCESS ) { + NS_ERROR("nsLDAPConnection::OnStopLookup(): Error" + " configuring connection to use SSL"); + rv = NS_ERROR_UNEXPECTED; + } + + rv = nsLDAPInstallSSL(mConnectionHandle, mDNSHost.get()); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPConnection::OnStopLookup(): Error" + " installing secure LDAP routines for" + " connection"); + } + } + } + } + + // Drop the DNS request object, we no longer need it, and set the flag + // indicating that DNS has finished. + // + mDNSRequest = nullptr; + mDNSHost.Truncate(); + + // Call the listener, and then we can release our reference to it. + // + mInitListener->OnLDAPInit(this, rv); + mInitListener = nullptr; + + return rv; +} + +nsLDAPConnectionRunnable::nsLDAPConnectionRunnable(int32_t aOperationID, + nsILDAPOperation *aOperation, + nsLDAPConnection *aConnection) + : mOperationID(aOperationID), mConnection(aConnection) +{ +} + +nsLDAPConnectionRunnable::~nsLDAPConnectionRunnable() +{ + if (mConnection) { + NS_ReleaseOnMainThread(mConnection.forget()); + } +} + +NS_IMPL_ISUPPORTS(nsLDAPConnectionRunnable, nsIRunnable) + +NS_IMETHODIMP nsLDAPConnectionRunnable::Run() +{ + if (!mOperationID) { + NS_ERROR("mOperationID is null"); + return NS_ERROR_NULL_POINTER; + } + + LDAPMessage *msgHandle; + bool operationFinished = true; + RefPtr<nsLDAPMessage> msg; + + struct timeval timeout = { 0, 0 }; + + nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); + int32_t returnCode = ldap_result(mConnection->mConnectionHandle, mOperationID, LDAP_MSG_ONE, &timeout, &msgHandle); + switch (returnCode) + { + // timeout + case 0: + // XXX do we need a timer? + return thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL); + case -1: + NS_ERROR("We don't know what went wrong with the ldap operation"); + return NS_ERROR_FAILURE; + + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + // XXX what should we do with LDAP_RES_SEARCH_EXTENDED + operationFinished = false; + MOZ_FALLTHROUGH; + default: + { + msg = new nsLDAPMessage; + if (!msg) + return NS_ERROR_NULL_POINTER; + + // initialize the message, using a protected method not available + // through nsILDAPMessage (which is why we need the raw pointer) + nsresult rv = msg->Init(mConnection, msgHandle); + + switch (rv) + { + case NS_OK: + { + int32_t errorCode; + msg->GetErrorCode(&errorCode); + + // maybe a version error, e.g., using v3 on a v2 server. + // if we're using v3, try v2. + if (errorCode == LDAP_PROTOCOL_ERROR && + mConnection->mVersion == nsILDAPConnection::VERSION3) + { + nsAutoCString password; + mConnection->mVersion = nsILDAPConnection::VERSION2; + ldap_set_option(mConnection->mConnectionHandle, + LDAP_OPT_PROTOCOL_VERSION, &mConnection->mVersion); + + if (NS_SUCCEEDED(rv)) + { + // We don't want to notify callers that we are done, so + // redispatch the runnable. + // XXX do we need a timer? + rv = thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; + } + } + // If we're midway through a SASL Bind, we need to continue + // without letting our caller know what we're up to! + // + if (errorCode == LDAP_SASL_BIND_IN_PROGRESS) { + struct berval *creds; + ldap_parse_sasl_bind_result( + mConnection->mConnectionHandle, msgHandle, + &creds, 0); + + nsCOMPtr<nsILDAPOperation> operation; + { + MutexAutoLock lock(mConnection->mPendingOperationsMutex); + mConnection->mPendingOperations.Get((uint32_t)mOperationID, getter_AddRefs(operation)); + } + + NS_ENSURE_TRUE(operation, NS_ERROR_NULL_POINTER); + + nsresult rv = operation->SaslStep(creds->bv_val, creds->bv_len); + if (NS_SUCCEEDED(rv)) + return NS_OK; + } + break; + } + // Error code handling in here + default: + return NS_OK; + } + + // invoke the callback on the nsILDAPOperation corresponding to + // this message + rv = mConnection->InvokeMessageCallback(msgHandle, msg, mOperationID, + operationFinished); + if (NS_FAILED(rv)) + { + NS_ERROR("CheckLDAPOperationResult(): error invoking message" + " callback"); + // punt and hope things work out better next time around + return NS_OK; + } + + if (!operationFinished) + { + // XXX do we need a timer? + rv = thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + } + + break; + } + } + return NS_OK; +} |