/* -*- 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/. */

/*
 * RDF datasource for the account manager
 */

#include "nsMsgAccountManagerDS.h"
#include "rdf.h"
#include "nsRDFCID.h"
#include "nsIRDFDataSource.h"
#include "nsEnumeratorUtils.h"
#include "nsIServiceManager.h"
#include "nsMsgRDFUtils.h"
#include "nsIMsgFolder.h"
#include "nsMsgBaseCID.h"

#include "nsICategoryManager.h"
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsArrayEnumerator.h"
#include "nsMsgUtils.h"
#include "mozilla/Services.h"
#include "nsArrayUtils.h"

// turn this on to see useful output
#undef DEBUG_amds

#define NC_RDF_PAGETITLE_PREFIX               NC_NAMESPACE_URI "PageTitle"
#define NC_RDF_PAGETITLE_MAIN                 NC_RDF_PAGETITLE_PREFIX "Main"
#define NC_RDF_PAGETITLE_SERVER               NC_RDF_PAGETITLE_PREFIX "Server"
#define NC_RDF_PAGETITLE_COPIES               NC_RDF_PAGETITLE_PREFIX "Copies"
#define NC_RDF_PAGETITLE_SYNCHRONIZATION      NC_RDF_PAGETITLE_PREFIX "Synchronization"
#define NC_RDF_PAGETITLE_DISKSPACE            NC_RDF_PAGETITLE_PREFIX "DiskSpace"
#define NC_RDF_PAGETITLE_ADDRESSING           NC_RDF_PAGETITLE_PREFIX "Addressing"
#define NC_RDF_PAGETITLE_SMTP                 NC_RDF_PAGETITLE_PREFIX "SMTP"
#define NC_RDF_PAGETITLE_JUNK                 NC_RDF_PAGETITLE_PREFIX "Junk"
#define NC_RDF_PAGETAG NC_NAMESPACE_URI "PageTag"


#define NC_RDF_ACCOUNTROOT "msgaccounts:/"

// the root resource (msgaccounts:/)
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_AccountRoot=nullptr;

// attributes of accounts
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_Name=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_FolderTreeName=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_FolderTreeSimpleName=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_NameSort=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_FolderTreeNameSort=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTag=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_IsDefaultServer=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_SupportsFilters=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_CanGetMessages=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_CanGetIncomingMessages=nullptr;

// containment
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_Child=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_Settings=nullptr;


// properties corresponding to interfaces
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_Account=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_Server=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_Identity=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_Junk=nullptr;

// individual pages
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTitleMain=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTitleServer=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTitleCopies=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTitleSynchronization=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTitleDiskSpace=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTitleAddressing=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTitleSMTP=nullptr;
nsIRDFResource* nsMsgAccountManagerDataSource::kNC_PageTitleJunk=nullptr;

// common literals
nsIRDFLiteral* nsMsgAccountManagerDataSource::kTrueLiteral = nullptr;

nsIAtom* nsMsgAccountManagerDataSource::kDefaultServerAtom = nullptr;

nsrefcnt nsMsgAccountManagerDataSource::gAccountManagerResourceRefCnt = 0;

// shared arc lists
nsCOMPtr<nsIMutableArray> nsMsgAccountManagerDataSource::mAccountArcsOut;
nsCOMPtr<nsIMutableArray> nsMsgAccountManagerDataSource::mAccountRootArcsOut;


// RDF to match
#define NC_RDF_ACCOUNT NC_NAMESPACE_URI "Account"
#define NC_RDF_SERVER  NC_NAMESPACE_URI "Server"
#define NC_RDF_IDENTITY NC_NAMESPACE_URI "Identity"
#define NC_RDF_SETTINGS NC_NAMESPACE_URI "Settings"
#define NC_RDF_JUNK NC_NAMESPACE_URI "Junk"
#define NC_RDF_ISDEFAULTSERVER NC_NAMESPACE_URI "IsDefaultServer"
#define NC_RDF_SUPPORTSFILTERS NC_NAMESPACE_URI "SupportsFilters"
#define NC_RDF_CANGETMESSAGES NC_NAMESPACE_URI "CanGetMessages"
#define NC_RDF_CANGETINCOMINGMESSAGES NC_NAMESPACE_URI "CanGetIncomingMessages"

nsMsgAccountManagerDataSource::nsMsgAccountManagerDataSource()
{
#ifdef DEBUG_amds
  printf("nsMsgAccountManagerDataSource() being created\n");
#endif

  // do per-class initialization here
  if (gAccountManagerResourceRefCnt++ == 0) {
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_CHILD), &kNC_Child);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_NAME), &kNC_Name);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_FOLDERTREENAME),
                                 &kNC_FolderTreeName);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_FOLDERTREESIMPLENAME),
                                 &kNC_FolderTreeSimpleName);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_NAME_SORT), &kNC_NameSort);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_FOLDERTREENAME_SORT),
                                 &kNC_FolderTreeNameSort);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETAG), &kNC_PageTag);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_ISDEFAULTSERVER),
                                 &kNC_IsDefaultServer);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_SUPPORTSFILTERS),
                                 &kNC_SupportsFilters);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_CANGETMESSAGES),
                                 &kNC_CanGetMessages);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_CANGETINCOMINGMESSAGES),
                                 &kNC_CanGetIncomingMessages);

    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_ACCOUNT), &kNC_Account);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_SERVER), &kNC_Server);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_IDENTITY), &kNC_Identity);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_JUNK), &kNC_Junk);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETITLE_MAIN),
                                 &kNC_PageTitleMain);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETITLE_SERVER),
                                 &kNC_PageTitleServer);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETITLE_COPIES),
                                 &kNC_PageTitleCopies);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETITLE_SYNCHRONIZATION),
                                 &kNC_PageTitleSynchronization);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETITLE_DISKSPACE),
                                 &kNC_PageTitleDiskSpace);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETITLE_ADDRESSING),
                                 &kNC_PageTitleAddressing);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETITLE_SMTP),
                                 &kNC_PageTitleSMTP);
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_PAGETITLE_JUNK),
                                 &kNC_PageTitleJunk);

    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_ACCOUNTROOT),
                                 &kNC_AccountRoot);

    getRDFService()->GetLiteral(MOZ_UTF16("true"),
                                &kTrueLiteral);

    // eventually these need to exist in some kind of array
    // that's easily extensible
    getRDFService()->GetResource(NS_LITERAL_CSTRING(NC_RDF_SETTINGS), &kNC_Settings);

    kDefaultServerAtom = MsgNewAtom("DefaultServer").take();
  }
}

