File: System\DirectoryServices\ActiveDirectory\DirectoryServerCollection.cs
Web Access
Project: src\src\runtime\src\libraries\System.DirectoryServices\src\System.DirectoryServices.csproj (System.DirectoryServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace System.DirectoryServices.ActiveDirectory
{
    public class DirectoryServerCollection : CollectionBase
    {
        internal readonly string? siteDN;
        internal readonly string? transportDN;
        internal readonly DirectoryContext context;
        internal bool initialized;
        internal readonly Hashtable? changeList;
        private readonly List<object> _copyList = new();
        private readonly DirectoryEntry? _crossRefEntry;
        private readonly bool _isADAM;
        private readonly bool _isForNC;

        internal DirectoryServerCollection(DirectoryContext context, string siteDN, string transportName)
        {
            Hashtable tempTable = new Hashtable();

            changeList = Hashtable.Synchronized(tempTable);
            this.context = context;
            this.siteDN = siteDN;
            this.transportDN = transportName;
        }

        internal DirectoryServerCollection(DirectoryContext context, DirectoryEntry? crossRefEntry, bool isADAM, ReadOnlyDirectoryServerCollection servers)
        {
            this.context = context;
            _crossRefEntry = crossRefEntry;
            _isADAM = isADAM;

            _isForNC = true;
            foreach (DirectoryServer server in servers)
            {
                InnerList.Add(server);
            }
        }

        public DirectoryServer this[int index]
        {
            get => (DirectoryServer)InnerList[index]!;
            set
            {
                DirectoryServer server = (DirectoryServer)value;

                if (server == null)
                    throw new ArgumentNullException(nameof(value));

                if (!Contains(server))
                    List[index] = server;
                else
                    throw new ArgumentException(SR.Format(SR.AlreadyExistingInCollection, server), nameof(value));
            }
        }

        public int Add(DirectoryServer server)
        {
            if (server == null)
                throw new ArgumentNullException(nameof(server));

            // make sure that it is within the current site
            if (_isForNC)
            {
                if ((!_isADAM))
                {
                    if (!(server is DomainController))
                        throw new ArgumentException(SR.ServerShouldBeDC, nameof(server));

                    // verify that the version >= 5.2
                    // DC should be Win 2003 or higher
                    if (((DomainController)server).NumericOSVersion < 5.2)
                    {
                        throw new ArgumentException(SR.ServerShouldBeW2K3, nameof(server));
                    }
                }

                if (!Contains(server))
                {
                    return List.Add(server);
                }
                else
                {
                    throw new ArgumentException(SR.Format(SR.AlreadyExistingInCollection, server), nameof(server));
                }
            }
            else
            {
                string siteName = (server is DomainController) ? ((DomainController)server).SiteObjectName : ((AdamInstance)server).SiteObjectName;
                Debug.Assert(siteName != null);
                if (Utils.Compare(siteDN, siteName) != 0)
                {
                    throw new ArgumentException(SR.NotWithinSite);
                }

                if (!Contains(server))
                    return List.Add(server);
                else
                    throw new ArgumentException(SR.Format(SR.AlreadyExistingInCollection, server), nameof(server));
            }
        }

        public void AddRange(DirectoryServer[] servers)
        {
            if (servers == null)
                throw new ArgumentNullException(nameof(servers));

            foreach (DirectoryServer s in servers)
            {
                if (s == null)
                {
                    throw new ArgumentException(null, nameof(servers));
                }
            }

            for (int i = 0; ((i) < (servers.Length)); i = ((i) + (1)))
                this.Add(servers[i]);
        }

        public bool Contains(DirectoryServer server)
        {
            if (server == null)
                throw new ArgumentNullException(nameof(server));

            for (int i = 0; i < InnerList.Count; i++)
            {
                DirectoryServer tmp = (DirectoryServer)InnerList[i]!;

                if (Utils.Compare(tmp.Name, server.Name) == 0)
                {
                    return true;
                }
            }
            return false;
        }

        public void CopyTo(DirectoryServer[] array, int index)
        {
            List.CopyTo(array, index);
        }

        public int IndexOf(DirectoryServer server)
        {
            if (server == null)
                throw new ArgumentNullException(nameof(server));

            for (int i = 0; i < InnerList.Count; i++)
            {
                DirectoryServer tmp = (DirectoryServer)InnerList[i]!;

                if (Utils.Compare(tmp.Name, server.Name) == 0)
                {
                    return i;
                }
            }
            return -1;
        }

        public void Insert(int index, DirectoryServer server)
        {
            if (server == null)
                throw new ArgumentNullException(nameof(server));

            if (_isForNC)
            {
                if ((!_isADAM))
                {
                    if (!(server is DomainController))
                        throw new ArgumentException(SR.ServerShouldBeDC, nameof(server));

                    // verify that the version >= 5.2
                    // DC should be Win 2003 or higher
                    if (((DomainController)server).NumericOSVersion < 5.2)
                    {
                        throw new ArgumentException(SR.ServerShouldBeW2K3, nameof(server));
                    }
                }

                if (!Contains(server))
                {
                    List.Insert(index, server);
                }
                else
                {
                    throw new ArgumentException(SR.Format(SR.AlreadyExistingInCollection, server), nameof(server));
                }
            }
            else
            {
                // make sure that it is within the current site
                string siteName = (server is DomainController) ? ((DomainController)server).SiteObjectName : ((AdamInstance)server).SiteObjectName;
                Debug.Assert(siteName != null);
                if (Utils.Compare(siteDN, siteName) != 0)
                {
                    throw new ArgumentException(SR.NotWithinSite, nameof(server));
                }

                if (!Contains(server))
                    List.Insert(index, server);
                else
                    throw new ArgumentException(SR.Format(SR.AlreadyExistingInCollection, server));
            }
        }

        public void Remove(DirectoryServer server)
        {
            if (server == null)
                throw new ArgumentNullException(nameof(server));

            for (int i = 0; i < InnerList.Count; i++)
            {
                DirectoryServer tmp = (DirectoryServer)InnerList[i]!;

                if (Utils.Compare(tmp.Name, server.Name) == 0)
                {
                    List.Remove(tmp);
                    return;
                }
            }

            // something that does not exist in the collection
            throw new ArgumentException(SR.Format(SR.NotFoundInCollection, server), nameof(server));
        }

        protected override void OnClear()
        {
            if (initialized && !_isForNC)
            {
                _copyList.Clear();
                foreach (object o in List)
                {
                    _copyList.Add(o);
                }
            }
        }

        protected override void OnClearComplete()
        {
            // if the property exists, clear it out
            if (_isForNC)
            {
                if (_crossRefEntry != null)
                {
                    try
                    {
                        if (_crossRefEntry.Properties.Contains(PropertyManager.MsDSNCReplicaLocations))
                        {
                            _crossRefEntry.Properties[PropertyManager.MsDSNCReplicaLocations].Clear();
                        }
                    }
                    catch (COMException e)
                    {
                        throw ExceptionHelper.GetExceptionFromCOMException(context, e);
                    }
                }
            }
            else if (initialized)
            {
                for (int i = 0; i < _copyList.Count; i++)
                {
                    OnRemoveComplete(i, _copyList[i]);
                }
            }
        }

#pragma warning disable CS8765 // Nullability doesn't match overridden member
        protected override void OnInsertComplete(int index, object value)
#pragma warning restore CS8765
        {
            if (_isForNC)
            {
                if (_crossRefEntry != null)
                {
                    try
                    {
                        DirectoryServer server = (DirectoryServer)value;
                        string ntdsaName = (server is DomainController) ? ((DomainController)server).NtdsaObjectName : ((AdamInstance)server).NtdsaObjectName;
                        _crossRefEntry.Properties[PropertyManager.MsDSNCReplicaLocations].Add(ntdsaName);
                    }
                    catch (COMException e)
                    {
                        throw ExceptionHelper.GetExceptionFromCOMException(context, e);
                    }
                }
            }
            else if (initialized)
            {
                DirectoryServer server = (DirectoryServer)value;
                string name = server.Name;
                string serverName = (server is DomainController) ? ((DomainController)server).ServerObjectName : ((AdamInstance)server).ServerObjectName;

                try
                {
                    if (changeList!.Contains(name))
                    {
                        ((DirectoryEntry)changeList[name]!).Properties["bridgeheadTransportList"].Value = this.transportDN;
                    }
                    else
                    {
                        DirectoryEntry de = DirectoryEntryManager.GetDirectoryEntry(context, serverName);

                        de.Properties["bridgeheadTransportList"].Value = this.transportDN;
                        changeList.Add(name, de);
                    }
                }
                catch (COMException e)
                {
                    throw ExceptionHelper.GetExceptionFromCOMException(context, e);
                }
            }
        }

#pragma warning disable CS8765 // Nullability doesn't match overridden member
        protected override void OnRemoveComplete(int index, object value)
#pragma warning restore CS8765
        {
            if (_isForNC)
            {
                try
                {
                    if (_crossRefEntry != null)
                    {
                        string ntdsaName = (value is DomainController) ? ((DomainController)value).NtdsaObjectName : ((AdamInstance)value).NtdsaObjectName;
                        _crossRefEntry.Properties[PropertyManager.MsDSNCReplicaLocations].Remove(ntdsaName);
                    }
                }
                catch (COMException e)
                {
                    throw ExceptionHelper.GetExceptionFromCOMException(context, e);
                }
            }
            else
            {
                DirectoryServer server = (DirectoryServer)value;
                string name = server.Name;
                string serverName = (server is DomainController) ? ((DomainController)server).ServerObjectName : ((AdamInstance)server).ServerObjectName;

                try
                {
                    if (changeList!.Contains(name))
                    {
                        ((DirectoryEntry)changeList[name]!).Properties["bridgeheadTransportList"].Clear();
                    }
                    else
                    {
                        DirectoryEntry de = DirectoryEntryManager.GetDirectoryEntry(context, serverName);

                        de.Properties["bridgeheadTransportList"].Clear();
                        changeList.Add(name, de);
                    }
                }
                catch (COMException e)
                {
                    throw ExceptionHelper.GetExceptionFromCOMException(context, e);
                }
            }
        }

#pragma warning disable CS8765 // Nullability doesn't match overridden member
        protected override void OnSetComplete(int index, object oldValue, object newValue)
#pragma warning restore CS8765
        {
            OnRemoveComplete(index, oldValue);
            OnInsertComplete(index, newValue);
        }

        protected override void OnValidate(object value)
        {
            if (value == null) throw new ArgumentNullException(nameof(value));

            if (_isForNC)
            {
                if (_isADAM)
                {
                    // for adam this should be an ADAMInstance
                    if (!(value is AdamInstance))
                        throw new ArgumentException(SR.ServerShouldBeAI, nameof(value));
                }
                else
                {
                    // for AD this should be a DomainController
                    if (!(value is DomainController))
                        throw new ArgumentException(SR.ServerShouldBeDC, nameof(value));
                }
            }
            else
            {
                if (!(value is DirectoryServer))
                    throw new ArgumentException(null, nameof(value));
            }
        }

        internal string[] GetMultiValuedProperty()
        {
            var values = new List<string>();
            for (int i = 0; i < InnerList.Count; i++)
            {
                DirectoryServer ds = (DirectoryServer)InnerList[i]!;

                string ntdsaName = (ds is DomainController) ? ((DomainController)ds).NtdsaObjectName : ((AdamInstance)ds).NtdsaObjectName;
                values.Add(ntdsaName);
            }
            return values.ToArray();
        }
    }
}