//
// X509CertificateStore.cs: Handles certificate stores.
//
// Author:
//	Sebastien Pouliot (spouliot@motus.com)
//
// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
//

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace Microsoft.Web.Services.Security.X509 {

	public class X509CertificateStore {

		[Serializable]
		public enum StoreLocation {
			CurrentService = 262144,
			CurrentUser = 65536,
			CurrentUserGroupPolicy = 458752,
			LocalMachine = 131072,
			LocalMachineEnterprise = 589824,
			LocalMachineGroupPolicy = 524288,
			Services = 327680,
			Unknown = 0,
			Users = 393216
		}

		[Flags]
		[Serializable]
		public enum StoreOpenFlags {
			CreateNew = 8192,
			DeferClose = 4,
			Delete = 16,
			None = 0,
			OpenExisting = 16384,
			ReadOnly = 32768
		}

		[Serializable]
		public enum StoreProvider {
			Collection = 11,
			File = 3,
			Memory = 1,
			System = 10
		} 

		public const string CAStore = "CA";
		public const string MyStore = "My";
		public const string RootStore = "Root";
		public const string TrustStore = "Trust";
		public const string UnTrustedStore = "Disallowed";

		private const string storeAlreadyOpened = "store already opened";
		private const string storeNotOpened = "store not opened";

		private StoreOpenFlags storeOpenFlags;
		private StoreProvider storeProvider;
		private StoreLocation storeLocation;
		private string storeName;
		private ICertificateStore store;

		public X509CertificateStore (StoreProvider provider, StoreLocation location, string storeName)
		{
			storeProvider = provider;
			storeLocation = location;
			this.storeName = storeName;
		}

		~X509CertificateStore () 
		{
			if (store != null) {
				store.Close ();
				store = null;
			}
		}

		public X509CertificateCollection Certificates {
			get { 
				if (store == null)
					return null;
				return store.GetCollection (); 
			}
		}

		public IntPtr Handle {
			get { 
				if (store == null)
					return (IntPtr) 0;
				return store.Handle; 
			}
		}

		public StoreLocation Location {
			get { return storeLocation; }
		}

		public bool Open () 
		{
			return InternalOpen (StoreOpenFlags.None);
		}

		public bool OpenRead () 
		{
			return InternalOpen (StoreOpenFlags.ReadOnly);
		}

		internal bool InternalOpen (StoreOpenFlags flags) 
		{
			if (store != null)
				throw new InvalidOperationException (storeAlreadyOpened);

			storeOpenFlags = flags;
			switch (storeProvider) {
				case StoreProvider.Collection:
					store = null;
					break;
				case StoreProvider.File:
					store = null;
					break;
				case StoreProvider.Memory:
					store = new MemoryCertificateStore (storeLocation, storeName, flags);
					break;
				case StoreProvider.System:
					store = null;
					break;
				default:
					throw new NotSupportedException ("Unknown store provider");
			}
			return (store != null);
		}

		public void Close ()
		{
			store.Close ();
			store = null;
			storeOpenFlags = StoreOpenFlags.None;
		}

		internal bool Compare (byte[] array1, byte[] array2) 
		{
			if ((array1 == null) && (array2 == null))
				return true;
			if ((array1 == null) || (array2 == null))
				return false;
			if (array1.Length != array2.Length)
				return false;
			for (int i=0; i < array1.Length; i++) {
				if (array1 [i] != array2 [i])
					return false;
			}
			return true;
		}

		public X509CertificateCollection FindCertificateByHash (byte[] certHash)
		{
			if (certHash == null)
				throw new ArgumentNullException ("certHash");
			if (store != null)
				throw new InvalidOperationException (storeNotOpened);
 
			X509CertificateCollection results = new X509CertificateCollection ();
			if (store != null) {
				X509CertificateCollection certs = store.GetCollection ();
				// apply filter
				foreach (X509Certificate c in certs) {
					if (Compare (c.GetCertHash (), certHash))
						results.Add (c);
				}
			}
			return results;
		}

		public X509CertificateCollection FindCertificateByKeyIdentifier (byte[] keyIdentifier)
		{
			if (keyIdentifier == null)
				throw new ArgumentNullException ("keyIdentifier");
			if (store != null)
				throw new InvalidOperationException (storeNotOpened);
 
			X509CertificateCollection results = new X509CertificateCollection ();
			if (store != null) {
				X509CertificateCollection certs = store.GetCollection ();
				// apply filter
				foreach (X509Certificate c in certs) {
					if (Compare (c.GetKeyIdentifier (), keyIdentifier))
						results.Add (c);
				}
			}
			return results;
		}

		public X509CertificateCollection FindCertificateBySubjectName (string subjectstring)
		{
			if (subjectstring == null)
				throw new ArgumentNullException ("subjectstring");
			if (store != null)
				throw new InvalidOperationException (storeNotOpened);

			X509CertificateCollection results = new X509CertificateCollection ();
			if (store != null) {
				X509CertificateCollection certs = store.GetCollection ();
				// apply filter
				foreach (X509Certificate c in certs) {
					if (c.GetName() != subjectstring)
						results.Add (c);
				}
			}
			return results;
		}

		public X509CertificateCollection FindCertificateBySubjectString (string subjectsubstring)
		{
			if (subjectsubstring == null)
				throw new ArgumentNullException ("subjectsubstring");
			if (store != null)
				throw new InvalidOperationException (storeNotOpened);

			X509CertificateCollection results = new X509CertificateCollection ();
			if (store != null) {
				X509CertificateCollection certs = store.GetCollection ();
				// apply filter
				foreach (X509Certificate c in certs) {
					if (c.GetName ().IndexOf (subjectsubstring) > 0)
						results.Add (c);
				}
			}
			return results;
		}

		public static X509CertificateStore CurrentUserStore (string storeName) 
		{
			return new X509CertificateStore (StoreProvider.System, StoreLocation.CurrentUser, storeName);
		}

		public static X509CertificateStore LocalMachineStore (string storeName) 
		{
			return new X509CertificateStore (StoreProvider.System, StoreLocation.LocalMachine, storeName);
		}
	}
}