nsMsgAccountManagerDataSource::~nsMsgAccountManagerDataSource()
{
  nsCOMPtr<nsIMsgAccountManager> am = do_QueryReferent(mAccountManager);
  if (am)
    am->RemoveIncomingServerListener(this);

  if (--gAccountManagerResourceRefCnt == 0) {
    NS_IF_RELEASE(kNC_Child);
    NS_IF_RELEASE(kNC_Name);
    NS_IF_RELEASE(kNC_FolderTreeName);
    NS_IF_RELEASE(kNC_FolderTreeSimpleName);
    NS_IF_RELEASE(kNC_NameSort);
    NS_IF_RELEASE(kNC_FolderTreeNameSort);
    NS_IF_RELEASE(kNC_PageTag);
    NS_IF_RELEASE(kNC_IsDefaultServer);
    NS_IF_RELEASE(kNC_SupportsFilters);
    NS_IF_RELEASE(kNC_CanGetMessages);
    NS_IF_RELEASE(kNC_CanGetIncomingMessages);
    NS_IF_RELEASE(kNC_Account);
    NS_IF_RELEASE(kNC_Server);
    NS_IF_RELEASE(kNC_Identity);
    NS_IF_RELEASE(kNC_Junk);
    NS_IF_RELEASE(kNC_PageTitleMain);
    NS_IF_RELEASE(kNC_PageTitleServer);
    NS_IF_RELEASE(kNC_PageTitleCopies);
    NS_IF_RELEASE(kNC_PageTitleSynchronization);
    NS_IF_RELEASE(kNC_PageTitleDiskSpace);
    NS_IF_RELEASE(kNC_PageTitleAddressing);
    NS_IF_RELEASE(kNC_PageTitleSMTP);
    NS_IF_RELEASE(kNC_PageTitleJunk);
    NS_IF_RELEASE(kTrueLiteral);

    NS_IF_RELEASE(kNC_AccountRoot);

    // eventually these need to exist in some kind of array
    // that's easily extensible
    NS_IF_RELEASE(kNC_Settings);


    NS_IF_RELEASE(kDefaultServerAtom);
    mAccountArcsOut = nullptr;
    mAccountRootArcsOut = nullptr;
  }

}

NS_IMPL_ADDREF_INHERITED(nsMsgAccountManagerDataSource, nsMsgRDFDataSource)
NS_IMPL_RELEASE_INHERITED(nsMsgAccountManagerDataSource, nsMsgRDFDataSource)
NS_INTERFACE_MAP_BEGIN(nsMsgAccountManagerDataSource)
  NS_INTERFACE_MAP_ENTRY(nsIIncomingServerListener)
  NS_INTERFACE_MAP_ENTRY(nsIFolderListener)
NS_INTERFACE_MAP_END_INHERITING(nsMsgRDFDataSource)

nsresult
nsMsgAccountManagerDataSource::Init()
{
  nsresult rv;

  rv = nsMsgRDFDataSource::Init();
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIMsgAccountManager> am;

  // get a weak ref to the account manager
  if (!mAccountManager) 
  {
    am = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
    mAccountManager = do_GetWeakReference(am);
  }
  else
    am = do_QueryReferent(mAccountManager);

  if (am) 
  {
    am->AddIncomingServerListener(this);
    am->AddRootFolderListener(this);
  }


  return NS_OK;
}

void nsMsgAccountManagerDataSource::Cleanup()
{
  nsCOMPtr<nsIMsgAccountManager> am = do_QueryReferent(mAccountManager);

  if (am)
  {
    am->RemoveIncomingServerListener(this);
    am->RemoveRootFolderListener(this);
  }

  nsMsgRDFDataSource::Cleanup();
}

