/* -*- 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 "nsSubscribeDataSource.h" #include "nsIRDFService.h" #include "nsRDFCID.h" #include "nsIComponentManager.h" #include "rdf.h" #include "nsIServiceManager.h" #include "nsEnumeratorUtils.h" #include "nsStringGlue.h" #include "nsCOMPtr.h" #include "nsIMsgFolder.h" #include "nsIMsgIncomingServer.h" #include "nsCOMArray.h" #include "nsArrayEnumerator.h" #include "nsServiceManagerUtils.h" #include "nsComponentManagerUtils.h" #include "nsMsgUtils.h" static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); nsSubscribeDataSource::nsSubscribeDataSource() { } nsSubscribeDataSource::~nsSubscribeDataSource() { } NS_IMPL_ISUPPORTS(nsSubscribeDataSource, nsIRDFDataSource, nsISubscribeDataSource) nsresult nsSubscribeDataSource::Init() { nsresult rv; mRDFService = do_GetService(kRDFServiceCID, &rv); NS_ASSERTION(NS_SUCCEEDED(rv) && mRDFService, "failed to get rdf service"); NS_ENSURE_SUCCESS(rv,rv); if (!mRDFService) return NS_ERROR_FAILURE; rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "child"), getter_AddRefs(kNC_Child)); NS_ENSURE_SUCCESS(rv,rv); rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"), getter_AddRefs(kNC_Name)); NS_ENSURE_SUCCESS(rv,rv); rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "LeafName"), getter_AddRefs(kNC_LeafName)); NS_ENSURE_SUCCESS(rv,rv); rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Subscribed"), getter_AddRefs(kNC_Subscribed)); NS_ENSURE_SUCCESS(rv,rv); rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Subscribable"), getter_AddRefs(kNC_Subscribable)); NS_ENSURE_SUCCESS(rv,rv); rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "ServerType"), getter_AddRefs(kNC_ServerType)); NS_ENSURE_SUCCESS(rv,rv); rv = mRDFService->GetLiteral(u"true", getter_AddRefs(kTrueLiteral)); NS_ENSURE_SUCCESS(rv,rv); rv = mRDFService->GetLiteral(u"false", getter_AddRefs(kFalseLiteral)); NS_ENSURE_SUCCESS(rv,rv); return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::GetURI(char * *aURI) { if ((*aURI = strdup("rdf:subscribe")) == nullptr) return NS_ERROR_OUT_OF_MEMORY; else return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::GetSource(nsIRDFResource *property, nsIRDFNode *target, bool tv, nsIRDFResource **source) { NS_PRECONDITION(property != nullptr, "null ptr"); if (! property) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(target != nullptr, "null ptr"); if (! target) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(source != nullptr, "null ptr"); if (! source) return NS_ERROR_NULL_POINTER; *source = nullptr; return NS_RDF_NO_VALUE; } NS_IMETHODIMP nsSubscribeDataSource::GetTarget(nsIRDFResource *source, nsIRDFResource *property, bool tv, nsIRDFNode **target /* out */) { nsresult rv = NS_RDF_NO_VALUE; NS_PRECONDITION(source != nullptr, "null ptr"); if (! source) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(property != nullptr, "null ptr"); if (! property) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(target != nullptr, "null ptr"); if (! target) return NS_ERROR_NULL_POINTER; *target = nullptr; // we only have positive assertions in the subscribe data source. if (! tv) return NS_RDF_NO_VALUE; nsCOMPtr server; nsCString relativePath; rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath)); if (NS_FAILED(rv) || !server) return NS_RDF_NO_VALUE; if (property == kNC_Name.get()) { nsCOMPtr name; rv = mRDFService->GetLiteral(NS_ConvertUTF8toUTF16(relativePath).get(), getter_AddRefs(name)); NS_ENSURE_SUCCESS(rv,rv); if (!name) rv = NS_RDF_NO_VALUE; if (rv == NS_RDF_NO_VALUE) return(rv); return name->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target); } else if (property == kNC_Child.get()) { nsCString childUri; rv = server->GetFirstChildURI(relativePath, childUri); if (NS_FAILED(rv)) return NS_RDF_NO_VALUE; if (childUri.IsEmpty()) return NS_RDF_NO_VALUE; nsCOMPtr childResource; rv = mRDFService->GetResource(childUri, getter_AddRefs(childResource)); NS_ENSURE_SUCCESS(rv,rv); return childResource->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target); } else if (property == kNC_Subscribed.get()) { bool isSubscribed; rv = server->IsSubscribed(relativePath, &isSubscribed); NS_ENSURE_SUCCESS(rv,rv); NS_IF_ADDREF(*target = (isSubscribed ? kTrueLiteral : kFalseLiteral)); return NS_OK; } else if (property == kNC_Subscribable.get()) { bool isSubscribable; rv = server->IsSubscribable(relativePath, &isSubscribable); NS_ENSURE_SUCCESS(rv,rv); NS_IF_ADDREF(*target = (isSubscribable ? kTrueLiteral : kFalseLiteral)); return NS_OK; } else if (property == kNC_ServerType.get()) { nsCString serverTypeStr; rv = GetServerType(server, serverTypeStr); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr serverType; rv = mRDFService->GetLiteral(NS_ConvertASCIItoUTF16(serverTypeStr).get(), getter_AddRefs(serverType)); NS_ENSURE_SUCCESS(rv,rv); if (!serverType) rv = NS_RDF_NO_VALUE; if (rv == NS_RDF_NO_VALUE) return rv; return serverType->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target); } else if (property == kNC_LeafName.get()) { nsString leafNameStr; rv = server->GetLeafName(relativePath, leafNameStr); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr leafName; rv = mRDFService->GetLiteral(leafNameStr.get(), getter_AddRefs(leafName)); NS_ENSURE_SUCCESS(rv,rv); if (!leafName) rv = NS_RDF_NO_VALUE; if (rv == NS_RDF_NO_VALUE) return rv; return leafName->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target); } else { // do nothing } return(NS_RDF_NO_VALUE); } NS_IMETHODIMP nsSubscribeDataSource::GetTargets(nsIRDFResource *source, nsIRDFResource *property, bool tv, nsISimpleEnumerator **targets /* out */) { nsresult rv = NS_OK; NS_PRECONDITION(source != nullptr, "null ptr"); if (! source) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(property != nullptr, "null ptr"); if (! property) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(targets != nullptr, "null ptr"); if (! targets) return NS_ERROR_NULL_POINTER; *targets = nullptr; // we only have positive assertions in the subscribe data source. if (!tv) return NS_RDF_NO_VALUE; nsCOMPtr server; nsCString relativePath; // UTF-8 rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath)); if (NS_FAILED(rv) || !server) { return NS_NewEmptyEnumerator(targets); } if (property == kNC_Child.get()) { rv = server->GetChildren(relativePath, targets); if (NS_FAILED(rv)) { return NS_NewEmptyEnumerator(targets); } return rv; } else if (property == kNC_LeafName.get()) { nsString leafNameStr; rv = server->GetLeafName(relativePath, leafNameStr); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr leafName; rv = mRDFService->GetLiteral(leafNameStr.get(), getter_AddRefs(leafName)); NS_ENSURE_SUCCESS(rv,rv); return NS_NewSingletonEnumerator(targets, leafName); } else if (property == kNC_Subscribed.get()) { bool isSubscribed; rv = server->IsSubscribed(relativePath, &isSubscribed); NS_ENSURE_SUCCESS(rv,rv); return NS_NewSingletonEnumerator(targets, isSubscribed ? kTrueLiteral : kFalseLiteral); } else if (property == kNC_Subscribable.get()) { bool isSubscribable; rv = server->IsSubscribable(relativePath, &isSubscribable); NS_ENSURE_SUCCESS(rv,rv); return NS_NewSingletonEnumerator(targets, isSubscribable ? kTrueLiteral : kFalseLiteral); } else if (property == kNC_Name.get()) { nsCOMPtr name; rv = mRDFService->GetLiteral(NS_ConvertUTF8toUTF16(relativePath).get(), getter_AddRefs(name)); NS_ENSURE_SUCCESS(rv,rv); return NS_NewSingletonEnumerator(targets, name); } else if (property == kNC_ServerType.get()) { nsCString serverTypeStr; rv = GetServerType(server, serverTypeStr); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr serverType; rv = mRDFService->GetLiteral(NS_ConvertASCIItoUTF16(serverTypeStr).get(), getter_AddRefs(serverType)); NS_ENSURE_SUCCESS(rv,rv); return NS_NewSingletonEnumerator(targets, serverType); } else { // do nothing } return NS_NewEmptyEnumerator(targets); } NS_IMETHODIMP nsSubscribeDataSource::Assert(nsIRDFResource *source, nsIRDFResource *property, nsIRDFNode *target, bool tv) { return NS_RDF_ASSERTION_REJECTED; } NS_IMETHODIMP nsSubscribeDataSource::Unassert(nsIRDFResource *source, nsIRDFResource *property, nsIRDFNode *target) { return NS_RDF_ASSERTION_REJECTED; } NS_IMETHODIMP nsSubscribeDataSource::Change(nsIRDFResource* aSource, nsIRDFResource* aProperty, nsIRDFNode* aOldTarget, nsIRDFNode* aNewTarget) { return NS_RDF_ASSERTION_REJECTED; } NS_IMETHODIMP nsSubscribeDataSource::Move(nsIRDFResource* aOldSource, nsIRDFResource* aNewSource, nsIRDFResource* aProperty, nsIRDFNode* aTarget) { return NS_RDF_ASSERTION_REJECTED; } nsresult nsSubscribeDataSource::GetServerType(nsISubscribableServer *server, nsACString& serverType) { NS_ENSURE_ARG_POINTER(server); nsresult rv; nsCOMPtr incomingServer(do_QueryInterface(server, &rv)); NS_ENSURE_SUCCESS(rv,rv); return incomingServer->GetType(serverType); } nsresult nsSubscribeDataSource::GetServerAndRelativePathFromResource(nsIRDFResource *source, nsISubscribableServer **server, char **relativePath) { nsresult rv = NS_OK; const char *sourceURI = nullptr; rv = source->GetValueConst(&sourceURI); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr folder(do_QueryInterface(source, &rv)); // we expect this to fail sometimes, so don't assert if (NS_FAILED(rv)) return rv; nsCOMPtr incomingServer; rv = folder->GetServer(getter_AddRefs(incomingServer)); NS_ENSURE_SUCCESS(rv,rv); rv = incomingServer->QueryInterface(NS_GET_IID(nsISubscribableServer), (void**)server); NS_ENSURE_SUCCESS(rv,rv); nsCString serverURI; rv = incomingServer->GetServerURI(serverURI); NS_ENSURE_SUCCESS(rv,rv); uint32_t serverURILen = serverURI.Length(); if (serverURILen == strlen(sourceURI)) *relativePath = nullptr; else { // XXX : perhaps, have to unescape before returning *relativePath = strdup(sourceURI + serverURILen + 1); if (!*relativePath) return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::HasAssertion(nsIRDFResource *source, nsIRDFResource *property, nsIRDFNode *target, bool tv, bool *hasAssertion /* out */) { nsresult rv = NS_OK; NS_PRECONDITION(source != nullptr, "null ptr"); if (! source) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(property != nullptr, "null ptr"); if (! property) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(target != nullptr, "null ptr"); if (! target) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(hasAssertion != nullptr, "null ptr"); if (! hasAssertion) return NS_ERROR_NULL_POINTER; *hasAssertion = false; // we only have positive assertions in the subscribe data source. if (!tv) return NS_OK; if (property == kNC_Child.get()) { nsCOMPtr server; nsCString relativePath; rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath)); if (NS_FAILED(rv) || !server) { *hasAssertion = false; return NS_OK; } // not everything has children rv = server->HasChildren(relativePath, hasAssertion); NS_ENSURE_SUCCESS(rv,rv); } else if (property == kNC_Name.get()) { // everything has a name *hasAssertion = true; } else if (property == kNC_LeafName.get()) { // everything has a leaf name *hasAssertion = true; } else if (property == kNC_Subscribed.get()) { // everything is subscribed or not *hasAssertion = true; } else if (property == kNC_Subscribable.get()) { // everything is subscribable or not *hasAssertion = true; } else if (property == kNC_ServerType.get()) { // everything has a server type *hasAssertion = true; } else { // do nothing } return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSubscribeDataSource::HasArcOut(nsIRDFResource *source, nsIRDFResource *aArc, bool *result) { nsresult rv = NS_OK; nsCOMPtr server; nsCString relativePath; if (aArc == kNC_Child.get()) { rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath)); if (NS_FAILED(rv) || !server) { *result = false; return NS_OK; } bool hasChildren = false; rv = server->HasChildren(relativePath, &hasChildren); NS_ENSURE_SUCCESS(rv,rv); *result = hasChildren; return NS_OK; } else if ((aArc == kNC_Subscribed.get()) || (aArc == kNC_Subscribable.get()) || (aArc == kNC_LeafName.get()) || (aArc == kNC_ServerType.get()) || (aArc == kNC_Name.get())) { *result = true; return NS_OK; } *result = false; return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::ArcLabelsIn(nsIRDFNode *node, nsISimpleEnumerator ** labels /* out */) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSubscribeDataSource::ArcLabelsOut(nsIRDFResource *source, nsISimpleEnumerator **labels /* out */) { nsresult rv = NS_OK; NS_PRECONDITION(source != nullptr, "null ptr"); if (! source) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(labels != nullptr, "null ptr"); if (! labels) return NS_ERROR_NULL_POINTER; nsCOMPtr server; nsCString relativePath; rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath)); if (NS_FAILED(rv) || !server) { return NS_NewEmptyEnumerator(labels); } bool hasChildren = false; rv = server->HasChildren(relativePath, &hasChildren); NS_ENSURE_SUCCESS(rv,rv); // Initialise with the number of items below, to save reallocating on each // addition. nsCOMArray array(hasChildren ? 6 : 5); array.AppendObject(kNC_Subscribed); array.AppendObject(kNC_Subscribable); array.AppendObject(kNC_Name); array.AppendObject(kNC_ServerType); array.AppendObject(kNC_LeafName); if (hasChildren) { array.AppendObject(kNC_Child); } return NS_NewArrayEnumerator(labels, array); } NS_IMETHODIMP nsSubscribeDataSource::GetAllResources(nsISimpleEnumerator** aCursor) { NS_NOTYETIMPLEMENTED("sorry!"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSubscribeDataSource::AddObserver(nsIRDFObserver *n) { NS_ENSURE_ARG_POINTER(n); mObservers.AppendElement(n); return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::RemoveObserver(nsIRDFObserver *n) { NS_ENSURE_ARG_POINTER(n); mObservers.RemoveElement(n); return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::GetHasObservers(bool *hasObservers) { NS_ENSURE_ARG_POINTER(hasObservers); *hasObservers = !mObservers.IsEmpty(); return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::GetAllCmds(nsIRDFResource* source, nsISimpleEnumerator/**/** commands) { return(NS_NewEmptyEnumerator(commands)); } NS_IMETHODIMP nsSubscribeDataSource::IsCommandEnabled(nsISupports/*nsISupportsArray*/* aSources, nsIRDFResource* aCommand, nsISupports/*nsISupportsArray*/* aArguments, bool* aResult) { return(NS_ERROR_NOT_IMPLEMENTED); } NS_IMETHODIMP nsSubscribeDataSource::DoCommand(nsISupports/*nsISupportsArray*/* aSources, nsIRDFResource* aCommand, nsISupports/*nsISupportsArray*/* aArguments) { return(NS_ERROR_NOT_IMPLEMENTED); } NS_IMETHODIMP nsSubscribeDataSource::BeginUpdateBatch() { return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::EndUpdateBatch() { return NS_OK; } NS_IMETHODIMP nsSubscribeDataSource::GetSources(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue, nsISimpleEnumerator **_retval) { NS_ASSERTION(false, "Not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } #define NOTIFY_SUBSCRIBE_LISTENERS(propertyfunc_, params_) \ PR_BEGIN_MACRO \ { \ nsTObserverArray >::ForwardIterator iter(mObservers); \ while (iter.HasMore()) \ { \ iter.GetNext()->propertyfunc_ params_; \ } \ } \ PR_END_MACRO NS_IMETHODIMP nsSubscribeDataSource::NotifyObservers(nsIRDFResource *subject, nsIRDFResource *property, nsIRDFNode *object, bool assert, bool change) { NS_ASSERTION(!(change && assert), "Can't change and assert at the same time!\n"); if (change) NOTIFY_SUBSCRIBE_LISTENERS(OnChange, (this, subject, property, nullptr, object)); else if (assert) NOTIFY_SUBSCRIBE_LISTENERS(OnAssert, (this, subject, property, object)); else NOTIFY_SUBSCRIBE_LISTENERS(OnUnassert, (this, subject, property, object)); return NS_OK; }