/* nsIRDFNode GetTarget (in nsIRDFResource aSource, in nsIRDFResource property, in boolean aTruthValue); */
NS_IMETHODIMP
nsMsgAccountManagerDataSource::GetTarget(nsIRDFResource *source,
                                         nsIRDFResource *property,
                                         bool aTruthValue,
                                         nsIRDFNode **target)
{
  nsresult rv;


  rv = NS_RDF_NO_VALUE;

  nsAutoString str;
  if (property == kNC_Name || property == kNC_FolderTreeName ||
      property == kNC_FolderTreeSimpleName) 
  {
    rv = getStringBundle();
    NS_ENSURE_SUCCESS(rv, rv);

    nsString pageTitle;
    if (source == kNC_PageTitleServer)
      mStringBundle->GetStringFromName(MOZ_UTF16("prefPanel-server"),
                                       getter_Copies(pageTitle));

    else if (source == kNC_PageTitleCopies)
      mStringBundle->GetStringFromName(MOZ_UTF16("prefPanel-copies"),
                                       getter_Copies(pageTitle));
    else if (source == kNC_PageTitleSynchronization)
      mStringBundle->GetStringFromName(MOZ_UTF16("prefPanel-synchronization"),
                                       getter_Copies(pageTitle));
    else if (source == kNC_PageTitleDiskSpace)
      mStringBundle->GetStringFromName(MOZ_UTF16("prefPanel-diskspace"),
                                       getter_Copies(pageTitle));
    else if (source == kNC_PageTitleAddressing)
      mStringBundle->GetStringFromName(MOZ_UTF16("prefPanel-addressing"),
                                       getter_Copies(pageTitle));
    else if (source == kNC_PageTitleSMTP)
      mStringBundle->GetStringFromName(MOZ_UTF16("prefPanel-smtp"),
                                       getter_Copies(pageTitle));
    else if (source == kNC_PageTitleJunk)
      mStringBundle->GetStringFromName(MOZ_UTF16("prefPanel-junk"),
                                       getter_Copies(pageTitle));

    else {
      // if it's a server, use the pretty name
      nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(source, &rv);
      if (NS_SUCCEEDED(rv) && folder) {
        bool isServer;
        rv = folder->GetIsServer(&isServer);
        if (NS_SUCCEEDED(rv) && isServer)
          rv = folder->GetPrettyName(pageTitle);
      }
      else {
        // allow for the accountmanager to be dynamically extended.

        nsCOMPtr<nsIStringBundleService> strBundleService =
          mozilla::services::GetStringBundleService();
        NS_ENSURE_TRUE(strBundleService, NS_ERROR_UNEXPECTED);

        const char *sourceValue;
        rv = source->GetValueConst(&sourceValue);
        NS_ENSURE_SUCCESS(rv,rv);

        // make sure the pointer math we're about to do is safe.
        NS_ENSURE_TRUE(sourceValue && (strlen(sourceValue) > strlen(NC_RDF_PAGETITLE_PREFIX)), NS_ERROR_UNEXPECTED);

        nsCOMPtr<nsIMsgAccountManager> am = do_QueryReferent(mAccountManager, &rv);
        NS_ENSURE_SUCCESS(rv, rv);

        // turn NC#PageTitlefoobar into foobar, so we can get the am-foobar.properties bundle
        nsCString chromePackageName;
        rv = am->GetChromePackageName(nsCString(sourceValue + strlen(NC_RDF_PAGETITLE_PREFIX)), chromePackageName);
        NS_ENSURE_SUCCESS(rv,rv);

        nsAutoCString bundleURL;
        bundleURL = "chrome://";
        bundleURL += chromePackageName;
        bundleURL += "/locale/am-";
        bundleURL += (sourceValue + strlen(NC_RDF_PAGETITLE_PREFIX));
        bundleURL += ".properties";

        nsCOMPtr <nsIStringBundle> bundle;
        rv = strBundleService->CreateBundle(bundleURL.get(), getter_AddRefs(bundle));

        NS_ENSURE_SUCCESS(rv,rv);

        nsAutoString panelTitleName;
        panelTitleName.AssignLiteral("prefPanel-");
        panelTitleName.Append(NS_ConvertASCIItoUTF16(sourceValue + strlen(NC_RDF_PAGETITLE_PREFIX)));
        bundle->GetStringFromName(panelTitleName.get(), getter_Copies(pageTitle));
      }
    }
    str = pageTitle.get();
  }
  else if (property == kNC_PageTag) {
    // do NOT localize these strings. these are the urls of the XUL files
    if (source == kNC_PageTitleServer)
      str.AssignLiteral("am-server.xul");
    else if (source == kNC_PageTitleCopies)
      str.AssignLiteral("am-copies.xul");
    else if ((source == kNC_PageTitleSynchronization) ||
             (source == kNC_PageTitleDiskSpace))
      str.AssignLiteral("am-offline.xul");
    else if (source == kNC_PageTitleAddressing)
      str.AssignLiteral("am-addressing.xul");
    else if (source == kNC_PageTitleSMTP)
      str.AssignLiteral("am-smtp.xul");
    else if (source == kNC_PageTitleJunk)
      str.AssignLiteral("am-junk.xul");
    else {
      nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(source, &rv);
      if (NS_SUCCEEDED(rv) && folder) {
        /* if this is a server, with no identities, then we show a special panel */
        nsCOMPtr<nsIMsgIncomingServer> server;
        rv = getServerForFolderNode(source, getter_AddRefs(server));
        if (server)
          server->GetAccountManagerChrome(str);
        else 
          str.AssignLiteral("am-main.xul");
      }
      else {
        // allow for the accountmanager to be dynamically extended
        const char *sourceValue;
        rv = source->GetValueConst(&sourceValue);
        NS_ENSURE_SUCCESS(rv,rv);

        // make sure the pointer math we're about to do is safe.
        NS_ENSURE_TRUE(sourceValue && (strlen(sourceValue) > strlen(NC_RDF_PAGETITLE_PREFIX)), NS_ERROR_UNEXPECTED);

        // turn NC#PageTitlefoobar into foobar, so we can get the am-foobar.xul file
        str.AssignLiteral("am-");
        str.Append(NS_ConvertASCIItoUTF16(sourceValue + strlen(NC_RDF_PAGETITLE_PREFIX)));
        str.AppendLiteral(".xul");
      }
    }
  }

  // handle sorting of servers
  else if ((property == kNC_NameSort) ||
           (property == kNC_FolderTreeNameSort)) {

    // order for the folder pane
    // and for the account manager tree is:
    //
    // - default mail account
    // - <other mail accounts>
    // - "Local Folders" account
    // - news accounts
    // - smtp settings (note, this is only in account manager tree)

    // make sure we're handling a root folder that is a server
    nsCOMPtr<nsIMsgIncomingServer> server;
    rv = getServerForFolderNode(source, getter_AddRefs(server));

    if (NS_SUCCEEDED(rv) && server) {
      int32_t accountNum;
      nsCOMPtr<nsIMsgAccountManager> am = do_QueryReferent(mAccountManager);

      if (isDefaultServer(server))
        str.AssignLiteral("000000000");
      else {
        rv = am->GetSortOrder(server, &accountNum);
        NS_ENSURE_SUCCESS(rv, rv);
        str.AppendInt(accountNum);
      }
    }
    else {
      const char *sourceValue;
      rv = source->GetValueConst(&sourceValue);
      NS_ENSURE_SUCCESS(rv, NS_RDF_NO_VALUE);

      // if this is a page (which we determine by the prefix of the URI)
      // we want to generate a sort value
      // so that we can sort the categories in the account manager tree
      // (or the folder pane)
      //
      // otherwise, return NS_RDF_NO_VALUE
      // so that the folder data source will take care of it.
      if (sourceValue && (strncmp(sourceValue, NC_RDF_PAGETITLE_PREFIX, strlen(NC_RDF_PAGETITLE_PREFIX)) == 0)) {
        if (source == kNC_PageTitleSMTP)
          str.AssignLiteral("900000000");
        else if (source == kNC_PageTitleServer)
          str.AssignLiteral("1");
        else if (source == kNC_PageTitleCopies)
          str.AssignLiteral("2");
        else if (source == kNC_PageTitleAddressing)
          str.AssignLiteral("3");
        else if (source == kNC_PageTitleSynchronization)
          str.AssignLiteral("4");
        else if (source == kNC_PageTitleDiskSpace)
          str.AssignLiteral("4");
        else if (source == kNC_PageTitleJunk)
          str.AssignLiteral("5");
        else {
          // allow for the accountmanager to be dynamically extended
          // all the other pages come after the standard ones
          // server, copies, addressing, disk space (or offline & disk space)
          CopyASCIItoUTF16(nsDependentCString(sourceValue), str);
        }
      }
      else {
        return NS_RDF_NO_VALUE;
      }
    }
  }

  // GetTargets() stuff - need to return a valid answer so that
  // twisties will appear
  else if (property == kNC_Settings) {
    nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(source,&rv);
    if (NS_FAILED(rv))
      return NS_RDF_NO_VALUE;

    bool isServer=false;
    folder->GetIsServer(&isServer);
    // no need to localize this!
    if (isServer)
      str.AssignLiteral("ServerSettings");
  }

  else if (property == kNC_IsDefaultServer) {
    nsCOMPtr<nsIMsgIncomingServer> thisServer;
    rv = getServerForFolderNode(source, getter_AddRefs(thisServer));
    if (NS_FAILED(rv) || !thisServer) return NS_RDF_NO_VALUE;

    if (isDefaultServer(thisServer))
      str.AssignLiteral("true");
  }

  else if (property == kNC_SupportsFilters) {
    nsCOMPtr<nsIMsgIncomingServer> server;
    rv = getServerForFolderNode(source, getter_AddRefs(server));
    if (NS_FAILED(rv) || !server) return NS_RDF_NO_VALUE;

    if (supportsFilters(server))
      str.AssignLiteral("true");
  }
  else if (property == kNC_CanGetMessages) {
    nsCOMPtr<nsIMsgIncomingServer> server;
    rv = getServerForFolderNode(source, getter_AddRefs(server));
    if (NS_FAILED(rv) || !server) return NS_RDF_NO_VALUE;

    if (canGetMessages(server))
      str.AssignLiteral("true");
  }
  else if (property == kNC_CanGetIncomingMessages) {
    nsCOMPtr<nsIMsgIncomingServer> server;
    rv = getServerForFolderNode(source, getter_AddRefs(server));
    if (NS_FAILED(rv) || !server) return NS_RDF_NO_VALUE;

    if (canGetIncomingMessages(server))
      str.AssignLiteral("true");
  }

  if (!str.IsEmpty())
    rv = createNode(str.get(), target, getRDFService());

  //if we have an empty string and we don't have an error value, then
  //we don't have a value for RDF.
  else if(NS_SUCCEEDED(rv))
    rv = NS_RDF_NO_VALUE;

  return rv;
}



/* nsISimpleEnumerator GetTargets (in nsIRDFResource aSource, in nsIRDFResource property, in boolean aTruthValue); */
NS_IMETHODIMP
nsMsgAccountManagerDataSource::GetTargets(nsIRDFResource *source,
                                          nsIRDFResource *property,
                                          bool aTruthValue,
                                          nsISimpleEnumerator **_retval)
{
  nsresult rv = NS_RDF_NO_VALUE;

  // create array and enumerator
  // even if we're not handling this we need to return something empty?
  nsCOMArray<nsIRDFResource> nodes;
  if (NS_FAILED(rv)) return rv;
  if (source == kNC_AccountRoot)
    rv = createRootResources(property, &nodes);
  else if (property == kNC_Settings)
    rv = createSettingsResources(source, &nodes);

  if (NS_FAILED(rv))
    return NS_RDF_NO_VALUE;
  return NS_NewArrayEnumerator(_retval, nodes);
}

// end of all arcs coming out of msgaccounts:/
nsresult
nsMsgAccountManagerDataSource::createRootResources(nsIRDFResource *property,
                                                   nsCOMArray<nsIRDFResource> *aNodeArray)
{
  nsresult rv = NS_OK;
  if (isContainment(property)) {

    nsCOMPtr<nsIMsgAccountManager> am = do_QueryReferent(mAccountManager);
    if (!am) return NS_ERROR_FAILURE;

    nsCOMPtr<nsIArray> servers;
    rv = am->GetAllServers(getter_AddRefs(servers));
    NS_ENSURE_SUCCESS(rv, rv);

    uint32_t length;
    rv = servers->GetLength(&length);
    NS_ENSURE_SUCCESS(rv, rv);

    for (uint32_t i = 0; i < length; ++i)
    {
      nsCOMPtr<nsIMsgIncomingServer> server(do_QueryElementAt(servers, i, &rv));
      if (NS_FAILED(rv))
        continue;

      nsCOMPtr<nsIMsgFolder> serverFolder;
      rv = server->GetRootFolder(getter_AddRefs(serverFolder));
      if (NS_FAILED(rv))
        continue;

      // add the resource to the array
      nsCOMPtr<nsIRDFResource> serverResource = do_QueryInterface(serverFolder);
      if (serverResource)
        (void) aNodeArray->AppendObject(serverResource);
    }

#ifdef DEBUG_amds
    uint32_t nodecount;
    aNodeArray->GetLength(&nodecount);
    printf("GetTargets(): added %d servers on %s\n", nodecount,
           (const char*)property_arc);
#endif
    // For the "settings" arc, we also want to add SMTP setting.
    if (property == kNC_Settings) {
      aNodeArray->AppendObject(kNC_PageTitleSMTP);
    }
  }

#ifdef DEBUG_amds
  else {
    printf("unknown arc %s on msgaccounts:/\n", (const char*)property_arc);
  }
#endif

  return rv;
}

nsresult
nsMsgAccountManagerDataSource::appendGenericSettingsResources(nsIMsgIncomingServer *server, nsCOMArray<nsIRDFResource> *aNodeArray)
{
  nsresult rv;

  nsCOMPtr<nsICategoryManager> catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv,rv);

  nsCOMPtr<nsISimpleEnumerator> e;
  rv = catman->EnumerateCategory(MAILNEWS_ACCOUNTMANAGER_EXTENSIONS, getter_AddRefs(e));
  if(NS_SUCCEEDED(rv) && e) {
    while (true) {
      nsCOMPtr<nsISupports> supports;
      rv = e->GetNext(getter_AddRefs(supports));
      nsCOMPtr<nsISupportsCString> catEntry = do_QueryInterface(supports);
      if (NS_FAILED(rv) || !catEntry)
        break;

      nsAutoCString entryString;
      rv = catEntry->GetData(entryString);
      if (NS_FAILED(rv))
        break;

      nsCString contractidString;
      rv = catman->GetCategoryEntry(MAILNEWS_ACCOUNTMANAGER_EXTENSIONS, entryString.get(), getter_Copies(contractidString));
      if (NS_FAILED(rv))
        break;

      nsCOMPtr <nsIMsgAccountManagerExtension> extension =
        do_GetService(contractidString.get(), &rv);
      if (NS_FAILED(rv) || !extension)
        break;

      bool showPanel;
      rv = extension->ShowPanel(server, &showPanel);
      if (NS_FAILED(rv))
        break;

      if (showPanel) {
        nsCString name;
        rv = extension->GetName(name);
        if (NS_FAILED(rv))
          break;

        rv = appendGenericSetting(name.get(), aNodeArray);
        if (NS_FAILED(rv))
          break;
      }
    }
  }
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::appendGenericSetting(const char *name,
                                                    nsCOMArray<nsIRDFResource> *aNodeArray)
{
  NS_ENSURE_ARG_POINTER(name);
  NS_ENSURE_ARG_POINTER(aNodeArray);

  nsCOMPtr <nsIRDFResource> resource;

  nsAutoCString resourceStr;
  resourceStr = NC_RDF_PAGETITLE_PREFIX;
  resourceStr += name;

  nsresult rv = getRDFService()->GetResource(resourceStr, getter_AddRefs(resource));
  NS_ENSURE_SUCCESS(rv,rv);

  // AppendElement will addref.
  aNodeArray->AppendObject(resource);
  return NS_OK;
}

// end of all #Settings arcs
nsresult
nsMsgAccountManagerDataSource::createSettingsResources(nsIRDFResource *aSource,
                                                       nsCOMArray<nsIRDFResource> *aNodeArray)
{
  // If this isn't a server, just return.
  if (aSource == kNC_PageTitleSMTP)
    return NS_OK;

  nsCOMPtr<nsIMsgIncomingServer> server;
  getServerForFolderNode(aSource, getter_AddRefs(server));
  if (server) {
    bool hasIdentities;
    nsresult rv = serverHasIdentities(server, &hasIdentities);

    if (hasIdentities) {
      aNodeArray->AppendObject(kNC_PageTitleServer);
      aNodeArray->AppendObject(kNC_PageTitleCopies);
      aNodeArray->AppendObject(kNC_PageTitleAddressing);
    }

    // Junk settings apply for all server types except for news and rss.
    nsCString serverType;
    server->GetType(serverType);
    if (!MsgLowerCaseEqualsLiteral(serverType, "nntp") &&
        !MsgLowerCaseEqualsLiteral(serverType, "rss"))
      aNodeArray->AppendObject(kNC_PageTitleJunk);

    // Check the offline capability before adding
    // offline item
    int32_t offlineSupportLevel = 0;
    rv = server->GetOfflineSupportLevel(&offlineSupportLevel);
    NS_ENSURE_SUCCESS(rv,rv);

    bool supportsDiskSpace;
    rv = server->GetSupportsDiskSpace(&supportsDiskSpace);
    NS_ENSURE_SUCCESS(rv,rv);

    // currently there is no offline without diskspace
    if (offlineSupportLevel >= OFFLINE_SUPPORT_LEVEL_REGULAR) 
      aNodeArray->AppendObject(kNC_PageTitleSynchronization);
    else if (supportsDiskSpace)
      aNodeArray->AppendObject(kNC_PageTitleDiskSpace);

    if (hasIdentities) {
      // extensions come after the default panels
      rv = appendGenericSettingsResources(server, aNodeArray);
      NS_ASSERTION(NS_SUCCEEDED(rv), "failed to add generic panels");
    }
  }

  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::serverHasIdentities(nsIMsgIncomingServer* aServer,
                                                   bool *aResult)
{
  nsresult rv;
  *aResult = false;

  nsCOMPtr<nsIMsgAccountManager> am = do_QueryReferent(mAccountManager, &rv);

  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIArray> identities;
  rv = am->GetIdentitiesForServer(aServer, getter_AddRefs(identities));

  // no identities just means no arcs
  if (NS_FAILED(rv)) return NS_OK;

  uint32_t count;
  rv = identities->GetLength(&count);
  if (NS_FAILED(rv)) return NS_OK;

  if (count >0)
    *aResult = true;
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::getAccountArcs(nsIMutableArray **aResult)
{
  nsresult rv;
  if (!mAccountArcsOut) {
    mAccountArcsOut = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    mAccountArcsOut->AppendElement(kNC_Settings, false);
    mAccountArcsOut->AppendElement(kNC_Name, false);
    mAccountArcsOut->AppendElement(kNC_FolderTreeName, false);
    mAccountArcsOut->AppendElement(kNC_FolderTreeSimpleName, false);
    mAccountArcsOut->AppendElement(kNC_NameSort, false);
    mAccountArcsOut->AppendElement(kNC_FolderTreeNameSort, false);
    mAccountArcsOut->AppendElement(kNC_PageTag, false);
  }

  *aResult = mAccountArcsOut;
  NS_IF_ADDREF(*aResult);
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::getAccountRootArcs(nsIMutableArray **aResult)
{
  nsresult rv;
  if (!mAccountRootArcsOut) {
    mAccountRootArcsOut = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    mAccountRootArcsOut->AppendElement(kNC_Server, false);
    mAccountRootArcsOut->AppendElement(kNC_Child, false);

    mAccountRootArcsOut->AppendElement(kNC_Settings, false);
    mAccountRootArcsOut->AppendElement(kNC_Name, false);
    mAccountRootArcsOut->AppendElement(kNC_FolderTreeName, false);
    mAccountRootArcsOut->AppendElement(kNC_FolderTreeSimpleName, false);
    mAccountRootArcsOut->AppendElement(kNC_NameSort, false);
    mAccountRootArcsOut->AppendElement(kNC_FolderTreeNameSort, false);
    mAccountRootArcsOut->AppendElement(kNC_PageTag, false);
  }

  *aResult = mAccountRootArcsOut;
  NS_IF_ADDREF(*aResult);
  return NS_OK;
}

NS_IMETHODIMP
nsMsgAccountManagerDataSource::HasArcOut(nsIRDFResource *source, nsIRDFResource *aArc, bool *result)
{
  if (aArc == kNC_Settings) {
    // based on createSettingsResources()
    // we only have settings for local folders and servers with identities
    nsCOMPtr<nsIMsgIncomingServer> server;
    nsresult rv = getServerForFolderNode(source, getter_AddRefs(server));
    if (NS_SUCCEEDED(rv) && server) {
      // Check the offline capability before adding arc
      int32_t offlineSupportLevel = 0;
      (void) server->GetOfflineSupportLevel(&offlineSupportLevel);
      if (offlineSupportLevel >= OFFLINE_SUPPORT_LEVEL_REGULAR) {
        *result = true;
        return NS_OK;
      }

      bool supportsDiskSpace;
      (void) server->GetSupportsDiskSpace(&supportsDiskSpace);
      if (supportsDiskSpace) {
        *result = true;
        return NS_OK;
      }
      return serverHasIdentities(server, result);
    }
  }

  *result = false;
  return NS_OK;
}

/* nsISimpleEnumerator ArcLabelsOut (in nsIRDFResource aSource); */
NS_IMETHODIMP
nsMsgAccountManagerDataSource::ArcLabelsOut(nsIRDFResource *source,
                                            nsISimpleEnumerator **_retval)
{
  nsresult rv;

  // we have to return something, so always create the array/enumerators
  nsCOMPtr<nsIMutableArray> arcs;
  if (source == kNC_AccountRoot)
    rv = getAccountRootArcs(getter_AddRefs(arcs));
  else
    rv = getAccountArcs(getter_AddRefs(arcs));
  NS_ENSURE_SUCCESS(rv, rv);

  rv = NS_NewArrayEnumerator(_retval, arcs);
  if (NS_FAILED(rv)) return rv;

#ifdef DEBUG_amds_
  printf("GetArcLabelsOut(%s): Adding child, settings, and name arclabels\n", value);
#endif

  return NS_OK;
}

NS_IMETHODIMP
nsMsgAccountManagerDataSource::HasAssertion(nsIRDFResource *aSource,
                                            nsIRDFResource *aProperty,
                                            nsIRDFNode *aTarget,
                                            bool aTruthValue,
                                            bool *_retval)
{
  nsresult rv=NS_ERROR_FAILURE;

  //
  // msgaccounts:/ properties
  //
  if (aSource == kNC_AccountRoot) {
    rv = HasAssertionAccountRoot(aProperty, aTarget, aTruthValue, _retval);
  }
  //
  // server properties
  //   try to convert the resource to a folder, and then only
  //   answer if it's a server.. any failure falls through to the default case
  //
  // short-circuit on property, so objects like filters, etc, don't get queried
  else if (aProperty == kNC_IsDefaultServer || aProperty == kNC_CanGetMessages ||
           aProperty == kNC_CanGetIncomingMessages || aProperty == kNC_SupportsFilters) {
    nsCOMPtr<nsIMsgIncomingServer> server;
    rv = getServerForFolderNode(aSource, getter_AddRefs(server));
    if (NS_SUCCEEDED(rv) && server)
      rv = HasAssertionServer(server, aProperty, aTarget,
                              aTruthValue, _retval);
  }

  // any failures above fallthrough to the parent class
  if (NS_FAILED(rv))
    return nsMsgRDFDataSource::HasAssertion(aSource, aProperty,
                                            aTarget, aTruthValue, _retval);
  return NS_OK;
}



nsresult
nsMsgAccountManagerDataSource::HasAssertionServer(nsIMsgIncomingServer *aServer,
                                                  nsIRDFResource *aProperty,
                                                  nsIRDFNode *aTarget,
                                                  bool aTruthValue,
                                                  bool *_retval)
{
  if (aProperty == kNC_IsDefaultServer)
    *_retval = (aTarget == kTrueLiteral) ? isDefaultServer(aServer) : !isDefaultServer(aServer);
  else if (aProperty == kNC_SupportsFilters)
    *_retval = (aTarget == kTrueLiteral) ? supportsFilters(aServer) : !supportsFilters(aServer);
  else if (aProperty == kNC_CanGetMessages)
   *_retval = (aTarget == kTrueLiteral) ? canGetMessages(aServer) : !canGetMessages(aServer);
  else if (aProperty == kNC_CanGetIncomingMessages)
    *_retval = (aTarget == kTrueLiteral) ? canGetIncomingMessages(aServer) : !canGetIncomingMessages(aServer);
  else
    *_retval = false;
  return NS_OK;
}

bool
nsMsgAccountManagerDataSource::isDefaultServer(nsIMsgIncomingServer *aServer)
{
  nsresult rv;
  if (!aServer) return false;

  nsCOMPtr<nsIMsgAccountManager> am = do_QueryReferent(mAccountManager, &rv);
  NS_ENSURE_SUCCESS(rv, false);

  nsCOMPtr<nsIMsgAccount> defaultAccount;
  rv = am->GetDefaultAccount(getter_AddRefs(defaultAccount));
  NS_ENSURE_SUCCESS(rv, false);
  if (!defaultAccount) return false;

  // in some weird case that there is no default and they asked
  // for the default
  nsCOMPtr<nsIMsgIncomingServer> defaultServer;
  rv = defaultAccount->GetIncomingServer(getter_AddRefs(defaultServer));
  NS_ENSURE_SUCCESS(rv, false);
  if (!defaultServer) return false;

  bool isEqual;
  rv = defaultServer->Equals(aServer, &isEqual);
  NS_ENSURE_SUCCESS(rv, false);

  return isEqual;
}


bool
nsMsgAccountManagerDataSource::supportsFilters(nsIMsgIncomingServer *aServer)
{
  bool supportsFilters;
  nsresult rv = aServer->GetCanHaveFilters(&supportsFilters);
  NS_ENSURE_SUCCESS(rv, false);

  return supportsFilters;
}

bool
nsMsgAccountManagerDataSource::canGetMessages(nsIMsgIncomingServer *aServer)
{
  nsCOMPtr<nsIMsgProtocolInfo> protocolInfo;
  nsresult rv = aServer->GetProtocolInfo(getter_AddRefs(protocolInfo));
  NS_ENSURE_SUCCESS(rv, false);

  bool canGetMessages;
  rv = protocolInfo->GetCanGetMessages(&canGetMessages);
  NS_ENSURE_SUCCESS(rv, false);

  return canGetMessages;
}

bool
nsMsgAccountManagerDataSource::canGetIncomingMessages(nsIMsgIncomingServer *aServer)
{
  nsCOMPtr<nsIMsgProtocolInfo> protocolInfo;
  nsresult rv = aServer->GetProtocolInfo(getter_AddRefs(protocolInfo));
  NS_ENSURE_SUCCESS(rv, false);

  bool canGetIncomingMessages;
  rv = protocolInfo->GetCanGetIncomingMessages(&canGetIncomingMessages);
  NS_ENSURE_SUCCESS(rv, false);

  return canGetIncomingMessages;
}

nsresult
nsMsgAccountManagerDataSource::HasAssertionAccountRoot(nsIRDFResource *aProperty,
                                                       nsIRDFNode *aTarget,
                                                       bool aTruthValue,
                                                       bool *_retval)
{
  // set up default
  *_retval = false;

  // for child and settings arcs, just make sure it's a valid server:
  if (isContainment(aProperty)) {

    nsCOMPtr<nsIMsgIncomingServer> server;
    nsresult rv = getServerForFolderNode(aTarget, getter_AddRefs(server));
    if (NS_FAILED(rv) || !server) return rv;

    nsCString serverKey;
    server->GetKey(serverKey);

    nsCOMPtr<nsIMsgAccountManager> am = do_QueryReferent(mAccountManager, &rv);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIArray> serverArray;
    rv = am->GetAllServers(getter_AddRefs(serverArray));
    NS_ENSURE_SUCCESS(rv, rv);

    uint32_t length;
    rv = serverArray->GetLength(&length);
    NS_ENSURE_SUCCESS(rv, rv);

    for (uint32_t i = 0; i < length; ++i)
    {
      nsCOMPtr<nsIMsgIncomingServer> server(do_QueryElementAt(serverArray, i, &rv));
      if (NS_FAILED(rv))
        continue;

      nsCString key;
      server->GetKey(key);
      if (key.Equals(serverKey))
      {
        *_retval = true;
        break;
      }
    }
  }

  return NS_OK;
}

bool
nsMsgAccountManagerDataSource::isContainment(nsIRDFResource *aProperty)
{

  if (aProperty == kNC_Child ||
      aProperty == kNC_Settings)
    return true;
  return false;
}

// returns failure if the object is not a root server
nsresult
nsMsgAccountManagerDataSource::getServerForFolderNode(nsIRDFNode *aResource,
                                                      nsIMsgIncomingServer **aResult)
{
  nsresult rv;
  nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(aResource, &rv);
  if (NS_SUCCEEDED(rv)) 
  {
    bool isServer;
    rv = folder->GetIsServer(&isServer);
    if (NS_SUCCEEDED(rv) && isServer) 
      return folder->GetServer(aResult);
  }
  return NS_ERROR_FAILURE;
}

nsresult
nsMsgAccountManagerDataSource::getStringBundle()
{
  if (mStringBundle) return NS_OK;

  nsCOMPtr<nsIStringBundleService> strBundleService =
    mozilla::services::GetStringBundleService();
  NS_ENSURE_TRUE(strBundleService, NS_ERROR_UNEXPECTED);

  return strBundleService->CreateBundle("chrome://messenger/locale/prefs.properties",
                                      getter_AddRefs(mStringBundle));
}

NS_IMETHODIMP
nsMsgAccountManagerDataSource::OnServerLoaded(nsIMsgIncomingServer* aServer)
{
  nsCOMPtr<nsIMsgFolder> serverFolder;
  nsresult rv = aServer->GetRootFolder(getter_AddRefs(serverFolder));
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIRDFResource> serverResource = do_QueryInterface(serverFolder,&rv);
  if (NS_FAILED(rv)) return rv;

  NotifyObservers(kNC_AccountRoot, kNC_Child, serverResource, nullptr, true, false);
  NotifyObservers(kNC_AccountRoot, kNC_Settings, serverResource, nullptr, true, false);

  return NS_OK;
}

NS_IMETHODIMP
nsMsgAccountManagerDataSource::OnServerUnloaded(nsIMsgIncomingServer* aServer)
{
  nsCOMPtr<nsIMsgFolder> serverFolder;
  nsresult rv = aServer->GetRootFolder(getter_AddRefs(serverFolder));
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIRDFResource> serverResource = do_QueryInterface(serverFolder,&rv);
  if (NS_FAILED(rv)) return rv;


  NotifyObservers(kNC_AccountRoot, kNC_Child, serverResource, nullptr, false, false);
  NotifyObservers(kNC_AccountRoot, kNC_Settings, serverResource, nullptr, false, false);

  return NS_OK;
}

NS_IMETHODIMP
nsMsgAccountManagerDataSource::OnServerChanged(nsIMsgIncomingServer *server)
{
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::OnItemPropertyChanged(nsIMsgFolder *, nsIAtom *, char const *, char const *)
{
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::OnItemUnicharPropertyChanged(nsIMsgFolder *, nsIAtom *, const char16_t *, const char16_t *)
{
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::OnItemRemoved(nsIMsgFolder *, nsISupports *)
{
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::OnItemPropertyFlagChanged(nsIMsgDBHdr *, nsIAtom *, uint32_t, uint32_t)
{
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::OnItemAdded(nsIMsgFolder *, nsISupports *)
{
  return NS_OK;
}


nsresult
nsMsgAccountManagerDataSource::OnItemBoolPropertyChanged(nsIMsgFolder *aItem,
                                                         nsIAtom *aProperty,
                                                         bool aOldValue,
                                                         bool aNewValue)
{
  if (aProperty == kDefaultServerAtom) {
    nsCOMPtr<nsIRDFResource> resource(do_QueryInterface(aItem));
    NotifyObservers(resource, kNC_IsDefaultServer, kTrueLiteral, nullptr, aNewValue, false);
  }
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::OnItemEvent(nsIMsgFolder *, nsIAtom *)
{
  return NS_OK;
}

nsresult
nsMsgAccountManagerDataSource::OnItemIntPropertyChanged(nsIMsgFolder *, nsIAtom *, int32_t, int32_t)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMsgAccountManagerDataSource::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
{
  nsMsgRDFDataSource::Observe(aSubject, aTopic, aData);

  return NS_OK;
}
